diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 18de892..3ada82d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -2,7 +2,7 @@ name: Build and Test on: push: - branches: [ master ] + branches: [ master, netstandard-2-0-target ] pull_request: branches: [ master ] @@ -11,6 +11,13 @@ jobs: runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: net7.0 + framework: net7.0 + - name: netstandard2.0 + framework: netstandard2.0 steps: - uses: actions/checkout@v2 - name: Setup .NET @@ -22,4 +29,4 @@ jobs: - name: Build run: dotnet build --no-restore - name: Test - run: dotnet test --no-build --verbosity normal --filter "TestCategory!~NCTL" + run: TEST_FRAMEWORK=${{ matrix.framework }} dotnet test --no-build --verbosity normal --filter "TestCategory!~NCTL" diff --git a/.github/workflows/integration-testing.yml b/.github/workflows/integration-testing.yml index a8b62be..58a3727 100644 --- a/.github/workflows/integration-testing.yml +++ b/.github/workflows/integration-testing.yml @@ -9,6 +9,14 @@ jobs: # The type of runner that the job will run on runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: net7.0 + framework: net7.0 + - name: netstandard2.0 + framework: netstandard2.0 + # Service containers to run with `runner-job` services: # Label used to access the service container @@ -37,4 +45,4 @@ jobs: - name: Build run: dotnet build --no-restore - name: Test - run: dotnet test --no-build --verbosity normal --settings Casper.Network.SDK.Test/test.runsettings --filter="TestCategory=NCTL" + run: TEST_FRAMEWORK=${{ matrix.framework }} dotnet test --no-build --verbosity normal --settings Casper.Network.SDK.Test/test.runsettings --filter="TestCategory=NCTL" diff --git a/.github/workflows/unity-compat.yml b/.github/workflows/unity-compat.yml new file mode 100644 index 0000000..03be418 --- /dev/null +++ b/.github/workflows/unity-compat.yml @@ -0,0 +1,24 @@ +ame: Unity Compat - Build and Test + +on: + push: + branches: [netstandard-2-0-target] + pull_request: + branches: [netstandard-2-0-target] + +jobs: + buildntest: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal --filter "TestCategory!~NCTL" diff --git a/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs b/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs index abccc0f..4862944 100644 --- a/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs +++ b/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs @@ -7,7 +7,6 @@ using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; using Casper.Network.SDK.Types; -using Newtonsoft.Json.Linq; using NUnit.Framework; using Org.BouncyCastle.Utilities.Encoders; @@ -148,7 +147,7 @@ public void DeserializeI32CLValue() var json = @"{""cl_type"":""I32"",""bytes"":""00000080"",""parsed"":-2147483648}"; var clValue = JsonSerializer.Deserialize(json); Assert.IsNotNull(clValue); - Assert.AreEqual(int.MinValue, BitConverter.ToInt32(clValue.Bytes)); + Assert.AreEqual(int.MinValue, BitConverterExtensions.ToInt32(clValue.Bytes)); Assert.AreEqual(int.MinValue, clValue.Parsed); } @@ -168,7 +167,7 @@ public void DeserializeU64CLValue() var clValue = JsonSerializer.Deserialize(json); Assert.IsNotNull(clValue); Assert.AreEqual(CLType.U64, clValue.TypeInfo.Type); - Assert.AreEqual(ulong.MaxValue, BitConverter.ToUInt64(clValue.Bytes)); + Assert.AreEqual(ulong.MaxValue, BitConverterExtensions.ToUInt64(clValue.Bytes)); Assert.AreEqual(ulong.MaxValue, clValue.Parsed); } diff --git a/Casper.Network.SDK.Test/CLValueToTypeTest.cs b/Casper.Network.SDK.Test/CLValueToTypeTest.cs index 028c06f..80db6e0 100644 --- a/Casper.Network.SDK.Test/CLValueToTypeTest.cs +++ b/Casper.Network.SDK.Test/CLValueToTypeTest.cs @@ -214,7 +214,7 @@ public void CLValueToBigInteger() Assert.AreEqual(new BigInteger(5123456789012), clValue.ToBigInteger()); Assert.AreEqual(new BigInteger(5123456789012), (BigInteger) clValue); - var bigInt = new BigInteger(Hex.Decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"), true); + var bigInt = BigIntegerCompat.Create(Hex.Decode("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"), true); clValue = CLValue.U128(bigInt); Assert.AreEqual(bigInt, clValue.ToBigInteger()); Assert.AreEqual(bigInt, (BigInteger) clValue); diff --git a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj index e14e06f..70b9c66 100644 --- a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj +++ b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj @@ -2,9 +2,8 @@ net7.0 - + 9.0 false - NetCasperTest @@ -15,10 +14,19 @@ - - + + + $(TEST_FRAMEWORK) + + net7.0 + + + + + + + - PreserveNewest diff --git a/Casper.Network.SDK.Test/KeysTest.cs b/Casper.Network.SDK.Test/KeysTest.cs index ba11605..6dfae71 100644 --- a/Casper.Network.SDK.Test/KeysTest.cs +++ b/Casper.Network.SDK.Test/KeysTest.cs @@ -21,7 +21,7 @@ public void TestValidBlakeEd25519() var publicKey = PublicKey.FromHexString(ED25519publicKey); Assert.AreEqual(KeyAlgo.ED25519, publicKey.KeyAlgorithm); Assert.AreEqual(ED25519publicKey, publicKey.ToAccountHex()); - Assert.IsTrue(Hex.Decode(ED25519publicKey)[1..].SequenceEqual(publicKey.RawBytes)); + Assert.IsTrue(Hex.Decode(ED25519publicKey).Slice(1).SequenceEqual(publicKey.RawBytes)); var hash = publicKey.GetAccountHash(); Assert.AreEqual(ED25519hash, hash.Substring("account-hash-".Length), "Unexpected ED25519 hash value"); @@ -29,19 +29,19 @@ public void TestValidBlakeEd25519() var pk2 = PublicKey.FromBytes(Hex.Decode(ED25519publicKey)); Assert.AreEqual(KeyAlgo.ED25519, pk2.KeyAlgorithm); Assert.AreEqual(ED25519publicKey, pk2.ToAccountHex()); - Assert.IsTrue(Hex.Decode(ED25519publicKey)[1..].SequenceEqual(pk2.RawBytes)); + Assert.IsTrue(Hex.Decode(ED25519publicKey).Slice(1).SequenceEqual(pk2.RawBytes)); - var pk3 = PublicKey.FromRawBytes(Hex.Decode(ED25519publicKey)[1..], KeyAlgo.ED25519); + var pk3 = PublicKey.FromRawBytes(Hex.Decode(ED25519publicKey).Slice(1), KeyAlgo.ED25519); Assert.AreEqual(KeyAlgo.ED25519, pk3.KeyAlgorithm); Assert.AreEqual(ED25519publicKey, pk3.ToAccountHex()); - Assert.IsTrue(Hex.Decode(ED25519publicKey)[1..].SequenceEqual(pk3.RawBytes)); + Assert.IsTrue(Hex.Decode(ED25519publicKey).Slice(1).SequenceEqual(pk3.RawBytes)); var pemfile = TestContext.CurrentContext.TestDirectory + "/TestData/test-ed25519-pk.pem"; var pk4 = PublicKey.FromPem(pemfile); Assert.AreEqual(KeyAlgo.ED25519, pk4.KeyAlgorithm); Assert.AreEqual(ED25519publicKey, pk4.ToAccountHex()); - Assert.IsTrue(Hex.Decode(ED25519publicKey)[1..].SequenceEqual(pk4.RawBytes)); + Assert.IsTrue(Hex.Decode(ED25519publicKey).Slice(1).SequenceEqual(pk4.RawBytes)); Assert.IsTrue(Hex.Decode(ED25519publicKey).SequenceEqual(pk4.GetBytes())); var hash3 = pk4.GetAccountHash(); @@ -60,19 +60,19 @@ public void TestValidBlakeSecp256k1() var pk2 = PublicKey.FromBytes(Hex.Decode(SECP256K1publicKey)); Assert.AreEqual(KeyAlgo.SECP256K1, pk2.KeyAlgorithm); Assert.AreEqual(SECP256K1publicKey, pk2.ToAccountHex()); - Assert.IsTrue(Hex.Decode(SECP256K1publicKey)[1..].SequenceEqual(pk2.RawBytes)); + Assert.IsTrue(Hex.Decode(SECP256K1publicKey).Slice(1).SequenceEqual(pk2.RawBytes)); - var pk3 = PublicKey.FromRawBytes(Hex.Decode(SECP256K1publicKey)[1..], KeyAlgo.SECP256K1); + var pk3 = PublicKey.FromRawBytes(Hex.Decode(SECP256K1publicKey).Slice(1), KeyAlgo.SECP256K1); Assert.AreEqual(KeyAlgo.SECP256K1, pk3.KeyAlgorithm); Assert.AreEqual(SECP256K1publicKey, pk3.ToAccountHex()); - Assert.IsTrue(Hex.Decode(SECP256K1publicKey)[1..].SequenceEqual(pk3.RawBytes)); + Assert.IsTrue(Hex.Decode(SECP256K1publicKey).Slice(1).SequenceEqual(pk3.RawBytes)); var pemfile = TestContext.CurrentContext.TestDirectory + "/TestData/test-secp256k1-pk.pem"; var pk4 = PublicKey.FromPem(pemfile); Assert.AreEqual(KeyAlgo.SECP256K1, pk4.KeyAlgorithm); Assert.AreEqual(SECP256K1publicKey, pk4.ToAccountHex()); - Assert.IsTrue(Hex.Decode(SECP256K1publicKey)[1..].SequenceEqual(pk4.RawBytes)); + Assert.IsTrue(Hex.Decode(SECP256K1publicKey).Slice(1).SequenceEqual(pk4.RawBytes)); Assert.IsTrue(Hex.Decode(SECP256K1publicKey).SequenceEqual(pk4.GetBytes())); var hash3 = pk4.GetAccountHash(); diff --git a/Casper.Network.SDK.Test/NctlContractTest.cs b/Casper.Network.SDK.Test/NctlContractTest.cs index 3e5ae2f..df520d2 100644 --- a/Casper.Network.SDK.Test/NctlContractTest.cs +++ b/Casper.Network.SDK.Test/NctlContractTest.cs @@ -25,7 +25,7 @@ public class NctlContractTest : NctlBase [Test, Order(1)] public async Task DeployContractTest() { - var wasmBytes = await File.ReadAllBytesAsync(_wasmFile); + var wasmBytes = await FileExtensions.ReadAllBytesAsync(_wasmFile); var deploy = DeployTemplates.ContractDeploy( wasmBytes, diff --git a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs index f3513ad..5e3a2e2 100644 --- a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs +++ b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs @@ -26,7 +26,7 @@ public class NctlMyDictContractTest : NctlBase [Test, Order(1)] public async Task DeployContractTest() { - var wasmBytes = await File.ReadAllBytesAsync(_wasmFile); + var wasmBytes = await FileExtensions.ReadAllBytesAsync(_wasmFile); var deploy = DeployTemplates.ContractDeploy( wasmBytes, diff --git a/Casper.Network.SDK.Test/NctlSpeculativeExecutionTest.cs b/Casper.Network.SDK.Test/NctlSpeculativeExecutionTest.cs index ee1da46..72217a9 100644 --- a/Casper.Network.SDK.Test/NctlSpeculativeExecutionTest.cs +++ b/Casper.Network.SDK.Test/NctlSpeculativeExecutionTest.cs @@ -40,7 +40,7 @@ public void Setup() [Test, Order(1)] public async Task SpeculativeExecutionTest() { - var wasmBytes = await File.ReadAllBytesAsync(_wasmFile); + var wasmBytes = await FileExtensions.ReadAllBytesAsync(_wasmFile); var deploy = DeployTemplates.ContractDeploy( wasmBytes, diff --git a/Casper.Network.SDK.sln.DotSettings.user b/Casper.Network.SDK.sln.DotSettings.user index b2dd7b0..2c5113e 100644 --- a/Casper.Network.SDK.sln.DotSettings.user +++ b/Casper.Network.SDK.sln.DotSettings.user @@ -1,2 +1,4 @@  - C:\Users\d\AppData\Local\JetBrains\Rider2021.3\resharper-host\temp\Rider\vAny\CoverageData\_Casper.Network.SDK.-947408436\Snapshot\snapshot.utdcvr \ No newline at end of file + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> \ No newline at end of file diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index ceff12a..ef16c9f 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -1,10 +1,12 @@ - net7.0 - 2.2.0.0 - 2.2.0 - 2.2.0 + net7.0;netstandard2.0 + 9.0 + CS1591 + 2.3.0.0 + 2.3.0 + 2.3.0 Casper.Network.SDK make-software https://github.com/make-software/casper-net-sdk @@ -18,11 +20,13 @@ Casper.Network.SDK true - bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + bin\$(Configuration)\$(AssemblyName).xml + + diff --git a/Casper.Network.SDK/Compat/BigIntegerCompat.cs b/Casper.Network.SDK/Compat/BigIntegerCompat.cs new file mode 100644 index 0000000..6f4ae31 --- /dev/null +++ b/Casper.Network.SDK/Compat/BigIntegerCompat.cs @@ -0,0 +1,35 @@ +using System; +using System.Numerics; + +public class BigIntegerCompat +{ + public static BigInteger Create(byte[] value, bool isUnsigned = false, bool isBigEndian = false) + { +#if NET7_0_OR_GREATER + return new BigInteger(value, true, false); +#elif NETSTANDARD2_0 + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (isBigEndian) + { + Array.Reverse(value); + } + + if (isUnsigned) + { + // Ensure that the resulting BigInteger is treated as an unsigned value by appending a 0 byte if necessary + if (value[value.Length - 1] >= 0x80) + { + Array.Resize(ref value, value.Length + 1); + value[value.Length - 1] = 0; + } + } + + return new BigInteger(value); +#endif + } +} + diff --git a/Casper.Network.SDK/Compat/BigIntegerExtensions.cs b/Casper.Network.SDK/Compat/BigIntegerExtensions.cs new file mode 100644 index 0000000..b6b2b55 --- /dev/null +++ b/Casper.Network.SDK/Compat/BigIntegerExtensions.cs @@ -0,0 +1,62 @@ +#if NETSTANDARD2_0 + +using System; +using System.Numerics; + +public static class BigIntegerExtensions +{ + public static byte[] ToByteArray(this BigInteger value, bool isUnsigned = false) + { + byte[] bytes = value.ToByteArray(); + + if (isUnsigned) + { + if (value < 0) + { + throw new InvalidOperationException("Cannot retrieve an unsigned byte array representation of a negative BigInteger."); + } + + // If the BigInteger is positive and the highest byte is 0x80 or higher, + // an additional byte is added to indicate a positive number when using two's complement notation. + // For the unsigned representation, this additional byte is not necessary. + if (bytes.Length > 1 && bytes[bytes.Length - 1] == 0) + { + Array.Resize(ref bytes, bytes.Length - 1); + } + } + + return bytes; + } + + public static int GetBitLength(this BigInteger value) + { + // If the value is zero, its bit length is 0 + if (value == BigInteger.Zero) + return 0; + + // Get the byte array without the sign + byte[] bytes = value.ToByteArray(); + + // Find the highest-order byte with a non-zero value + int highestByte = bytes.Length - 1; + while (highestByte > 0 && bytes[highestByte] == 0) + { + highestByte--; + } + + // Count the bits in the last used byte + int bitsInLastByte = 0; + byte lastByte = bytes[highestByte]; + while (lastByte != 0) + { + bitsInLastByte++; + lastByte >>= 1; + } + + // Calculate the total bit length + // (Number of full bytes minus 1) * 8 + bits in last byte + return highestByte * 8 + bitsInLastByte; + } +} + +#endif diff --git a/Casper.Network.SDK/Compat/BitConverterExtensions.cs b/Casper.Network.SDK/Compat/BitConverterExtensions.cs new file mode 100644 index 0000000..dcad241 --- /dev/null +++ b/Casper.Network.SDK/Compat/BitConverterExtensions.cs @@ -0,0 +1,26 @@ + +using System; + +public static class BitConverterExtensions +{ + public static Int64 ToInt64(byte[] value, int startIndex = 0) + { + return BitConverter.ToInt64(value, startIndex); + } + + public static Int32 ToInt32(byte[] value, int startIndex = 0) + { + return BitConverter.ToInt32(value, startIndex); + } + + public static UInt64 ToUInt64(byte[] value, int startIndex = 0) + { + return BitConverter.ToUInt64(value, startIndex); + } + + public static UInt32 ToUInt32(byte[] value, int startIndex = 0) + { + return BitConverter.ToUInt32(value, startIndex); + } +} + diff --git a/Casper.Network.SDK/Compat/BytesExtensions.cs b/Casper.Network.SDK/Compat/BytesExtensions.cs new file mode 100644 index 0000000..592d8c3 --- /dev/null +++ b/Casper.Network.SDK/Compat/BytesExtensions.cs @@ -0,0 +1,23 @@ + +using System; + +public static class BytesExtensions +{ + public static byte[] Slice(this byte[] bytes, int from, int? to = null) + { + int actualTo = to ?? bytes.Length; // if to is not provided, use the last index + + if (from < 0 || actualTo > bytes.Length || from > actualTo) + { + throw new IndexOutOfRangeException(); + } + + int length = actualTo - from; + byte[] result = new byte[length]; + + Array.Copy(bytes, from, result, 0, length); + + return result; + } +} + diff --git a/Casper.Network.SDK/Compat/DateTimeCompat.cs b/Casper.Network.SDK/Compat/DateTimeCompat.cs new file mode 100644 index 0000000..3322c41 --- /dev/null +++ b/Casper.Network.SDK/Compat/DateTimeCompat.cs @@ -0,0 +1,12 @@ +using System; +using System.Globalization; + +public static class DateTimeCompat +{ +#if NET7_0_OR_GREATER + public static readonly DateTime UnixEpoch = DateTime.UnixEpoch; +#elif NETSTANDARD2_0 + public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#endif +} + diff --git a/Casper.Network.SDK/Compat/EnumCompat.cs b/Casper.Network.SDK/Compat/EnumCompat.cs new file mode 100644 index 0000000..cae5ad5 --- /dev/null +++ b/Casper.Network.SDK/Compat/EnumCompat.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; + +public static class EnumCompat +{ + public static TEnum Parse(this String value) where TEnum : struct + { + return (TEnum)Enum.Parse(typeof(TEnum), value); + } + + public static string GetName(TEnum value) where TEnum : Enum + { + return Enum.GetName(typeof(TEnum), value); + } +} diff --git a/Casper.Network.SDK/Compat/FileExtensions.cs b/Casper.Network.SDK/Compat/FileExtensions.cs new file mode 100644 index 0000000..9b4b1b9 --- /dev/null +++ b/Casper.Network.SDK/Compat/FileExtensions.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Threading.Tasks; + +public static class FileExtensions +{ + public static async Task ReadAllBytesAsync(string path) + { +#if NET7_0_OR_GREATER + return await File.ReadAllBytesAsync(path); +#elif NETSTANDARD2_0 + + // Using FileStream with asynchronous flag + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, + FileOptions.Asynchronous)) + { + // Allocate memory for the file's content + byte[] bytes = new byte[stream.Length]; + // Read the entire file asynchronously + int numBytesToRead = (int)stream.Length; + int numBytesRead = 0; + while (numBytesToRead > 0) + { + // Read may return anything from 0 to numBytesToRead. + int n = await stream.ReadAsync(bytes, numBytesRead, numBytesToRead); + // Break when the end of the file is reached. + if (n == 0) + break; + + numBytesRead += n; + numBytesToRead -= n; + } + + return bytes; + } +#endif + } +} + diff --git a/Casper.Network.SDK/Compat/HttpClientExtensions.cs b/Casper.Network.SDK/Compat/HttpClientExtensions.cs new file mode 100644 index 0000000..507a929 --- /dev/null +++ b/Casper.Network.SDK/Compat/HttpClientExtensions.cs @@ -0,0 +1,22 @@ +#if NETSTANDARD2_0 + +using System; +using System.IO; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +public static class HttpClientExtensions +{ + public static async Task GetStreamAsync(this HttpClient client, Uri uri, CancellationToken cancelToken) + { + var response = await client.GetAsync(uri, cancelToken); + if (!response.IsSuccessStatusCode) + { + throw new HttpRequestException($"Request to {uri} returned with HTTP status code {response.StatusCode}"); + } + return await response.Content.ReadAsStreamAsync(); + } +} + +#endif diff --git a/Casper.Network.SDK/Compat/IsExternalInit.cs b/Casper.Network.SDK/Compat/IsExternalInit.cs new file mode 100644 index 0000000..3147b6c --- /dev/null +++ b/Casper.Network.SDK/Compat/IsExternalInit.cs @@ -0,0 +1,18 @@ +#if NETSTANDARD2_0 + +using System.ComponentModel; + +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} + +#endif diff --git a/Casper.Network.SDK/Compat/MemoryStreamExtensions.cs b/Casper.Network.SDK/Compat/MemoryStreamExtensions.cs new file mode 100644 index 0000000..5fcc3bf --- /dev/null +++ b/Casper.Network.SDK/Compat/MemoryStreamExtensions.cs @@ -0,0 +1,13 @@ +#if NETSTANDARD2_0 + +using System.IO; + +public static class MemoryStreamExtensions +{ + public static void Write(this MemoryStream stream, byte[] buffer) + { + stream.Write(buffer, 0, buffer.Length); + } +} + +#endif diff --git a/Casper.Network.SDK/Compat/Tuple.cs b/Casper.Network.SDK/Compat/Tuple.cs new file mode 100644 index 0000000..7089a64 --- /dev/null +++ b/Casper.Network.SDK/Compat/Tuple.cs @@ -0,0 +1,92 @@ +#if NETSTANDARD2_0 + +using System; + +public partial interface ITuple +{ + object this[int index] { get; } + int Length { get; } +} + +public class Tuple : ITuple +{ + public T1 Item1 { get; } + + public Tuple(T1 item1) + { + Item1 = item1; + } + + public int Length => 1; + + public object this[int index] + { + get + { + switch (index) + { + case 0: return Item1; + default: throw new IndexOutOfRangeException(); + } + } + } +} + +public class Tuple : ITuple +{ + public T1 Item1 { get; } + public T2 Item2 { get; } + + public Tuple(T1 item1, T2 item2) + { + Item1 = item1; + Item2 = item2; + } + + public int Length => 2; + + public object this[int index] + { + get + { + switch (index) + { + case 0: return Item1; + case 1: return Item2; + default: throw new IndexOutOfRangeException(); + } + } + } +} + +public class Tuple : ITuple +{ + public T1 Item1 { get; } + public T2 Item2 { get; } + public T3 Item3 { get; } + + public Tuple(T1 item1, T2 item2, T3 item3) + { + Item1 = item1; + Item2 = item2; + Item3 = item3; + } + + public int Length => 3; + + public object this[int index] + { + get + { + switch (index) + { + case 0: return Item1; + case 1: return Item2; + case 2: return Item3; + default: throw new IndexOutOfRangeException(); + } + } + } +} + +#endif diff --git a/Casper.Network.SDK/JsonRpc/RpcClient.cs b/Casper.Network.SDK/JsonRpc/RpcClient.cs index a6ef724..1f72e74 100644 --- a/Casper.Network.SDK/JsonRpc/RpcClient.cs +++ b/Casper.Network.SDK/JsonRpc/RpcClient.cs @@ -65,8 +65,8 @@ protected async Task> SendAsync(RpcMethod me strMethod, System.Text.Encoding.UTF8, "application/json"); //CONTENT-TYPE header - if(request.Content.Headers.ContentType != null) - request.Content.Headers.ContentType.CharSet = ""; + request.Content.Headers.Remove("Content-Type"); // "{application/json; charset=utf-8}" + request.Content.Headers.Add("Content-Type", "application/json"); try { diff --git a/Casper.Network.SDK/Types/CLValue.cs b/Casper.Network.SDK/Types/CLValue.cs index 99b373e..670dd23 100644 --- a/Casper.Network.SDK/Types/CLValue.cs +++ b/Casper.Network.SDK/Types/CLValue.cs @@ -536,7 +536,7 @@ public static CLValue Key(GlobalStateKey key) private void ParseResultError(byte[] Bytes, CLResultTypeInfo resultTypeInfo) { - var reader = new BinaryReader(new MemoryStream(Bytes[1..])); + var reader = new BinaryReader(new MemoryStream(Bytes.Slice(1))); var item = reader.ReadCLItem(resultTypeInfo.Err, null); if (item == null) @@ -554,7 +554,7 @@ private byte[] GetOkInnerTypeOrFail(ref CLTypeInfo typeInfo) ParseResultError(Bytes, resultTypeInfo); typeInfo = resultTypeInfo.Ok; - return Bytes[1..]; + return Bytes.Slice(1); } /// @@ -786,8 +786,9 @@ public URef ToURef() if (typeInfo.Type == CLType.URef) return new URef(bytes); - if (typeInfo.Type == CLType.Key && ((CLKeyTypeInfo) typeInfo).KeyIdentifier == KeyIdentifier.URef) - return new URef(bytes[1..]); + if (typeInfo.Type == CLType.Key && ((CLKeyTypeInfo) typeInfo).KeyIdentifier == KeyIdentifier.URef) { + return new URef(bytes.Slice(1)); + } throw new FormatException($"Cannot convert '{typeInfo.Type}' to 'URef'."); } @@ -945,7 +946,7 @@ public Dictionary ToDictionary() } /// - /// Converts Tuple1 CLValue to Tuple<T1;>. + /// Converts Tuple1 CLValue to Tuple<T1>. /// public Tuple ToTuple1() { @@ -968,7 +969,7 @@ public Tuple ToTuple1() } /// - /// Converts Tuple2 CLValue to Tuple<T1,T2;>. + /// Converts Tuple2 CLValue to Tuple<T1,T2>. /// public Tuple ToTuple2() { @@ -991,7 +992,7 @@ public Tuple ToTuple2() } /// - /// Converts Tuple2 CLValue to Tuple<T1,T2,T3;>. + /// Converts Tuple2 CLValue to Tuple<T1,T2,T3>. /// public Tuple ToTuple3() { @@ -1014,7 +1015,7 @@ public Tuple ToTuple3() } /// - /// Converts Result CLValue to Result<TOk,TErr;>. + /// Converts Result CLValue to Result<TOk,TErr>. /// public Result ToResult() { @@ -1029,13 +1030,13 @@ public Result ToResult() if (Bytes[0] == 0x01) { - var reader = new BinaryReader(new MemoryStream(Bytes[1..])); + var reader = new BinaryReader(new MemoryStream(Bytes.Slice(1))); var v = reader.ReadCLItem(resultTypeInfo.Ok, typeof(TOk)); return Result.Ok((TOk) v); } else { - var reader = new BinaryReader(new MemoryStream(Bytes[1..])); + var reader = new BinaryReader(new MemoryStream(Bytes.Slice(1))); var v = reader.ReadCLItem(resultTypeInfo.Err, typeof(TErr)); return Result.Fail((TErr)v); } @@ -1076,7 +1077,7 @@ public bool Some(out object value) if(typeInfo==null) throw new ArgumentException(nameof(value), $"value is not an 'Option' CLValue."); - var reader = new BinaryReader(new MemoryStream(Bytes[1..])); + var reader = new BinaryReader(new MemoryStream(Bytes.Slice(1))); value = reader.ReadCLItem(typeInfo.OptionType, null); } @@ -1098,7 +1099,7 @@ public bool Some(out T value) if(typeInfo==null) throw new ArgumentException(nameof(value), $"value is not an 'Option' CLValue."); - var reader = new BinaryReader(new MemoryStream(Bytes[1..])); + var reader = new BinaryReader(new MemoryStream(Bytes.Slice(1))); value = (T) reader.ReadCLItem(typeInfo.OptionType, typeof(T)); return true; diff --git a/Casper.Network.SDK/Types/CLValueException.cs b/Casper.Network.SDK/Types/CLValueException.cs index 7999f09..2905b1f 100644 --- a/Casper.Network.SDK/Types/CLValueException.cs +++ b/Casper.Network.SDK/Types/CLValueException.cs @@ -20,7 +20,7 @@ public CLValueException(byte[] bytes, object err, CLTypeInfo okType, CLTypeInfo } public CLValueException(byte[] bytes, CLTypeInfo okType, CLTypeInfo errType) - : base($"Cannot convert {Hex.ToHexString(bytes[1..])}' to '{errType}'.") + : base($"Cannot convert {Hex.ToHexString(bytes.Slice(1))}' to '{errType}'.") { Bytes = bytes; ErrorValue = null; diff --git a/Casper.Network.SDK/Types/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey.cs index 6c036c5..aef55ce 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey.cs @@ -171,21 +171,21 @@ public static GlobalStateKey FromBytes(byte[] bytes) { return bytes[0] switch { - 0x00 => new AccountHashKey("account-hash-" + CEP57Checksum.Encode(bytes[1..])), - 0x01 => new HashKey("hash-" + CEP57Checksum.Encode(bytes[1..])), - 0x02 => new URef(bytes[1..]), - 0x03 => new TransferKey("transfer-" + CEP57Checksum.Encode(bytes[1..])), - 0x04 => new DeployInfoKey("deploy-" + CEP57Checksum.Encode(bytes[1..])), + 0x00 => new AccountHashKey("account-hash-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x01 => new HashKey("hash-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x02 => new URef(bytes.Slice(1)), + 0x03 => new TransferKey("transfer-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x04 => new DeployInfoKey("deploy-" + CEP57Checksum.Encode(bytes.Slice(1))), 0x05 => new EraInfoKey("era-" + BitConverter.ToInt64(bytes, 1)), - 0x06 => new BalanceKey("balance-" + CEP57Checksum.Encode(bytes[1..])), - 0x07 => new BidKey("bid-" + CEP57Checksum.Encode(bytes[1..])), - 0x08 => new WithdrawKey("withdraw-" + CEP57Checksum.Encode(bytes[1..])), - 0x09 => new DictionaryKey("dictionary-" + CEP57Checksum.Encode(bytes[1..])), - 0x0a => new SystemContractRegistryKey("system-contract-registry-" + CEP57Checksum.Encode(bytes[1..])), - 0x0b => new EraSummaryKey("era-summary-" + CEP57Checksum.Encode(bytes[1..])), - 0x0c => new UnbondKey("unbond-" + CEP57Checksum.Encode(bytes[1..])), - 0x0d => new ChainspecRegistryKey("chainspec-registry-" + CEP57Checksum.Encode(bytes[1..])), - 0x0e => new ChecksumRegistryKey("checksum-registry-" + CEP57Checksum.Encode(bytes[1..])), + 0x06 => new BalanceKey("balance-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x07 => new BidKey("bid-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x08 => new WithdrawKey("withdraw-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x09 => new DictionaryKey("dictionary-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0a => new SystemContractRegistryKey("system-contract-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0b => new EraSummaryKey("era-summary-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0c => new UnbondKey("unbond-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0d => new ChainspecRegistryKey("chainspec-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0e => new ChecksumRegistryKey("checksum-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), _ => throw new ArgumentException($"Unknown key identifier '{bytes[0]}'") }; } diff --git a/Casper.Network.SDK/Types/PublicKey.cs b/Casper.Network.SDK/Types/PublicKey.cs index 2cb862e..7228b40 100644 --- a/Casper.Network.SDK/Types/PublicKey.cs +++ b/Casper.Network.SDK/Types/PublicKey.cs @@ -93,8 +93,8 @@ public static PublicKey FromBytes(byte[] bytes) (int expectedPublicKeySize, string algo) = algoIdent switch { - 0x01 => (KeyAlgo.ED25519.GetKeySizeInBytes(), Enum.GetName(KeyAlgo.ED25519)), - 0x02 => (KeyAlgo.SECP256K1.GetKeySizeInBytes(), Enum.GetName(KeyAlgo.SECP256K1)), + 0x01 => (KeyAlgo.ED25519.GetKeySizeInBytes(), EnumCompat.GetName(KeyAlgo.ED25519)), + 0x02 => (KeyAlgo.SECP256K1.GetKeySizeInBytes(), EnumCompat.GetName(KeyAlgo.SECP256K1)), _ => throw new ArgumentException("Wrong public key algorithm identifier", nameof(bytes)) }; diff --git a/Casper.Network.SDK/Types/Signature.cs b/Casper.Network.SDK/Types/Signature.cs index d837b49..a28b27d 100644 --- a/Casper.Network.SDK/Types/Signature.cs +++ b/Casper.Network.SDK/Types/Signature.cs @@ -58,7 +58,7 @@ public static Signature FromBytes(byte[] bytes) _ => throw new ArgumentOutOfRangeException(nameof(bytes), "Wrong signature algorithm identifier") }; - return new Signature(bytes[1..], algoIdent); + return new Signature(bytes.Slice(1), algoIdent); } /// diff --git a/Casper.Network.SDK/Types/Transform.cs b/Casper.Network.SDK/Types/Transform.cs index 6b424ab..e9ee8e9 100644 --- a/Casper.Network.SDK/Types/Transform.cs +++ b/Casper.Network.SDK/Types/Transform.cs @@ -84,7 +84,7 @@ public override Transform Read( { var stype = reader.GetString(); if (stype != null) - type = Enum.Parse(stype); + type = EnumCompat.Parse(stype); reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) @@ -92,7 +92,7 @@ public override Transform Read( reader.Read(); var stype = reader.GetString(); if (stype != null) - type = Enum.Parse(stype); + type = EnumCompat.Parse(stype); reader.Read(); switch (type) { diff --git a/Casper.Network.SDK/Types/URef.cs b/Casper.Network.SDK/Types/URef.cs index aff2b64..e69ddc6 100644 --- a/Casper.Network.SDK/Types/URef.cs +++ b/Casper.Network.SDK/Types/URef.cs @@ -45,7 +45,7 @@ public URef(string value) : base(value) /// Creates an URef from a 33 bytes array. Last byte corresponds to the access rights. /// public URef(byte[] bytes) - : this($"{KEYPREFIX}{Hex.ToHexString(bytes[..32])}-{(int)bytes[32]:000}") + : this($"{KEYPREFIX}{Hex.ToHexString(bytes.Slice(0,32))}-{(int)bytes[32]:000}") { } diff --git a/Casper.Network.SDK/Utils/BinaryReaderExtensions.cs b/Casper.Network.SDK/Utils/BinaryReaderExtensions.cs index 558cd18..31e4033 100644 --- a/Casper.Network.SDK/Utils/BinaryReaderExtensions.cs +++ b/Casper.Network.SDK/Utils/BinaryReaderExtensions.cs @@ -15,7 +15,7 @@ public static int ReadCLI32(this BinaryReader reader) var bytes = reader.ReadBytes(4); if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); - return BitConverter.ToInt32(bytes); + return BitConverterExtensions.ToInt32(bytes); } public static long ReadCLI64(this BinaryReader reader) @@ -23,7 +23,7 @@ public static long ReadCLI64(this BinaryReader reader) var bytes = reader.ReadBytes(8); if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); - return BitConverter.ToInt64(bytes); + return BitConverterExtensions.ToInt64(bytes); } public static byte ReadCLU8(this BinaryReader reader) @@ -36,7 +36,7 @@ public static uint ReadCLU32(this BinaryReader reader) var bytes = reader.ReadBytes(4); if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); - return BitConverter.ToUInt32(bytes); + return BitConverterExtensions.ToUInt32(bytes); } public static ulong ReadCLU64(this BinaryReader reader) @@ -44,14 +44,14 @@ public static ulong ReadCLU64(this BinaryReader reader) var bytes = reader.ReadBytes(8); if (!BitConverter.IsLittleEndian) Array.Reverse(bytes); - return BitConverter.ToUInt64(bytes); + return BitConverterExtensions.ToUInt64(bytes); } public static BigInteger ReadCLBigInteger(this BinaryReader reader) { var length = (int) reader.ReadByte(); var bytes = reader.ReadBytes(length); - return new BigInteger(bytes, true, false); + return BigIntegerCompat.Create(bytes, true, false); } public static string ReadCLString(this BinaryReader reader) diff --git a/Casper.Network.SDK/Utils/DateUtils.cs b/Casper.Network.SDK/Utils/DateUtils.cs index af22654..c7cf09f 100644 --- a/Casper.Network.SDK/Utils/DateUtils.cs +++ b/Casper.Network.SDK/Utils/DateUtils.cs @@ -30,7 +30,7 @@ public static ulong ToEpochTime(DateTime datetime) /// public static string ToISOString(ulong epochTimeInMillis) { - return DateTime.UnixEpoch + return DateTimeCompat.UnixEpoch .AddMilliseconds(epochTimeInMillis) .ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture); } diff --git a/README.md b/README.md index 3c7fddf..c980519 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,12 @@ dotnet test --filter 'TestCategory!~NCTL' On Windows use a PowerShell terminal to run the tests. +To test against `netstandard2.0` framework, launch the tests as follows: + +``` +TEST_FRAMEWORK=netstandard2.0 dotnet test --filter 'TestCategory!~NCTL' +``` + ### Integration tests The command above excludes integration tests. If you're running a Casper network locally with NCTL, follow these steps to run the integrations tests: @@ -70,6 +76,12 @@ dotnet test --settings Casper.Network.SDK.Test/test.runsettings --filter 'TestCa NOTE: Make sure your NCTL network has booted up and nodes are emitting blocks before running the tests. +To test against `netstandard2.0` framework, launch the tests as follows: + +``` +TEST_FRAMEWORK=netstandard2.0 dotnet test --settings Casper.Network.SDK.Test/test.runsettings --filter 'TestCategory~NCTL' +``` + ## Create a workspace in Gitpod Click the button to start coding in Gitpod with an online IDE.