Skip to content

Commit

Permalink
Neo as dotnet standard (#3082)
Browse files Browse the repository at this point in the history
* neo as dotnet standard

* More changes

* Clean usings

* More changes

* Awaits

* Fix ContractTask<T>

* Fix UT

* Remove extra method

* Clean changes

* More clean changes

* Use cschuchardt88 GetBitLength version

* Add comments

* Remove compiler directive

* Remove duplicate code

* Add UT

* NotImplemented

* Copy Sqrt exception

* var clean

* Rename

* Try algorithm

* @cschuchardt88 solution

* Max Size

* Fix limits

* clean code

* Check match

* Possible solution (ut not fixed)

* Limit to 256 bits

* Compiler directive

* Add comment

* Fix comment

* Fix ut

* Revert change

* Revert indent

* add -1

* fix

* Revert to same logic

* Jimmy's feedback

---------

Co-authored-by: Jimmy <jinghui@wayne.edu>
  • Loading branch information
shargon and Jim8y authored Feb 7, 2024
1 parent a1291a6 commit 9681219
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 44 deletions.
34 changes: 25 additions & 9 deletions src/Neo.VM/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace Neo.VM
{
internal static class Utility
public static class Utility
{
public static Encoding StrictUTF8 { get; }

Expand All @@ -34,11 +34,11 @@ public static BigInteger ModInverse(this BigInteger value, BigInteger modulus)
BigInteger r = value, old_r = modulus, s = 1, old_s = 0;
while (r > 0)
{
BigInteger q = old_r / r;
var q = old_r / r;
(old_r, r) = (r, old_r % r);
(old_s, s) = (s, old_s - q * s);
}
BigInteger result = old_s % modulus;
var result = old_s % modulus;
if (result < 0) result += modulus;
if (!(value * result % modulus).IsOne) throw new InvalidOperationException();
return result;
Expand All @@ -61,17 +61,33 @@ public static BigInteger Sqrt(this BigInteger value)
return z;
}

#if !NET5_0_OR_GREATER
static int GetBitLength(this BigInteger i)
/// <summary>
/// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit.
/// </summary>
/// <returns>The minimum non-negative number of bits in two's complement notation without the sign bit.</returns>
/// <remarks>This method returns 0 if the value of current object is equal to <see cref="BigInteger.Zero"/> or <see cref="BigInteger.MinusOne"/>. For positive integers the return value is equal to the ordinary binary representation string length.</remarks>
public static long GetBitLength(this BigInteger value)
{
byte[] b = i.ToByteArray();
return (b.Length - 1) * 8 + BitLen(i.Sign > 0 ? b[b.Length - 1] : 255 - b[b.Length - 1]);
#if NET5_0_OR_GREATER
return value.GetBitLength();
#else
if (value == 0 || value == BigInteger.MinusOne) return 0;

// Note: This method is imprecise and might not work as expected with integers larger than 256 bits.
var b = value.ToByteArray();
if (b.Length == 1 || (b.Length == 2 && b[1] == 0))
{
return BitLen(value.Sign > 0 ? b[0] : (byte)(255 - b[0]));
}
return (b.Length - 1) * 8 + BitLen(value.Sign > 0 ? b[^1] : 255 - b[^1]);
#endif
}

#if !NET5_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int BitLen(int w)
{
return (w < 1 << 15 ? (w < 1 << 7
return w < 1 << 15 ? (w < 1 << 7
? (w < 1 << 3 ? (w < 1 << 1
? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1)
: (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5
Expand All @@ -83,7 +99,7 @@ static int BitLen(int w)
? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19))
: (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27
? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27))
: (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31)))));
: (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))));
}
#endif
}
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/BigDecimal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public BigDecimal(BigInteger value, byte decimals)
public unsafe BigDecimal(decimal value)
{
Span<int> span = stackalloc int[4];
decimal.GetBits(value, span);
span = decimal.GetBits(value);
fixed (int* p = span)
{
ReadOnlySpan<byte> buffer = new(p, 16);
Expand All @@ -73,7 +73,7 @@ public unsafe BigDecimal(decimal value)
public unsafe BigDecimal(decimal value, byte decimals)
{
Span<int> span = stackalloc int[4];
decimal.GetBits(value, span);
span = decimal.GetBits(value);
fixed (int* p = span)
{
ReadOnlySpan<byte> buffer = new(p, 16);
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Cryptography/ECC/ECCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ECCurve
private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G)
{
this.Q = Q;
this.ExpectedECPointLength = ((int)Q.GetBitLength() + 7) / 8;
this.ExpectedECPointLength = ((int)VM.Utility.GetBitLength(Q) + 7) / 8;
this.A = new ECFieldElement(A, this);
this.B = new ECFieldElement(B, this);
this.N = N;
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/Cryptography/ECC/ECFieldElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public bool Equals(ECFieldElement other)

private static BigInteger[] FastLucasSequence(BigInteger p, BigInteger P, BigInteger Q, BigInteger k)
{
int n = (int)k.GetBitLength();
int n = (int)VM.Utility.GetBitLength(k);
int s = k.GetLowestSetBit();

BigInteger Uh = 1;
Expand Down Expand Up @@ -126,7 +126,7 @@ public ECFieldElement Sqrt()
BigInteger P;
do
{
P = rand.NextBigInteger((int)curve.Q.GetBitLength());
P = rand.NextBigInteger((int)VM.Utility.GetBitLength(curve.Q));
}
while (P >= curve.Q || BigInteger.ModPow(P * P - fourQ, legendreExponent, curve.Q) != qMinusOne);
BigInteger[] result = FastLucasSequence(curve.Q, P, Q, k);
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/Cryptography/ECC/ECPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public override int GetHashCode()
internal static ECPoint Multiply(ECPoint p, BigInteger k)
{
// floor(log2(k))
int m = (int)k.GetBitLength();
int m = (int)VM.Utility.GetBitLength(k);

// width of the Window NAF
sbyte width;
Expand Down Expand Up @@ -391,7 +391,7 @@ internal ECPoint Twice()

private static sbyte[] WindowNaf(sbyte width, BigInteger k)
{
sbyte[] wnaf = new sbyte[k.GetBitLength() + 1];
sbyte[] wnaf = new sbyte[VM.Utility.GetBitLength(k) + 1];
short pow2wB = (short)(1 << width);
int i = 0;
int length = 0;
Expand Down
27 changes: 26 additions & 1 deletion src/Neo/Cryptography/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System;
using System.Buffers.Binary;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using static Neo.Helper;
Expand Down Expand Up @@ -99,7 +100,7 @@ public static byte[] Murmur128(this byte[] value, uint seed)
/// <returns>The computed hash code.</returns>
public static byte[] Murmur128(this ReadOnlySpan<byte> value, uint seed)
{
byte[] buffer = GC.AllocateUninitializedArray<byte>(16);
byte[] buffer = new byte[16];
using Murmur128 murmur = new(seed);
murmur.TryComputeHash(value, buffer, out _);
return buffer;
Expand Down Expand Up @@ -239,5 +240,29 @@ internal static bool Test(this BloomFilter filter, Transaction tx)
return true;
return false;
}

/// <summary>
/// Rotates the specified value left by the specified number of bits.
/// Similar in behavior to the x86 instruction ROL.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="offset">The number of bits to rotate by.
/// Any value outside the range [0..31] is treated as congruent mod 32.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateLeft(uint value, int offset)
=> (value << offset) | (value >> (32 - offset));

/// <summary>
/// Rotates the specified value left by the specified number of bits.
/// Similar in behavior to the x86 instruction ROL.
/// </summary>
/// <param name="value">The value to rotate.</param>
/// <param name="offset">The number of bits to rotate by.
/// Any value outside the range [0..63] is treated as congruent mod 64.</param>
/// <returns>The rotated value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong RotateLeft(ulong value, int offset)
=> (value << offset) | (value >> (64 - offset));
}
}
14 changes: 7 additions & 7 deletions src/Neo/Cryptography/Murmur128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,19 @@ protected override void HashCore(ReadOnlySpan<byte> source)
{
ulong k1 = BinaryPrimitives.ReadUInt64LittleEndian(source[i..]);
k1 *= c1;
k1 = BitOperations.RotateLeft(k1, r1);
k1 = Helper.RotateLeft(k1, r1);
k1 *= c2;
H1 ^= k1;
H1 = BitOperations.RotateLeft(H1, 27);
H1 = Helper.RotateLeft(H1, 27);
H1 += H2;
H1 = H1 * m + n1;

ulong k2 = BinaryPrimitives.ReadUInt64LittleEndian(source[(i + 8)..]);
k2 *= c2;
k2 = BitOperations.RotateLeft(k2, r2);
k2 = Helper.RotateLeft(k2, r2);
k2 *= c1;
H2 ^= k2;
H2 = BitOperations.RotateLeft(H2, 31);
H2 = Helper.RotateLeft(H2, 31);
H2 += H1;
H2 = H2 * m + n2;
}
Expand All @@ -102,14 +102,14 @@ protected override void HashCore(ReadOnlySpan<byte> source)
case 1: remainingBytesL ^= (ulong)source[alignedLength] << 0; break;
}

H2 ^= BitOperations.RotateLeft(remainingBytesH * c2, r2) * c1;
H1 ^= BitOperations.RotateLeft(remainingBytesL * c1, r1) * c2;
H2 ^= Helper.RotateLeft(remainingBytesH * c2, r2) * c1;
H1 ^= Helper.RotateLeft(remainingBytesL * c1, r1) * c2;
}
}

protected override byte[] HashFinal()
{
byte[] buffer = GC.AllocateUninitializedArray<byte>(sizeof(ulong) * 2);
byte[] buffer = new byte[sizeof(ulong) * 2];
TryHashFinal(buffer, out _);
return buffer;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Neo/Cryptography/Murmur32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ protected override void HashCore(ReadOnlySpan<byte> source)
{
uint k = BinaryPrimitives.ReadUInt32LittleEndian(source);
k *= c1;
k = BitOperations.RotateLeft(k, r1);
k = Helper.RotateLeft(k, r1);
k *= c2;
hash ^= k;
hash = BitOperations.RotateLeft(hash, r2);
hash = Helper.RotateLeft(hash, r2);
hash = hash * m + n;
}
if (source.Length > 0)
Expand All @@ -72,15 +72,15 @@ protected override void HashCore(ReadOnlySpan<byte> source)
case 1: remainingBytes ^= source[0]; break;
}
remainingBytes *= c1;
remainingBytes = BitOperations.RotateLeft(remainingBytes, r1);
remainingBytes = Helper.RotateLeft(remainingBytes, r1);
remainingBytes *= c2;
hash ^= remainingBytes;
}
}

