Skip to content

Commit

Permalink
Savepoint
Browse files Browse the repository at this point in the history
  • Loading branch information
skibitsky committed Oct 1, 2024
1 parent 8254a03 commit c18d17f
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 167 deletions.
148 changes: 72 additions & 76 deletions .idea/.idea.Reown/.idea/workspace.xml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Reown.Sign/Runtime/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,7 @@ await Task.WhenAll(
{
Type = "caip122",
Chains = authParams.Chains,
Methods = authParams.Methods,
Statement = authParams.Statement,
Aud = authParams.Uri,
Domain = authParams.Domain,
Expand Down
6 changes: 1 addition & 5 deletions src/Reown.Sign/Runtime/Models/Engine/AuthParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,14 @@ public class AuthParams
[JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)]
public List<string>? Resources;

[JsonProperty("pairingTopic", NullValueHandling = NullValueHandling.Ignore)]
public string? PairingTopic;

[JsonProperty("methods", NullValueHandling = NullValueHandling.Ignore)]
public string[]? Methods;

public AuthParams()
{
}

public AuthParams(string[] chains, string domain, string nonce, string uri, long? nbf, long? exp, string? statement, string? requestId, List<string>? resources, string? pairingTopic, string[]? methods)
public AuthParams(string[] chains, string domain, string nonce, string uri, long? nbf, long? exp, string? statement, string? requestId, List<string>? resources, string[]? methods)
{
Chains = chains;
Domain = domain;
Expand All @@ -55,7 +52,6 @@ public AuthParams(string[] chains, string domain, string nonce, string uri, long
Statement = statement;
RequestId = requestId;
Resources = resources;
PairingTopic = pairingTopic;
Methods = methods;
}
}
Expand Down
126 changes: 88 additions & 38 deletions src/Reown.Sign/Runtime/Utils/ReCapUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ public static Dictionary<string, Dictionary<string, object>[]> AssignAbilityToAc

return abilitiesDictionary;
}

public static bool TryGetDecodedRecapFromResources(IEnumerable<string> resources, out ReCap recap)
{
var success = TryGetRecapFromResources(resources, out var recapStr);
recap = success ? DecodeRecap(recapStr) : null;

return success;
}

public static bool TryGetRecapFromResources(IEnumerable<string> resources, out string recap)
{
Expand All @@ -97,11 +105,15 @@ public static bool IsReCap(string resource)
public static string[] GetMethodsFromRecap(string recapStr)
{
var decodedRecap = DecodeRecap(recapStr);
return GetMethodsFromRecap(decodedRecap);
}

public static string[] GetMethodsFromRecap(ReCap recap)
{
try
{
// Methods are only available for eip155 as per the current implementation
return decodedRecap.Att["eip155"] is { } resources
return recap.Att["eip155"] is { } resources
? resources.Properties.Keys.Select(ability => ability.Split("/")[1]).ToArray()
: Array.Empty<string>();
}
Expand Down Expand Up @@ -159,6 +171,10 @@ public static ReCap DecodeRecap(string recapStr)
{
var paddedRecap = recapStr.Replace("urn:recap:", string.Empty);

paddedRecap = paddedRecap.TrimEnd('=');
var paddingNeeded = (4 - paddedRecap.Length % 4) % 4;
paddedRecap = paddedRecap.PadRight(paddedRecap.Length + paddingNeeded, '=');

var decodedRecap = Convert.FromBase64String(paddedRecap);
var decodedRecapStr = System.Text.Encoding.UTF8.GetString(decodedRecap);
var recap = JsonConvert.DeserializeObject<ReCap>(decodedRecapStr);
Expand Down Expand Up @@ -193,12 +209,8 @@ public static void ValidateRecap(ReCap recap)

foreach (var limitToken in limits)
{
if (limitToken is not JObject limitObject)
if (limitToken is not JObject)
throw new ArgumentException($"Each limit for ability '{key}' must be an object. Invalid limit: {limitToken}");

var limitDict = limitObject.ToObject<Dictionary<string, object>>();
if (limitDict == null || limitDict.Count == 0)
throw new ArgumentException($"Each limit object for ability '{key}' must contain at least one key-value pair.");
}
}
}
Expand All @@ -219,53 +231,91 @@ public static ReCap MergeRecaps(ReCap recap1, ReCap recap2)

var mergedRecap = new ReCap
{
Att = new Dictionary<string, AttValue>()
Att = new Dictionary<string, AttValue>(StringComparer.Ordinal)
};

// Merge the keys from both recaps
var keys = recap1.Att.Keys
.Concat(recap2.Att.Keys)
.Distinct()
.OrderBy(k => k, StringComparer.Ordinal)
.ToList();
var allKeys = recap1.Att.Keys.Union(recap2.Att.Keys, StringComparer.Ordinal).OrderBy(k => k);

foreach (var key in keys)
foreach (var key in allKeys)
{
if (!mergedRecap.Att.ContainsKey(key))
recap1.Att.TryGetValue(key, out var value1);
recap2.Att.TryGetValue(key, out var value2);

var mergedValue = new AttValue
{
mergedRecap.Att[key] = new AttValue
{
Properties = new Dictionary<string, JToken>()
};
}
Properties = new Dictionary<string, JToken>(StringComparer.Ordinal)
};

var mergedProperties = mergedRecap.Att[key].Properties;
var allActions = (value1?.Properties?.Keys ?? Enumerable.Empty<string>())
.Union(value2?.Properties?.Keys ?? Enumerable.Empty<string>(), StringComparer.Ordinal)
.OrderBy(a => a);

// Get actions from both recaps for the current key
var actions1 = recap1.Att.TryGetValue(key, out var value1) ? value1.Properties.Keys : Enumerable.Empty<string>();
var actions2 = recap2.Att.TryGetValue(key, out var value2) ? value2.Properties.Keys : Enumerable.Empty<string>();
foreach (var action in allActions)
{
JToken property1 = null;
JToken property2 = null;

var actions = actions1
.Concat(actions2)
.Distinct()
.OrderBy(a => a, StringComparer.Ordinal)
.ToList();
value1?.Properties?.TryGetValue(action, out property1);
value2?.Properties?.TryGetValue(action, out property2);

foreach (var action in actions)
{
if (recap1.Att.ContainsKey(key) && recap1.Att[key].Properties.ContainsKey(action))
{
mergedProperties[action] = recap1.Att[key].Properties[action];
}
else if (recap2.Att.ContainsKey(key) && recap2.Att[key].Properties.ContainsKey(action))
JToken mergedProperty;

if (property1 == null)
mergedProperty = property2;
else if (property2 == null)
mergedProperty = property1;
else
{
mergedProperties[action] = recap2.Att[key].Properties[action];
switch (property1)
{
case JArray arr1 when property2 is JArray arr2:
{
var mergedArray = new JArray();
foreach (var item1 in arr1)
{
if (item1 is JObject obj1)
{
var mergedItem = obj1.DeepClone();
foreach (var item2 in arr2)
if (item2 is JObject obj2)
((JObject)mergedItem).Merge(obj2, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});

mergedArray.Add(mergedItem);
}
else
{
mergedArray.Add(item1);
}
}

mergedProperty = mergedArray;
break;
}
case JObject obj1 when property2 is JObject obj2:
mergedProperty = obj1.DeepClone();
((JObject)mergedProperty).Merge(obj2, new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
break;
default:
mergedProperty = property1;
break;
}
}

mergedValue.Properties[action] = mergedProperty;
}

mergedRecap.Att[key] = mergedValue;
}

ValidateRecap(mergedRecap);
return mergedRecap;
}
}

