Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ECPoint cache #2616

Merged
merged 8 commits into from
Nov 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions src/neo/Cryptography/ECC/ECPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// modifications are permitted.

using Neo.IO;
using Neo.IO.Caching;
using System;
using System.IO;
using System.Numerics;
Expand All @@ -35,6 +36,9 @@ public bool IsInfinity

public int Size => IsInfinity ? 1 : 33;

private static IO.Caching.ECPointCache pointCacheK1 { get; } = new(1000);
private static IO.Caching.ECPointCache pointCacheR1 { get; } = new(1000);

/// <summary>
/// Initializes a new instance of the <see cref="ECPoint"/> class with the secp256r1 curve.
/// </summary>
Expand Down Expand Up @@ -74,10 +78,7 @@ public static ECPoint DecodePoint(ReadOnlySpan<byte> encoded, ECCurve curve)
{
if (encoded.Length != (curve.ExpectedECPointLength + 1))
throw new FormatException("Incorrect length for compressed encoding");
int yTilde = encoded[0] & 1;
BigInteger X1 = new(encoded[1..], isUnsigned: true, isBigEndian: true);
p = DecompressPoint(yTilde, X1, curve);
p._compressedPoint = encoded.ToArray();
p = DecompressPoint(encoded, curve);
break;
}
case 0x04: // uncompressed
Expand All @@ -98,6 +99,25 @@ public static ECPoint DecodePoint(ReadOnlySpan<byte> encoded, ECCurve curve)
return p;
}

private static ECPoint DecompressPoint(ReadOnlySpan<byte> encoded, ECCurve curve)
{
ECPointCache pointCache = null;
if (curve == ECCurve.Secp256k1) pointCache = pointCacheK1;
else if (curve == ECCurve.Secp256r1) pointCache = pointCacheR1;
else throw new FormatException("Invalid curve " + curve);

byte[] compressedPoint = encoded.ToArray();
if (!pointCache.TryGet(compressedPoint, out ECPoint p))
{
int yTilde = encoded[0] & 1;
BigInteger X1 = new(encoded[1..], isUnsigned: true, isBigEndian: true);
p = DecompressPoint(yTilde, X1, curve);
p._compressedPoint = compressedPoint;
pointCache.Add(p);
}
return p;
}

private static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve)
{
ECFieldElement x = new(X1, curve);
Expand Down
5 changes: 3 additions & 2 deletions src/neo/IO/Caching/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public CacheItem(TKey key, TValue value)
}

protected readonly ReaderWriterLockSlim RwSyncRootLock = new(LockRecursionPolicy.SupportsRecursion);
protected readonly Dictionary<TKey, CacheItem> InnerDictionary = new();
protected readonly Dictionary<TKey, CacheItem> InnerDictionary;
private readonly int max_capacity;

public TValue this[TKey key]
Expand Down Expand Up @@ -72,9 +72,10 @@ public int Count

public bool IsReadOnly => false;

public Cache(int max_capacity)
public Cache(int max_capacity, IEqualityComparer<TKey> comparer = null)
{
this.max_capacity = max_capacity;
this.InnerDictionary = new Dictionary<TKey, CacheItem>(comparer);
}

public void Add(TValue item)
Expand Down
27 changes: 27 additions & 0 deletions src/neo/IO/Caching/ECPointCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (C) 2015-2021 The Neo Project.
//
// The neo is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Cryptography.ECC;

namespace Neo.IO.Caching
{
internal class ECPointCache : FIFOCache<byte[], ECPoint>
{
public ECPointCache(int max_capacity)
: base(max_capacity, ByteArrayEqualityComparer.Default)
{
}

protected override byte[] GetKeyForItem(ECPoint item)
{
return item.EncodePoint(true);
}
}
}
6 changes: 4 additions & 2 deletions src/neo/IO/Caching/FIFOCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System.Collections.Generic;

namespace Neo.IO.Caching
{
internal abstract class FIFOCache<TKey, TValue> : Cache<TKey, TValue>
{
public FIFOCache(int max_capacity)
: base(max_capacity)
public FIFOCache(int max_capacity, IEqualityComparer<TKey> comparer = null)
: base(max_capacity, comparer)
{
}

Expand Down
30 changes: 30 additions & 0 deletions tests/neo.UnitTests/IO/Caching/UT_ECPointCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Cryptography.ECC;
using Neo.IO.Caching;
using Neo.Network.P2P.Payloads;
using System;

namespace Neo.UnitTests.IO.Caching
{
[TestClass]
public class UT_ECPointCache
{
ECPointCache relayCache;

[TestInitialize]
public void SetUp()
{
relayCache = new ECPointCache(10);
}

[TestMethod]
public void TestGetKeyForItem()
{
relayCache.Add(ECCurve.Secp256r1.G);
relayCache.Contains(ECCurve.Secp256r1.G).Should().BeTrue();
relayCache.TryGet(ECCurve.Secp256r1.G.EncodePoint(true), out ECPoint tmp).Should().BeTrue();
(tmp is ECPoint).Should().BeTrue();
}
}
}