protected override byte[] HashFinal()
{
byte[] buffer = GC.AllocateUninitializedArray<byte>(sizeof(uint));
byte[] buffer = new byte[sizeof(uint)];
TryHashFinal(buffer, out _);
return buffer;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/IO/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static ISerializable AsSerializable(this ReadOnlyMemory<byte> value, Type
public static ReadOnlyMemory<byte> CompressLz4(this ReadOnlySpan<byte> data)
{
int maxLength = LZ4Codec.MaximumOutputSize(data.Length);
byte[] buffer = GC.AllocateUninitializedArray<byte>(sizeof(uint) + maxLength);
byte[] buffer = new byte[sizeof(uint) + maxLength];
BinaryPrimitives.WriteInt32LittleEndian(buffer, data.Length);
int length = LZ4Codec.Encode(data, buffer.AsSpan(sizeof(uint)));
return buffer.AsMemory(0, sizeof(uint) + length);
Expand All @@ -118,7 +118,7 @@ public static byte[] DecompressLz4(this ReadOnlySpan<byte> data, int maxOutput)
{
int length = BinaryPrimitives.ReadInt32LittleEndian(data);
if (length < 0 || length > maxOutput) throw new FormatException();
byte[] result = GC.AllocateUninitializedArray<byte>(length);
byte[] result = new byte[length];
if (LZ4Codec.Decode(data[4..], result) != length)
throw new FormatException();
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Neo.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.1;net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PackageTags>NEO;AntShares;Blockchain;Smart Contract</PackageTags>
</PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Neo/Network/UPnP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ private static XmlDocument SOAPRequest(string url, string soap, string function)
request.Headers.Add("Content-Type", "text/xml; charset=\"utf-8\"");
request.Content = new StringContent(req);
using HttpClient http = new();
using HttpResponseMessage response = http.Send(request);
using Stream stream = response.EnsureSuccessStatusCode().Content.ReadAsStream();
using HttpResponseMessage response = http.SendAsync(request).GetAwaiter().GetResult();
using Stream stream = response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync().GetAwaiter().GetResult();
XmlDocument resp = new() { XmlResolver = null };
resp.Load(stream);
return resp;
Expand Down
10 changes: 3 additions & 7 deletions src/Neo/SmartContract/ContractTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,15 @@ public ContractTask()
}

protected virtual ContractTaskAwaiter CreateAwaiter() => new();

public virtual ContractTaskAwaiter GetAwaiter() => awaiter;

public virtual object GetResult() => null;
}

[AsyncMethodBuilder(typeof(ContractTaskMethodBuilder<>))]
class ContractTask<T> : ContractTask
{
protected override ContractTaskAwaiter<T> CreateAwaiter() => new();

public override ContractTaskAwaiter<T> GetAwaiter() => (ContractTaskAwaiter<T>)base.GetAwaiter();

public override object GetResult() => GetAwaiter().GetResult();
protected override ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter<T>();
public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter<T>)base.GetAwaiter();
public override object GetResult() => ((ContractTaskAwaiter<T>)GetAwaiter()).GetResult();
}
}
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/ContractTaskMethodBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void SetException(Exception exception)

