diff --git a/src/Neo.VM/Utility.cs b/src/Neo.VM/Utility.cs
index 940c98d7d9..32f6f157dd 100644
--- a/src/Neo.VM/Utility.cs
+++ b/src/Neo.VM/Utility.cs
@@ -16,7 +16,7 @@
namespace Neo.VM
{
- internal static class Utility
+ public static class Utility
{
public static Encoding StrictUTF8 { get; }
@@ -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;
@@ -61,17 +61,33 @@ public static BigInteger Sqrt(this BigInteger value)
return z;
}
-#if !NET5_0_OR_GREATER
- static int GetBitLength(this BigInteger i)
+ ///
+ /// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit.
+ ///
+ /// The minimum non-negative number of bits in two's complement notation without the sign bit.
+ /// This method returns 0 if the value of current object is equal to or . For positive integers the return value is equal to the ordinary binary representation string length.
+ 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
@@ -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
}
diff --git a/src/Neo/BigDecimal.cs b/src/Neo/BigDecimal.cs
index 1658ea9c8e..05cdf86626 100644
--- a/src/Neo/BigDecimal.cs
+++ b/src/Neo/BigDecimal.cs
@@ -55,7 +55,7 @@ public BigDecimal(BigInteger value, byte decimals)
public unsafe BigDecimal(decimal value)
{
Span span = stackalloc int[4];
- decimal.GetBits(value, span);
+ span = decimal.GetBits(value);
fixed (int* p = span)
{
ReadOnlySpan buffer = new(p, 16);
@@ -73,7 +73,7 @@ public unsafe BigDecimal(decimal value)
public unsafe BigDecimal(decimal value, byte decimals)
{
Span span = stackalloc int[4];
- decimal.GetBits(value, span);
+ span = decimal.GetBits(value);
fixed (int* p = span)
{
ReadOnlySpan buffer = new(p, 16);
diff --git a/src/Neo/Cryptography/ECC/ECCurve.cs b/src/Neo/Cryptography/ECC/ECCurve.cs
index 7da6c5f3b8..b228f7ab31 100644
--- a/src/Neo/Cryptography/ECC/ECCurve.cs
+++ b/src/Neo/Cryptography/ECC/ECCurve.cs
@@ -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;
diff --git a/src/Neo/Cryptography/ECC/ECFieldElement.cs b/src/Neo/Cryptography/ECC/ECFieldElement.cs
index c00a779315..e08b5bb71f 100644
--- a/src/Neo/Cryptography/ECC/ECFieldElement.cs
+++ b/src/Neo/Cryptography/ECC/ECFieldElement.cs
@@ -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;
@@ -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);
diff --git a/src/Neo/Cryptography/ECC/ECPoint.cs b/src/Neo/Cryptography/ECC/ECPoint.cs
index 6b74232c81..b2d44bf6cf 100644
--- a/src/Neo/Cryptography/ECC/ECPoint.cs
+++ b/src/Neo/Cryptography/ECC/ECPoint.cs
@@ -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;
@@ -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;
diff --git a/src/Neo/Cryptography/Helper.cs b/src/Neo/Cryptography/Helper.cs
index 2f0ad0ce35..840b6ebb7b 100644
--- a/src/Neo/Cryptography/Helper.cs
+++ b/src/Neo/Cryptography/Helper.cs
@@ -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;
@@ -99,7 +100,7 @@ public static byte[] Murmur128(this byte[] value, uint seed)
/// The computed hash code.
public static byte[] Murmur128(this ReadOnlySpan value, uint seed)
{
- byte[] buffer = GC.AllocateUninitializedArray(16);
+ byte[] buffer = new byte[16];
using Murmur128 murmur = new(seed);
murmur.TryComputeHash(value, buffer, out _);
return buffer;
@@ -239,5 +240,29 @@ internal static bool Test(this BloomFilter filter, Transaction tx)
return true;
return false;
}
+
+ ///
+ /// Rotates the specified value left by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROL.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..31] is treated as congruent mod 32.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint RotateLeft(uint value, int offset)
+ => (value << offset) | (value >> (32 - offset));
+
+ ///
+ /// Rotates the specified value left by the specified number of bits.
+ /// Similar in behavior to the x86 instruction ROL.
+ ///
+ /// The value to rotate.
+ /// The number of bits to rotate by.
+ /// Any value outside the range [0..63] is treated as congruent mod 64.
+ /// The rotated value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong RotateLeft(ulong value, int offset)
+ => (value << offset) | (value >> (64 - offset));
}
}
diff --git a/src/Neo/Cryptography/Murmur128.cs b/src/Neo/Cryptography/Murmur128.cs
index 18e67a91ac..3ee258b4a2 100644
--- a/src/Neo/Cryptography/Murmur128.cs
+++ b/src/Neo/Cryptography/Murmur128.cs
@@ -63,19 +63,19 @@ protected override void HashCore(ReadOnlySpan 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;
}
@@ -102,14 +102,14 @@ protected override void HashCore(ReadOnlySpan 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(sizeof(ulong) * 2);
+ byte[] buffer = new byte[sizeof(ulong) * 2];
TryHashFinal(buffer, out _);
return buffer;
}
diff --git a/src/Neo/Cryptography/Murmur32.cs b/src/Neo/Cryptography/Murmur32.cs
index a48c5d6750..8feaa92d37 100644
--- a/src/Neo/Cryptography/Murmur32.cs
+++ b/src/Neo/Cryptography/Murmur32.cs
@@ -56,10 +56,10 @@ protected override void HashCore(ReadOnlySpan 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)
@@ -72,7 +72,7 @@ protected override void HashCore(ReadOnlySpan source)
case 1: remainingBytes ^= source[0]; break;
}
remainingBytes *= c1;
- remainingBytes = BitOperations.RotateLeft(remainingBytes, r1);
+ remainingBytes = Helper.RotateLeft(remainingBytes, r1);
remainingBytes *= c2;
hash ^= remainingBytes;
}
@@ -80,7 +80,7 @@ protected override void HashCore(ReadOnlySpan source)
protected override byte[] HashFinal()
{
- byte[] buffer = GC.AllocateUninitializedArray(sizeof(uint));
+ byte[] buffer = new byte[sizeof(uint)];
TryHashFinal(buffer, out _);
return buffer;
}
diff --git a/src/Neo/IO/Helper.cs b/src/Neo/IO/Helper.cs
index 7449dbfab9..2e6d362ee1 100644
--- a/src/Neo/IO/Helper.cs
+++ b/src/Neo/IO/Helper.cs
@@ -102,7 +102,7 @@ public static ISerializable AsSerializable(this ReadOnlyMemory value, Type
public static ReadOnlyMemory CompressLz4(this ReadOnlySpan data)
{
int maxLength = LZ4Codec.MaximumOutputSize(data.Length);
- byte[] buffer = GC.AllocateUninitializedArray(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);
@@ -118,7 +118,7 @@ public static byte[] DecompressLz4(this ReadOnlySpan data, int maxOutput)
{
int length = BinaryPrimitives.ReadInt32LittleEndian(data);
if (length < 0 || length > maxOutput) throw new FormatException();
- byte[] result = GC.AllocateUninitializedArray(length);
+ byte[] result = new byte[length];
if (LZ4Codec.Decode(data[4..], result) != length)
throw new FormatException();
return result;
diff --git a/src/Neo/Neo.csproj b/src/Neo/Neo.csproj
index de628a66de..234fbc62c6 100644
--- a/src/Neo/Neo.csproj
+++ b/src/Neo/Neo.csproj
@@ -1,7 +1,7 @@
- net7.0
+ netstandard2.1;net7.0
true
NEO;AntShares;Blockchain;Smart Contract
diff --git a/src/Neo/Network/UPnP.cs b/src/Neo/Network/UPnP.cs
index a80e6e69fb..a0cd0ce78a 100644
--- a/src/Neo/Network/UPnP.cs
+++ b/src/Neo/Network/UPnP.cs
@@ -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;
diff --git a/src/Neo/SmartContract/ContractTask.cs b/src/Neo/SmartContract/ContractTask.cs
index 523b8b115a..6df3627c37 100644
--- a/src/Neo/SmartContract/ContractTask.cs
+++ b/src/Neo/SmartContract/ContractTask.cs
@@ -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 : ContractTask
{
- protected override ContractTaskAwaiter CreateAwaiter() => new();
-
- public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter();
-
- public override object GetResult() => GetAwaiter().GetResult();
+ protected override ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter();
+ public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter();
+ public override object GetResult() => ((ContractTaskAwaiter)GetAwaiter()).GetResult();
}
}
diff --git a/src/Neo/SmartContract/ContractTaskMethodBuilder.cs b/src/Neo/SmartContract/ContractTaskMethodBuilder.cs
index 83090bf583..bae1146f25 100644
--- a/src/Neo/SmartContract/ContractTaskMethodBuilder.cs
+++ b/src/Neo/SmartContract/ContractTaskMethodBuilder.cs
@@ -71,7 +71,7 @@ public void SetException(Exception exception)
public void SetResult(T result)
{
- Task.GetAwaiter().SetResult(result);
+ ((ContractTaskAwaiter)Task.GetAwaiter()).SetResult(result);
}
public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine)
diff --git a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs
index 646ee76c46..cc8d0a16d5 100644
--- a/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs
+++ b/src/Neo/SmartContract/Manifest/ContractMethodDescriptor.cs
@@ -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;
}
diff --git a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs
index a393bb02b1..61906558d0 100644
--- a/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs
+++ b/src/Neo/SmartContract/Manifest/ContractParameterDefinition.cs
@@ -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;
}
diff --git a/src/Neo/SmartContract/StorageKey.cs b/src/Neo/SmartContract/StorageKey.cs
index 21d7a64191..a0136e4456 100644
--- a/src/Neo/SmartContract/StorageKey.cs
+++ b/src/Neo/SmartContract/StorageKey.cs
@@ -73,7 +73,7 @@ public byte[] ToArray()
{
if (cache is null)
{
- cache = GC.AllocateUninitializedArray(sizeof(int) + Key.Length);
+ cache = new byte[sizeof(int) + Key.Length];
BinaryPrimitives.WriteInt32LittleEndian(cache, Id);
Key.CopyTo(cache.AsMemory(sizeof(int)));
}
diff --git a/tests/Neo.VM.Tests/UT_Utility.cs b/tests/Neo.VM.Tests/UT_Utility.cs
index 2d0756e190..38678bcddb 100644
--- a/tests/Neo.VM.Tests/UT_Utility.cs
+++ b/tests/Neo.VM.Tests/UT_Utility.cs
@@ -32,6 +32,64 @@ public void TestSqrtTest()
Assert.AreEqual(new BigInteger(9), new BigInteger(81).Sqrt());
}
+ private static byte[] GetRandomByteArray(Random random)
+ {
+ var byteValue = random.Next(0, 32);
+ var value = new byte[byteValue];
+
+ random.NextBytes(value);
+ return value;
+ }
+
+ private void VerifyGetBitLength(BigInteger value, long expected)
+ {
+ var result = value.GetBitLength();
+ Assert.AreEqual(expected, value.GetBitLength(), "Native method has not the expected result");
+ Assert.AreEqual(result, Utility.GetBitLength(value), "Result doesn't match");
+ }
+
+ [TestMethod]
+ public void GetBitLengthTest()
+ {
+ var random = new Random();
+
+ // Big Number (net standard didn't work)
+ VerifyGetBitLength(BigInteger.One << 32 << int.MaxValue, 2147483680);
+
+ // Trivial cases
+ // sign bit|shortest two's complement
+ // string w/o sign bit
+ VerifyGetBitLength(0, 0); // 0|
+ VerifyGetBitLength(1, 1); // 0|1
+ VerifyGetBitLength(-1, 0); // 1|
+ VerifyGetBitLength(2, 2); // 0|10
+ VerifyGetBitLength(-2, 1); // 1|0
+ VerifyGetBitLength(3, 2); // 0|11
+ VerifyGetBitLength(-3, 2); // 1|01
+ VerifyGetBitLength(4, 3); // 0|100
+ VerifyGetBitLength(-4, 2); // 1|00
+ VerifyGetBitLength(5, 3); // 0|101
+ VerifyGetBitLength(-5, 3); // 1|011
+ VerifyGetBitLength(6, 3); // 0|110
+ VerifyGetBitLength(-6, 3); // 1|010
+ VerifyGetBitLength(7, 3); // 0|111
+ VerifyGetBitLength(-7, 3); // 1|001
+ VerifyGetBitLength(8, 4); // 0|1000
+ VerifyGetBitLength(-8, 3); // 1|000
+
+ // Random cases
+ for (uint i = 0; i < 1000; i++)
+ {
+ var bi = new BigInteger(GetRandomByteArray(random));
+ Assert.AreEqual(bi.GetBitLength(), Utility.GetBitLength(bi), message: $"Error comparing: {bi}");
+ }
+
+ foreach (var bi in new[] { BigInteger.Zero, BigInteger.One, BigInteger.MinusOne, new BigInteger(ulong.MaxValue), new BigInteger(long.MinValue) })
+ {
+ Assert.AreEqual(bi.GetBitLength(), Utility.GetBitLength(bi), message: $"Error comparing: {bi}");
+ }
+ }
+
[TestMethod]
public void TestModInverseTest()
{