Skip to content

Commit 33ee291

Browse files
authored
Merge pull request #22 from remcoros/fix/idpreimage_serialization
address serialization issues in 'ToIdPreimage' and 'BaseNostrEventTagJsonConverter'
2 parents 6553686 + 5c69ae0 commit 33ee291

File tree

5 files changed

+62
-20
lines changed

5 files changed

+62
-20
lines changed

NNostr.Client/JsonConverters/BaseNostrEventTagJsonConverter.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ namespace NNostr.Client.JsonConverters;
2121
{
2222
if (i == 0)
2323
{
24-
result.TagIdentifier = StringEscaperJsonConverter.JavaScriptStringEncode(reader.GetString(), false);
24+
result.TagIdentifier = reader.GetString();
2525
}
2626
else
2727
{
28-
result.Data.Add(StringEscaperJsonConverter.JavaScriptStringEncode(reader.GetString(), false));
28+
result.Data.Add(reader.GetString());
2929
}
3030

3131
reader.Read();

NNostr.Client/NNostr.Client.csproj

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
4+
<TargetFrameworks>net6.0;net7.0;net8.0;netstandard2.1</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<LangVersion>11</LangVersion>
@@ -15,8 +15,6 @@
1515
<license>MIT</license>
1616
<RepositoryUrl>https://github.com/Kukks/NNostr</RepositoryUrl>
1717
<PackageTags>Nostr</PackageTags>
18-
<TargetFrameworks>net6.0;net7.0;netstandard2.1</TargetFrameworks>
19-
2018
</PropertyGroup>
2119

2220
<ItemGroup>

NNostr.Client/NostrEventTag.cs

-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System.Text.Encodings.Web;
2-
using System.Text.Json;
31
using System.Text.Json.Serialization;
42
using NNostr.Client.JsonConverters;
53

@@ -8,16 +6,9 @@ namespace NNostr.Client
86
[JsonConverter(typeof(NostrEventTagJsonConverter))]
97
public class NostrEventTag
108
{
11-
private static readonly JsonSerializerOptions _unsafeJsonEscapingOptions = new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
129
public string TagIdentifier { get; set; }
1310
public List<string> Data { get; set; } = new();
1411

15-
public override string ToString()
16-
{
17-
var d = TagIdentifier is null ? Data : Data.Prepend(TagIdentifier);
18-
return JsonSerializer.Serialize(d, _unsafeJsonEscapingOptions);
19-
}
20-
2112
public bool Equals(NostrEventTag? x, NostrEventTag? y)
2213
{
2314
if (ReferenceEquals(x, y)) return true;

NNostr.Client/NostrExtensions.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
using System.Net.WebSockets;
22
using System.Runtime.CompilerServices;
3-
using System.Security.Cryptography;
43
using System.Threading.Channels;
5-
using LinqKit;
64
using NBitcoin.Secp256k1;
75
using NNostr.Client.JsonConverters;
8-
using SHA256 = System.Security.Cryptography.SHA256;
96

107
namespace NNostr.Client
118
{
129
public static class NostrExtensions
1310
{
14-
1511
public static string ToIdPreimage<TNostrEvent, TEventTag>(this TNostrEvent nostrEvent, bool withoutId)
1612
where TNostrEvent : BaseNostrEvent<TEventTag> where TEventTag : NostrEventTag
1713
{
18-
var content = StringEscaperJsonConverter.JavaScriptStringEncode(nostrEvent.Content, false);
14+
var content = StringEscaperJsonConverter.JavaScriptStringEncode(nostrEvent.Content ?? string.Empty, false);
15+
var tagContent = string.Join(',', nostrEvent.Tags.Select(tag =>
16+
{
17+
var data = tag.TagIdentifier is null ? tag.Data : tag.Data.Prepend(tag.TagIdentifier);
18+
return $"[{string.Join(",", data.Select(d => $"\"{StringEscaperJsonConverter.JavaScriptStringEncode(d, false)}\""))}]";
19+
}));
1920
return
20-
$"[{(withoutId ? 0 : $"\"{nostrEvent.Id}\"")},\"{nostrEvent.PublicKey}\",{nostrEvent.CreatedAt?.ToUnixTimeSeconds()},{nostrEvent.Kind},[{string.Join(',', nostrEvent.Tags.Select(tag => tag))}],\"{content}\"]";
21+
$"[{(withoutId ? 0 : $"\"{nostrEvent.Id}\"")},\"{nostrEvent.PublicKey}\",{nostrEvent.CreatedAt?.ToUnixTimeSeconds() ?? 0},{nostrEvent.Kind},[{tagContent}],\"{content}\"]";
2122
}
2223