public void SetResult(T result)
{
Task.GetAwaiter().SetResult(result);
((ContractTaskAwaiter<T>)Task.GetAwaiter()).SetResult(result);
}

public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter)
};
if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException();
_ = descriptor.Parameters.ToDictionary(p => p.Name);
if (!Enum.IsDefined(descriptor.ReturnType)) throw new FormatException();
if (!Enum.IsDefined(typeof(ContractParameterType), descriptor.ReturnType)) throw new FormatException();
if (descriptor.Offset < 0) throw new FormatException();
return descriptor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static ContractParameterDefinition FromJson(JObject json)
};
if (string.IsNullOrEmpty(parameter.Name))
throw new FormatException();
if (!Enum.IsDefined(parameter.Type) || parameter.Type == ContractParameterType.Void)
if (!Enum.IsDefined(typeof(ContractParameterType), parameter.Type) || parameter.Type == ContractParameterType.Void)
throw new FormatException();
return parameter;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/SmartContract/StorageKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public byte[] ToArray()
{
if (cache is null)
{
cache = GC.AllocateUninitializedArray<byte>(sizeof(int) + Key.Length);
cache = new byte[sizeof(int) + Key.Length];
BinaryPrimitives.WriteInt32LittleEndian(cache, Id);
Key.CopyTo(cache.AsMemory(sizeof(int)));
}
Expand Down
Loading

0 comments on commit 9681219

Please sign in to comment.