From 93c53c4a13a39d97d03be600c4b8c411f7b8b155 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 29 Aug 2023 12:28:41 +0300 Subject: [PATCH] Add dotnet tcp/noise/yamux --- multidim-interop/Makefile | 7 +- multidim-interop/impl/dotnet/v1.0/.gitignore | 5 + multidim-interop/impl/dotnet/v1.0/Dockerfile | 11 ++ multidim-interop/impl/dotnet/v1.0/Makefile | 13 ++ multidim-interop/impl/dotnet/v1.0/Program.cs | 142 ++++++++++++++++++ .../impl/dotnet/v1.0/TestPlansApp.csproj | 25 +++ .../impl/dotnet/v1.0/TestPlansApp.sln | 25 +++ multidim-interop/versions.ts | 6 + 8 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 multidim-interop/impl/dotnet/v1.0/.gitignore create mode 100644 multidim-interop/impl/dotnet/v1.0/Dockerfile create mode 100644 multidim-interop/impl/dotnet/v1.0/Makefile create mode 100644 multidim-interop/impl/dotnet/v1.0/Program.cs create mode 100644 multidim-interop/impl/dotnet/v1.0/TestPlansApp.csproj create mode 100644 multidim-interop/impl/dotnet/v1.0/TestPlansApp.sln diff --git a/multidim-interop/Makefile b/multidim-interop/Makefile index ed98ff716..58cbc0b3c 100644 --- a/multidim-interop/Makefile +++ b/multidim-interop/Makefile @@ -4,8 +4,9 @@ RUST_SUBDIRS := $(wildcard impl/rust/*/.) NIM_SUBDIRS := $(wildcard impl/nim/*/.) ZIG_SUBDIRS := $(wildcard impl/zig/*/.) JAVA_SUBDIRS := $(wildcard impl/java/*/.) +DOTNET_SUBDIRS := $(wildcard dotnet/*/.) -all: $(GO_SUBDIRS) $(JS_SUBDIRS) $(RUST_SUBDIRS) $(NIM_SUBDIRS) $(ZIG_SUBDIRS) $(JAVA_SUBDIRS) +all: $(GO_SUBDIRS) $(JS_SUBDIRS) $(RUST_SUBDIRS) $(NIM_SUBDIRS) $(ZIG_SUBDIRS) $(JAVA_SUBDIRS) $(DOTNET_SUBDIRS) $(JS_SUBDIRS): $(MAKE) -C $@ $(GO_SUBDIRS): @@ -18,5 +19,7 @@ $(ZIG_SUBDIRS): $(MAKE) -C $@ $(JAVA_SUBDIRS): $(MAKE) -C $@ +$(DOTNET_SUBDIRS): + $(MAKE) -C $@ -.PHONY: $(GO_SUBDIRS) $(JS_SUBDIRS) $(RUST_SUBDIRS) $(NIM_SUBDIRS) $(ZIG_SUBDIRS) $(JAVA_SUBDIRS) all +.PHONY: $(GO_SUBDIRS) $(JS_SUBDIRS) $(RUST_SUBDIRS) $(NIM_SUBDIRS) $(ZIG_SUBDIRS) $(JAVA_SUBDIRS) $(DOTNET_SUBDIRS) all diff --git a/multidim-interop/impl/dotnet/v1.0/.gitignore b/multidim-interop/impl/dotnet/v1.0/.gitignore new file mode 100644 index 000000000..cf96887c5 --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +.vs/ +.vscode/ +image.json diff --git a/multidim-interop/impl/dotnet/v1.0/Dockerfile b/multidim-interop/impl/dotnet/v1.0/Dockerfile new file mode 100644 index 000000000..61bc5f73f --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/Dockerfile @@ -0,0 +1,11 @@ +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env +WORKDIR /app + +COPY . ./ +RUN dotnet restore +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/runtime:7.0 +WORKDIR /app +COPY --from=build-env /app/out . +ENTRYPOINT ["dotnet", "TestPlansApp.dll"] diff --git a/multidim-interop/impl/dotnet/v1.0/Makefile b/multidim-interop/impl/dotnet/v1.0/Makefile new file mode 100644 index 000000000..7e356a667 --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/Makefile @@ -0,0 +1,13 @@ +image_name := dotnet-v1.0 + +all: image.json + +image.json: Dockerfile Program.cs Protocols.cs TestPlansApp.csproj TestPlansApp.sln + IMAGE_NAME=${image_name} ../../../dockerBuildWrapper.sh . + docker image inspect ${image_name} -f "{{.Id}}" | \ + xargs -I {} echo "{\"imageID\": \"{}\"}" > $@ + +.PHONY: clean all + +clean: + rm -f bin/ obj/ diff --git a/multidim-interop/impl/dotnet/v1.0/Program.cs b/multidim-interop/impl/dotnet/v1.0/Program.cs new file mode 100644 index 000000000..8c3e796b5 --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/Program.cs @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Microsoft.Extensions.DependencyInjection; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols; +using StackExchange.Redis; +using System.Diagnostics; +using System.Net.NetworkInformation; +using Microsoft.Extensions.Logging; + +try +{ + string transport = Environment.GetEnvironmentVariable("transport")!; + string muxer = Environment.GetEnvironmentVariable("muxer")!; + string security = Environment.GetEnvironmentVariable("security")!; + + bool isDialer = bool.Parse(Environment.GetEnvironmentVariable("is_dialer")!); + string ip = Environment.GetEnvironmentVariable("ip") ?? "0.0.0.0"; + + string redisAddr = Environment.GetEnvironmentVariable("redis_addr") ?? "redis:6379"; + + int testTimeoutSeconds = int.Parse(Environment.GetEnvironmentVariable("test_timeout_seconds") ?? "180"); + + IPeerFactory peerFactory = new TestPlansPeerFactoryBuilder(transport, muxer, security).Build(); + + Log($"Connecting to redis at {redisAddr}..."); + ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisAddr); + IDatabase db = redis.GetDatabase(); + + if (isDialer) + { + ILocalPeer localPeer = peerFactory.Create(localAddr: $"/ip4/0.0.0.0/tcp/0"); + string? listenerAddr = null; + while ((listenerAddr = db.ListRightPop("listenerAddr")) is null) + { + await Task.Delay(20); + } + + Log($"Dialing {listenerAddr}..."); + Stopwatch handshakeStartInstant = Stopwatch.StartNew(); + IRemotePeer remotePeer = await localPeer.DialAsync(listenerAddr); + + Stopwatch pingIstant = Stopwatch.StartNew(); + await remotePeer.DialAsync(); + long pingRTT = pingIstant.ElapsedMilliseconds; + + long handshakePlusOneRTT = handshakeStartInstant.ElapsedMilliseconds; + + PrintResult($"{{\"handshakePlusOneRTTMillis\": {handshakePlusOneRTT}, \"pingRTTMilllis\": {pingRTT}}}"); + Log("Done"); + return 0; + } + else + { + if (ip == "0.0.0.0") + { + IEnumerable addresses = NetworkInterface.GetAllNetworkInterfaces()! + .FirstOrDefault(i => i.Name == "eth0")! + .GetIPProperties() + .UnicastAddresses + .Where(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork); + + Log("Available addresses detected, picking the first: " + string.Join(",", addresses.Select(a => a.Address))); + ip = addresses.First().Address.ToString()!; + } + Log("Starting to listen..."); + ILocalPeer localPeer = peerFactory.Create(localAddr: $"/ip4/{ip}/tcp/0"); + IListener listener = await localPeer.ListenAsync(localPeer.Address); + listener.OnConnection += async (peer) => Log($"Connected {peer.Address}"); + Log($"Listening on {listener.Address}"); + db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.Address.ToString())); + await Task.Delay(testTimeoutSeconds * 1000); + await listener.DisconnectAsync(); + return -1; + } +} +catch (Exception ex) +{ + Log(ex.Message); + return -1; +} + +static void Log(string info) => Console.Error.WriteLine(info); +static void PrintResult(string info) => Console.WriteLine(info); + +class TestPlansPeerFactoryBuilder : PeerFactoryBuilderBase +{ + private readonly string transport; + private readonly string? muxer; + private readonly string? security; + private static IPeerFactoryBuilder? defaultPeerFactoryBuilder; + + public TestPlansPeerFactoryBuilder(string transport, string? muxer, string? security) + : base(new ServiceCollection() + .AddLogging(builder => + builder.SetMinimumLevel(LogLevel.Trace) + .AddSimpleConsole(l => + { + l.SingleLine = true; + l.TimestampFormat = "[HH:mm:ss.FFF]"; + })) + .AddScoped(_ => defaultPeerFactoryBuilder!) + .BuildServiceProvider()) + { + defaultPeerFactoryBuilder = this; + this.transport = transport; + this.muxer = muxer; + this.security = security; + } + + private static readonly string[] stacklessProtocols = new[] { "quic", "quic-v1", "webtransport" }; + + protected override ProtocolStack BuildStack() + { + ProtocolStack stack = transport switch + { + "tcp" => Over(), + _ => throw new NotImplementedException(), + }; + + if (!stacklessProtocols.Contains(transport)) + { + stack = stack.Over(); + stack = security switch + { + "noise" => stack.Over(), + _ => throw new NotImplementedException(), + }; + stack = stack.Over(); + stack = muxer switch + { + "yamux" => stack.Over(), + _ => throw new NotImplementedException(), + }; + stack = stack.Over(); + } + + return stack.AddAppLayerProtocol() + .AddAppLayerProtocol(); + } +} diff --git a/multidim-interop/impl/dotnet/v1.0/TestPlansApp.csproj b/multidim-interop/impl/dotnet/v1.0/TestPlansApp.csproj new file mode 100644 index 000000000..c6b15ebb7 --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/TestPlansApp.csproj @@ -0,0 +1,25 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/multidim-interop/impl/dotnet/v1.0/TestPlansApp.sln b/multidim-interop/impl/dotnet/v1.0/TestPlansApp.sln new file mode 100644 index 000000000..51151cd81 --- /dev/null +++ b/multidim-interop/impl/dotnet/v1.0/TestPlansApp.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34018.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPlansApp", "TestPlansApp.csproj", "{4B9D7919-740C-4EF0-8890-AB43E6102952}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4B9D7919-740C-4EF0-8890-AB43E6102952}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B9D7919-740C-4EF0-8890-AB43E6102952}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B9D7919-740C-4EF0-8890-AB43E6102952}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B9D7919-740C-4EF0-8890-AB43E6102952}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7BF72338-A0C5-4E70-A0F1-54B1EB8BB378} + EndGlobalSection +EndGlobal diff --git a/multidim-interop/versions.ts b/multidim-interop/versions.ts index e60f54950..fe5b595d2 100644 --- a/multidim-interop/versions.ts +++ b/multidim-interop/versions.ts @@ -125,4 +125,10 @@ export const versions: Array = [ secureChannels: ["tls", "noise"], muxers: ["mplex", "yamux"], }, + { + id: "dotnet-v1.0", + transports: ["tcp"], + secureChannels: ["noise"], + muxers: ["yamux"], + }, ].map((v: Version) => (typeof v.containerImageID === "undefined" ? ({ ...v, containerImageID: canonicalImageIDLookup }) : v))