2324
public static string ComputeEventId(this string eventJson)

NNostr.Tests/NIP57Tests.cs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Threading.Tasks;
4+
using NNostr.Client;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace NNostr.Tests;
9+
10+
public class NIP57Tests
11+
{
12+
private readonly ITestOutputHelper _output;
13+
14+
public NIP57Tests(ITestOutputHelper output)
15+
{
16+
_output = output;
17+
}
18+
19+
[Fact]
20+
public async Task ValidateZapReceipt()
21+
{
22+
var privKeyStr = "0514ce6aae1eb9897d32ccce0cc40c6942a1f2f04f5554618dd97f430c9e386f";
23+
var privKey = NostrExtensions.ParseKey(privKeyStr);
24+
var pubKey = privKey.CreateXOnlyPubKey();
25+
var pubKeyHex = pubKey.ToHex();
26+
27+
var evtRequestStr =
28+
"""{"id":"cd8bb08cb5a74d67d49d73f8838057385fb8c584427629d81b54e29a1c7708bb","pubkey":"8cacc4de163d6547e740ac4338a3c4569ce4028f0299fae841a00028d68c04e3","created_at":1717408922,"kind":9734,"content":"Great post 👍","tags":[["p","23378c18cb34edc0ea5f979b41703df2799fed769595e31312fef04b8011c0d6"],["amount","21000"],["relays","wss://nostr.mom"],["e","e6dceff3a4cd834edd17c170a41146086e6a200c5f012232bc80840856ddfa27"]],"sig":"a950026e445b27c818013f97a120d1c3c21f94a0e0362379c76cad1a95131ec14071f9b4339afaca2e17144138058db77f415202b92ce52763966fdda97c219d"}""";
29+
var evtReceipt = new NostrEvent()
30+
{
31+
Kind = 9735,
32+
Content = "Great post 👍",
33+
CreatedAt = DateTimeOffset.FromUnixTimeSeconds(1717408928),
34+
PublicKey = pubKeyHex,
35+
Tags = {
36+
new(){ TagIdentifier = "p", Data = { "23378c18cb34edc0ea5f979b41703df2799fed769595e31312fef04b8011c0d6" }},
37+
new(){ TagIdentifier = "e", Data = { "e6dceff3a4cd834edd17c170a41146086e6a200c5f012232bc80840856ddfa27" }},
38+
new(){ TagIdentifier = "description", Data = { evtRequestStr }},
39+
}
40+
};
41+
evtReceipt = await evtReceipt.ComputeIdAndSignAsync(privKey);
42+
43+
var evtReceiptStr = JsonSerializer.Serialize(evtReceipt);
44+
45+
_output.WriteLine("Receipt:");
46+
_output.WriteLine(evtReceiptStr);
47+
48+
// Since we both sign and verify from the same 'ToIdPreimage', this will always succeed, even if the computation of the id is invalid
49+
// Use a tool like https://nak.nostr.com/ to copy/paste the event receipt string and verify if the signature is valid
50+
Assert.True(evtReceipt.Verify());
51+
}
52+
}

0 commit comments

Comments
 (0)