< Summary

Information
Class: NostrSure.Domain.Services.CachedEventIdCalculator
Assembly: NostrSure.Domain
File(s): /home/runner/work/NostrSure/NostrSure/NostrSure.Domain/Services/CachedEventIdCalculator.cs
Line coverage
72%
Covered lines: 45
Uncovered lines: 17
Coverable lines: 62
Total lines: 99
Line coverage: 72.5%
Branch coverage
37%
Covered branches: 3
Total branches: 8
Branch coverage: 37.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
.ctor(...)100%11100%
CalculateEventId(...)100%2284.61%
CreateCacheKey(...)16.66%10653.84%

File(s)

/home/runner/work/NostrSure/NostrSure/NostrSure.Domain/Services/CachedEventIdCalculator.cs

#LineLine coverage
 1using Microsoft.Extensions.Caching.Memory;
 2using NostrSure.Domain.Entities;
 3using NostrSure.Domain.Validation;
 4using System.Security.Cryptography;
 5using System.Text;
 6using System.Text.Encodings.Web;
 7using System.Text.Json;
 8
 9namespace NostrSure.Domain.Services;
 10
 11/// <summary>
 12/// High-performance event ID calculator with caching support
 13/// This implementation closely matches the original legacy implementation to ensure compatibility
 14/// </summary>
 15public sealed class CachedEventIdCalculator : IEventIdCalculator
 16{
 17    private readonly IMemoryCache _cache;
 18    private readonly JsonSerializerOptions _jsonOptions;
 019    private static readonly SHA256 _sha256 = SHA256.Create();
 20
 1321    public CachedEventIdCalculator(IMemoryCache cache)
 1322    {
 1323        _cache = cache;
 1324        _jsonOptions = new JsonSerializerOptions
 1325        {
 1326            WriteIndented = false,
 1327            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
 1328        };
 1329    }
 30
 31    public string CalculateEventId(NostrEvent evt)
 10632    {
 33        // Create cache key based on event content that affects the hash
 10634        var cacheKey = CreateCacheKey(evt);
 35
 10636        if (_cache.TryGetValue(cacheKey, out string? cachedId))
 10037            return cachedId!;
 38
 39        // Match the exact legacy implementation format for compatibility
 640        var tagsArrays = evt.Tags.Select(tag =>
 041        {
 042            var array = new List<string> { tag.Name };
 043            array.AddRange(tag.Values);
 044            return array.ToArray();
 645        }).ToArray();
 46
 647        var eventArray = new object[]
 648        {
 649            0,
 650            evt.Pubkey.Value,
 651            evt.CreatedAt.ToUnixTimeSeconds(),
 652            (int)evt.Kind,
 653            tagsArrays,
 654            evt.Content
 655        };
 56
 657        var serialized = JsonSerializer.Serialize(eventArray, _jsonOptions);
 58
 659        var utf8Bytes = Encoding.UTF8.GetBytes(serialized);
 660        var hash = NBitcoin.Crypto.Hashes.SHA256(utf8Bytes);
 661        var eventId = Convert.ToHexString(hash).ToLowerInvariant();
 62
 63        // Cache for 5 minutes to balance memory usage and performance
 664        _cache.Set(cacheKey, eventId, TimeSpan.FromMinutes(5));
 665        return eventId;
 10666    }
 67
 68    private string CreateCacheKey(NostrEvent evt)
 10669    {
 70        // Create a hash-based cache key that includes all fields that affect the event ID
 10671        var keyBuilder = new StringBuilder(200);
 10672        keyBuilder.Append("eventid:");
 10673        keyBuilder.Append(evt.Pubkey.Value);
 10674        keyBuilder.Append(':');
 10675        keyBuilder.Append(evt.CreatedAt.ToUnixTimeSeconds());
 10676        keyBuilder.Append(':');
 10677        keyBuilder.Append((int)evt.Kind);
 10678        keyBuilder.Append(':');
 10679        keyBuilder.Append(evt.Content.GetHashCode());
 10680        keyBuilder.Append(':');
 81
 82        // Include a hash of tags to handle tag changes
 10683        if (evt.Tags.Count > 0)
 084        {
 085            var tagsHash = 0;
 086            foreach (var tag in evt.Tags)
 087            {
 088                tagsHash = HashCode.Combine(tagsHash, tag.Name.GetHashCode());
 089                foreach (var value in tag.Values)
 090                {
 091                    tagsHash = HashCode.Combine(tagsHash, value.GetHashCode());
 092                }
 093            }
 094            keyBuilder.Append(tagsHash);
 095        }
 96
 10697        return keyBuilder.ToString();
 10698    }
 99}