public static string FormatStatementFromRecap(ReCap recap, string statement = "")
{
Expand Down
67 changes: 67 additions & 0 deletions test/Reown.Sign.Test/AuthenticateTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Reown.Sign.Models.Engine;
using Xunit;
using Xunit.Abstractions;

namespace Reown.Sign.Test;

public class AuthenticateTests : IClassFixture<SignClientFixture>
{
private readonly ITestOutputHelper _testOutputHelper;
private readonly SignClientFixture _signClientFixture;

public AuthenticateTests(SignClientFixture signClientFixture, ITestOutputHelper testOutputHelper)
{
_signClientFixture = signClientFixture;
_testOutputHelper = testOutputHelper;
}

private static AuthParams GetTestAuthParams()
{
return new AuthParams(
[
"eip155:1"
],
"service.invalid",
"32891756",
"https://service.invalid/login",
null,
null,
"I accept the ServiceOrg Terms of Service: https://service.invalid/tos",
null,
[
"ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/",
"https://example.com/my-web2-claim.json"
],
[
"personal_sign",
"eth_sendTransaction"
]
);
}


[Fact]
public async Task TestAuthenticate()
{
await _signClientFixture.WaitForClientsReady();

var dapp = _signClientFixture.ClientA;
var wallet = _signClientFixture.ClientB;

var tcs = new TaskCompletionSource<bool>();
wallet.SessionAuthenticateRequest += async (sender, args) =>
{
_testOutputHelper.WriteLine("SessionAuthenticateRequest");
tcs.SetResult(true);
};

var authParams = GetTestAuthParams();

var authData = await dapp.Authenticate(authParams);
_testOutputHelper.WriteLine(authData.Uri);

await wallet.Pair(authData.Uri);

await tcs.Task;
}
}
2 changes: 1 addition & 1 deletion test/Reown.Sign.Test/CacaoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public CacaoTests(ITestOutputHelper testOutputHelper)
_testOutputHelper = testOutputHelper;
}

[Fact]
[Fact] [Trait("Category", "unit")]
public void FormatMessage_WithoutRecap_ReturnsExpectedMessage()
{
var payload = new CacaoPayload(
Expand Down
4 changes: 2 additions & 2 deletions test/Reown.Sign.Test/NamespaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void WithMethod_AppendsNewMethod()
Assert.Contains("newMethod", ns.Methods);
}

[Fact] [Trait("Category", "unit")] [Trait("Category", "unit")]
[Fact] [Trait("Category", "unit")]
public void WithChain_AppendsNewChain_WhenChainsIsNull()
{
var ns = new Namespace();
Expand All @@ -23,7 +23,7 @@ public void WithChain_AppendsNewChain_WhenChainsIsNull()
Assert.Contains("newChain", ns.Chains);
}

[Fact] [Trait("Category", "unit")] [Trait("Category", "unit")]
[Fact] [Trait("Category", "unit")]
public void WithChain_AppendsNewChain_WhenChainsIsNotNull()
{
var ns = new Namespace();
Expand Down
Loading

0 comments on commit c18d17f

Please sign in to comment.