From 6525e6aa729d480f2f30066acd7359b395420936 Mon Sep 17 00:00:00 2001 From: skibitsky Date: Wed, 8 May 2024 19:33:10 +0300 Subject: [PATCH 1/2] Handle json exception caused by multiple SessionRequestEvents subscriptions --- .../WalletConnectSharp.Sign.Test/SignTests.cs | 177 ++++++++++++++++++ .../Controllers/TypedMessageHandler.cs | 31 +-- 2 files changed, 197 insertions(+), 11 deletions(-) diff --git a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs index cd7538d..d7d005b 100644 --- a/Tests/WalletConnectSharp.Sign.Test/SignTests.cs +++ b/Tests/WalletConnectSharp.Sign.Test/SignTests.cs @@ -34,6 +34,62 @@ public class TestRequest2 public int y; } + // represents array of strings requests, similar to personal_sign + [RpcMethod("complex_test_method")] [RpcRequestOptions(Clock.ONE_MINUTE, 99990)] + public class ComplexTestRequest : List + { + public ComplexTestRequest() + { + } + + public ComplexTestRequest(params string[] args) : base(args) + { + } + + public int A + { + get + { + if (Count != 2 || !int.TryParse(this[0], out var a)) + { + return 0; + } + + return a; + } + } + + public int B + { + get + { + if (Count != 2 || !int.TryParse(this[1], out var b)) + { + return 0; + } + + return b; + } + } + } + + // represents array of objects requests, similar to eth_sendTransaction + [RpcMethod("complex_test_method_2")] [RpcRequestOptions(Clock.ONE_MINUTE, 99991)] [RpcResponseOptions(Clock.ONE_MINUTE, 99992)] + public class ComplexTestRequest2 : List + { + public ComplexTestRequest2() + { + } + + public ComplexTestRequest2(params TestRequest2[] args) : base(args) + { + } + + public string X => this.FirstOrDefault()?.x ?? string.Empty; + + public int Y => this.FirstOrDefault()?.y ?? -1; + } + [RpcResponseOptions(Clock.ONE_MINUTE, 99999)] public class TestResponse { @@ -387,6 +443,127 @@ public async Task TestTwoUniqueSessionRequestResponse() Assert.True(responseReturned2); } + + [Fact] [Trait("Category", "integration")] + public async Task TestTwoUniqueComplexSessionRequestResponse() + { + await _cryptoFixture.WaitForClientsReady(); + + var testAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; + var testMethod = "complex_test_method"; + var testMethod2 = "complex_test_method_2"; + + var dappConnectOptions = new ConnectOptions() + { + RequiredNamespaces = new RequiredNamespaces() + { + { + "eip155", new ProposedNamespace() + { + Methods = new[] + { + testMethod, + testMethod2 + }, + Chains = new[] + { + "eip155:1", + "eip155:10" + }, + Events = new[] + { + "chainChanged", + "accountsChanged" + } + } + } + } + }; + + var dappClient = ClientA; + var connectData = await dappClient.Connect(dappConnectOptions); + + var walletClient = ClientB; + var proposal = await walletClient.Pair(connectData.Uri); + + var approveData = await walletClient.Approve(proposal, testAddress); + + var sessionData = await connectData.Approval; + await approveData.Acknowledged(); + + var rnd = new Random(); + var a = rnd.Next(100); + var b = rnd.Next(100); + var x = rnd.NextStrings(AllowedChars, (Math.Min(a, b), Math.Max(a, b)), 1).First(); + var y = x.Length; + + var testData = new ComplexTestRequest(a.ToString(), b.ToString()); + var testData2 = new ComplexTestRequest2(new TestRequest2() + { + x = x, y = y + }); + + var pending = new TaskCompletionSource(); + + // Step 1. Setup event listener for request + + // The wallet client will listen for the request with the "test_method" rpc method + walletClient.Engine.SessionRequestEvents() + .OnRequest += (requestData) => + { + var request = requestData.Request; + var data = request.Params; + + requestData.Response = new TestResponse() + { + result = data.A * data.B + }; + + return Task.CompletedTask; + }; + + // The wallet client will listen for the request with the "test_method" rpc method + walletClient.Engine.SessionRequestEvents() + .OnRequest += (requestData) => + { + var request = requestData.Request; + var data = request.Params; + + requestData.Response = data.X.Length == data.Y; + + return Task.CompletedTask; + }; + + // The dapp client will listen for the response + // Normally, we wouldn't do this and just rely on the return value + // from the dappClient.Engine.Request function call (the response Result or throws an Exception) + // We do it here for the sake of testing + dappClient.Engine.SessionRequestEvents() + .FilterResponses((r) => r.Topic == sessionData.Topic) + .OnResponse += (responseData) => + { + var response = responseData.Response; + + var data = response.Result; + + pending.TrySetResult(data.result); + + return Task.CompletedTask; + }; + + // 2. Send the request from the dapp client + var responseReturned = await dappClient.Engine.Request(sessionData.Topic, testData); + var responseReturned2 = await dappClient.Engine.Request(sessionData.Topic, testData2); + + // 3. Wait for the response from the event listener + var eventResult = await pending.Task.WithTimeout(TimeSpan.FromSeconds(5)); + + Assert.Equal(eventResult, a * b); + Assert.Equal(eventResult, testData.A * testData.B); + Assert.Equal(eventResult, responseReturned.result); + + Assert.True(responseReturned2); + } [Fact, Trait("Category", "integration")] public async Task TestTwoUniqueSessionRequestResponseUsingAddressProviderDefaults() diff --git a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs index 10ab8e8..a5b9a7a 100644 --- a/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs +++ b/WalletConnectSharp.Core/Controllers/TypedMessageHandler.cs @@ -98,23 +98,32 @@ public async Task HandleMessageType(Func>(topic, message, options); + var payload = await Core.Crypto.Decode>(topic, message, options); - (await this.Core.History.JsonRpcHistoryOfType()).Set(topic, payload, null); + (await Core.History.JsonRpcHistoryOfType()).Set(topic, payload, null); - await requestCallback(topic, payload); + await requestCallback(topic, payload); + } + catch (JsonException) + { + } } async void ResponseCallback(object sender, MessageEvent e) From b2ae9e463c16c6c14fad1605448f332f5a6b3345 Mon Sep 17 00:00:00 2001 From: skibitsky Date: Thu, 9 May 2024 15:02:34 +0300 Subject: [PATCH 2/2] Optimise Verifier --- .../Models/Verify/Verifier.cs | 31 ++++++++++--------- WalletConnectSharp.Core/WalletConnectCore.cs | 1 + 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/WalletConnectSharp.Core/Models/Verify/Verifier.cs b/WalletConnectSharp.Core/Models/Verify/Verifier.cs index 18a18b6..4d4d84b 100644 --- a/WalletConnectSharp.Core/Models/Verify/Verifier.cs +++ b/WalletConnectSharp.Core/Models/Verify/Verifier.cs @@ -1,35 +1,38 @@ -using System.Net; -using Newtonsoft.Json; -using WalletConnectSharp.Common.Utils; +using Newtonsoft.Json; +using WalletConnectSharp.Common.Logging; namespace WalletConnectSharp.Core.Models.Verify; -public class Verifier +public sealed class Verifier : IDisposable { - public const string VerifyServer = "https://verify.walletconnect.com"; - - public CancellationTokenSource CancellationTokenSource { get; } + private const string VerifyServer = "https://verify.walletconnect.com"; - public Verifier() + private readonly HttpClient _client = new() { - this.CancellationTokenSource = new CancellationTokenSource(Clock.AsTimeSpan(Clock.FIVE_SECONDS)); - } + Timeout = TimeSpan.FromSeconds(5) + }; public async Task Resolve(string attestationId) { try { - using HttpClient client = new HttpClient(); var url = $"{VerifyServer}/attestation/{attestationId}"; - var results = await client.GetStringAsync(url); + WCLogger.Log($"[Verifier] Resolving attestation {attestationId} from {url}"); + var results = await _client.GetStringAsync(url); + WCLogger.Log($"[Verifier] Resolved attestation. Results: {results}"); var verifiedContext = JsonConvert.DeserializeObject(results); - return verifiedContext != null ? verifiedContext.Origin : ""; + return verifiedContext != null ? verifiedContext.Origin : string.Empty; } catch { - return ""; + return string.Empty; } } + + public void Dispose() + { + _client?.Dispose(); + } } diff --git a/WalletConnectSharp.Core/WalletConnectCore.cs b/WalletConnectSharp.Core/WalletConnectCore.cs index 5b5a78b..a86845f 100644 --- a/WalletConnectSharp.Core/WalletConnectCore.cs +++ b/WalletConnectSharp.Core/WalletConnectCore.cs @@ -219,6 +219,7 @@ protected virtual void Dispose(bool disposing) MessageHandler?.Dispose(); Expirer?.Dispose(); Pairing?.Dispose(); + Verify?.Dispose(); } Disposed = true;