From 7d238020d0153fa26ea7fa84c8f73e54d100a3a3 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 4 Sep 2024 10:28:50 +0300 Subject: [PATCH 01/14] Add pubsub peer discvoery --- src/libp2p/Libp2p.Core/Dto/KeyPair.cs | 2 + .../Libp2p.Generators.Protobuf.csproj | 3 +- .../Libp2p.Generators.Protobuf.generated.sln | 25 ++ .../Libp2p.Generators.Protobuf/README.md | 23 ++ .../Libp2p.Protocols.Identify/Dto/Identify.cs | 1 + .../Libp2p.Protocols.Identify.csproj | 2 +- .../Libp2p.Protocols.Noise/Dto/Exchange.cs | 4 + .../Dto/Exchange.cs | 1 + src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs | 10 + .../Dto/TopicDescriptor.cs | 3 + .../{ITopic.cs => ITopicSubscription.cs} | 3 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 2 +- src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs | 24 +- .../Dto/Peer.cs | 256 ++++++++++++++++++ .../Dto/Peer.proto | 6 + .../Libp2p.Protocols.PubsubDiscovery.csproj | 41 +++ .../PubsubDiscoveryProtocol.cs | 67 +++++ .../README.md | 3 + src/libp2p/Libp2p.sln | 7 + src/samples/pubsub-chat/Program.cs | 2 +- 20 files changed, 473 insertions(+), 12 deletions(-) create mode 100644 src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln rename src/libp2p/Libp2p.Protocols.Pubsub/{ITopic.cs => ITopicSubscription.cs} (77%) create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md diff --git a/src/libp2p/Libp2p.Core/Dto/KeyPair.cs b/src/libp2p/Libp2p.Core/Dto/KeyPair.cs index 7bf8edf9..0c6e1aa7 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 @@ -307,6 +308,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 diff --git a/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.csproj b/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.csproj index b7865950..c3b582d4 100644 --- a/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.csproj +++ b/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.csproj @@ -10,6 +10,7 @@ Nethermind.$(MSBuildProjectName.Replace(" ", "_")) true true + 3.25.1 @@ -46,7 +47,7 @@ - <_Parameter1>$(NuGetPackageRoot)google.protobuf.tools\3.24.1\tools\$(protoc) + <_Parameter1>$(NuGetPackageRoot)google.protobuf.tools\3.25.1\tools\$(protoc) diff --git a/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln b/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln new file mode 100644 index 00000000..335b13c0 --- /dev/null +++ b/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Generators.Protobuf", "Libp2p.Generators.Protobuf.csproj", "{699FF970-2414-4EED-AD9A-5B908BDAB4D8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {459B48E6-7D97-4644-BC5B-B33ED0318CF6} + EndGlobalSection +EndGlobal diff --git a/src/libp2p/Libp2p.Generators.Protobuf/README.md b/src/libp2p/Libp2p.Generators.Protobuf/README.md index 4d046e12..938250ed 100644 --- a/src/libp2p/Libp2p.Generators.Protobuf/README.md +++ b/src/libp2p/Libp2p.Generators.Protobuf/README.md @@ -1 +1,24 @@ # Protobuf generator + +The project automatically generates C# types for protobuf types using protoc(no need to install it separately, it's in the package). + +## Usage + +1. Add [Google.Protobuf](https://www.nuget.org/packages/Google.Protobuf) package +1. Add [Libp2p.Generators.Protobuf](https://www.nuget.org/packages/Nethermind.Libp2p.Generators.Protobuf) as analyzer dependency to you project + ```xml + ... + + ... + + + ... + ``` +1. Make sure that Google.Protobuf version matches Libp2p.Generators.Protobuf project version +1. Add a .proto file and set "Build Action" to "C# analyzer additional file" for it, "Copy to Output Directory"="Do not copy". It should look like + `` in csproj file. +1. .cs file should be generated next to .proto one. + +## Additional notes + +In case you want to use protobuf version that is more modern that is used by the generator, feel free to create an issue with request to update the version or just make a pr. diff --git a/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs b/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs index 90d75e5f..577444af 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 diff --git a/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj b/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj index 4f264a38..ff4289e7 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj +++ b/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs index 89e7782c..042a567d 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 @@ -310,6 +311,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 @@ -514,6 +516,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 @@ -771,6 +774,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 diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs index a5dee6f0..08264983 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 diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs index ce0e9f34..6b60688e 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 @@ -313,6 +314,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 @@ -575,6 +577,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 @@ -1033,6 +1036,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 @@ -1315,6 +1319,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 @@ -1544,6 +1549,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 @@ -1722,6 +1728,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 @@ -1925,6 +1932,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 @@ -2214,6 +2222,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 @@ -2468,6 +2477,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 diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs index 05c12f39..7845bbde 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 @@ -347,6 +348,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 @@ -593,6 +595,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 diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs similarity index 77% rename from src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs rename to src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs index a4f51cc7..9bf5d83c 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs @@ -3,8 +3,9 @@ namespace Nethermind.Libp2p.Protocols.Pubsub; -public interface ITopic +public interface ITopicSubscription { event Action? OnMessage; void Publish(byte[] bytes); + void Unsubscribe(); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 0748613b..b69d8bc9 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -245,7 +245,7 @@ private async Task Reconnect(CancellationToken token) } } - public ITopic Subscribe(string topicId) + public ITopicSubscription Subscribe(string topicId) { Topic topic = topicState.GetOrAdd(topicId, (tId) => new(this, tId)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs index ac69e05b..7ceec8d7 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Protocols.Pubsub.Dto; + namespace Nethermind.Libp2p.Protocols.Pubsub; -class Topic : ITopic +class Topic : ITopicSubscription { private readonly PubsubRouter router; private readonly string topicName; @@ -12,13 +14,15 @@ public Topic(PubsubRouter router, string topicName) { this.router = router; this.topicName = topicName; - router.OnMessage += (topicName, message) => + router.OnMessage += OnRouterMessage; + } + + private void OnRouterMessage(string topicName, byte[] message) + { + if (OnMessage is not null && this.topicName == topicName) { - if (OnMessage is not null && this.topicName == topicName) - { - OnMessage(message); - } - }; + OnMessage(message); + } } public DateTime LastPublished { get; set; } @@ -29,4 +33,10 @@ public void Publish(byte[] value) { router.Publish(topicName, value); } + + public void Unsubscribe() + { + router.OnMessage -= OnRouterMessage; + OnMessage = null; + } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs new file mode 100644 index 00000000..72629d3f --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs @@ -0,0 +1,256 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Peer.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; +/// Holder for reflection information generated from Peer.proto +public static partial class PeerReflection { + + #region Descriptor + /// File descriptor for Peer.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static PeerReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgpQZWVyLnByb3RvIigKBFBlZXISEQoJcHVibGljS2V5GAEgASgMEg0KBWFk", + "ZHJzGAIgAygMYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Peer), global::Peer.Parser, new[]{ "PublicKey", "Addrs" }, null, null, null, null) + })); + } + #endregion + +} +#region Messages +[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] +public sealed partial class Peer : pb::IMessage +#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage +#endif +{ + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Peer()); + 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::PeerReflection.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 Peer() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Peer(Peer other) : this() { + publicKey_ = other.publicKey_; + addrs_ = other.addrs_.Clone(); + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Peer Clone() { + return new Peer(this); + } + + /// Field number for the "publicKey" field. + public const int PublicKeyFieldNumber = 1; + private pb::ByteString publicKey_ = pb::ByteString.Empty; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString PublicKey { + get { return publicKey_; } + set { + publicKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "addrs" field. + public const int AddrsFieldNumber = 2; + private static readonly pb::FieldCodec _repeated_addrs_codec + = pb::FieldCodec.ForBytes(18); + private readonly pbc::RepeatedField addrs_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField Addrs { + get { return addrs_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Peer); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Peer other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (PublicKey != other.PublicKey) return false; + if(!addrs_.Equals(other.addrs_)) 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 (PublicKey.Length != 0) hash ^= PublicKey.GetHashCode(); + hash ^= addrs_.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 (PublicKey.Length != 0) { + output.WriteRawTag(10); + output.WriteBytes(PublicKey); + } + addrs_.WriteTo(output, _repeated_addrs_codec); + 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 (PublicKey.Length != 0) { + output.WriteRawTag(10); + output.WriteBytes(PublicKey); + } + addrs_.WriteTo(ref output, _repeated_addrs_codec); + 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 (PublicKey.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(PublicKey); + } + size += addrs_.CalculateSize(_repeated_addrs_codec); + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Peer other) { + if (other == null) { + return; + } + if (other.PublicKey.Length != 0) { + PublicKey = other.PublicKey; + } + addrs_.Add(other.addrs_); + _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) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + PublicKey = input.ReadBytes(); + break; + } + case 18: { + addrs_.AddEntriesFrom(input, _repeated_addrs_codec); + 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) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + PublicKey = input.ReadBytes(); + break; + } + case 18: { + addrs_.AddEntriesFrom(ref input, _repeated_addrs_codec); + break; + } + } + } + } + #endif + +} + +#endregion + + +#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto new file mode 100644 index 00000000..cbe4fb34 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +message Peer { + bytes publicKey = 1; + repeated bytes addrs = 2; +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj new file mode 100644 index 00000000..12ca89ea --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj @@ -0,0 +1,41 @@ + + + + enable + enable + latest + Nethermind.$(MSBuildProjectName) + Nethermind.$(MSBuildProjectName.Replace(" ", "_")) + + + + README.md + libp2p network mdns discovery + + + + + Never + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs new file mode 100644 index 00000000..49e98351 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols.Pubsub; +using Google.Protobuf; + +namespace Libp2p.Protocols.PubSubDiscovery; +public class PubSubDiscoverySettings +{ + public string[] Topics { get; set; } = ["_peer-discovery._p2p._pubsub"]; + public int Interval { get; set; } = 10_000; + public bool ListenOnly { get; set; } +} + +public class PubSubDiscovery(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, ILocalPeer peer) : IDiscoveryProtocol +{ + private readonly PubsubRouter _pubSubRouter = pubSubRouter; + private readonly PubSubDiscoverySettings _settings = settings; + + public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) + { + ITopicSubscription[] topics = _settings.Topics.Select(topic => + { + ITopicSubscription subscription = _pubSubRouter.Subscribe(topic); + subscription.OnMessage += OnPeerMessage; + return subscription; + }).ToArray(); + + token.Register(() => + { + foreach (var topic in topics) + { + topic.Unsubscribe(); + } + }); + + if (!_settings.ListenOnly) + { + while (!token.IsCancellationRequested) + { + await Task.Delay(_settings.Interval, token); + foreach (var topic in topics) + { + topic.Publish(new Peer + { + PublicKey = peer.Identity.PrivateKey.ToByteString(), + Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, + }.ToByteArray()); + } + } + } + } + private void OnPeerMessage(byte[] msg) + { + Peer peer = Peer.Parser.ParseFrom(msg); + OnAddPeer?.Invoke([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); + } + + public Func? OnAddPeer { private get; set; } + + public Func? OnRemovePeer { private get; set; } + +} + diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md new file mode 100644 index 00000000..261b0fdc --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md @@ -0,0 +1,3 @@ +# Pubsub based peer discovery protocol + +Implementation adheres to [js version](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery) diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index b818ff76..2b527755 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -65,6 +65,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Yamux.Test 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.PubsubDiscovery", "Libp2p.Protocols.PubsubDiscovery\Libp2p.Protocols.PubsubDiscovery.csproj", "{F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -175,6 +177,10 @@ Global {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 + {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -199,6 +205,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 135216d4..bda3996b 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -33,7 +33,7 @@ ILocalPeer peer = peerFactory.Create(localPeerIdentity, Multiaddress.Decode(addr)); PubsubRouter router = serviceProvider.GetService()!; -ITopic topic = router.Subscribe("chat-room:awesome-chat-room"); +ITopicSubscription topic = router.Subscribe("chat-room:awesome-chat-room"); topic.OnMessage += (byte[] msg) => { try From a8e03ed050ab797253dbb580c98a84839fa69e54 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 4 Sep 2024 17:17:39 +0300 Subject: [PATCH 02/14] Protobug stuff --- src/libp2p/Libp2p.Core/Dto/KeyPair.cs | 24 +++- .../Libp2p.Protocols.Identify/Dto/Identify.cs | 12 +- .../Libp2p.Protocols.Noise/Dto/Exchange.cs | 48 +++++-- .../Dto/Exchange.cs | 12 +- src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs | 120 +++++++++++++++--- .../Dto/TopicDescriptor.cs | 36 +++++- .../Dto/Peer.cs | 12 +- 7 files changed, 220 insertions(+), 44 deletions(-) diff --git a/src/libp2p/Libp2p.Core/Dto/KeyPair.cs b/src/libp2p/Libp2p.Core/Dto/KeyPair.cs index 0c6e1aa7..32ce19fd 100644 --- a/src/libp2p/Libp2p.Core/Dto/KeyPair.cs +++ b/src/libp2p/Libp2p.Core/Dto/KeyPair.cs @@ -266,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; @@ -289,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; @@ -524,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; @@ -547,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 577444af..a3491133 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs @@ -374,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; @@ -413,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 042a567d..5c210f6f 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs @@ -269,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; @@ -292,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; @@ -474,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; @@ -497,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; @@ -732,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; @@ -755,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; @@ -1019,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; @@ -1049,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 08264983..6b32d93d 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs @@ -250,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; @@ -273,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 6b60688e..80d458ee 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs @@ -255,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; @@ -285,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; @@ -530,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; @@ -553,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; @@ -962,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; @@ -1001,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; @@ -1253,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; @@ -1288,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; @@ -1507,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; @@ -1530,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; @@ -1694,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; @@ -1713,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; @@ -1898,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; @@ -1917,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; @@ -2172,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; @@ -2199,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; @@ -2435,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; @@ -2458,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; @@ -2622,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; @@ -2641,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 7845bbde..531eb295 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs @@ -283,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; @@ -316,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; @@ -539,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; @@ -562,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; @@ -786,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; @@ -809,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; diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs index 72629d3f..0eafc63e 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs @@ -208,7 +208,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; @@ -231,7 +235,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 5f021ee91ce9ece21123ca5201904dbe5798fcd2 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 9 Sep 2024 14:41:48 +0300 Subject: [PATCH 03/14] Testing framework wip --- .../Libp2p.Core.TestsBase/Dto/MuxerPacket.cs | 401 ++++++++++++++++++ .../Dto/MuxerPacket.proto | 16 + .../Libp2p.Core.TestsBase/E2e/ChannelBus.cs | 36 ++ .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 15 + .../E2e/TestLocalPeer.cs | 22 + .../E2e/TestMuxerProtocol.cs | 190 +++++++++ .../E2e/TestMuxerTests.cs | 30 ++ .../E2e/TestPeerFactory.cs | 18 + .../E2e/TestPingProtocol.cs | 24 ++ .../Libp2p.Core.TestsBase/E2e/TestSuite.cs | 12 + .../Libp2p.Core.TestsBase.csproj | 13 +- .../{TestLocalPeer.cs => LocalPeerStub.cs} | 10 +- ...Factory.cs => TestContextLoggerFactory.cs} | 8 +- src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs | 14 +- .../Discovery/IDiscoveryProtocol.cs | 21 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 6 +- .../FloodsubProtocolTests.cs | 2 +- .../GossipsubProtocolTests.cs | 4 +- .../PubsubProtocolTests.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 47 +- ...p2p.Protocols.PubsubDiscovery.Tests.csproj | 30 ++ .../MultistreamProtocolTests.cs | 119 ++++++ .../Usings.cs | 7 + .../PubsubDiscoveryProtocol.cs | 11 +- .../YamuxProtocolTests.cs | 2 +- src/libp2p/Libp2p.sln | 7 + src/samples/pubsub-chat/Program.cs | 2 +- 27 files changed, 1006 insertions(+), 63 deletions(-) create mode 100644 src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs create mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs rename src/libp2p/Libp2p.Core.TestsBase/{TestLocalPeer.cs => LocalPeerStub.cs} (83%) rename src/libp2p/Libp2p.Core.TestsBase/{DebugLoggerFactory.cs => TestContextLoggerFactory.cs} (75%) create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs new file mode 100644 index 00000000..82f2456f --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs @@ -0,0 +1,401 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: MuxerPacket.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 Libp2p.Core.TestsBase.Dto { + + /// Holder for reflection information generated from MuxerPacket.proto + public static partial class MuxerPacketReflection { + + #region Descriptor + /// File descriptor for MuxerPacket.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static MuxerPacketReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChFNdXhlclBhY2tldC5wcm90byJhCgtNdXhlclBhY2tldBIeCgRUeXBlGAEg", + "AigOMhAuTXV4ZXJQYWNrZXRUeXBlEhEKCUNoYW5uZWxJZBgCIAIoDRIMCgRE", + "YXRhGAQgASgMEhEKCVByb3RvY29scxgDIAMoCSpYCg9NdXhlclBhY2tldFR5", + "cGUSFAoQTmV3U3RyZWFtUmVxdWVzdBAAEhUKEU5ld1N0cmVhbVJlc3BvbnNl", + "EAESCAoERGF0YRACEg4KCkNsb3NlV3JpdGUQA0IcqgIZTGlicDJwLkNvcmUu", + "VGVzdHNCYXNlLkR0bw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Libp2p.Core.TestsBase.Dto.MuxerPacketType), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Libp2p.Core.TestsBase.Dto.MuxerPacket), global::Libp2p.Core.TestsBase.Dto.MuxerPacket.Parser, new[]{ "Type", "ChannelId", "Data", "Protocols" }, null, null, null, null) + })); + } + #endregion + + } + #region Enums + public enum MuxerPacketType { + [pbr::OriginalName("NewStreamRequest")] NewStreamRequest = 0, + [pbr::OriginalName("NewStreamResponse")] NewStreamResponse = 1, + [pbr::OriginalName("Data")] Data = 2, + [pbr::OriginalName("CloseWrite")] CloseWrite = 3, + } + + #endregion + + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class MuxerPacket : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new MuxerPacket()); + private pb::UnknownFieldSet _unknownFields; + private int _hasBits0; + [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::Libp2p.Core.TestsBase.Dto.MuxerPacketReflection.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 MuxerPacket() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MuxerPacket(MuxerPacket other) : this() { + _hasBits0 = other._hasBits0; + type_ = other.type_; + channelId_ = other.channelId_; + data_ = other.data_; + protocols_ = other.protocols_.Clone(); + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MuxerPacket Clone() { + return new MuxerPacket(this); + } + + /// Field number for the "Type" field. + public const int TypeFieldNumber = 1; + private readonly static global::Libp2p.Core.TestsBase.Dto.MuxerPacketType TypeDefaultValue = global::Libp2p.Core.TestsBase.Dto.MuxerPacketType.NewStreamRequest; + + private global::Libp2p.Core.TestsBase.Dto.MuxerPacketType type_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Libp2p.Core.TestsBase.Dto.MuxerPacketType Type { + get { if ((_hasBits0 & 1) != 0) { return type_; } else { return TypeDefaultValue; } } + set { + _hasBits0 |= 1; + type_ = value; + } + } + /// Gets whether the "Type" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool HasType { + get { return (_hasBits0 & 1) != 0; } + } + /// Clears the value of the "Type" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void ClearType() { + _hasBits0 &= ~1; + } + + /// Field number for the "ChannelId" field. + public const int ChannelIdFieldNumber = 2; + private readonly static uint ChannelIdDefaultValue = 0; + + private uint channelId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint ChannelId { + get { if ((_hasBits0 & 2) != 0) { return channelId_; } else { return ChannelIdDefaultValue; } } + set { + _hasBits0 |= 2; + channelId_ = value; + } + } + /// Gets whether the "ChannelId" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool HasChannelId { + get { return (_hasBits0 & 2) != 0; } + } + /// Clears the value of the "ChannelId" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void ClearChannelId() { + _hasBits0 &= ~2; + } + + /// Field number for the "Data" field. + public const int DataFieldNumber = 4; + private readonly static pb::ByteString DataDefaultValue = pb::ByteString.Empty; + + private pb::ByteString data_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString Data { + get { return data_ ?? DataDefaultValue; } + set { + data_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "Data" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool HasData { + get { return data_ != null; } + } + /// Clears the value of the "Data" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void ClearData() { + data_ = null; + } + + /// Field number for the "Protocols" field. + public const int ProtocolsFieldNumber = 3; + private static readonly pb::FieldCodec _repeated_protocols_codec + = pb::FieldCodec.ForString(26); + private readonly pbc::RepeatedField protocols_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField Protocols { + get { return protocols_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as MuxerPacket); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(MuxerPacket other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Type != other.Type) return false; + if (ChannelId != other.ChannelId) return false; + if (Data != other.Data) return false; + if(!protocols_.Equals(other.protocols_)) 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 (HasType) hash ^= Type.GetHashCode(); + if (HasChannelId) hash ^= ChannelId.GetHashCode(); + if (HasData) hash ^= Data.GetHashCode(); + hash ^= protocols_.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 (HasType) { + output.WriteRawTag(8); + output.WriteEnum((int) Type); + } + if (HasChannelId) { + output.WriteRawTag(16); + output.WriteUInt32(ChannelId); + } + protocols_.WriteTo(output, _repeated_protocols_codec); + if (HasData) { + output.WriteRawTag(34); + output.WriteBytes(Data); + } + 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 (HasType) { + output.WriteRawTag(8); + output.WriteEnum((int) Type); + } + if (HasChannelId) { + output.WriteRawTag(16); + output.WriteUInt32(ChannelId); + } + protocols_.WriteTo(ref output, _repeated_protocols_codec); + if (HasData) { + output.WriteRawTag(34); + output.WriteBytes(Data); + } + 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 (HasType) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); + } + if (HasChannelId) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ChannelId); + } + if (HasData) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Data); + } + size += protocols_.CalculateSize(_repeated_protocols_codec); + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(MuxerPacket other) { + if (other == null) { + return; + } + if (other.HasType) { + Type = other.Type; + } + if (other.HasChannelId) { + ChannelId = other.ChannelId; + } + if (other.HasData) { + Data = other.Data; + } + protocols_.Add(other.protocols_); + _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 8: { + Type = (global::Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); + break; + } + case 16: { + ChannelId = input.ReadUInt32(); + break; + } + case 26: { + protocols_.AddEntriesFrom(input, _repeated_protocols_codec); + break; + } + case 34: { + Data = 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 8: { + Type = (global::Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); + break; + } + case 16: { + ChannelId = input.ReadUInt32(); + break; + } + case 26: { + protocols_.AddEntriesFrom(ref input, _repeated_protocols_codec); + break; + } + case 34: { + Data = input.ReadBytes(); + break; + } + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto new file mode 100644 index 00000000..8055d501 --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; +option csharp_namespace = "Libp2p.Core.TestsBase.Dto"; + +enum MuxerPacketType { + NewStreamRequest = 0; + NewStreamResponse = 1; + Data = 2; + CloseWrite = 3; +} + +message MuxerPacket { + required MuxerPacketType Type = 1; + required uint32 ChannelId = 2; + optional bytes Data = 4; + repeated string Protocols = 3; +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs new file mode 100644 index 00000000..cd33001b --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using System.Threading.Channels; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; + +public class ChannelBus +{ + Dictionary> channels = []; + + public IAsyncEnumerable GetIncomingRequests(PeerId serverId) + { + Channel col = System.Threading.Channels.Channel.CreateUnbounded(); + + if (!channels.TryAdd(serverId, col)) + { + throw new Exception("Test listener with such peer id alread exists."); + } + + return col.Reader.ReadAllAsync(); + } + + public IChannel Dial(PeerId self, PeerId serverId) + { + if (!channels.TryGetValue(serverId, out Channel? col)) + { + throw new Exception("Test listener with such peer id does not exist."); + } + + Channel channel = new(); + _ = col.Writer.WriteAsync(channel.Reverse); + return channel; + } +} + diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs new file mode 100644 index 00000000..bcd136db --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Microsoft.Extensions.DependencyInjection; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; + +public class TestBuilder(ChannelBus? commmonBus = null, IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) +{ + protected override ProtocolStack BuildStack() + { + return Over(new TestMuxerProtocol(commmonBus ?? new ChannelBus(), new TestContextLoggerFactory())) + .AddAppLayerProtocol(); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs new file mode 100644 index 00000000..d09db191 --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; + +internal class TestLocalPeer(Identity id) : ILocalPeer +{ + 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) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(Multiaddress addr, CancellationToken token = default) + { + throw new NotImplementedException(); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs new file mode 100644 index 00000000..458ce487 --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -0,0 +1,190 @@ + +using Google.Protobuf; +using Libp2p.Core.TestsBase.Dto; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase.E2e; +using Org.BouncyCastle.Utilities.Encoders; +using System.Buffers; +using System.Threading.Channels; + +class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : IProtocol +{ + private const string id = "test-muxer"; + + private readonly ILogger? logger = loggerFactory?.CreateLogger(id); + + public string Id => id; + + public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + { + 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)); + } + + public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + { + context.ListenerReady(); + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listen async"); + await foreach (var item in bus.GetIncomingRequests(context.LocalPeer.Identity.PeerId)) + { + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listener handles new con"); + _ = HandleRemote(item, upChannelFactory!, context, true); + } + } + + private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelFactory, IPeerContext context, bool isListen = false) + { + uint counter = isListen ? 1u : 2u; + Dictionary chans = []; + + string peer = ""; + if (isListen) + { + peer = await downChannel.ReadLineAsync(); + await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listener handles remote {peer}"); + context.RemotePeer.Address = $"/p2p/{peer}"; + } + else + { + await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); + peer = await downChannel.ReadLineAsync(); + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Dialer handles remote {peer}"); + context.RemotePeer.Address = $"/p2p/{peer}"; + } + + string logPrefix = $"{context.LocalPeer.Identity.PeerId}<>{peer}"; + + _ = Task.Run(async () => + { + foreach (var item in context.SubDialRequests) + { + uint chanId = Interlocked.Add(ref counter, 2); + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}({chanId}): Sub-request {item.SubProtocol} {item.CompletionSource is not null}"); + + chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; + var response = new MuxerPacket() + { + ChannelId = chanId, + Type = MuxerPacketType.NewStreamRequest, + Protocols = { item.SubProtocol!.Id } + }; + await downChannel.WriteSizeAndProtobufAsync(response); + } + }); + + while (true) + { + var packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); + + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + switch (packet.Type) + { + case MuxerPacketType.NewStreamRequest: + IProtocol? selected = null; + foreach (var proto in packet.Protocols) + { + selected = upChannelFactory.SubProtocols.FirstOrDefault(x => x.Id == proto); + if (selected is not null) break; + } + if (selected is not null) + { + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Matched {selected}"); + var response = new MuxerPacket() + { + ChannelId = packet.ChannelId, + Type = MuxerPacketType.NewStreamResponse, + Protocols = + { + selected.Id + } + }; + + var req = new ChannelRequest { SubProtocol = selected }; + + IChannel upChannel = upChannelFactory.SubListen(context, req); + chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; + _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); + await downChannel.WriteSizeAndProtobufAsync(response); + } + else + { + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No match {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + var response = new MuxerPacket() + { + ChannelId = packet.ChannelId, + Type = MuxerPacketType.NewStreamResponse, + }; + await downChannel.WriteSizeAndProtobufAsync(response); + } + break; + 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); + chans[packet.ChannelId].UpChannel = upChannel; + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SubProtocol}"); + _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); + } + 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())); + break; + case MuxerPacketType.CloseWrite: + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Remote EOF"); + + chans[packet.ChannelId].RemoteClosedWrites = true; + _ = chans[packet.ChannelId].UpChannel!.WriteEofAsync(); + break; + default: + break; + } + } + } + + private Task HandleUpchannelData(IChannel downChannel, Dictionary chans, uint channelId, IChannel upChannel, string logPrefix) + { + return Task.Run(async () => + { + await foreach (var item in upChannel.ReadAllAsync()) + { + var data = item.ToArray(); + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel data {data.Length} {Hex.ToHexString(data, false)}"); + + var packet = new MuxerPacket() + { + ChannelId = channelId, + Type = MuxerPacketType.Data, + Data = ByteString.CopyFrom(item.ToArray()) + }; + await downChannel.WriteSizeAndProtobufAsync(packet); + } + if (chans[channelId].RemoteClosedWrites) + { + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel dial/listen complete"); + chans[channelId].Tcs?.SetResult(); + } + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel write close"); + + await downChannel.WriteSizeAndProtobufAsync(new MuxerPacket() + { + ChannelId = channelId, + Type = MuxerPacketType.CloseWrite, + }); + + }); + } + + class MuxerChannel + { + public IChannel? UpChannel { get; set; } + public TaskCompletionSource? Tcs { get; set; } + public bool RemoteClosedWrites { get; set; } + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs new file mode 100644 index 00000000..abbf08b8 --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using NUnit.Framework; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; +internal class TestMuxerTests +{ + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake() + { + AppDomain.CurrentDomain.UnhandledException += (s, e) => + { + + }; + TaskScheduler.UnobservedTaskException += (s, e) => + { + + }; + IPeerFactory peerFactory = new TestBuilder().Build(); + + 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)); + + IRemotePeer remotePeerB = await peerA.DialAsync(peerB.Address); + await remotePeerB.DialAsync(); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs new file mode 100644 index 00000000..dcfcaebb --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using System.Collections.Concurrent; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; + +internal class TestPeerFactory(IServiceProvider serviceProvider) : PeerFactory(serviceProvider) +{ + ConcurrentDictionary peers = new(); + + public override ILocalPeer Create(Identity? identity = null, Multiaddress? localAddr = null) + { + 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 new file mode 100644 index 00000000..0a735853 --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using NUnit.Framework; + +namespace Nethermind.Libp2p.Core.TestsBase.E2e; +class TestPingProtocol : IProtocol +{ + public string Id => "test-ping"; + + public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + { + string str = "hello"; + await downChannel.WriteLineAsync(str); + string res = await downChannel.ReadLineAsync(); + Assert.That(res, Is.EqualTo(str + " there")); + } + + public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + { + string str = await downChannel.ReadLineAsync(); + await downChannel.WriteLineAsync(str + " there"); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs new file mode 100644 index 00000000..a355e05b --- /dev/null +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs @@ -0,0 +1,12 @@ +// 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.TestsBase/Libp2p.Core.TestsBase.csproj b/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj index ea2db10a..01c7ec59 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj +++ b/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj @@ -1,4 +1,4 @@ - + enable @@ -8,6 +8,16 @@ Nethermind.$(MSBuildProjectName) + + + + + + + Never + + + @@ -22,6 +32,7 @@ + diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs similarity index 83% rename from src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs rename to src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index 5165f78e..84cba547 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -5,9 +5,9 @@ namespace Nethermind.Libp2p.Core.TestsBase; -public class TestLocalPeer : ILocalPeer +public class LocalPeerStub : ILocalPeer { - public TestLocalPeer() + public LocalPeerStub() { Identity = new(Enumerable.Repeat((byte)42, 32).ToArray()); Address = $"/p2p/{Identity.PeerId}"; @@ -29,10 +29,10 @@ public Task ListenAsync(Multiaddress addr, CancellationToken token = public class TestRemotePeer : IRemotePeer { - public TestRemotePeer(Multiaddress addr) + public TestRemotePeer(int testPeerIndex) { - Identity = TestPeers.Identity(addr); - Address = addr; + Identity = TestPeers.Identity(testPeerIndex); + Address = TestPeers.Multiaddr(testPeerIndex); } public Identity Identity { get; set; } diff --git a/src/libp2p/Libp2p.Core.TestsBase/DebugLoggerFactory.cs b/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs similarity index 75% rename from src/libp2p/Libp2p.Core.TestsBase/DebugLoggerFactory.cs rename to src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs index 33bb3000..cd337265 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/DebugLoggerFactory.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs @@ -3,12 +3,13 @@ using Microsoft.Extensions.Logging; using NUnit.Framework; +using System.Diagnostics; namespace Nethermind.Libp2p.Core.TestsBase; -public class DebugLoggerFactory : ILoggerFactory +public class TestContextLoggerFactory : ILoggerFactory { - class DebugLogger(string categoryName) : ILogger, IDisposable + class TestContextLogger(string categoryName) : ILogger, IDisposable { private readonly string _categoryName = categoryName; @@ -29,6 +30,7 @@ public bool IsEnabled(LogLevel logLevel) 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))}"); } } @@ -39,7 +41,7 @@ public void AddProvider(ILoggerProvider provider) public ILogger CreateLogger(string categoryName) { - return new DebugLogger(categoryName); + return new TestContextLogger(categoryName); } public void Dispose() diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs index f4341e10..048b8c47 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs @@ -10,20 +10,16 @@ namespace Nethermind.Libp2p.Core.TestsBase; public class TestPeers { - private static readonly ConcurrentDictionary testPeerAddrs = new(); - private static readonly ConcurrentDictionary testPeerIds = new(); + private static readonly ConcurrentDictionary testPeerIdentities = new(); - public static Multiaddress Multiaddr(int i) => testPeerAddrs.GetOrAdd(i, i => + public static Identity Identity(int i) => testPeerIdentities.GetOrAdd(i, i => { byte[] key = new byte[32]; BinaryPrimitives.WriteInt32BigEndian(key.AsSpan(32 - 4, 4), i); - return Multiaddress.Decode($"/p2p/{new Identity(key).PeerId}"); + return new Identity(key); }); - public static PeerId PeerId(int i) => testPeerIds.GetOrAdd(i, i => new PeerId(testPeerAddrs[i].Get().ToString())); - - public static PeerId PeerId(Multiaddress addr) => new PeerId(addr.Get().ToString()); - - public static Identity Identity(Multiaddress addr) => new(Core.PeerId.ExtractPublicKey(PeerId(addr).Bytes)); + public static PeerId PeerId(int i) => Identity(i).PeerId; + public static Multiaddress Multiaddr(int i) => $"/p2p/{Identity(i).PeerId}"; } diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index e883d11a..6d40ac82 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -8,6 +8,23 @@ namespace Nethermind.Libp2p.Core.Discovery; public interface IDiscoveryProtocol { Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default); - Func? OnAddPeer { set; } - Func? OnRemovePeer { set; } +} + + +public class PeerStore +{ + List store = []; + + public void Discover(Multiaddress[] addrs) + { + store.Add(addrs); + OnNewPeer?.Invoke(addrs); + } + + public event Action? OnNewPeer; + + public override string ToString() + { + return $"peerStore({store.Count}):{string.Join(",", store.Select(x=>x.FirstOrDefault()?.GetPeerId()?.ToString() ?? "null"))})"; + } } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 7aa9d474..71dd5106 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -44,7 +44,9 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { - return new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); + ProtocolStack result = new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); + result.Root = result; + return result; } public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol @@ -58,7 +60,7 @@ protected class ProtocolStack private readonly IPeerFactoryBuilder builder; private readonly IServiceProvider serviceProvider; - public ProtocolStack? Root { get; private set; } + public ProtocolStack? Root { get; set; } public ProtocolStack? Parent { get; private set; } public ProtocolStack? PrevSwitch { get; private set; } public IProtocol Protocol { get; } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index fda59a2c..51a6ed61 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -28,7 +28,7 @@ public async Task Test_Peer_is_in_fpeers() CancellationToken token = default; List sentRpcs = new(); - _ = router.RunAsync(peer, discovery, token: token); + _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); router.Subscribe(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index cb0e293a..c71c31c5 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -19,12 +19,12 @@ 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(); + ILocalPeer peer = new LocalPeerStub(); TestDiscoveryProtocol discovery = new(); CancellationToken token = default; List sentRpcs = new(); - _ = router.RunAsync(peer, discovery, token: token); + _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); router.Subscribe(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index 2b1d99a3..8a2bb1b2 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -24,7 +24,7 @@ public async Task Test_Peer_is_dialed_when_added_by_discovery() TestDiscoveryProtocol discovery = new(); CancellationToken token = default; - _ = router.RunAsync(peer, discovery, token: token); + _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); discovery.OnAddPeer!([discoveredPeer]); await Task.Delay(100); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index b69d8bc9..50a1057d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -114,6 +114,7 @@ public Action? SendRpc public Func? VerifyMessage = null; private Settings settings; + private PeerStore store; private TtlCache messageCache; private TtlCache limboMessageCache; private TtlCache<(PeerId, MessageId)> dontWantMessages; @@ -150,7 +151,7 @@ static PubsubRouter() Canceled = cts.Token; } - public async Task RunAsync(ILocalPeer localPeer, IDiscoveryProtocol discoveryProtocol, Settings? settings = null, CancellationToken token = default) + public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? settings = null, CancellationToken token = default) { if (this.localPeer is not null) { @@ -159,6 +160,7 @@ public async Task RunAsync(ILocalPeer localPeer, IDiscoveryProtocol discoveryPro this.localPeer = localPeer; peer = new ManagedPeer(localPeer); this.settings = settings ?? Settings.Default; + this.store = store; messageCache = new(this.settings.MessageCacheTtl); limboMessageCache = new(this.settings.MessageCacheTtl); dontWantMessages = new(this.settings.MessageCacheTtl); @@ -166,30 +168,9 @@ public async Task RunAsync(ILocalPeer localPeer, IDiscoveryProtocol discoveryPro LocalPeerId = new PeerId(localPeer.Address.Get().ToString()!); _ = localPeer.ListenAsync(localPeer.Address, token); - _ = StartDiscoveryAsync(discoveryProtocol, token); logger?.LogInformation("Started"); - // reconnection if needed - _ = Task.Run(async () => - { - while (!token.IsCancellationRequested) - { - await Task.Delay(this.settings.ReconnectionPeriod); - await Reconnect(token); - } - }, token); - - await Task.Delay(Timeout.Infinite, token); - messageCache.Dispose(); - limboMessageCache.Dispose(); - } - - private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(localPeer); - - ObservableCollection col = []; - discoveryProtocol.OnAddPeer = (addrs) => + store.OnNewPeer += (addrs) => { _ = Task.Run(async () => { @@ -208,24 +189,36 @@ private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, Can } catch { - reconnections.Add(new Reconnection(addrs, settings.ReconnectionAttempts)); + reconnections.Add(new Reconnection(addrs, this.settings.ReconnectionAttempts)); } }); - return true; }; _ = Task.Run(async () => { while (!token.IsCancellationRequested) { - await Task.Delay(settings.HeartbeatInterval); + await Task.Delay(this.settings.HeartbeatInterval); await Heartbeat(); } }, token); - await discoveryProtocol.DiscoverAsync(localPeer.Address, token); + // reconnection if needed + _ = Task.Run(async () => + { + while (!token.IsCancellationRequested) + { + await Task.Delay(this.settings.ReconnectionPeriod); + await Reconnect(token); + } + }, token); + + await Task.Delay(Timeout.Infinite, token); + messageCache.Dispose(); + limboMessageCache.Dispose(); } + private async Task Reconnect(CancellationToken token) { while (reconnections.TryTake(out Reconnection? rec)) diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj new file mode 100644 index 00000000..290b89b0 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj @@ -0,0 +1,30 @@ + + + + enable + enable + Nethermind.$(MSBuildProjectName.Replace(" ", "_")) + false + Nethermind.$(MSBuildProjectName) + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs new file mode 100644 index 00000000..06960c0b --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Libp2p.Protocols.PubSubDiscovery; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Core.TestsBase.E2e; +using Nethermind.Libp2p.Protocols.Pubsub; + +namespace Libp2p.Protocols.Multistream.Tests; + +[TestFixture] +[Parallelizable(scope: ParallelScope.All)] +public class MultistreamProtocolTests +{ + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake2() + { + IPeerFactory peerFactory = new TestBuilder().Build(); + ChannelBus commonBus = new(); + + ServiceProvider sp1 = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestContextLoggerFactory()) + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => sp.GetService()!.Build()) + .BuildServiceProvider(); + + + ServiceProvider sp2 = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestContextLoggerFactory()) + .AddSingleton() + .AddSingleton() + .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)); + + IRemotePeer remotePeerB = await peerA.DialAsync(peerB.Address); + await remotePeerB.DialAsync(); + } + + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake() + { + // There is common communication point + ChannelBus commonBus = new(); + ILocalPeer[] peers = new ILocalPeer[10]; + PeerStore[] peerStores = new PeerStore[10]; + PubsubRouter[] routers = new PubsubRouter[10]; + + + for (int i = 0; i < peers.Length; i++) + { + // But we create a seprate setup for every peer + ServiceProvider sp = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestContextLoggerFactory()) + .AddSingleton() + .AddSingleton() + .AddSingleton(sp=> sp.GetService()!.Build()) + .BuildServiceProvider(); + + IPeerFactory peerFactory = sp.GetService()!; + ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); + PubsubRouter router = routers[i] = sp.GetService()!; + PeerStore peerStore = sp.GetService()!; + PubSubDiscoveryProtocol disc = new(router, new PubSubDiscoverySettings() { Interval = 300 }, peerStore, peer); + _ = router.RunAsync(peer, peerStore); + peerStores[i] = peerStore; + _ = disc.DiscoverAsync(peers[i].Address); + } + + for (int i = 0; i < peers.Length; i++) + { + peerStores[i].Discover([peers[(i + 1) % 10].Address]); + } + await Task.Delay(1500000); + await Task.Delay(2000); + //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(), 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, peerContext, proto1); + //await downChannel.CloseAsync(); + } + + +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs new file mode 100644 index 00000000..852eb690 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs @@ -0,0 +1,7 @@ +// 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.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs index 49e98351..0fbe13b7 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -15,7 +15,7 @@ public class PubSubDiscoverySettings public bool ListenOnly { get; set; } } -public class PubSubDiscovery(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, ILocalPeer peer) : IDiscoveryProtocol +public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, PeerStore peerStore, ILocalPeer peer) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; private readonly PubSubDiscoverySettings _settings = settings; @@ -46,7 +46,7 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to { topic.Publish(new Peer { - PublicKey = peer.Identity.PrivateKey.ToByteString(), + PublicKey = peer.Identity.PublicKey.ToByteString(), Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, }.ToByteArray()); } @@ -56,12 +56,7 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to private void OnPeerMessage(byte[] msg) { Peer peer = Peer.Parser.ParseFrom(msg); - OnAddPeer?.Invoke([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); + peerStore.Discover([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); } - - public Func? OnAddPeer { private get; set; } - - public Func? OnRemovePeer { private get; set; } - } diff --git a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs index 673c7fdc..72a1cf75 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs @@ -50,7 +50,7 @@ public async Task Test_Protocol_Communication() listenerUpchannelFactory.SubListen(Arg.Any(), Arg.Any()) .Returns(listenerUpChannel); - YamuxProtocol proto = new(loggerFactory: new DebugLoggerFactory()); + YamuxProtocol proto = new(loggerFactory: new TestContextLoggerFactory()); _ = proto.ListenAsync(listenerDownChannel, listenerUpchannelFactory, listenerPeerContext); diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 2b527755..589f358a 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -67,6 +67,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransportInterop", "..\samp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubDiscovery", "Libp2p.Protocols.PubsubDiscovery\Libp2p.Protocols.PubsubDiscovery.csproj", "{F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubDiscovery.Tests", "Libp2p.Protocols.PubsubDiscovery.Tests\Libp2p.Protocols.PubsubDiscovery.Tests.csproj", "{5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -181,6 +183,10 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -206,6 +212,7 @@ 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index bda3996b..508c2f33 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -51,7 +51,7 @@ } }; -_ = router.RunAsync(peer, new MDnsDiscoveryProtocol(serviceProvider.GetService()), token: ts.Token); +_ = router.RunAsync(peer, new Nethermind.Libp2p.Core.Discovery.PeerStore(), token: ts.Token); From 64e7aaae943a14c5ff63e0eaf363ba27a8e00100 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 9 Sep 2024 19:39:33 +0300 Subject: [PATCH 04/14] Fix multiaddr and more --- src/libp2p/Directory.Packages.props | 2 +- .../E2e/TestMuxerProtocol.cs | 5 +- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 6 +- src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs | 2 + .../Discovery/IDiscoveryProtocol.cs | 18 +++- src/libp2p/Libp2p.Core/PeerFactory.cs | 3 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 32 ++++--- .../ITopicSubscription.cs | 6 ++ .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 89 +++++++++++-------- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 20 ++++- .../MultistreamProtocolTests.cs | 43 ++------- .../PubsubDiscoveryProtocol.cs | 20 ++++- .../transport-interop/packages.lock.json | 6 +- 13 files changed, 142 insertions(+), 110 deletions(-) diff --git a/src/libp2p/Directory.Packages.props b/src/libp2p/Directory.Packages.props index d7c3246b..d3c28284 100644 --- a/src/libp2p/Directory.Packages.props +++ b/src/libp2p/Directory.Packages.props @@ -22,7 +22,7 @@ - + diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 458ce487..e7a30bba 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -6,7 +6,6 @@ using Nethermind.Libp2p.Core.TestsBase.E2e; using Org.BouncyCastle.Utilities.Encoders; using System.Buffers; -using System.Threading.Channels; class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : IProtocol { @@ -45,16 +44,16 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF peer = await downChannel.ReadLineAsync(); await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listener handles remote {peer}"); - context.RemotePeer.Address = $"/p2p/{peer}"; } else { await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); peer = await downChannel.ReadLineAsync(); logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Dialer handles remote {peer}"); - context.RemotePeer.Address = $"/p2p/{peer}"; } + context.RemotePeer.Address = $"/p2p/{peer}"; + string logPrefix = $"{context.LocalPeer.Identity.PeerId}<>{peer}"; _ = Task.Run(async () => diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index 84cba547..86045f3c 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -29,10 +29,10 @@ public Task ListenAsync(Multiaddress addr, CancellationToken token = public class TestRemotePeer : IRemotePeer { - public TestRemotePeer(int testPeerIndex) + public TestRemotePeer(Multiaddress addr) { - Identity = TestPeers.Identity(testPeerIndex); - Address = TestPeers.Multiaddr(testPeerIndex); + Identity = TestPeers.Identity(addr); + Address = addr; } public Identity Identity { get; set; } diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs index 048b8c47..8dd20342 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs @@ -21,5 +21,7 @@ public static Identity Identity(int i) => testPeerIdentities.GetOrAdd(i, i => public static PeerId PeerId(int i) => Identity(i).PeerId; public static Multiaddress Multiaddr(int i) => $"/p2p/{Identity(i).PeerId}"; + public static Identity Identity(Multiaddress addr) => testPeerIdentities.First(i => $"/p2p/{i}" == addr.ToString()).Value; + } diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index 6d40ac82..f8c959d6 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.Discovery; @@ -13,18 +14,27 @@ public interface IDiscoveryProtocol public class PeerStore { - List store = []; + ConcurrentDictionary store = []; public void Discover(Multiaddress[] addrs) { - store.Add(addrs); - OnNewPeer?.Invoke(addrs); + if (addrs is { Length: 0 }) + { + return; + } + + PeerId? peerId = addrs.FirstOrDefault()?.GetPeerId(); + + if (peerId is not null && store.TryAdd(peerId, addrs)) + { + OnNewPeer?.Invoke(addrs); + } } public event Action? OnNewPeer; public override string ToString() { - return $"peerStore({store.Count}):{string.Join(",", store.Select(x=>x.FirstOrDefault()?.GetPeerId()?.ToString() ?? "null"))})"; + return $"peerStore({store.Count}):{string.Join(",", store.Select(x => x.Key.ToString() ?? "null"))})"; } } diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 5e343eb0..d39a614e 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.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 System.Runtime.CompilerServices; @@ -100,7 +101,7 @@ private Task DialAsync(IPeerContext peerContext, CancellationToken to TaskCompletionSource cts = new(token); peerContext.SubDialRequests.Add(new ChannelRequest { - SubProtocol = PeerFactoryBuilderBase.CreateProtocolInstance(_serviceProvider), + SubProtocol = (_serviceProvider.GetService() as ICreateProtocolInstance)!.CreateProtocolInstance(_serviceProvider), CompletionSource = cts }); return cts.Task; diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 71dd5106..15f66979 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -5,11 +5,18 @@ namespace Nethermind.Libp2p.Core; -public static class PeerFactoryBuilderBase +public interface ICreateProtocolInstance { - private static HashSet protocols = new(); + IProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol; +} + +public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder, ICreateProtocolInstance + where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder + where TPeerFactory : PeerFactory +{ + private HashSet protocols = new(); - internal static IProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol + public IProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol { if (instance is not null) { @@ -24,12 +31,7 @@ internal static IProtocol CreateProtocolInstance(IServiceProvider ser } return existing; } -} -public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder - where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder - where TPeerFactory : PeerFactory -{ private readonly List _appLayerProtocols = new(); public IEnumerable AppLayerProtocols { get => _appLayerProtocols; } @@ -44,14 +46,14 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { - ProtocolStack result = new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); + ProtocolStack result = new ProtocolStack(this, ServiceProvider, CreateProtocolInstance(ServiceProvider, instance), this); result.Root = result; return result; } public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol { - _appLayerProtocols.Add(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance)); + _appLayerProtocols.Add(CreateProtocolInstance(ServiceProvider!, instance)); return (TBuilder)this; } @@ -59,6 +61,7 @@ protected class ProtocolStack { private readonly IPeerFactoryBuilder builder; private readonly IServiceProvider serviceProvider; + private readonly ICreateProtocolInstance createProtocolInstance; public ProtocolStack? Root { get; set; } public ProtocolStack? Parent { get; private set; } @@ -67,11 +70,12 @@ protected class ProtocolStack public HashSet TopProtocols { get; } = new(); public ChannelFactory UpChannelsFactory { get; } - public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol) + public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol, ICreateProtocolInstance createProtocolInstance) { this.builder = builder; this.serviceProvider = serviceProvider; Protocol = protocol; + this.createProtocolInstance = createProtocolInstance; UpChannelsFactory = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); } @@ -83,7 +87,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, createProtocolInstance.CreateProtocolInstance(serviceProvider!, instance), createProtocolInstance); return Over(nextNode); } @@ -93,8 +97,8 @@ public ProtocolStack Or(TProtocol? instance = default) where TProtoco { throw new NotImplementedException(); } - IProtocol protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); - ProtocolStack stack = new(builder, serviceProvider, protocol); + IProtocol protocol = createProtocolInstance.CreateProtocolInstance(serviceProvider!, instance); + ProtocolStack stack = new(builder, serviceProvider, protocol, createProtocolInstance); return Or(stack); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs index 9bf5d83c..25d41f6b 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs @@ -1,11 +1,17 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Google.Protobuf; + namespace Nethermind.Libp2p.Protocols.Pubsub; public interface ITopicSubscription { event Action? OnMessage; void Publish(byte[] bytes); + void Publish(IMessage msg) + { + Publish(msg.ToByteArray()); + } void Unsubscribe(); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 83638670..786548be 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -28,62 +28,73 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) { - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); - - TaskCompletionSource dialTcs = new(); - CancellationToken token = router.OutboundConnection(context.RemotePeer.Address, Id, dialTcs.Task, (rpc) => + try { - var t = channel.WriteSizeAndProtobufAsync(rpc); - _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); - t.AsTask().ContinueWith((t) => + string peerId = context.RemotePeer.Address.Get().ToString()!; + _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); + + TaskCompletionSource dialTcs = new(); + CancellationToken token = router.OutboundConnection(context.RemotePeer.Address, Id, dialTcs.Task, (rpc) => { - if (!t.IsCompletedSuccessfully) + var t = channel.WriteSizeAndProtobufAsync(rpc); + t.AsTask().ContinueWith((t) => { - _logger?.LogWarning($"Sending RPC failed message to {peerId}: {rpc}"); - } + if (!t.IsCompletedSuccessfully) + { + _logger?.LogWarning($"Sending RPC failed message to {peerId}: {rpc}"); + } + }); + _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); }); - _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); - }); - await channel; - dialTcs.SetResult(); - _logger?.LogDebug($"Finished dial({context.Id}) {context.RemotePeer.Address}"); + await channel; + dialTcs.SetResult(); + _logger?.LogDebug($"Finished dial({context.Id}) {context.RemotePeer.Address}"); + } + catch + { + } } public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) { - - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Listen({context.Id}) to {context.RemotePeer.Address}"); - - TaskCompletionSource listTcs = new(); - TaskCompletionSource dialTcs = new(); - - CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => + try { - context.SubDialRequests.Add(new ChannelRequest { SubProtocol = this }); - return dialTcs.Task; - }); + string peerId = context.RemotePeer.Address.Get().ToString()!; + _logger?.LogDebug($"Listen({context.Id}) to {context.RemotePeer.Address}"); - while (!token.IsCancellationRequested) - { - Rpc? rpc = await channel.ReadAnyPrefixedProtobufAsync(Rpc.Parser, token); - if (rpc is null) + TaskCompletionSource listTcs = new(); + TaskCompletionSource dialTcs = new(); + + CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => { - _logger?.LogDebug($"Received a broken message or EOF from {peerId}"); - break; - } - else + context.SubDialRequests.Add(new ChannelRequest { SubProtocol = this }); + return dialTcs.Task; + }); + + while (!token.IsCancellationRequested) { - _logger?.LogTrace($"Received message from {peerId}: {rpc}"); - _ = router.OnRpc(peerId, rpc); + Rpc? rpc = await channel.ReadAnyPrefixedProtobufAsync(Rpc.Parser, token); + if (rpc is null) + { + _logger?.LogDebug($"Received a broken message or EOF from {peerId}"); + break; + } + else + { + _logger?.LogTrace($"Received message from {peerId}: {rpc}"); + _ = router.OnRpc(peerId, rpc); + } } + listTcs.SetResult(); + _logger?.LogDebug($"Finished({context.Id}) list {context.RemotePeer.Address}"); + } + catch + { + } - listTcs.SetResult(); - _logger?.LogDebug($"Finished({context.Id}) list {context.RemotePeer.Address}"); } public override string ToString() diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 00cb9e9c..00e39d5c 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -10,6 +10,7 @@ using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.ObjectModel; +using System.Diagnostics; namespace Nethermind.Libp2p.Protocols.Pubsub; @@ -26,6 +27,12 @@ internal interface IRoutingStateContainer public class PubsubRouter(ILoggerFactory? loggerFactory = default) : IRoutingStateContainer { + static int ctr = 0; + int _ctr = Interlocked.Increment(ref ctr); + public override string ToString() + { + return $"Router#{_ctr}: {localPeer?.Address.GetPeerId() ?? "null"}"; + } public const string FloodsubProtocolVersion = "/floodsub/1.0.0"; public const string GossipsubProtocolVersionV10 = "/meshsub/1.0.0"; public const string GossipsubProtocolVersionV11 = "/meshsub/1.1.0"; @@ -74,6 +81,7 @@ public Action? SendRpc { get => _sendRpc; set { + Debug.WriteLine($"Set SENDRPC for {this.PeerId}: {value}"); _sendRpc = value; if (_sendRpc is not null) lock (SendRpcQueue) @@ -172,6 +180,10 @@ public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? sett store.OnNewPeer += (addrs) => { + if (addrs.Any(a => a.GetPeerId()! == localPeer.Identity.PeerId)) + { + return; + } _ = Task.Run(async () => { try @@ -218,7 +230,7 @@ public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? sett limboMessageCache.Dispose(); } - + private async Task Reconnect(CancellationToken token) { while (reconnections.TryTake(out Reconnection? rec)) @@ -456,7 +468,7 @@ internal CancellationToken OutboundConnection(Multiaddress addr, string protocol { if (peerState[peerId].SendRpc is null) { - peer.SendRpc = sendRpc; + peerState[peerId].SendRpc = sendRpc; } else { @@ -497,7 +509,7 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI PubsubPeer? remotePeer; PeerId? peerId = addr.GetPeerId(); - if (peerId is null) + if (peerId is null || peerId == localPeer!.Identity.PeerId) { return Canceled; } @@ -506,7 +518,7 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI logger?.LogDebug("Inbound {peerId}", peerId); if (peerState.TryAdd(peerId, remotePeer)) { - logger?.LogDebug("Inbound, lets dial {peerId} via remotely initiated connection", peerId); + logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); listTask.ContinueWith(t => { peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs index 06960c0b..b50a941c 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -4,10 +4,12 @@ using Libp2p.Protocols.PubSubDiscovery; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Multiformats.Address; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.TestsBase.E2e; using Nethermind.Libp2p.Protocols.Pubsub; +using System.Diagnostics.Metrics; namespace Libp2p.Protocols.Multistream.Tests; @@ -51,11 +53,12 @@ public async Task Test_ConnectionEstablished_AfterHandshake2() [Test] public async Task Test_ConnectionEstablished_AfterHandshake() { + int totalCount = 10; // There is common communication point ChannelBus commonBus = new(); - ILocalPeer[] peers = new ILocalPeer[10]; - PeerStore[] peerStores = new PeerStore[10]; - PubsubRouter[] routers = new PubsubRouter[10]; + ILocalPeer[] peers = new ILocalPeer[totalCount]; + PeerStore[] peerStores = new PeerStore[totalCount]; + PubsubRouter[] routers = new PubsubRouter[totalCount]; for (int i = 0; i < peers.Length; i++) @@ -81,39 +84,9 @@ public async Task Test_ConnectionEstablished_AfterHandshake() for (int i = 0; i < peers.Length; i++) { - peerStores[i].Discover([peers[(i + 1) % 10].Address]); + peerStores[i].Discover([peers[(i + 1) % totalCount].Address]); } - await Task.Delay(1500000); - await Task.Delay(2000); - //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(), 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, peerContext, proto1); - //await downChannel.CloseAsync(); + await Task.Delay(5000); } - - } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs index 0fbe13b7..efcc5ed8 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -18,10 +18,12 @@ public class PubSubDiscoverySettings public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, PeerStore peerStore, ILocalPeer peer) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; + private Multiaddress? _localPeerAddr; private readonly PubSubDiscoverySettings _settings = settings; public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) { + _localPeerAddr = localPeerAddr; ITopicSubscription[] topics = _settings.Topics.Select(topic => { ITopicSubscription subscription = _pubSubRouter.Subscribe(topic); @@ -48,15 +50,27 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to { PublicKey = peer.Identity.PublicKey.ToByteString(), Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, - }.ToByteArray()); + }); } } } } + private void OnPeerMessage(byte[] msg) { - Peer peer = Peer.Parser.ParseFrom(msg); - peerStore.Discover([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); + try + { + Peer peer = Peer.Parser.ParseFrom(msg); + ByteString? addr = peer.Addrs.FirstOrDefault(); + if (addr is not null && Multiaddress.Decode(addr.ToByteArray()) != _localPeerAddr) + { + peerStore.Discover([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); + } + } + catch + { + + } } } diff --git a/src/samples/transport-interop/packages.lock.json b/src/samples/transport-interop/packages.lock.json index d84287f4..81d806c8 100644 --- a/src/samples/transport-interop/packages.lock.json +++ b/src/samples/transport-interop/packages.lock.json @@ -179,8 +179,8 @@ }, "Nethermind.Multiformats.Address": { "type": "Transitive", - "resolved": "1.1.2", - "contentHash": "CqtzYjreMd/nHBBnrkBX0DMoTjBCBET4Axm2WFMEKX3luSwCvrHEXUnrodvit2Eox+XRI/w59G0A1hlM6xlxRA==", + "resolved": "1.1.3", + "contentHash": "5cF9yHsP6w8AzvJuIJyB3fcuqpocD8e5PgPzYBjLmg09kDjo6dVATFUBMddwymgWFkzvVnE64m/vpHC7P0s6jA==", "dependencies": { "BinaryEncoding": "1.4.0", "Nethermind.Multiformats.Base": "2.0.3-preview.1", @@ -1203,7 +1203,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.2, )", + "Nethermind.Multiformats.Address": "[1.1.3, )", "SimpleBase": "[4.0.0, )" } }, From 3ef7660cb615b15b59322a6f124e76b2bd748e98 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 13 Sep 2024 11:41:21 +0300 Subject: [PATCH 05/14] Add profiler project --- .../Libp2p.Core.TestsBase/E2e/ChannelBus.cs | 17 +- .../E2e/TestMuxerProtocol.cs | 205 +++++++++++------- .../Libp2p.Protocols.Pubsub.Profiler.csproj | 16 ++ .../Program.cs | 90 ++++++++ .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 64 ++++-- .../MultistreamProtocolTests.cs | 157 +++++++++++++- .../PubsubDiscoveryProtocol.cs | 9 +- src/libp2p/Libp2p.sln | 7 + 8 files changed, 457 insertions(+), 108 deletions(-) create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs index cd33001b..c73a06f7 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs @@ -1,15 +1,18 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; using System.Threading.Channels; namespace Nethermind.Libp2p.Core.TestsBase.E2e; -public class ChannelBus +public class ChannelBus(TestContextLoggerFactory? fac = null) { + ILogger? logger = fac?.CreateLogger("bus"); + Dictionary> channels = []; - public IAsyncEnumerable GetIncomingRequests(PeerId serverId) + public async IAsyncEnumerable GetIncomingRequests(PeerId serverId) { Channel col = System.Threading.Channels.Channel.CreateUnbounded(); @@ -17,8 +20,14 @@ public IAsyncEnumerable GetIncomingRequests(PeerId serverId) { throw new Exception("Test listener with such peer id alread exists."); } + logger?.LogDebug($"Listen {serverId}"); - return col.Reader.ReadAllAsync(); + await foreach (var item in col.Reader.ReadAllAsync()) + { + logger?.LogDebug($"New request to {serverId}"); + yield return item; + } + logger?.LogDebug($"Listen end {serverId}"); } public IChannel Dial(PeerId self, PeerId serverId) @@ -28,6 +37,8 @@ public IChannel Dial(PeerId self, PeerId serverId) throw new Exception("Test listener with such peer id does not exist."); } + logger?.LogDebug($"Dial {self} -> {serverId}"); + Channel channel = new(); _ = col.Writer.WriteAsync(channel.Reverse); return channel; diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index e7a30bba..c0cf1d53 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.TestsBase.E2e; +using NUnit.Framework.Constraints; +using Org.BouncyCastle.Ocsp; using Org.BouncyCastle.Utilities.Encoders; using System.Buffers; @@ -11,7 +13,7 @@ class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : { private const string id = "test-muxer"; - private readonly ILogger? logger = loggerFactory?.CreateLogger(id); + private readonly ILogger? logger = null;// loggerFactory?.CreateLogger(id); public string Id => id; @@ -35,7 +37,7 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFa private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelFactory, IPeerContext context, bool isListen = false) { - uint counter = isListen ? 1u : 2u; + uint counter = isListen ? 1u : 0u; Dictionary chans = []; string peer = ""; @@ -58,10 +60,10 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF _ = Task.Run(async () => { - foreach (var item in context.SubDialRequests) + foreach (var item in context.SubDialRequests.GetConsumingEnumerable()) { uint chanId = Interlocked.Add(ref counter, 2); - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}({chanId}): Sub-request {item.SubProtocol} {item.CompletionSource is not null}"); + logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}({chanId}): Sub-request {item.SubProtocol} {item.CompletionSource is not null} from {context.RemotePeer.Address.GetPeerId()}"); chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; var response = new MuxerPacket() @@ -70,80 +72,104 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF Type = MuxerPacketType.NewStreamRequest, Protocols = { item.SubProtocol!.Id } }; - await downChannel.WriteSizeAndProtobufAsync(response); + + 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"); + }); while (true) { - var packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); + try { + logger?.LogDebug($"{logPrefix}: < READY({(isListen?"list":"dial")})"); - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + var packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); - switch (packet.Type) - { - case MuxerPacketType.NewStreamRequest: - IProtocol? selected = null; - foreach (var proto in packet.Protocols) - { - selected = upChannelFactory.SubProtocols.FirstOrDefault(x => x.Id == proto); - if (selected is not null) break; - } - if (selected is not null) - { - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Matched {selected}"); - var response = new MuxerPacket() + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): < Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + switch (packet.Type) + { + case MuxerPacketType.NewStreamRequest: + IProtocol? selected = null; + foreach (var proto in packet.Protocols) + { + selected = upChannelFactory.SubProtocols.FirstOrDefault(x => x.Id == proto); + if (selected is not null) break; + } + if (selected is not null) { - ChannelId = packet.ChannelId, - Type = MuxerPacketType.NewStreamResponse, - Protocols = + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Matched {selected}"); + var response = new MuxerPacket() + { + ChannelId = packet.ChannelId, + Type = MuxerPacketType.NewStreamResponse, + Protocols = { selected.Id } - }; + }; - var req = new ChannelRequest { SubProtocol = selected }; + var req = new ChannelRequest { SubProtocol = selected }; - IChannel upChannel = upChannelFactory.SubListen(context, req); - chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; - _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); - await downChannel.WriteSizeAndProtobufAsync(response); - } - else - { - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No match {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + IChannel upChannel = upChannelFactory.SubListen(context, req); + chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; + _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); + + logger?.LogDebug($"{logPrefix}({response.ChannelId}): > Packet {response.Type} {string.Join(",", response.Protocols)} {response.Data?.Length ?? 0}"); - var response = new MuxerPacket() + _ = downChannel.WriteSizeAndProtobufAsync(response); + } + else { - ChannelId = packet.ChannelId, - Type = MuxerPacketType.NewStreamResponse, - }; - await downChannel.WriteSizeAndProtobufAsync(response); - } - break; - 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); - chans[packet.ChannelId].UpChannel = upChannel; - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SubProtocol}"); - _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); - } - 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())); - break; - case MuxerPacketType.CloseWrite: - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Remote EOF"); - - chans[packet.ChannelId].RemoteClosedWrites = true; - _ = chans[packet.ChannelId].UpChannel!.WriteEofAsync(); - break; - default: - break; + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No match {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + var response = new MuxerPacket() + { + ChannelId = packet.ChannelId, + Type = MuxerPacketType.NewStreamResponse, + }; + + logger?.LogDebug($"{logPrefix}({response.ChannelId}): > Packet {response.Type} {string.Join(",", response.Protocols)} {response.Data?.Length ?? 0}"); + + _ = downChannel.WriteSizeAndProtobufAsync(response); + } + break; + 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); + chans[packet.ChannelId].UpChannel = upChannel; + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SubProtocol}"); + _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); + } + else{ + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No protocols = no upchanel"); + } + 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())); + break; + case MuxerPacketType.CloseWrite: + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Remote EOF"); + + chans[packet.ChannelId].RemoteClosedWrites = true; + _ = chans[packet.ChannelId].UpChannel!.WriteEofAsync(); + break; + default: + break; + } } + catch + { + + + } + } } @@ -151,32 +177,47 @@ private Task HandleUpchannelData(IChannel downChannel, Dictionary { - await foreach (var item in upChannel.ReadAllAsync()) + try { - var data = item.ToArray(); - logger?.LogDebug($"{logPrefix}({channelId}): Upchannel data {data.Length} {Hex.ToHexString(data, false)}"); + await foreach (var item in upChannel.ReadAllAsync()) + { + var data = item.ToArray(); + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel data {data.Length} {Hex.ToHexString(data, false)}"); + + var packet = new MuxerPacket() + { + ChannelId = channelId, + Type = MuxerPacketType.Data, + Data = ByteString.CopyFrom(item.ToArray()) + }; + + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): > Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); - var packet = new MuxerPacket() + _ = downChannel.WriteSizeAndProtobufAsync(packet); + } + if (chans[channelId].RemoteClosedWrites) { - ChannelId = channelId, - Type = MuxerPacketType.Data, - Data = ByteString.CopyFrom(item.ToArray()) - }; - await downChannel.WriteSizeAndProtobufAsync(packet); + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel dial/listen complete"); + chans[channelId].Tcs?.SetResult(); + } + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel write close"); + + { + var packet = new MuxerPacket() + { + ChannelId = channelId, + Type = MuxerPacketType.CloseWrite, + }; + + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): > Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + _ = downChannel.WriteSizeAndProtobufAsync(packet); + } } - if (chans[channelId].RemoteClosedWrites) + catch { - logger?.LogDebug($"{logPrefix}({channelId}): Upchannel dial/listen complete"); - chans[channelId].Tcs?.SetResult(); - } - logger?.LogDebug($"{logPrefix}({channelId}): Upchannel write close"); - await downChannel.WriteSizeAndProtobufAsync(new MuxerPacket() - { - ChannelId = channelId, - Type = MuxerPacketType.CloseWrite, - }); - + } }); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj new file mode 100644 index 00000000..67992949 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs new file mode 100644 index 00000000..2f24f487 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + + +using Libp2p.Protocols.PubSubDiscovery; +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 System.Text; + +int totalCount = 5; +TestContextLoggerFactory fac = new TestContextLoggerFactory(); +// There is common communication point +ChannelBus commonBus = new(fac); +ILocalPeer[] peers = new ILocalPeer[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(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => fac) + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => sp.GetService()!.Build()) + .BuildServiceProvider(); + + IPeerFactory peerFactory = sp.GetService()!; + ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); + PubsubRouter router = routers[i] = sp.GetService()!; + PeerStore peerStore = sp.GetService()!; + PubSubDiscoveryProtocol disc = new(router, new PubSubDiscoverySettings() { Interval = 300 }, peerStore, peer); + _ = router.RunAsync(peer, peerStore); + peerStores[i] = peerStore; + _ = disc.DiscoverAsync(peers[i].Address); +} + +for (int i = 0; i < peers.Length; i++) +{ + peerStores[i].Discover([peers[(i + 1) % totalCount].Address]); +} + +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()); +} + +for (int i = 0; i < routers.Length; i++) +{ + routers[i].Subscribe("test"); +} + +await Task.Delay(5000); + +var testTopic = routers[1].Subscribe("test"); +var testTopicEnd = routers[totalCount - 1].Subscribe("test"); +testTopicEnd.OnMessage += (s) => Console.WriteLine(Encoding.UTF8.GetString(s)); + +testTopic.Publish(Encoding.UTF8.GetBytes("test")); + +for (int i = 0; i < 20; i++) +{ + Console.WriteLine(i * 100); + await Task.Delay(100); +} + +Console.WriteLine("Routers"); + +for (int i = 0; i < routers.Length; i++) +{ + Console.WriteLine(routers[i].ToString()); +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 00e39d5c..6b24cb8d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -31,7 +31,13 @@ public class PubsubRouter(ILoggerFactory? loggerFactory = default) : IRoutingSta int _ctr = Interlocked.Increment(ref ctr); public override string ToString() { - return $"Router#{_ctr}: {localPeer?.Address.GetPeerId() ?? "null"}"; + //{string.Join("|", peerState.Select(x => $"{x.Key}:{x.Value.SendRpc is not null}"))} + return $"Router#{_ctr}: {localPeer?.Address.GetPeerId() ?? "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}"))}, " + + $"fPeers: {string.Join("|", fPeers.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + + $"gPeers: {string.Join("|", gPeers.Select(m => $"{m.Key}:{m.Value.Count}"))}"; } public const string FloodsubProtocolVersion = "/floodsub/1.0.0"; public const string GossipsubProtocolVersionV10 = "/meshsub/1.0.0"; @@ -454,25 +460,35 @@ public void Publish(string topicId, byte[] message) internal CancellationToken OutboundConnection(Multiaddress addr, string protocolId, Task dialTask, Action sendRpc) { - PubsubPeer peer; + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out"); PeerId? peerId = addr.GetPeerId(); if (peerId is null) { + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 1 peerid null"); return Canceled; } - peer = new PubsubPeer(peerId, protocolId) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }; + PubsubPeer peer = peerState.GetOrAdd(peerId, (id) => new PubsubPeer(peerId, protocolId) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }); - if (!peerState.TryAdd(peerId, peer)) + lock (peer) { - if (peerState[peerId].SendRpc is null) + if (peer.SendRpc == sendRpc) { - peerState[peerId].SendRpc = sendRpc; + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 2 Peer created in outbound"); } else { - return Canceled; + if (peer.SendRpc is null) + { + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 3 Peer created in inbound"); + peer.SendRpc = sendRpc; + } + else + { + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 3 Peer created in another outbound"); + return Canceled; + } } } @@ -498,27 +514,37 @@ internal CancellationToken OutboundConnection(Multiaddress addr, string protocol } reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); }); + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 4 send hello {string.Join(",", topicState.Keys)}"); + Rpc helloMessage = new Rpc().WithTopics(topicState.Keys.ToList(), Enumerable.Empty()); peer.Send(helloMessage); logger?.LogDebug("Outbound {peerId}", peerId); + + logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 5 return token {peer.TokenSource.Token}"); + return peer.TokenSource.Token; } internal CancellationToken InboundConnection(Multiaddress addr, string protocolId, Task listTask, Task dialTask, Func subDial) { - PubsubPeer? remotePeer; PeerId? peerId = addr.GetPeerId(); + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1 remote peer {peerId}"); if (peerId is null || peerId == localPeer!.Identity.PeerId) { + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.1 remote cancel {peerId}"); + return Canceled; } - remotePeer = new PubsubPeer(peerId, protocolId) { Address = addr, InititatedBy = ConnectionInitiation.Remote }; logger?.LogDebug("Inbound {peerId}", peerId); - if (peerState.TryAdd(peerId, remotePeer)) + + PubsubPeer? newPeer = null; + PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); + if (newPeer is not null) { - logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: Created in inbound {peerId}"); + //logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); listTask.ContinueWith(t => { peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); @@ -543,11 +569,16 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI }); subDial(); - return remotePeer.TokenSource.Token; + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 3 subdialed"); + + return newPeer.TokenSource.Token; } else { - return peerState[peerId].TokenSource.Token; + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: Already created, no inbound {peerId}"); + + logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.2 new peer null"); + return existingPeer.TokenSource.Token; } } @@ -578,6 +609,13 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) { fPeers.GetOrAdd(sub.Topicid, _ => new HashSet()).Add(peerId); } + if (mesh.ContainsKey(sub.Topicid)) + { + mesh[sub.Topicid].Add(peerId); + }else + { + fanout.GetOrAdd(sub.Topicid, _ => new HashSet()).Add(peerId); + } } else { diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs index b50a941c..2169ef2d 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -9,6 +9,9 @@ using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.TestsBase.E2e; using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Protocols.Pubsub.Dto; +using NUnit.Framework.Internal; +using System.Data.Common; using System.Diagnostics.Metrics; namespace Libp2p.Protocols.Multistream.Tests; @@ -28,7 +31,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake2() .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() .AddSingleton() - .AddSingleton(sp => sp.GetService()!.Build()) + .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); @@ -37,7 +40,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake2() .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() .AddSingleton() - .AddSingleton(sp => sp.GetService()!.Build()) + .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); @@ -50,23 +53,34 @@ public async Task Test_ConnectionEstablished_AfterHandshake2() await remotePeerB.DialAsync(); } + [Test] + public async Task Test_Bus() + { + int totalCount = 3; + TestContextLoggerFactory fac = new TestContextLoggerFactory(); + // There is common communication point + ChannelBus bus = new(fac); + //bus.GetIncomingRequests(); + } + [Test] public async Task Test_ConnectionEstablished_AfterHandshake() { - int totalCount = 10; + int totalCount = 20; + TestContextLoggerFactory fac = new TestContextLoggerFactory(); // There is common communication point - ChannelBus commonBus = new(); + ChannelBus commonBus = new(fac); ILocalPeer[] peers = new ILocalPeer[totalCount]; PeerStore[] peerStores = new PeerStore[totalCount]; PubsubRouter[] routers = new PubsubRouter[totalCount]; - for (int i = 0; i < peers.Length; i++) + for (int i = 0; i < totalCount; i++) { // But we create a seprate setup for every peer ServiceProvider sp = new ServiceCollection() .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) - .AddSingleton(sp => new TestContextLoggerFactory()) + .AddSingleton(sp => fac) .AddSingleton() .AddSingleton() .AddSingleton(sp=> sp.GetService()!.Build()) @@ -82,11 +96,140 @@ public async Task Test_ConnectionEstablished_AfterHandshake() _ = disc.DiscoverAsync(peers[i].Address); } + await Task.Delay(1000); + for (int i = 0; i < peers.Length; i++) { peerStores[i].Discover([peers[(i + 1) % totalCount].Address]); } - await Task.Delay(5000); + await Task.Delay(50000); } + + //[Test] + //public async Task Derp() + //{ + + + // internal CancellationToken OutboundConnection(Multiaddress addr, string protocolId, Task dialTask, Action sendRpc) + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out"); + // PeerId? peerId = addr.GetPeerId(); + + // if (peerId is null) + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 1 peerid null"); + // return Canceled; + // } + + // PubsubPeer peer = peerState.GetOrAdd(peerId, (id) => new PubsubPeer(peerId, protocolId) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }); + + // lock (peer) + // { + // if (peer.SendRpc == sendRpc) + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 2 SendRpc null"); + // } + // else + // { + // if (peer.SendRpc is null) + // { + // peer.SendRpc = sendRpc; + // } + // else + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 3 SendRpc not null, cancelled"); + // return Canceled; + // } + // } + // } + + // dialTask.ContinueWith(t => + // { + // peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); + // peerState.TryRemove(peerId, out _); + // foreach (var topicPeers in fPeers) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in gPeers) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in fanout) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in mesh) + // { + // topicPeers.Value.Remove(peerId); + // } + // reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); + // }); + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 4 send hello {string.Join(",", topicState.Keys)}"); + + // Rpc helloMessage = new Rpc().WithTopics(topicState.Keys.ToList(), Enumerable.Empty()); + // peer.Send(helloMessage); + // logger?.LogDebug("Outbound {peerId}", peerId); + + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 5 return token {peer.TokenSource.Token}"); + + // return peer.TokenSource.Token; + // } + + // internal CancellationToken InboundConnection(Multiaddress addr, string protocolId, Task listTask, Task dialTask, Func subDial) + // { + // PeerId? peerId = addr.GetPeerId(); + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1 remote peer {peerId}"); + + // if (peerId is null || peerId == localPeer!.Identity.PeerId) + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.1 remote cancel {peerId}"); + + // return Canceled; + // } + + // logger?.LogDebug("Inbound {peerId}", peerId); + + // PubsubPeer? newPeer = null; + // PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); + // if (newPeer is not null) + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 2 new peer {peerId}"); + // //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 (var topicPeers in fPeers) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in gPeers) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in fanout) + // { + // topicPeers.Value.Remove(peerId); + // } + // foreach (var topicPeers in mesh) + // { + // topicPeers.Value.Remove(peerId); + // } + // reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); + // }); + + // subDial(); + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 3 subdialed"); + + // return newPeer.TokenSource.Token; + // } + // else + // { + // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.2 new peer null"); + // return existingPeer.TokenSource.Token; + // } + // } + //} } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs index efcc5ed8..62b5df5d 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -6,6 +6,7 @@ using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols.Pubsub; using Google.Protobuf; +using System.Diagnostics; namespace Libp2p.Protocols.PubSubDiscovery; public class PubSubDiscoverySettings @@ -61,11 +62,13 @@ private void OnPeerMessage(byte[] msg) try { Peer peer = Peer.Parser.ParseFrom(msg); - ByteString? addr = peer.Addrs.FirstOrDefault(); - if (addr is not null && Multiaddress.Decode(addr.ToByteArray()) != _localPeerAddr) + Multiaddress[] addrs = [.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]; + PeerId? remotePeerId = addrs.FirstOrDefault()?.GetPeerId(); + if (remotePeerId is not null && remotePeerId != _localPeerAddr?.GetPeerId()!) { - peerStore.Discover([.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]); + peerStore.Discover(addrs); } + Debug.WriteLine($"{_localPeerAddr}: New peer discovered {peer}"); } catch { diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 589f358a..652fb6d1 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -69,6 +69,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubDisc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubDiscovery.Tests", "Libp2p.Protocols.PubsubDiscovery.Tests\Libp2p.Protocols.PubsubDiscovery.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}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -187,6 +189,10 @@ Global {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 + {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -213,6 +219,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} From 8ab6abbbbcbb257b325709ce7f0493802f622be6 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 13 Sep 2024 17:09:20 +0300 Subject: [PATCH 06/14] Compiles --- .../Libp2p.Core.Benchmarks/Benchmarks.cs | 4 +- .../Libp2p.Core.TestsBase/Dto/MuxerPacket.cs | 22 +- .../Dto/MuxerPacket.proto | 2 +- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 2 - .../E2e/TestMuxerProtocol.cs | 12 +- src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs | 1 - .../MultistreamProtocolTests.cs | 4 +- .../Program.cs | 9 +- .../FloodsubProtocolTests.cs | 2 +- .../GossipsubProtocolTests.cs | 2 +- .../{ITopicSubscription.cs => ITopic.cs} | 11 +- .../PubsubRouter.Topics.cs | 161 ++++++++++++++ .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 144 +------------ src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs | 14 +- src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs | 1 + .../MultistreamProtocolTests.cs | 196 +++++------------- .../PubSubTestSetup.cs | 56 +++++ .../Usings.cs | 1 - .../PubsubDiscoveryProtocol.cs | 48 +++-- .../Usings.cs | 10 + src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 1 - .../Libp2p/ServiceProviderExtensions.cs | 1 - src/samples/pubsub-chat/Program.cs | 3 +- 23 files changed, 354 insertions(+), 353 deletions(-) rename src/libp2p/Libp2p.Protocols.Pubsub/{ITopicSubscription.cs => ITopic.cs} (64%) create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs diff --git a/src/libp2p/Libp2p.Core.Benchmarks/Benchmarks.cs b/src/libp2p/Libp2p.Core.Benchmarks/Benchmarks.cs index 56433be1..76b9eac0 100644 --- a/src/libp2p/Libp2p.Core.Benchmarks/Benchmarks.cs +++ b/src/libp2p/Libp2p.Core.Benchmarks/Benchmarks.cs @@ -2,10 +2,8 @@ using System.Buffers; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using Nethermind.Libp2p.Core; -using Channel = Nethermind.Libp2p.Core.Channel; -namespace Libp2p.Core.Benchmarks; +namespace Nethermind.Libp2p.Core.Benchmarks; [MemoryDiagnoser] public class ChannelsBenchmark diff --git a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs index 82f2456f..c64f5b8a 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.cs @@ -9,7 +9,7 @@ using pbc = global::Google.Protobuf.Collections; using pbr = global::Google.Protobuf.Reflection; using scg = global::System.Collections.Generic; -namespace Libp2p.Core.TestsBase.Dto { +namespace Nethermind.Libp2p.Core.TestsBase.Dto { /// Holder for reflection information generated from MuxerPacket.proto public static partial class MuxerPacketReflection { @@ -28,12 +28,12 @@ static MuxerPacketReflection() { "AigOMhAuTXV4ZXJQYWNrZXRUeXBlEhEKCUNoYW5uZWxJZBgCIAIoDRIMCgRE", "YXRhGAQgASgMEhEKCVByb3RvY29scxgDIAMoCSpYCg9NdXhlclBhY2tldFR5", "cGUSFAoQTmV3U3RyZWFtUmVxdWVzdBAAEhUKEU5ld1N0cmVhbVJlc3BvbnNl", - "EAESCAoERGF0YRACEg4KCkNsb3NlV3JpdGUQA0IcqgIZTGlicDJwLkNvcmUu", - "VGVzdHNCYXNlLkR0bw==")); + "EAESCAoERGF0YRACEg4KCkNsb3NlV3JpdGUQA0InqgIkTmV0aGVybWluZC5M", + "aWJwMnAuQ29yZS5UZXN0c0Jhc2UuRHRv")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, - new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Libp2p.Core.TestsBase.Dto.MuxerPacketType), }, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Libp2p.Core.TestsBase.Dto.MuxerPacket), global::Libp2p.Core.TestsBase.Dto.MuxerPacket.Parser, new[]{ "Type", "ChannelId", "Data", "Protocols" }, null, null, null, null) + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacket), global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacket.Parser, new[]{ "Type", "ChannelId", "Data", "Protocols" }, null, null, null, null) })); } #endregion @@ -66,7 +66,7 @@ public sealed partial class MuxerPacket : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Libp2p.Core.TestsBase.Dto.MuxerPacketReflection.Descriptor.MessageTypes[0]; } + get { return global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketReflection.Descriptor.MessageTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -102,12 +102,12 @@ public MuxerPacket Clone() { /// Field number for the "Type" field. public const int TypeFieldNumber = 1; - private readonly static global::Libp2p.Core.TestsBase.Dto.MuxerPacketType TypeDefaultValue = global::Libp2p.Core.TestsBase.Dto.MuxerPacketType.NewStreamRequest; + private readonly static global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType TypeDefaultValue = global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType.NewStreamRequest; - private global::Libp2p.Core.TestsBase.Dto.MuxerPacketType type_; + private global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType type_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::Libp2p.Core.TestsBase.Dto.MuxerPacketType Type { + public global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType Type { get { if ((_hasBits0 & 1) != 0) { return type_; } else { return TypeDefaultValue; } } set { _hasBits0 |= 1; @@ -337,7 +337,7 @@ public void MergeFrom(pb::CodedInputStream input) { _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; case 8: { - Type = (global::Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); + Type = (global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); break; } case 16: { @@ -372,7 +372,7 @@ public void MergeFrom(pb::CodedInputStream input) { _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; case 8: { - Type = (global::Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); + Type = (global::Nethermind.Libp2p.Core.TestsBase.Dto.MuxerPacketType) input.ReadEnum(); break; } case 16: { diff --git a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto index 8055d501..67302da5 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto +++ b/src/libp2p/Libp2p.Core.TestsBase/Dto/MuxerPacket.proto @@ -1,5 +1,5 @@ syntax = "proto2"; -option csharp_namespace = "Libp2p.Core.TestsBase.Dto"; +option csharp_namespace = "Nethermind.Libp2p.Core.TestsBase.Dto"; enum MuxerPacketType { NewStreamRequest = 0; diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index bcd136db..91f51f44 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Microsoft.Extensions.DependencyInjection; - namespace Nethermind.Libp2p.Core.TestsBase.E2e; public class TestBuilder(ChannelBus? commmonBus = null, IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index c0cf1d53..adde8658 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -1,11 +1,9 @@ using Google.Protobuf; -using Libp2p.Core.TestsBase.Dto; using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase.Dto; using Nethermind.Libp2p.Core.TestsBase.E2e; -using NUnit.Framework.Constraints; -using Org.BouncyCastle.Ocsp; using Org.BouncyCastle.Utilities.Encoders; using System.Buffers; @@ -83,8 +81,9 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF while (true) { - try { - logger?.LogDebug($"{logPrefix}: < READY({(isListen?"list":"dial")})"); + try + { + logger?.LogDebug($"{logPrefix}: < READY({(isListen ? "list" : "dial")})"); var packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); @@ -146,7 +145,8 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SubProtocol}"); _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); } - else{ + else + { logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No protocols = no upchanel"); } break; diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs index 8dd20342..948ed7f2 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using Multiformats.Address.Protocols; using System.Buffers.Binary; using System.Collections.Concurrent; diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs index cc682465..8b9ce61c 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs @@ -1,9 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Protocols; - -namespace Libp2p.Protocols.Multistream.Tests; +namespace Nethermind.Libp2p.Protocols.Multistream.Tests; [TestFixture] [Parallelizable(scope: ParallelScope.All)] diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index 2f24f487..e1f43e43 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -1,14 +1,13 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT - -using Libp2p.Protocols.PubSubDiscovery; 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 System.Text; @@ -65,13 +64,13 @@ for (int i = 0; i < routers.Length; i++) { - routers[i].Subscribe("test"); + routers[i].GetTopic("test"); } await Task.Delay(5000); -var testTopic = routers[1].Subscribe("test"); -var testTopicEnd = routers[totalCount - 1].Subscribe("test"); +var testTopic = routers[1].GetTopic("test"); +var testTopicEnd = routers[totalCount - 1].GetTopic("test"); testTopicEnd.OnMessage += (s) => Console.WriteLine(Encoding.UTF8.GetString(s)); testTopic.Publish(Encoding.UTF8.GetBytes("test")); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index 51a6ed61..adcd47ed 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -29,7 +29,7 @@ public async Task Test_Peer_is_in_fpeers() List sentRpcs = new(); _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); - router.Subscribe(commonTopic); + router.GetTopic(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); discovery.OnAddPeer!([discoveredPeer]); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index c71c31c5..b748fd11 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -25,7 +25,7 @@ public async Task Test_New_messages_are_sent_to_mesh_only() List sentRpcs = new(); _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); - router.Subscribe(commonTopic); + router.GetTopic(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs similarity index 64% rename from src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs rename to src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs index 25d41f6b..f6d22a9d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ITopicSubscription.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/ITopic.cs @@ -5,13 +5,14 @@ namespace Nethermind.Libp2p.Protocols.Pubsub; -public interface ITopicSubscription +public interface ITopic { event Action? OnMessage; + void Publish(byte[] bytes); - void Publish(IMessage msg) - { - Publish(msg.ToByteArray()); - } + void Publish(IMessage msg) => Publish(msg.ToByteArray()); + + bool IsSubscribed { get; } + void Subscribe(); void Unsubscribe(); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs new file mode 100644 index 00000000..f4fc1c65 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols.Pubsub.Dto; +using System.Buffers.Binary; +using System.Collections.Concurrent; + +namespace Nethermind.Libp2p.Protocols.Pubsub; + +public partial class PubsubRouter +{ + private readonly ConcurrentDictionary topicState = new(); + + public ITopic GetTopic(string topicId, bool subscribe = true) + { + Topic topic = topicState.GetOrAdd(topicId, (tId) => new(this, tId)); + + if (subscribe) + { + Subscribe(topicId); + } + + return topic; + } + + public void Subscribe(string topicId) + { + topicState.GetOrAdd(topicId, (id) => new Topic(this, topicId)).IsSubscribed = true; + + if (!fPeers.TryAdd(topicId, [])) + { + // Already exists + return; + } + + gPeers.TryAdd(topicId, []); + + HashSet meshPeers = mesh.GetOrAdd(topicId, []); + + if (fanout.TryGetValue(topicId, out HashSet? fanoutPeers)) + { + foreach (PeerId peerId in fanoutPeers) + { + meshPeers.Add(peerId); + } + + fanoutPeers.Clear(); + } + + Rpc topicUpdate = new Rpc().WithTopics([topicId], []); + foreach (var peer in peerState) + { + peer.Value.Send(topicUpdate); + } + } + + public void Unsubscribe(string topicId) + { + topicState.GetOrAdd(topicId, (id) => new Topic(this, topicId)).IsSubscribed = false; + + foreach (PeerId peerId in fPeers[topicId]) + { + Rpc msg = new Rpc() + .WithTopics([], [topicId]); + + peerState.GetValueOrDefault(peerId)?.Send(msg); + } + + foreach (PeerId peerId in gPeers[topicId]) + { + Rpc msg = new Rpc() + .WithTopics([], [topicId]); + + if (mesh.TryGetValue(topicId, out HashSet? topicMesh) && topicMesh.Contains(peerId)) + { + msg.Ensure(r => r.Control.Prune).Add(new ControlPrune { TopicID = topicId }); + } + peerState.GetValueOrDefault(peerId)?.Send(msg); + } + } + + public void UnsubscribeAll() + { + foreach (PeerId? peerId in fPeers.SelectMany(kv => kv.Value)) + { + Rpc msg = new Rpc() + .WithTopics(Enumerable.Empty(), topicState.Keys); + + peerState.GetValueOrDefault(peerId)?.Send(msg); + } + ConcurrentDictionary peerMessages = new(); + + foreach (PeerId? peerId in gPeers.SelectMany(kv => kv.Value)) + { + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .WithTopics(Enumerable.Empty(), topicState.Keys); + } + + foreach (KeyValuePair> topicMesh in mesh) + { + foreach (PeerId peerId in topicMesh.Value) + { + peerMessages.GetOrAdd(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); + } + } + + public void Publish(string topicId, byte[] message) + { + topicState.GetOrAdd(topicId, (id) => new Topic(this, topicId)); + + ulong seqNo = this.seqNo++; + byte[] seqNoBytes = new byte[8]; + BinaryPrimitives.WriteUInt64BigEndian(seqNoBytes, seqNo); + Rpc rpc = new Rpc().WithMessages(topicId, seqNo, localPeer!.Address.GetPeerId()!.Bytes, message, localPeer.Identity); + + foreach (PeerId peerId in fPeers[topicId]) + { + peerState.GetValueOrDefault(peerId)?.Send(rpc); + } + + if (mesh.ContainsKey(topicId)) + { + foreach (PeerId peerId in mesh[topicId]) + { + peerState.GetValueOrDefault(peerId)?.Send(rpc); + } + } + else + { + fanoutLastPublished[topicId] = DateTime.Now; + HashSet topicFanout = fanout.GetOrAdd(topicId, _ => new HashSet()); + + if (topicFanout.Count == 0) + { + HashSet? topicPeers = gPeers.GetValueOrDefault(topicId); + if (topicPeers is { Count: > 0 }) + { + foreach (PeerId peer in topicPeers.Take(settings.Degree)) + { + topicFanout.Add(peer); + } + } + } + + foreach (PeerId peerId in topicFanout) + { + peerState.GetValueOrDefault(peerId)?.Send(rpc); + } + } + } + +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 6b24cb8d..94e6eb09 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -7,9 +7,7 @@ using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols.Pubsub.Dto; -using System.Buffers.Binary; using System.Collections.Concurrent; -using System.Collections.ObjectModel; using System.Diagnostics; namespace Nethermind.Libp2p.Protocols.Pubsub; @@ -25,7 +23,7 @@ internal interface IRoutingStateContainer Task Heartbeat(); } -public class PubsubRouter(ILoggerFactory? loggerFactory = default) : IRoutingStateContainer +public partial class PubsubRouter(ILoggerFactory? loggerFactory = default) : IRoutingStateContainer { static int ctr = 0; int _ctr = Interlocked.Increment(ref ctr); @@ -33,7 +31,7 @@ public override string ToString() { //{string.Join("|", peerState.Select(x => $"{x.Key}:{x.Value.SendRpc is not null}"))} return $"Router#{_ctr}: {localPeer?.Address.GetPeerId() ?? "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}, " + $"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}"))}, " + @@ -122,8 +120,6 @@ public Action? SendRpc Task IRoutingStateContainer.Heartbeat() => Heartbeat(); #endregion - public PeerId? LocalPeerId { get; private set; } - public event Action? OnMessage; public Func? VerifyMessage = null; @@ -139,20 +135,20 @@ public Action? SendRpc // all floodsub peers in topics private readonly ConcurrentDictionary> fPeers = new(); + // all gossipsub peers in topics private readonly ConcurrentDictionary> gPeers = new(); // gossip peers in mesh, which is subnet for message exchange private readonly ConcurrentDictionary> mesh = new(); - // gossip peers in mesh, which is subnet for message exchange for topics that we did not subscribe to + // gossip peers in mesh, which is subnet for message exchange for topics that we did not subscribe to, but we sent messages recently private readonly ConcurrentDictionary> fanout = new(); private readonly ConcurrentDictionary fanoutLastPublished = new(); - // all peers with their connection status private readonly ConcurrentDictionary peerState = new(); - private readonly ConcurrentDictionary topicState = new(); + private readonly ConcurrentBag reconnections = new(); private ulong seqNo = 1; @@ -179,8 +175,6 @@ public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? sett limboMessageCache = new(this.settings.MessageCacheTtl); dontWantMessages = new(this.settings.MessageCacheTtl); - LocalPeerId = new PeerId(localPeer.Address.Get().ToString()!); - _ = localPeer.ListenAsync(localPeer.Address, token); logger?.LogInformation("Started"); @@ -256,90 +250,11 @@ private async Task Reconnect(CancellationToken token) } } - public ITopicSubscription Subscribe(string topicId) - { - Topic topic = topicState.GetOrAdd(topicId, (tId) => new(this, tId)); - - if (!fPeers.TryAdd(topicId, [])) - { - // Already exists - return topic; - } - - gPeers.TryAdd(topicId, []); - - mesh.TryAdd(topicId, []); - - Rpc topicUpdate = new Rpc().WithTopics([topicId], []); - foreach (var peer in peerState) - { - peer.Value.Send(topicUpdate); - } - - return topic; - } - - public void Unsubscribe(string topicId) - { - foreach (PeerId peerId in fPeers[topicId]) - { - Rpc msg = new Rpc() - .WithTopics([], [topicId]); - - peerState.GetValueOrDefault(peerId)?.Send(msg); - } - foreach (PeerId peerId in gPeers[topicId]) - { - Rpc msg = new Rpc() - .WithTopics([], [topicId]); - - if (mesh.TryGetValue(topicId, out HashSet? topicMesh) && topicMesh.Contains(peerId)) - { - msg.Ensure(r => r.Control.Prune).Add(new ControlPrune { TopicID = topicId }); - } - peerState.GetValueOrDefault(peerId)?.Send(msg); - } - } - - public void UnsubscribeAll() - { - foreach (PeerId? peerId in fPeers.SelectMany(kv => kv.Value)) - { - Rpc msg = new Rpc() - .WithTopics(Enumerable.Empty(), topicState.Keys); - - peerState.GetValueOrDefault(peerId)?.Send(msg); - } - ConcurrentDictionary peerMessages = new(); - - foreach (PeerId? peerId in gPeers.SelectMany(kv => kv.Value)) - { - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .WithTopics(Enumerable.Empty(), topicState.Keys); - } - - foreach (KeyValuePair> topicMesh in mesh) - { - foreach (PeerId peerId in topicMesh.Value) - { - peerMessages.GetOrAdd(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); - } - } - public Task Heartbeat() { ConcurrentDictionary peerMessages = new(); lock (this) { - foreach (KeyValuePair> mesh in mesh) { if (mesh.Value.Count < settings.LowestDegree) @@ -391,7 +306,7 @@ public Task Heartbeat() IEnumerable> msgs = messageCache.ToList().GroupBy(m => m.Topic); - foreach (string? topic in mesh.Keys.Concat(fanout.Keys).Distinct().ToArray()) + foreach (string? topic in gPeers.Keys.Concat(fanout.Keys).Distinct().ToArray()) { IGrouping? msgsInTopic = msgs.FirstOrDefault(mit => mit.Key == topic); if (msgsInTopic is not null) @@ -416,48 +331,6 @@ public Task Heartbeat() return Task.CompletedTask; } - public void Publish(string topicId, byte[] message) - { - ulong seqNo = this.seqNo++; - byte[] seqNoBytes = new byte[8]; - BinaryPrimitives.WriteUInt64BigEndian(seqNoBytes, seqNo); - Rpc rpc = new Rpc().WithMessages(topicId, seqNo, LocalPeerId.Bytes, message, localPeer.Identity); - - foreach (PeerId peerId in fPeers[topicId]) - { - peerState.GetValueOrDefault(peerId)?.Send(rpc); - } - if (mesh.ContainsKey(topicId)) - { - foreach (PeerId peerId in mesh[topicId]) - { - peerState.GetValueOrDefault(peerId)?.Send(rpc); - } - } - else - { - fanoutLastPublished[topicId] = DateTime.Now; - HashSet topicFanout = fanout.GetOrAdd(topicId, _ => new HashSet()); - - if (topicFanout.Count == 0) - { - HashSet? topicPeers = gPeers.GetValueOrDefault(topicId); - if (topicPeers is { Count: > 0 }) - { - foreach (PeerId peer in topicPeers.Take(settings.Degree)) - { - topicFanout.Add(peer); - } - } - } - - foreach (PeerId peerId in topicFanout) - { - peerState.GetValueOrDefault(peerId)?.Send(rpc); - } - } - } - internal CancellationToken OutboundConnection(Multiaddress addr, string protocolId, Task dialTask, Action sendRpc) { logger?.LogDebug($"{localPeer?.Identity.PeerId}-out"); @@ -589,7 +462,6 @@ 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) @@ -612,7 +484,8 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) if (mesh.ContainsKey(sub.Topicid)) { mesh[sub.Topicid].Add(peerId); - }else + } + else { fanout.GetOrAdd(sub.Topicid, _ => new HashSet()).Add(peerId); } @@ -737,6 +610,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) peerMessages.GetOrAdd(peerId, _ => new Rpc()) .Ensure(r => r.Control.Prune) .Add(new ControlPrune { TopicID = prune.TopicID }); + } } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs index 7ceec8d7..24400a88 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Topic.cs @@ -1,11 +1,9 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Protocols.Pubsub.Dto; - namespace Nethermind.Libp2p.Protocols.Pubsub; -class Topic : ITopicSubscription +internal class Topic : ITopic { private readonly PubsubRouter router; private readonly string topicName; @@ -27,6 +25,8 @@ private void OnRouterMessage(string topicName, byte[] message) public DateTime LastPublished { get; set; } + public bool IsSubscribed { get; internal set; } + public event Action? OnMessage; public void Publish(byte[] value) @@ -36,7 +36,11 @@ public void Publish(byte[] value) public void Unsubscribe() { - router.OnMessage -= OnRouterMessage; - OnMessage = null; + if (!IsSubscribed) router.Unsubscribe(topicName); + } + + public void Subscribe() + { + if (!IsSubscribed) router.Subscribe(topicName); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs index 32a118f8..cab0e71f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs @@ -5,3 +5,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Libp2p.Protocols.Pubsub.Tests")] +[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests")] diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs index 2169ef2d..8f77f42d 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -1,27 +1,21 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Libp2p.Protocols.PubSubDiscovery; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Multiformats.Address; -using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.TestsBase.E2e; using Nethermind.Libp2p.Protocols.Pubsub; -using Nethermind.Libp2p.Protocols.Pubsub.Dto; using NUnit.Framework.Internal; -using System.Data.Common; -using System.Diagnostics.Metrics; -namespace Libp2p.Protocols.Multistream.Tests; +namespace Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests; [TestFixture] [Parallelizable(scope: ParallelScope.All)] public class MultistreamProtocolTests { [Test] - public async Task Test_ConnectionEstablished_AfterHandshake2() + public async Task Test_PeersConnect() { IPeerFactory peerFactory = new TestBuilder().Build(); ChannelBus commonBus = new(); @@ -53,20 +47,10 @@ public async Task Test_ConnectionEstablished_AfterHandshake2() await remotePeerB.DialAsync(); } - [Test] - public async Task Test_Bus() - { - int totalCount = 3; - TestContextLoggerFactory fac = new TestContextLoggerFactory(); - // There is common communication point - ChannelBus bus = new(fac); - //bus.GetIncomingRequests(); - } - [Test] public async Task Test_ConnectionEstablished_AfterHandshake() { - int totalCount = 20; + int totalCount = 5; TestContextLoggerFactory fac = new TestContextLoggerFactory(); // There is common communication point ChannelBus commonBus = new(fac); @@ -74,7 +58,6 @@ public async Task Test_ConnectionEstablished_AfterHandshake() 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 @@ -83,7 +66,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() .AddSingleton(sp => fac) .AddSingleton() .AddSingleton() - .AddSingleton(sp=> sp.GetService()!.Build()) + .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); IPeerFactory peerFactory = sp.GetService()!; @@ -103,133 +86,50 @@ public async Task Test_ConnectionEstablished_AfterHandshake() peerStores[i].Discover([peers[(i + 1) % totalCount].Address]); } - await Task.Delay(50000); + await Task.Delay(30000); + + foreach (var router in routers) + { + Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); + } } - //[Test] - //public async Task Derp() - //{ - - - // internal CancellationToken OutboundConnection(Multiaddress addr, string protocolId, Task dialTask, Action sendRpc) - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out"); - // PeerId? peerId = addr.GetPeerId(); - - // if (peerId is null) - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 1 peerid null"); - // return Canceled; - // } - - // PubsubPeer peer = peerState.GetOrAdd(peerId, (id) => new PubsubPeer(peerId, protocolId) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }); - - // lock (peer) - // { - // if (peer.SendRpc == sendRpc) - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 2 SendRpc null"); - // } - // else - // { - // if (peer.SendRpc is null) - // { - // peer.SendRpc = sendRpc; - // } - // else - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 3 SendRpc not null, cancelled"); - // return Canceled; - // } - // } - // } - - // dialTask.ContinueWith(t => - // { - // peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); - // peerState.TryRemove(peerId, out _); - // foreach (var topicPeers in fPeers) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in gPeers) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in fanout) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in mesh) - // { - // topicPeers.Value.Remove(peerId); - // } - // reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); - // }); - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 4 send hello {string.Join(",", topicState.Keys)}"); - - // Rpc helloMessage = new Rpc().WithTopics(topicState.Keys.ToList(), Enumerable.Empty()); - // peer.Send(helloMessage); - // logger?.LogDebug("Outbound {peerId}", peerId); - - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-out: 5 return token {peer.TokenSource.Token}"); - - // return peer.TokenSource.Token; - // } - - // internal CancellationToken InboundConnection(Multiaddress addr, string protocolId, Task listTask, Task dialTask, Func subDial) - // { - // PeerId? peerId = addr.GetPeerId(); - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1 remote peer {peerId}"); - - // if (peerId is null || peerId == localPeer!.Identity.PeerId) - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.1 remote cancel {peerId}"); - - // return Canceled; - // } - - // logger?.LogDebug("Inbound {peerId}", peerId); - - // PubsubPeer? newPeer = null; - // PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); - // if (newPeer is not null) - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 2 new peer {peerId}"); - // //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 (var topicPeers in fPeers) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in gPeers) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in fanout) - // { - // topicPeers.Value.Remove(peerId); - // } - // foreach (var topicPeers in mesh) - // { - // topicPeers.Value.Remove(peerId); - // } - // reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); - // }); - - // subDial(); - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 3 subdialed"); - - // return newPeer.TokenSource.Token; - // } - // else - // { - // logger?.LogDebug($"{localPeer?.Identity.PeerId}-in: 1.2 new peer null"); - // return existingPeer.TokenSource.Token; - // } - // } - //} + [Test] + public async Task Test_ConnectionEstablished_AfterHandshak3e() + { + int totalCount = 5; + + PubSubTestSetup setup = new(); + Dictionary discoveries = []; + + setup.Add(totalCount); + + // discover in circle + for (int i = 0; i < setup.Peers.Count; i++) + { + setup.PeerStores[i].Discover([setup.Peers[(i + 1) % setup.Peers.Count].Address]); + } + + for (int i = 0; i < setup.Peers.Count; i++) + { + discoveries[i] = new(setup.Routers[i], new PubSubDiscoverySettings() { Interval = int.MaxValue }, setup.PeerStores[i], setup.Peers[i]); + _ = discoveries[i].DiscoverAsync(setup.Peers[i].Address); + } + + 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 (var router in setup.Routers.Values) + { + Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); + } + } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs new file mode 100644 index 00000000..58ad6889 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs @@ -0,0 +1,56 @@ +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.PubsubDiscovery.Tests; + +class PubSubTestSetup +{ + static TestContextLoggerFactory fac = new TestContextLoggerFactory(); + + public ChannelBus CommonBus { get; } = new(fac); + public Dictionary Peers { get; } = new(); + public Dictionary PeerStores { get; } = new(); + public Dictionary Routers { get; } = new(); + + public void Add(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 Settings + { + HeartbeatInterval = int.MaxValue, + }; + + ServiceProvider sp = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(CommonBus, sp).AddAppLayerProtocol()) + .AddSingleton((Func)(sp => fac)) + .AddSingleton() + .AddSingleton(settings) + .AddSingleton() + .AddSingleton(sp => sp.GetService()!.Build()) + .BuildServiceProvider(); + + IPeerFactory peerFactory = sp.GetService()!; + ILocalPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); + PubsubRouter router = Routers[i] = sp.GetService()!; + PeerStore peerStore = sp.GetService()!; + _ = router.RunAsync(peer, peerStore); + PeerStores[i] = peerStore; + } + } + + internal async Task Heartbeat() + { + foreach (PubsubRouter router in Routers.Values) + { + await router.Heartbeat(); + } + } +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs index 852eb690..6c255752 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs @@ -3,5 +3,4 @@ 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.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs index 62b5df5d..b4c322fa 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -1,14 +1,8 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Protocols.Pubsub; -using Google.Protobuf; -using System.Diagnostics; +namespace Nethermind.Libp2p.Protocols; -namespace Libp2p.Protocols.PubSubDiscovery; public class PubSubDiscoverySettings { public string[] Topics { get; set; } = ["_peer-discovery._p2p._pubsub"]; @@ -16,18 +10,20 @@ public class PubSubDiscoverySettings public bool ListenOnly { get; set; } } -public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, PeerStore peerStore, ILocalPeer peer) : IDiscoveryProtocol +public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, PeerStore peerStore, ILocalPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; private Multiaddress? _localPeerAddr; + private ITopic[]? topics; private readonly PubSubDiscoverySettings _settings = settings; + private ILogger? logger = loggerFactory?.CreateLogger(); public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) { _localPeerAddr = localPeerAddr; - ITopicSubscription[] topics = _settings.Topics.Select(topic => + topics = _settings.Topics.Select(topic => { - ITopicSubscription subscription = _pubSubRouter.Subscribe(topic); + ITopic subscription = _pubSubRouter.GetTopic(topic); subscription.OnMessage += OnPeerMessage; return subscription; }).ToArray(); @@ -45,18 +41,28 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to while (!token.IsCancellationRequested) { await Task.Delay(_settings.Interval, token); - foreach (var topic in topics) - { - topic.Publish(new Peer - { - PublicKey = peer.Identity.PublicKey.ToByteString(), - Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, - }); - } + BroadcastPeerInfo(); } } } + internal void BroadcastPeerInfo() + { + if (topics is null) + { + throw new NullReferenceException($"{nameof(topics)} should be previously set in ${nameof(DiscoverAsync)}"); + } + + foreach (var topic in topics) + { + topic.Publish(new Peer + { + PublicKey = peer.Identity.PublicKey.ToByteString(), + Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, + }); + } + } + private void OnPeerMessage(byte[] msg) { try @@ -68,11 +74,11 @@ private void OnPeerMessage(byte[] msg) { peerStore.Discover(addrs); } - Debug.WriteLine($"{_localPeerAddr}: New peer discovered {peer}"); + logger?.LogDebug($"{_localPeerAddr}: New peer discovered {peer}"); } - catch + catch (Exception ex) { - + logger?.LogError(ex, "Peer message handling caused an exception"); } } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs new file mode 100644 index 00000000..44ac6a95 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs @@ -0,0 +1,10 @@ +global using Multiformats.Address; +global using Nethermind.Libp2p.Core.Discovery; +global using Nethermind.Libp2p.Core; +global using Nethermind.Libp2p.Protocols.Pubsub; +global using Google.Protobuf; +global using Microsoft.Extensions.Logging; + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests")] diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 1d720ccd..023d6af9 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -4,7 +4,6 @@ using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Protocols.Pubsub; -using System.Runtime.Versioning; namespace Nethermind.Libp2p.Stack; diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 8a254ee2..bf955f9a 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols.Pubsub; -using System.Runtime.Versioning; namespace Nethermind.Libp2p.Stack; diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 508c2f33..690bc513 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Stack; using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Protocols; using System.Text; using System.Text.Json; using Nethermind.Libp2p.Protocols.Pubsub; @@ -33,7 +32,7 @@ ILocalPeer peer = peerFactory.Create(localPeerIdentity, Multiaddress.Decode(addr)); PubsubRouter router = serviceProvider.GetService()!; -ITopicSubscription topic = router.Subscribe("chat-room:awesome-chat-room"); +ITopic topic = router.GetTopic("chat-room:awesome-chat-room"); topic.OnMessage += (byte[] msg) => { try From 32bfd4ac4844f1ccbe9e004166cf206f650ee20c Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 24 Sep 2024 17:01:58 +0300 Subject: [PATCH 07/14] Test fixes and more --- .../E2e/TestMuxerTests.cs | 17 +- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 1 + .../TestDiscoveryProtocol.cs | 22 +- src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs | 5 +- .../Discovery/IDiscoveryProtocol.cs | 29 -- src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 56 ++++ .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 312 +++++++++--------- .../MDnsDiscoveryProtocol.cs | 48 ++- .../Program.cs | 6 +- .../FloodsubProtocolTests.cs | 31 +- .../GossipsubProtocolTests.cs | 18 +- .../Libp2p.Protocols.Pubsub.Tests.csproj | 2 +- .../PubsubProtocolTests.cs | 21 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 88 +++-- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 7 +- .../MultistreamProtocolTests.cs | 14 +- .../PubSubTestSetup.cs | 2 +- .../PubsubDiscoveryProtocol.cs | 2 +- src/libp2p/Libp2p/Libp2p.csproj | 1 + .../Libp2p/ServiceProviderExtensions.cs | 6 +- src/samples/pubsub-chat/Program.cs | 7 +- 21 files changed, 365 insertions(+), 330 deletions(-) create mode 100644 src/libp2p/Libp2p.Core/Discovery/PeerStore.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs index abbf08b8..56467e9e 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core.Discovery; using NUnit.Framework; namespace Nethermind.Libp2p.Core.TestsBase.E2e; @@ -9,15 +12,13 @@ internal class TestMuxerTests [Test] public async Task Test_ConnectionEstablished_AfterHandshake() { - AppDomain.CurrentDomain.UnhandledException += (s, e) => - { + ServiceProvider sp = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(null, sp)) + .AddSingleton() + .AddSingleton(sp => sp.GetService()!.Build()) + .BuildServiceProvider(); - }; - TaskScheduler.UnobservedTaskException += (s, e) => - { - - }; - IPeerFactory peerFactory = new TestBuilder().Build(); + IPeerFactory peerFactory = sp.GetService()!; ILocalPeer peerA = peerFactory.Create(TestPeers.Identity(1)); await peerA.ListenAsync(TestPeers.Multiaddr(1)); diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index 86045f3c..1818804c 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using System; namespace Nethermind.Libp2p.Core.TestsBase; diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs index 197bd3b3..df644ba6 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs @@ -6,15 +6,15 @@ namespace Nethermind.Libp2p.Core.TestsBase; -public class TestDiscoveryProtocol : IDiscoveryProtocol -{ - public Func? OnAddPeer { get; set; } - public Func? OnRemovePeer { get; set; } +//public class TestDiscoveryProtocol : IDiscoveryProtocol +//{ +// public Func? OnAddPeer { get; set; } +// public Func? OnRemovePeer { get; set; } - public Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) - { - TaskCompletionSource task = new(); - token.Register(task.SetResult); - return task.Task; - } -} +// public Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) +// { +// TaskCompletionSource task = new(); +// token.Register(task.SetResult); +// return task.Task; +// } +//} diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs index 948ed7f2..5b1f35d1 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestPeers.cs @@ -18,9 +18,8 @@ public static Identity Identity(int i) => testPeerIdentities.GetOrAdd(i, i => return new Identity(key); }); + public static Identity Identity(Multiaddress addr) => testPeerIdentities.First(i => $"/p2p/{i.Value.PeerId}" == addr.ToString()).Value; public static PeerId PeerId(int i) => Identity(i).PeerId; + public static Multiaddress Multiaddr(Identity id) => $"/p2p/{id.PeerId}"; public static Multiaddress Multiaddr(int i) => $"/p2p/{Identity(i).PeerId}"; - public static Identity Identity(Multiaddress addr) => testPeerIdentities.First(i => $"/p2p/{i}" == addr.ToString()).Value; - - } diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index f8c959d6..abd933ec 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.Discovery; @@ -10,31 +9,3 @@ public interface IDiscoveryProtocol { Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default); } - - -public class PeerStore -{ - ConcurrentDictionary store = []; - - public void Discover(Multiaddress[] addrs) - { - if (addrs is { Length: 0 }) - { - return; - } - - PeerId? peerId = addrs.FirstOrDefault()?.GetPeerId(); - - if (peerId is not null && store.TryAdd(peerId, addrs)) - { - OnNewPeer?.Invoke(addrs); - } - } - - public event Action? OnNewPeer; - - public override string ToString() - { - return $"peerStore({store.Count}):{string.Join(",", store.Select(x => x.Key.ToString() ?? "null"))})"; - } -} diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs new file mode 100644 index 00000000..d458b5cf --- /dev/null +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using System.Collections.Concurrent; +using System.Diagnostics; + +namespace Nethermind.Libp2p.Core.Discovery; + +public class PeerStore +{ + ConcurrentDictionary store = []; + + public void Discover(Multiaddress[] addrs) + { + if (addrs is { Length: 0 }) + { + return; + } + + PeerId? peerId = addrs.FirstOrDefault()?.GetPeerId(); + + if (peerId is not null && store.TryAdd(peerId, addrs)) + { + onNewPeer?.Invoke(addrs); + } + } + + private Action? onNewPeer = null; + + public event Action? OnNewPeer + { + add + { + if (value is null) + { + return; + } + + onNewPeer += value; + foreach (var item in store.Select(x => x.Value).ToArray()) + { + value.Invoke(item); + } + } + remove + { + onNewPeer -= value; + } + } + + public override string ToString() + { + return $"peerStore({store.Count}):{string.Join(",", store.Select(x => x.Key.ToString() ?? "null"))})"; + } +} diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index c9a7f2bc..18b4a727 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -19,214 +19,228 @@ public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : IProtocol public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) { - if (channelFactory is null) + try { - throw new Exception("Protocol is not properly instantiated"); - } + if (channelFactory is null) + { + throw new Exception("Protocol is not properly instantiated"); + } - 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()); + 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()); - Socket srv = new(SocketType.Stream, ProtocolType.Tcp); - srv.Bind(new IPEndPoint(ipAddress, tcpPort)); - srv.Listen(tcpPort); - signalingChannel.GetAwaiter().OnCompleted(() => - { - srv.Close(); - }); + Socket srv = new(SocketType.Stream, ProtocolType.Tcp); + srv.Bind(new IPEndPoint(ipAddress, tcpPort)); + srv.Listen(tcpPort); + signalingChannel.GetAwaiter().OnCompleted(() => + { + srv.Close(); + }); - IPEndPoint localIpEndpoint = (IPEndPoint)srv.LocalEndPoint!; + 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; + 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) - { - context.LocalPeer.Address = context.LocalPeer.Address - .ReplaceOrAdd(localIpEndpoint.Port); - } + if (tcpPort == 0) + { + context.LocalPeer.Address = context.LocalPeer.Address + .ReplaceOrAdd(localIpEndpoint.Port); + } - _logger?.LogDebug("Ready to handle connections"); - context.ListenerReady(); + _logger?.LogDebug("Ready to handle connections"); + context.ListenerReady(); - await Task.Run(async () => - { - for (; ; ) + await Task.Run(async () => { - Socket client = await srv.AcceptAsync(); - IPeerContext clientContext = context.Fork(); - IPEndPoint remoteIpEndpoint = (IPEndPoint)client.RemoteEndPoint!; + for (; ; ) + { + Socket client = await srv.AcceptAsync(); + IPeerContext clientContext = context.Fork(); + IPEndPoint remoteIpEndpoint = (IPEndPoint)client.RemoteEndPoint!; - Multiaddress remoteMultiaddress = new(); - remoteMultiaddress = isIP4 ? remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv4()) : remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv6()); - remoteMultiaddress = remoteMultiaddress.Add(remoteIpEndpoint.Port); + Multiaddress remoteMultiaddress = new(); + remoteMultiaddress = isIP4 ? remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv4()) : remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv6()); + remoteMultiaddress = remoteMultiaddress.Add(remoteIpEndpoint.Port); - clientContext.RemoteEndpoint = clientContext.RemotePeer.Address = remoteMultiaddress; + clientContext.RemoteEndpoint = clientContext.RemotePeer.Address = remoteMultiaddress; - IChannel upChannel = channelFactory.SubListen(clientContext); + IChannel upChannel = channelFactory.SubListen(clientContext); - _ = Task.Run(async () => - { - try + _ = Task.Run(async () => { - for (; client.Connected;) + try { - if (client.Available == 0) + for (; client.Connected;) { - await Task.Yield(); - } + if (client.Available == 0) + { + await Task.Yield(); + } - 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) + 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 { break; } } - else - { - break; - } } - } - catch (SocketException e) - { - await upChannel.CloseAsync(); - } - }); - _ = Task.Run(async () => - { - try + catch (SocketException e) + { + await upChannel.CloseAsync(); + } + }); + _ = Task.Run(async () => { - await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) + try { - int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); - if (sent is 0 || !client.Connected) + await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - break; + int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); + if (sent is 0 || !client.Connected) + { + break; + } } } - } - catch (SocketException) - { - _logger?.LogInformation($"Disconnected({context.Id}) due to a socket exception"); - await upChannel.CloseAsync(); - } - }); - } - }); + catch (SocketException) + { + _logger?.LogInformation($"Disconnected({context.Id}) due to a socket exception"); + await upChannel.CloseAsync(); + } + }); + } + }); + } + catch + { + + } } public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) { - if (channelFactory is null) + try { - throw new ProtocolViolationException(); - } + 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; + 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); + _logger?.LogDebug("Dialing {0}:{1}", ipAddress, tcpPort); - try - { - await client.ConnectAsync(new IPEndPoint(ipAddress, tcpPort), signalingChannel.CancellationToken); - } - catch (SocketException e) - { - _logger?.LogDebug($"Failed({context.Id}) to connect {addr}"); - _logger?.LogTrace($"Failed with {e.GetType()}: {e.Message}"); - _ = signalingChannel.CloseAsync(); - return; - } + try + { + await client.ConnectAsync(new IPEndPoint(ipAddress, tcpPort), signalingChannel.CancellationToken); + } + catch (SocketException e) + { + _logger?.LogDebug($"Failed({context.Id}) to connect {addr}"); + _logger?.LogTrace($"Failed with {e.GetType()}: {e.Message}"); + _ = signalingChannel.CloseAsync(); + return; + } - signalingChannel.GetAwaiter().OnCompleted(() => - { - client.Close(); - }); + signalingChannel.GetAwaiter().OnCompleted(() => + { + client.Close(); + }); - IPEndPoint localEndpoint = (IPEndPoint)client.LocalEndPoint!; - IPEndPoint remoteEndpoint = (IPEndPoint)client.RemoteEndPoint!; + IPEndPoint localEndpoint = (IPEndPoint)client.LocalEndPoint!; + IPEndPoint remoteEndpoint = (IPEndPoint)client.RemoteEndPoint!; - var isIP4 = addr.Has(); + 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); + 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); - 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); + 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()); + context.LocalPeer.Address = context.LocalEndpoint.Add(context.LocalPeer.Identity.PeerId.ToString()); - IChannel upChannel = channelFactory.SubDial(context); + IChannel upChannel = channelFactory.SubDial(context); - Task receiveTask = Task.Run(async () => - { - byte[] buf = new byte[client.ReceiveBufferSize]; - try + Task receiveTask = Task.Run(async () => { - for (; client.Connected;) + byte[] buf = new byte[client.ReceiveBufferSize]; + try { - int dataLength = await client.ReceiveAsync(buf, SocketFlags.None); - if (dataLength != 0) + for (; client.Connected;) { - _logger?.LogDebug("Receive {0} data, len={1}", context.Id, dataLength); - if ((await upChannel.WriteAsync(new ReadOnlySequence(buf[..dataLength]))) != IOResult.Ok) + 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 { break; } } - else - { - break; - } - } - } - catch (SocketException) - { - _ = upChannel.CloseAsync(); - } - }); + } + catch (SocketException) + { + _ = upChannel.CloseAsync(); + } + }); - Task sendTask = Task.Run(async () => - { - try + Task sendTask = Task.Run(async () => { - await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) + try { - _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) + await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - break; + _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) + { + break; + } } } - } - catch (SocketException) - { - _ = upChannel.CloseAsync(); - } - }); + catch (SocketException) + { + _ = upChannel.CloseAsync(); + } + }); - await Task.WhenAll(receiveTask, sendTask); - _ = upChannel.CloseAsync(); + await Task.WhenAll(receiveTask, sendTask); + _ = upChannel.CloseAsync(); + } + catch + { + + } } } diff --git a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs index 2d18cb29..492b5c63 100644 --- a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs @@ -12,29 +12,22 @@ namespace Nethermind.Libp2p.Protocols; -public class MDnsDiscoveryProtocol : IDiscoveryProtocol +public class MDnsDiscoveryProtocol(PeerStore peerStore, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { - private readonly ILogger? _logger; + private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - public MDnsDiscoveryProtocol(ILoggerFactory? loggerFactory = null) - { - _logger = loggerFactory?.CreateLogger(); - } - - public string Id => "mdns"; - - private string ServiceName = "_p2p._udp.local"; + public static string Id => "mdns"; - private string? ServiceNameOverride = "pubsub-chat-example"; + private readonly string ServiceName = "_p2p._udp.local"; - public Func? OnAddPeer { get; set; } - public Func? OnRemovePeer { get; set; } + private readonly string? ServiceNameOverride = "pubsub-chat-example"; private string PeerName = null!; public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) { - ObservableCollection peers = new(); + ObservableCollection peers = []; + ServiceDiscovery sd = new(); try { @@ -56,16 +49,13 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to service.Resources.Add(new TXTRecord() { Name = service.FullyQualifiedName, - Strings = new List - { - $"dnsaddr={localPeerAddr}" - } + Strings = [$"dnsaddr={localPeerAddr}"] }); } _logger?.LogInformation("Started as {0} {1}", PeerName, ServiceNameOverride ?? ServiceName); - ServiceDiscovery sd = new(); + sd.ServiceDiscovered += (s, serviceName) => { @@ -84,23 +74,31 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to { peers.Add(peer); } - OnAddPeer?.Invoke(records); + peerStore.Discover(records); } }; sd.Advertise(service); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Error setting up mDNS"); + } - while (!token.IsCancellationRequested) + while (!token.IsCancellationRequested) + { + try { _logger?.LogTrace("Querying {0}", ServiceNameOverride ?? ServiceName); sd.QueryServiceInstances(ServiceNameOverride ?? ServiceName); - await Task.Delay(5000, token); } + catch (Exception ex) + { + _logger?.LogError(ex, "Error setting up mDNS"); + } + await Task.Delay(5000, token); } - catch - { - } } private static string RandomString(int length) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index e1f43e43..972e221b 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -34,10 +34,8 @@ IPeerFactory peerFactory = sp.GetService()!; ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; - PeerStore peerStore = sp.GetService()!; - PubSubDiscoveryProtocol disc = new(router, new PubSubDiscoverySettings() { Interval = 300 }, peerStore, peer); - _ = router.RunAsync(peer, peerStore); - peerStores[i] = peerStore; + PubSubDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubSubDiscoverySettings() { Interval = 300 }, peer); + _ = router.RunAsync(peer); _ = disc.DiscoverAsync(peers[i].Address); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index adcd47ed..74dff9ec 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols.Pubsub.Dto; -namespace Nethermind.Libp2p.Protocols.Multistream.Tests; +namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] public class FloodsubProtocolTests @@ -13,38 +13,41 @@ public class FloodsubProtocolTests [Test] public async Task Test_Peer_is_in_fpeers() { - PubsubRouter router = new(); + PeerStore peerStore = new(); + PubsubRouter router = new(peerStore); IRoutingStateContainer state = router; - Multiaddress discoveredPeer = TestPeers.Multiaddr(1); - PeerId peerId = TestPeers.PeerId(1); + + Identity discoveredPeer = TestPeers.Identity(1); + Multiaddress discoveredPeerAddress = TestPeers.Multiaddr(1); Multiaddress localPeerAddr = TestPeers.Multiaddr(2); + const string commonTopic = "topic1"; ILocalPeer peer = Substitute.For(); peer.Address.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)); - TestDiscoveryProtocol discovery = new(); CancellationToken token = default; List sentRpcs = new(); - _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); + _ = router.RunAsync(peer, token: token); router.GetTopic(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); - discovery.OnAddPeer!([discoveredPeer]); + peerStore.Discover([discoveredPeerAddress]); await Task.Delay(100); - _ = peer.Received().DialAsync(discoveredPeer, Arg.Any()); + _ = peer.Received().DialAsync(discoveredPeerAddress, Arg.Any()); TaskCompletionSource tcs = new(); - router.OutboundConnection(discoveredPeer, PubsubRouter.FloodsubProtocolVersion, tcs.Task, sentRpcs.Add); - router.InboundConnection(discoveredPeer, PubsubRouter.FloodsubProtocolVersion, tcs.Task, tcs.Task, () => Task.CompletedTask); - await router.OnRpc(peerId, new Rpc().WithTopics(new[] { commonTopic }, Enumerable.Empty())); + 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 }, [])); Assert.Multiple(() => { - Assert.That(state.FloodsubPeers[commonTopic], Has.Member(peerId)); + Assert.That(state.FloodsubPeers[commonTopic], Has.Member(discoveredPeer.PeerId)); Assert.That(sentRpcs.Any(rpc => rpc.Subscriptions.Any(s => s.Subscribe && s.Topicid == commonTopic)), Is.True); }); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index b748fd11..303c1d94 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols.Pubsub.Dto; -namespace Nethermind.Libp2p.Protocols.Multistream.Tests; +namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] public class GossipsubProtocolTests @@ -13,18 +13,18 @@ public class GossipsubProtocolTests [Test] public async Task Test_New_messages_are_sent_to_mesh_only() { - PubsubRouter router = new(); + PeerStore peerStore = new(); + PubsubRouter router = new(peerStore); Settings settings = new() { HeartbeatInterval = int.MaxValue }; IRoutingStateContainer state = router; int peerCount = Settings.Default.Degree * 2; const string commonTopic = "topic1"; ILocalPeer peer = new LocalPeerStub(); - TestDiscoveryProtocol discovery = new(); CancellationToken token = default; - List sentRpcs = new(); + List sentRpcs = []; - _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); + _ = router.RunAsync(peer, token: token); router.GetTopic(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); @@ -36,10 +36,10 @@ public async Task Test_New_messages_are_sent_to_mesh_only() Multiaddress discoveredPeer = TestPeers.Multiaddr(index); PeerId peerId = TestPeers.PeerId(index); - discovery.OnAddPeer!(new[] { discoveredPeer }); + 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(new[] { commonTopic }, Enumerable.Empty())); + await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); } await router.Heartbeat(); @@ -47,7 +47,7 @@ public async Task Test_New_messages_are_sent_to_mesh_only() Assert.Multiple(() => { Assert.That(state.GossipsubPeers[commonTopic], Has.Count.EqualTo(peerCount)); - Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(Settings.Default.Degree)); + Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(Settings.Default.HighestDegree)); }); tcs.SetResult(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/Libp2p.Protocols.Pubsub.Tests.csproj b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/Libp2p.Protocols.Pubsub.Tests.csproj index 21d55e1b..576c2d96 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/Libp2p.Protocols.Pubsub.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/Libp2p.Protocols.Pubsub.Tests.csproj @@ -3,7 +3,7 @@ enable enable - Nethermind.Libp2p.Protocols.Multistream.Tests + Nethermind.Libp2p.Protocols.Pubsub.Tests false diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index 87454da6..a7ce5aa9 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using Nethermind.Libp2p.Core.Discovery; namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; @@ -11,28 +12,28 @@ public class PubsubProtocolTests [Test] public async Task Test_Peer_is_dialed_when_added_by_discovery() { - PubsubRouter router = new(); + PeerStore peerStore = new(); + PubsubRouter router = new(peerStore); IRoutingStateContainer state = router; - Multiaddress discoveredPeer = TestPeers.Multiaddr(1); - PeerId peerId = TestPeers.PeerId(1); + Multiaddress discoveredPeerAddr = TestPeers.Multiaddr(1); Multiaddress localPeer = TestPeers.Multiaddr(2); ILocalPeer peer = Substitute.For(); peer.Address.Returns(localPeer); - peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); + peer.Identity.Returns(TestPeers.Identity(2)); + peer.DialAsync(discoveredPeerAddr, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddr)); - TestDiscoveryProtocol discovery = new(); CancellationToken token = default; TaskCompletionSource taskCompletionSource = new(); - _ = router.RunAsync(peer, new Core.Discovery.PeerStore(), token: token); - discovery.OnAddPeer!([discoveredPeer]); + _ = router.RunAsync(peer, token: token); + peerStore.Discover([discoveredPeerAddr]); await Task.Delay(100); - _ = peer.Received().DialAsync(discoveredPeer, Arg.Any()); + _ = peer.Received().DialAsync(discoveredPeerAddr, Arg.Any()); - router.OutboundConnection(discoveredPeer, PubsubRouter.FloodsubProtocolVersion, taskCompletionSource.Task, (rpc) => { }); - Assert.That(state.ConnectedPeers, Has.Member(peerId)); + router.OutboundConnection(discoveredPeerAddr, PubsubRouter.FloodsubProtocolVersion, taskCompletionSource.Task, (rpc) => { }); + Assert.That(state.ConnectedPeers, Has.Member(discoveredPeerAddr.GetPeerId())); taskCompletionSource.SetResult(); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 786548be..2b77d6c3 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -28,73 +28,59 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) { - try - { - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); + string peerId = context.RemotePeer.Address.Get().ToString()!; + _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); - TaskCompletionSource dialTcs = new(); - CancellationToken token = router.OutboundConnection(context.RemotePeer.Address, Id, dialTcs.Task, (rpc) => + TaskCompletionSource dialTcs = new(); + CancellationToken token = router.OutboundConnection(context.RemotePeer.Address, Id, dialTcs.Task, (rpc) => + { + var t = channel.WriteSizeAndProtobufAsync(rpc); + t.AsTask().ContinueWith((t) => { - var t = channel.WriteSizeAndProtobufAsync(rpc); - t.AsTask().ContinueWith((t) => + if (!t.IsCompletedSuccessfully) { - if (!t.IsCompletedSuccessfully) - { - _logger?.LogWarning($"Sending RPC failed message to {peerId}: {rpc}"); - } - }); - _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); + _logger?.LogWarning($"Sending RPC failed message to {peerId}: {rpc}"); + } }); + _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); + }); - await channel; - dialTcs.SetResult(); - _logger?.LogDebug($"Finished dial({context.Id}) {context.RemotePeer.Address}"); - } - catch - { - - } + await channel; + dialTcs.SetResult(); + _logger?.LogDebug($"Finished dial({context.Id}) {context.RemotePeer.Address}"); } public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) { - try - { - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Listen({context.Id}) to {context.RemotePeer.Address}"); + string peerId = context.RemotePeer.Address.Get().ToString()!; + _logger?.LogDebug($"Listen({context.Id}) to {context.RemotePeer.Address}"); - TaskCompletionSource listTcs = new(); - TaskCompletionSource dialTcs = new(); + TaskCompletionSource listTcs = new(); + TaskCompletionSource dialTcs = new(); - CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => - { - context.SubDialRequests.Add(new ChannelRequest { SubProtocol = this }); - return dialTcs.Task; - }); + CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => + { + context.SubDialRequests.Add(new ChannelRequest { SubProtocol = this }); + return dialTcs.Task; + }); - while (!token.IsCancellationRequested) + while (!token.IsCancellationRequested) + { + Rpc? rpc = await channel.ReadAnyPrefixedProtobufAsync(Rpc.Parser, token); + if (rpc is null) { - Rpc? rpc = await channel.ReadAnyPrefixedProtobufAsync(Rpc.Parser, token); - if (rpc is null) - { - _logger?.LogDebug($"Received a broken message or EOF from {peerId}"); - break; - } - else - { - _logger?.LogTrace($"Received message from {peerId}: {rpc}"); - _ = router.OnRpc(peerId, rpc); - } + _logger?.LogDebug($"Received a broken message or EOF from {peerId}"); + break; + } + else + { + _logger?.LogTrace($"Received message from {peerId}: {rpc}"); + _ = router.OnRpc(peerId, rpc); } - listTcs.SetResult(); - _logger?.LogDebug($"Finished({context.Id}) list {context.RemotePeer.Address}"); - } - catch - { - } + listTcs.SetResult(); + _logger?.LogDebug($"Finished({context.Id}) list {context.RemotePeer.Address}"); } public override string ToString() diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 94e6eb09..142d0d79 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -23,7 +23,7 @@ internal interface IRoutingStateContainer Task Heartbeat(); } -public partial class PubsubRouter(ILoggerFactory? loggerFactory = default) : IRoutingStateContainer +public partial class PubsubRouter(PeerStore store, ILoggerFactory? loggerFactory = default) : IRoutingStateContainer { static int ctr = 0; int _ctr = Interlocked.Increment(ref ctr); @@ -124,7 +124,6 @@ public Action? SendRpc public Func? VerifyMessage = null; private Settings settings; - private PeerStore store; private TtlCache messageCache; private TtlCache limboMessageCache; private TtlCache<(PeerId, MessageId)> dontWantMessages; @@ -161,7 +160,7 @@ static PubsubRouter() Canceled = cts.Token; } - public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? settings = null, CancellationToken token = default) + public async Task RunAsync(ILocalPeer localPeer, Settings? settings = null, CancellationToken token = default) { if (this.localPeer is not null) { @@ -170,12 +169,10 @@ public async Task RunAsync(ILocalPeer localPeer, PeerStore store, Settings? sett this.localPeer = localPeer; peer = new ManagedPeer(localPeer); this.settings = settings ?? Settings.Default; - this.store = store; messageCache = new(this.settings.MessageCacheTtl); limboMessageCache = new(this.settings.MessageCacheTtl); dontWantMessages = new(this.settings.MessageCacheTtl); - _ = localPeer.ListenAsync(localPeer.Address, token); logger?.LogInformation("Started"); store.OnNewPeer += (addrs) => diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs index 8f77f42d..4ad84536 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -10,11 +10,11 @@ namespace Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests; -[TestFixture] +[TestFixture, Ignore("No support of time mock yet")] [Parallelizable(scope: ParallelScope.All)] public class MultistreamProtocolTests { - [Test] + [Test, CancelAfter(5000)] public async Task Test_PeersConnect() { IPeerFactory peerFactory = new TestBuilder().Build(); @@ -51,7 +51,7 @@ public async Task Test_PeersConnect() public async Task Test_ConnectionEstablished_AfterHandshake() { int totalCount = 5; - TestContextLoggerFactory fac = new TestContextLoggerFactory(); + TestContextLoggerFactory fac = new(); // There is common communication point ChannelBus commonBus = new(fac); ILocalPeer[] peers = new ILocalPeer[totalCount]; @@ -73,8 +73,8 @@ public async Task Test_ConnectionEstablished_AfterHandshake() ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; - PubSubDiscoveryProtocol disc = new(router, new PubSubDiscoverySettings() { Interval = 300 }, peerStore, peer); - _ = router.RunAsync(peer, peerStore); + PubSubDiscoveryProtocol disc = new(router, peerStore, new PubSubDiscoverySettings() { Interval = 300 }, peer); + _ = router.RunAsync(peer); peerStores[i] = peerStore; _ = disc.DiscoverAsync(peers[i].Address); } @@ -94,7 +94,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() } } - [Test] + [Test, CancelAfter(5000)] public async Task Test_ConnectionEstablished_AfterHandshak3e() { int totalCount = 5; @@ -112,7 +112,7 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() for (int i = 0; i < setup.Peers.Count; i++) { - discoveries[i] = new(setup.Routers[i], new PubSubDiscoverySettings() { Interval = int.MaxValue }, setup.PeerStores[i], setup.Peers[i]); + discoveries[i] = new(setup.Routers[i], setup.PeerStores[i], new PubSubDiscoverySettings() { Interval = int.MaxValue }, setup.Peers[i]); _ = discoveries[i].DiscoverAsync(setup.Peers[i].Address); } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs index 58ad6889..9c44dede 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs @@ -41,7 +41,7 @@ public void Add(int count) ILocalPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = Routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; - _ = router.RunAsync(peer, peerStore); + _ = router.RunAsync(peer); PeerStores[i] = peerStore; } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs index b4c322fa..b7f8c21d 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs @@ -10,7 +10,7 @@ public class PubSubDiscoverySettings public bool ListenOnly { get; set; } } -public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PubSubDiscoverySettings settings, PeerStore peerStore, ILocalPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol +public class PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubSubDiscoverySettings settings, ILocalPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; private Multiaddress? _localPeerAddr; diff --git a/src/libp2p/Libp2p/Libp2p.csproj b/src/libp2p/Libp2p/Libp2p.csproj index da269e33..1e4bed73 100644 --- a/src/libp2p/Libp2p/Libp2p.csproj +++ b/src/libp2p/Libp2p/Libp2p.csproj @@ -15,6 +15,7 @@ + diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index bf955f9a..c92347c7 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Protocols.Pubsub; namespace Nethermind.Libp2p.Stack; @@ -15,8 +17,10 @@ public static IServiceCollection AddLibp2p(this IServiceCollection services, Fun .AddScoped(sp => factorySetup(new Libp2pPeerFactoryBuilder(sp))) .AddScoped(sp => (ILibp2pPeerFactoryBuilder)factorySetup(new Libp2pPeerFactoryBuilder(sp))) .AddScoped(sp => sp.GetService()!.Build()) - .AddScoped() .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() ; } } diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 690bc513..1d58649a 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -9,6 +9,9 @@ using Nethermind.Libp2p.Protocols.Pubsub; using Multiformats.Address.Protocols; using Multiformats.Address; +using Nethermind.Libp2p.Protocols; +using Nethermind.Libp2p.Core.Discovery; +using Noise; ServiceProvider serviceProvider = new ServiceCollection() .AddLibp2p(builder => builder) @@ -50,9 +53,11 @@ } }; -_ = router.RunAsync(peer, new Nethermind.Libp2p.Core.Discovery.PeerStore(), token: ts.Token); +await peer.ListenAsync(addr, ts.Token); +_ = serviceProvider.GetService()!.DiscoverAsync(peer.Address, token: ts.Token); +_ = router.RunAsync(peer, token: ts.Token); string peerId = peer.Address.Get().ToString(); From b6eda04df0acbc0f087ca3231a262a60ba7b6b42 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 26 Sep 2024 12:41:44 +0300 Subject: [PATCH 08/14] Fix profiler --- .../Libp2p.Protocols.Pubsub.Profiler/Program.cs | 14 ++++++++++++-- .../Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs | 2 +- src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs | 2 +- .../MultistreamProtocolTests.cs | 3 ++- .../PubSubTestSetup.cs | 3 ++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index 972e221b..c18afd67 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -10,9 +10,17 @@ using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Protocols.Pubsub; using System.Text; +using System.Text.RegularExpressions; +using static Org.BouncyCastle.Bcpg.Attr.ImageAttrib; +using static System.Net.Mime.MediaTypeNames; + +TaskScheduler.UnobservedTaskException += (s, e) => Console.WriteLine(e.Exception); + +AppDomain.CurrentDomain.UnhandledException += (s, e) => Console.WriteLine(e); + int totalCount = 5; -TestContextLoggerFactory fac = new TestContextLoggerFactory(); +TestContextLoggerFactory fac = new(); // There is common communication point ChannelBus commonBus = new(fac); ILocalPeer[] peers = new ILocalPeer[totalCount]; @@ -35,8 +43,10 @@ ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; PubSubDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubSubDiscoverySettings() { Interval = 300 }, peer); + + await peer.ListenAsync(TestPeers.Multiaddr(i)); _ = router.RunAsync(peer); - _ = disc.DiscoverAsync(peers[i].Address); + _ = disc.DiscoverAsync(peer.Address); } for (int i = 0; i < peers.Length; i++) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index f4fc1c65..c81fa36c 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -129,7 +129,7 @@ public void Publish(string topicId, byte[] message) if (mesh.ContainsKey(topicId)) { - foreach (PeerId peerId in mesh[topicId]) + foreach (PeerId peerId in mesh[topicId].ToList()) { peerState.GetValueOrDefault(peerId)?.Send(rpc); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 142d0d79..c096653c 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -130,7 +130,7 @@ public Action? SendRpc private ILocalPeer? localPeer; private ManagedPeer peer; - private ILogger? logger = loggerFactory?.CreateLogger(); + private readonly ILogger? logger = loggerFactory?.CreateLogger(); // all floodsub peers in topics private readonly ConcurrentDictionary> fPeers = new(); diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs index 4ad84536..89c807a0 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs @@ -74,6 +74,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() PubsubRouter router = routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; PubSubDiscoveryProtocol disc = new(router, peerStore, new PubSubDiscoverySettings() { Interval = 300 }, peer); + await peer.ListenAsync(TestPeers.Multiaddr(i)); _ = router.RunAsync(peer); peerStores[i] = peerStore; _ = disc.DiscoverAsync(peers[i].Address); @@ -102,7 +103,7 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() PubSubTestSetup setup = new(); Dictionary discoveries = []; - setup.Add(totalCount); + await setup.AddAsync(totalCount); // discover in circle for (int i = 0; i < setup.Peers.Count; i++) diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs index 9c44dede..7978b225 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs @@ -15,7 +15,7 @@ class PubSubTestSetup public Dictionary PeerStores { get; } = new(); public Dictionary Routers { get; } = new(); - public void Add(int count) + public async Task AddAsync(int count) { int initialCount = Peers.Count; // There is common communication point @@ -41,6 +41,7 @@ public void Add(int count) ILocalPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = Routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; + await peer.ListenAsync(TestPeers.Multiaddr(i)); _ = router.RunAsync(peer); PeerStores[i] = peerStore; } From 170d8d96846aad1114ccc3912f35cc7da1e385be Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 26 Sep 2024 13:12:37 +0300 Subject: [PATCH 09/14] Clean up more --- .../Program.cs | 5 -- .../Libp2p.Protocols.Tls/TlsProtocol.cs | 48 ++++++++----------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index 63c10215..9af5993f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -11,11 +11,6 @@ using Nethermind.Libp2p.Protocols.Pubsub; using System.Text; -TaskScheduler.UnobservedTaskException += (s, e) => Console.WriteLine(e.Exception); - -AppDomain.CurrentDomain.UnhandledException += (s, e) => Console.WriteLine(e); - - int totalCount = 5; TestContextLoggerFactory fac = new(); // There is common communication point diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index 839d513f..5a261fa0 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -16,6 +16,8 @@ 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 SslApplicationProtocol? LastNegotiatedApplicationProtocol { get; private set; } public string Id => "/tls/1.0.0"; @@ -26,24 +28,20 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? channelFact { throw new ArgumentException("Protocol is not properly instantiated"); } + 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); - var _protocols = multiplexerSettings is null ? - new List { } : - !multiplexerSettings.Multiplexers.Any() ? - new List { } : - multiplexerSettings.Multiplexers.Select(proto => new SslApplicationProtocol(proto.Id)).ToList(); SslServerAuthenticationOptions serverAuthenticationOptions = new() { - ApplicationProtocols = _protocols, + ApplicationProtocols = ApplicationProtocols.Value, RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.RemotePeer.Address, certificate), ServerCertificate = certificate, ClientCertificateRequired = true, }; - _logger?.LogTrace("SslServerAuthenticationOptions initialized with ApplicationProtocols: {Protocols}.", string.Join(", ", _protocols.Select(p => p.Protocol))); + _logger?.LogTrace("SslServerAuthenticationOptions initialized with ApplicationProtocols: {Protocols}.", string.Join(", ", ApplicationProtocols.Value)); SslStream sslStream = new(str, false, serverAuthenticationOptions.RemoteCertificateValidationCallback); _logger?.LogTrace("SslStream initialized."); try @@ -78,12 +76,6 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? channelFactor MultiaddressProtocol ipProtocol = isIP4 ? addr.Get() : addr.Get(); IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); - var _protocols = multiplexerSettings is null ? - new List { } : - !multiplexerSettings.Multiplexers.Any() ? - new List { } : - multiplexerSettings.Multiplexers.Select(proto => new SslApplicationProtocol(proto.Id)).ToList(); - SslClientAuthenticationOptions clientAuthenticationOptions = new() { CertificateChainPolicy = new X509ChainPolicy @@ -92,7 +84,7 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? channelFactor VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority }, TargetHost = ipAddress.ToString(), - ApplicationProtocols = _protocols, + 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) }, @@ -135,10 +127,10 @@ private static async Task ExchangeData(SslStream sslStream, IChannel upChannel, logger?.LogDebug("Starting to write to sslStream"); await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - logger.LogDebug($"Got data to send to peer: {{{Encoding.UTF8.GetString(data).Replace("\n", "\\n").Replace("\r", "\\r")}}}!"); + logger?.LogDebug($"Got data to send to peer: {{{Encoding.UTF8.GetString(data).Replace("\n", "\\n").Replace("\r", "\\r")}}}"); await sslStream.WriteAsync(data.ToArray()); await sslStream.FlushAsync(); - logger.LogDebug($"Data sent to sslStream {{{Encoding.UTF8.GetString(data).Replace("\n", "\\n").Replace("\r", "\\r")}}}!!"); + logger?.LogDebug($"Data sent to sslStream {{{Encoding.UTF8.GetString(data).Replace("\n", "\\n").Replace("\r", "\\r")}}}"); } } catch (Exception ex) @@ -156,19 +148,21 @@ private static async Task ExchangeData(SslStream sslStream, IChannel upChannel, { byte[] data = new byte[1024]; int len = await sslStream.ReadAtLeastAsync(data, 1, false); - if (len != 0) + if (len == 0) + { + break; + } + + logger?.LogDebug($"Received {len} bytes from sslStream: {{{Encoding.UTF8.GetString(data, 0, len).Replace("\r", "\\r").Replace("\n", "\\n")}}}"); + try + { + await upChannel.WriteAsync(new ReadOnlySequence(data.ToArray()[..len])); + } + catch (Exception ex) { - logger?.LogDebug($"Received {len} bytes from sslStream: {{{Encoding.UTF8.GetString(data, 0, len).Replace("\r", "\\r").Replace("\n", "\\n")}}}"); - try - { - await upChannel.WriteAsync(new ReadOnlySequence(data.ToArray()[..len])); - } - catch (Exception ex) - { - logger?.LogError(ex, "Error while reading from sslStream"); - } - logger.LogDebug($"Data received from sslStream, {len}"); + logger?.LogError(ex, "Error while reading from sslStream"); } + logger?.LogDebug($"Data received from sslStream, {len}"); } await upChannel.WriteEofAsync(); } From fe3149ca53770592e6de7ca43f634e8f10b6bce1 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 2 Oct 2024 15:04:14 +0300 Subject: [PATCH 10/14] Clean up --- .../Libp2p.Generators.Protobuf.generated.sln | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln diff --git a/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln b/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln deleted file mode 100644 index 335b13c0..00000000 --- a/src/libp2p/Libp2p.Generators.Protobuf/Libp2p.Generators.Protobuf.generated.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Generators.Protobuf", "Libp2p.Generators.Protobuf.csproj", "{699FF970-2414-4EED-AD9A-5B908BDAB4D8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {699FF970-2414-4EED-AD9A-5B908BDAB4D8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {459B48E6-7D97-4644-BC5B-B33ED0318CF6} - EndGlobalSection -EndGlobal From 31f062adc74d2b0405e6b81bd0995b78662bd3b8 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 2 Oct 2024 16:07:49 +0300 Subject: [PATCH 11/14] Add more pacakges to publish --- .github/workflows/publish.yml | 2 ++ src/libp2p/Libp2p.sln | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 19dfde3f..e0c8edda 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -73,6 +73,8 @@ jobs: dotnet pack Libp2p.Protocols.Ping ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Plaintext ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Pubsub ${{ env.PACK_OPTS }} + dotnet pack Libp2p.Protocols.PubsubDiscovery ${{ env.PACK_OPTS }} + dotnet pack Libp2p.Protocols.Tls ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Quic ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Yamux ${{ env.PACK_OPTS }} diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 15abf186..eb30bce1 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -59,6 +59,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props + ..\..\.github\workflows\format.yml = ..\..\.github\workflows\format.yml + ..\..\.github\workflows\publish.yml = ..\..\.github\workflows\publish.yml + ..\..\.github\workflows\test.yml = ..\..\.github\workflows\test.yml EndProjectSection 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}" @@ -71,7 +74,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubDisc 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}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.Tls", "Libp2p.Protocols.Tls\Libp2p.Protocols.Tls.csproj", "{C3CDBAAE-C790-443A-A293-D6E2330160F7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Tls", "Libp2p.Protocols.Tls\Libp2p.Protocols.Tls.csproj", "{C3CDBAAE-C790-443A-A293-D6E2330160F7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From e1f59c8d9c75e28661a053c93691e926ec0c304b Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 2 Oct 2024 16:25:40 +0300 Subject: [PATCH 12/14] Rename --- .../Libp2p.Protocols.Pubsub.Profiler.csproj | 2 +- .../Program.cs | 2 +- src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs | 2 +- .../Dto/Peer.cs | 264 ----------------- ...rotocols.PubsubPeerDiscovery.Tests.csproj} | 2 +- .../MultistreamProtocolTests.cs | 10 +- .../PubSubTestSetup.cs | 4 +- .../Usings.cs | 0 .../Dto/Peer.cs | 268 ++++++++++++++++++ .../Dto/Peer.proto | 2 + ...bp2p.Protocols.PubsubPeerDiscovery.csproj} | 0 .../PubsubPeerDiscoveryProtocol.cs} | 10 +- .../README.md | 0 .../Usings.cs | 2 +- src/libp2p/Libp2p.sln | 4 +- 15 files changed, 290 insertions(+), 282 deletions(-) delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj => Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj} (90%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery.Tests => Libp2p.Protocols.PubsubPeerDiscovery.Tests}/MultistreamProtocolTests.cs (92%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery.Tests => Libp2p.Protocols.PubsubPeerDiscovery.Tests}/PubSubTestSetup.cs (95%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery.Tests => Libp2p.Protocols.PubsubPeerDiscovery.Tests}/Usings.cs (100%) create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.cs rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery => Libp2p.Protocols.PubsubPeerDiscovery}/Dto/Peer.proto (51%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj => Libp2p.Protocols.PubsubPeerDiscovery/Libp2p.Protocols.PubsubPeerDiscovery.csproj} (100%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs => Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs} (83%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery => Libp2p.Protocols.PubsubPeerDiscovery}/README.md (100%) rename src/libp2p/{Libp2p.Protocols.PubsubDiscovery => Libp2p.Protocols.PubsubPeerDiscovery}/Usings.cs (93%) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj index 67992949..b3a22fa1 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index 9af5993f..55b49493 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -34,7 +34,7 @@ IPeerFactory peerFactory = sp.GetService()!; ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; - PubSubDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubSubDiscoverySettings() { Interval = 300 }, peer); + PubsubPeerDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); await peer.ListenAsync(TestPeers.Multiaddr(i)); _ = router.RunAsync(peer); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs index cab0e71f..d3c1b48b 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Usings.cs @@ -5,4 +5,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Libp2p.Protocols.Pubsub.Tests")] -[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests")] +[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests")] diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs b/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs deleted file mode 100644 index 0eafc63e..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.cs +++ /dev/null @@ -1,264 +0,0 @@ -// -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Peer.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; -/// Holder for reflection information generated from Peer.proto -public static partial class PeerReflection { - - #region Descriptor - /// File descriptor for Peer.proto - public static pbr::FileDescriptor Descriptor { - get { return descriptor; } - } - private static pbr::FileDescriptor descriptor; - - static PeerReflection() { - byte[] descriptorData = global::System.Convert.FromBase64String( - string.Concat( - "CgpQZWVyLnByb3RvIigKBFBlZXISEQoJcHVibGljS2V5GAEgASgMEg0KBWFk", - "ZHJzGAIgAygMYgZwcm90bzM=")); - descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, - new pbr::FileDescriptor[] { }, - new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Peer), global::Peer.Parser, new[]{ "PublicKey", "Addrs" }, null, null, null, null) - })); - } - #endregion - -} -#region Messages -[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] -public sealed partial class Peer : pb::IMessage -#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage -#endif -{ - private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Peer()); - 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::PeerReflection.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 Peer() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Peer(Peer other) : this() { - publicKey_ = other.publicKey_; - addrs_ = other.addrs_.Clone(); - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Peer Clone() { - return new Peer(this); - } - - /// Field number for the "publicKey" field. - public const int PublicKeyFieldNumber = 1; - private pb::ByteString publicKey_ = pb::ByteString.Empty; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pb::ByteString PublicKey { - get { return publicKey_; } - set { - publicKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// Field number for the "addrs" field. - public const int AddrsFieldNumber = 2; - private static readonly pb::FieldCodec _repeated_addrs_codec - = pb::FieldCodec.ForBytes(18); - private readonly pbc::RepeatedField addrs_ = new pbc::RepeatedField(); - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField Addrs { - get { return addrs_; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as Peer); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(Peer other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (PublicKey != other.PublicKey) return false; - if(!addrs_.Equals(other.addrs_)) 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 (PublicKey.Length != 0) hash ^= PublicKey.GetHashCode(); - hash ^= addrs_.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 (PublicKey.Length != 0) { - output.WriteRawTag(10); - output.WriteBytes(PublicKey); - } - addrs_.WriteTo(output, _repeated_addrs_codec); - 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 (PublicKey.Length != 0) { - output.WriteRawTag(10); - output.WriteBytes(PublicKey); - } - addrs_.WriteTo(ref output, _repeated_addrs_codec); - 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 (PublicKey.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeBytesSize(PublicKey); - } - size += addrs_.CalculateSize(_repeated_addrs_codec); - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(Peer other) { - if (other == null) { - return; - } - if (other.PublicKey.Length != 0) { - PublicKey = other.PublicKey; - } - addrs_.Add(other.addrs_); - _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: { - PublicKey = input.ReadBytes(); - break; - } - case 18: { - addrs_.AddEntriesFrom(input, _repeated_addrs_codec); - 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: { - PublicKey = input.ReadBytes(); - break; - } - case 18: { - addrs_.AddEntriesFrom(ref input, _repeated_addrs_codec); - break; - } - } - } - } - #endif - -} - -#endregion - - -#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj similarity index 90% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj index 290b89b0..2132529c 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Libp2p.Protocols.PubsubDiscovery.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs similarity index 92% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs index 89c807a0..e9f98a90 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs @@ -8,7 +8,7 @@ using Nethermind.Libp2p.Protocols.Pubsub; using NUnit.Framework.Internal; -namespace Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests; +namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; [TestFixture, Ignore("No support of time mock yet")] [Parallelizable(scope: ParallelScope.All)] @@ -73,7 +73,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; - PubSubDiscoveryProtocol disc = new(router, peerStore, new PubSubDiscoverySettings() { Interval = 300 }, peer); + PubsubPeerDiscoveryProtocol disc = new(router, peerStore, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); await peer.ListenAsync(TestPeers.Multiaddr(i)); _ = router.RunAsync(peer); peerStores[i] = peerStore; @@ -100,8 +100,8 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() { int totalCount = 5; - PubSubTestSetup setup = new(); - Dictionary discoveries = []; + PubsubTestSetup setup = new(); + Dictionary discoveries = []; await setup.AddAsync(totalCount); @@ -113,7 +113,7 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() for (int i = 0; i < setup.Peers.Count; i++) { - discoveries[i] = new(setup.Routers[i], setup.PeerStores[i], new PubSubDiscoverySettings() { Interval = int.MaxValue }, setup.Peers[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); } diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs similarity index 95% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs index 7978b225..00e24b7f 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs @@ -4,9 +4,9 @@ using Nethermind.Libp2p.Core.TestsBase.E2e; using Nethermind.Libp2p.Protocols.Pubsub; -namespace Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests; +namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; -class PubSubTestSetup +class PubsubTestSetup { static TestContextLoggerFactory fac = new TestContextLoggerFactory(); diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs similarity index 100% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery.Tests/Usings.cs rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.cs new file mode 100644 index 00000000..f4a9a211 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.cs @@ -0,0 +1,268 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Peer.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.PubsubPeerDiscovery.Dto { + + /// Holder for reflection information generated from Peer.proto + public static partial class PeerReflection { + + #region Descriptor + /// File descriptor for Peer.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static PeerReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgpQZWVyLnByb3RvIigKBFBlZXISEQoJcHVibGljS2V5GAEgASgMEg0KBWFk", + "ZHJzGAIgAygMQjaqAjNOZXRoZXJtaW5kLkxpYnAycC5Qcm90b2NvbHMuUHVi", + "c3ViUGVlckRpc2NvdmVyeS5EdG9iBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Dto.Peer), global::Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Dto.Peer.Parser, new[]{ "PublicKey", "Addrs" }, null, null, null, null) + })); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Peer : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Peer()); + 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.PubsubPeerDiscovery.Dto.PeerReflection.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 Peer() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Peer(Peer other) : this() { + publicKey_ = other.publicKey_; + addrs_ = other.addrs_.Clone(); + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Peer Clone() { + return new Peer(this); + } + + /// Field number for the "publicKey" field. + public const int PublicKeyFieldNumber = 1; + private pb::ByteString publicKey_ = pb::ByteString.Empty; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString PublicKey { + get { return publicKey_; } + set { + publicKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "addrs" field. + public const int AddrsFieldNumber = 2; + private static readonly pb::FieldCodec _repeated_addrs_codec + = pb::FieldCodec.ForBytes(18); + private readonly pbc::RepeatedField addrs_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField Addrs { + get { return addrs_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Peer); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Peer other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (PublicKey != other.PublicKey) return false; + if(!addrs_.Equals(other.addrs_)) 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 (PublicKey.Length != 0) hash ^= PublicKey.GetHashCode(); + hash ^= addrs_.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 (PublicKey.Length != 0) { + output.WriteRawTag(10); + output.WriteBytes(PublicKey); + } + addrs_.WriteTo(output, _repeated_addrs_codec); + 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 (PublicKey.Length != 0) { + output.WriteRawTag(10); + output.WriteBytes(PublicKey); + } + addrs_.WriteTo(ref output, _repeated_addrs_codec); + 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 (PublicKey.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(PublicKey); + } + size += addrs_.CalculateSize(_repeated_addrs_codec); + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Peer other) { + if (other == null) { + return; + } + if (other.PublicKey.Length != 0) { + PublicKey = other.PublicKey; + } + addrs_.Add(other.addrs_); + _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: { + PublicKey = input.ReadBytes(); + break; + } + case 18: { + addrs_.AddEntriesFrom(input, _repeated_addrs_codec); + 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: { + PublicKey = input.ReadBytes(); + break; + } + case 18: { + addrs_.AddEntriesFrom(ref input, _repeated_addrs_codec); + break; + } + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.proto similarity index 51% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.proto index cbe4fb34..2590294e 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Dto/Peer.proto +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Dto/Peer.proto @@ -1,5 +1,7 @@ syntax = "proto3"; +option csharp_namespace = "Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Dto"; + message Peer { bytes publicKey = 1; repeated bytes addrs = 2; diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Libp2p.Protocols.PubsubPeerDiscovery.csproj similarity index 100% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery/Libp2p.Protocols.PubsubDiscovery.csproj rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Libp2p.Protocols.PubsubPeerDiscovery.csproj diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs similarity index 83% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs index b7f8c21d..6e0cc306 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/PubsubDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs @@ -1,22 +1,24 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Dto; + namespace Nethermind.Libp2p.Protocols; -public class PubSubDiscoverySettings +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 PubSubDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubSubDiscoverySettings settings, ILocalPeer 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 Multiaddress? _localPeerAddr; private ITopic[]? topics; - private readonly PubSubDiscoverySettings _settings = settings; - private ILogger? logger = loggerFactory?.CreateLogger(); + private readonly PubsubPeerDiscoverySettings _settings = settings; + private ILogger? logger = loggerFactory?.CreateLogger(); public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) { diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/README.md similarity index 100% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery/README.md rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/README.md diff --git a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Usings.cs similarity index 93% rename from src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Usings.cs index 44ac6a95..81ff89bc 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubDiscovery/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/Usings.cs @@ -7,4 +7,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubDiscovery.Tests")] +[assembly: InternalsVisibleTo("Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests")] diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index eb30bce1..6d6664dc 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -68,9 +68,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Yamux.Test 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.PubsubDiscovery", "Libp2p.Protocols.PubsubDiscovery\Libp2p.Protocols.PubsubDiscovery.csproj", "{F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}" +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.PubsubDiscovery.Tests", "Libp2p.Protocols.PubsubDiscovery.Tests\Libp2p.Protocols.PubsubDiscovery.Tests.csproj", "{5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}" +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}" EndProject From 98895dba3b727236eb5b67aa2dc292b2764e97e1 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 2 Oct 2024 18:21:30 +0300 Subject: [PATCH 13/14] Remove tls for now --- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index f5d891c0..bf43fcc8 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -21,7 +21,7 @@ public Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = default) : b protected override ProtocolStack BuildStack() { - ProtocolStack tcpEncryptionStack = enforcePlaintext ? Over() : Over().Or(); + ProtocolStack tcpEncryptionStack = enforcePlaintext ? Over() : Over(); ProtocolStack tcpStack = Over() .Over() From 66bf64fa9860d7766f98ecad8a7bba942ed83fc5 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 2 Oct 2024 18:56:13 +0300 Subject: [PATCH 14/14] Fix package name --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e0c8edda..6a8c6adf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -73,7 +73,7 @@ jobs: dotnet pack Libp2p.Protocols.Ping ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Plaintext ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Pubsub ${{ env.PACK_OPTS }} - dotnet pack Libp2p.Protocols.PubsubDiscovery ${{ env.PACK_OPTS }} + dotnet pack Libp2p.Protocols.PubsubPeerDiscovery ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Tls ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Quic ${{ env.PACK_OPTS }} dotnet pack Libp2p.Protocols.Yamux ${{ env.PACK_OPTS }}