diff --git a/src/Neo.Json/JNumber.cs b/src/Neo.Json/JNumber.cs
index 5b142feb41..0842ed92e4 100644
--- a/src/Neo.Json/JNumber.cs
+++ b/src/Neo.Json/JNumber.cs
@@ -10,6 +10,7 @@
// modifications are permitted.
using System.Globalization;
+using System.Numerics;
using System.Text.Json;
namespace Neo.Json
@@ -38,9 +39,20 @@ public class JNumber : JToken
/// Initializes a new instance of the class with the specified value.
///
/// The value of the JSON token.
- public JNumber(double value = 0)
+ /// True if we want to ensure that the value is in the limits.
+ public JNumber(double value = 0, bool checkMinMax = true)
{
- if (!double.IsFinite(value)) throw new FormatException();
+ if (checkMinMax)
+ {
+ if (value > MAX_SAFE_INTEGER)
+ throw new ArgumentException("value is higher than MAX_SAFE_INTEGER", nameof(value));
+ if (value < MIN_SAFE_INTEGER)
+ throw new ArgumentException("value is lower than MIN_SAFE_INTEGER", nameof(value));
+ }
+
+ if (!double.IsFinite(value))
+ throw new ArgumentException("value is not finite", nameof(value));
+
Value = value;
}
@@ -125,6 +137,22 @@ public static implicit operator JNumber(long value)
return new JNumber(value);
}
+ public static implicit operator JNumber(BigInteger value)
+ {
+ return new JNumber((long)value);
+ }
+
+ ///
+ /// Check if two JNumber are equal.
+ ///
+ /// Non null value
+ /// Nullable value
+ /// bool
+ ///
+ /// If the left is null, throw an .
+ /// If the right is null, return false.
+ /// If the left and right are the same object, return true.
+ ///
public static bool operator ==(JNumber left, JNumber? right)
{
if (right is null) return false;
diff --git a/src/Neo.Json/JToken.cs b/src/Neo.Json/JToken.cs
index a01beba974..b4dbf38ca3 100644
--- a/src/Neo.Json/JToken.cs
+++ b/src/Neo.Json/JToken.cs
@@ -9,6 +9,7 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
+using System.Numerics;
using System.Text.Json;
using static Neo.Json.Utility;
@@ -295,10 +296,15 @@ public static implicit operator JToken(JToken?[] value)
public static implicit operator JToken(bool value)
{
- return (JBoolean)value;
+ return new JBoolean(value);
}
public static implicit operator JToken(double value)
+ {
+ return new JNumber(value);
+ }
+
+ public static implicit operator JToken(BigInteger value)
{
return (JNumber)value;
}
diff --git a/src/Neo/SmartContract/JsonSerializer.cs b/src/Neo/SmartContract/JsonSerializer.cs
index d2055be7d8..482adefc54 100644
--- a/src/Neo/SmartContract/JsonSerializer.cs
+++ b/src/Neo/SmartContract/JsonSerializer.cs
@@ -35,14 +35,15 @@ public static class JsonSerializer
/// Serializes a to a .
///
/// The to serialize.
+ /// Hardfork checker
/// The serialized object.
- public static JToken Serialize(StackItem item)
+ public static JToken Serialize(StackItem item, Func hardforkChecker = null)
{
switch (item)
{
case Array array:
{
- return array.Select(p => Serialize(p)).ToArray();
+ return array.Select(p => Serialize(p, hardforkChecker)).ToArray();
}
case ByteString _:
case Buffer _:
@@ -51,10 +52,7 @@ public static JToken Serialize(StackItem item)
}
case Integer num:
{
- var integer = num.GetInteger();
- if (integer > JNumber.MAX_SAFE_INTEGER || integer < JNumber.MIN_SAFE_INTEGER)
- throw new InvalidOperationException();
- return (double)integer;
+ return new JNumber((long)num.GetInteger(), hardforkChecker == null || hardforkChecker(Hardfork.HF_Echidna));
}
case Boolean boolean:
{
@@ -66,10 +64,10 @@ public static JToken Serialize(StackItem item)
foreach (var entry in map)
{
- if (!(entry.Key is ByteString)) throw new FormatException();
+ if (entry.Key is not ByteString) throw new FormatException();
var key = entry.Key.GetString();
- var value = Serialize(entry.Value);
+ var value = Serialize(entry.Value, hardforkChecker);
ret[key] = value;
}
diff --git a/tests/Neo.Json.UnitTests/UT_JNumber.cs b/tests/Neo.Json.UnitTests/UT_JNumber.cs
index df8bdca619..082598c126 100644
--- a/tests/Neo.Json.UnitTests/UT_JNumber.cs
+++ b/tests/Neo.Json.UnitTests/UT_JNumber.cs
@@ -39,20 +39,47 @@ public void SetUp()
public void TestAsBoolean()
{
maxInt.AsBoolean().Should().BeTrue();
+ minInt.AsBoolean().Should().BeTrue();
zero.AsBoolean().Should().BeFalse();
}
+ [TestMethod]
+ public void TestBigInteger()
+ {
+ ((JNumber)BigInteger.One).AsNumber().Should().Be(1);
+ ((JNumber)BigInteger.Zero).AsNumber().Should().Be(0);
+ ((JNumber)BigInteger.MinusOne).AsNumber().Should().Be(-1);
+ ((JNumber)JNumber.MAX_SAFE_INTEGER).AsNumber().Should().Be(JNumber.MAX_SAFE_INTEGER);
+ ((JNumber)JNumber.MIN_SAFE_INTEGER).AsNumber().Should().Be(JNumber.MIN_SAFE_INTEGER);
+ }
+
+ [TestMethod]
+ public void TestNullEqual()
+ {
+ JNumber nullJNumber = null;
+
+ Assert.IsFalse(maxInt.Equals(null));
+ Assert.IsFalse(maxInt == null);
+ Assert.IsFalse(minInt.Equals(null));
+ Assert.IsFalse(minInt == null);
+ Assert.IsFalse(zero == null);
+
+ Assert.ThrowsException(() => nullJNumber == maxInt);
+ Assert.ThrowsException(() => nullJNumber == minInt);
+ Assert.ThrowsException(() => nullJNumber == zero);
+ }
+
[TestMethod]
public void TestAsString()
{
Action action1 = () => new JNumber(double.PositiveInfinity).AsString();
- action1.Should().Throw();
+ action1.Should().Throw();
Action action2 = () => new JNumber(double.NegativeInfinity).AsString();
- action2.Should().Throw();
+ action2.Should().Throw();
Action action3 = () => new JNumber(double.NaN).AsString();
- action3.Should().Throw();
+ action3.Should().Throw();
}
[TestMethod]
diff --git a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
index fc29c95c12..2cbed61293 100644
--- a/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
+++ b/tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
@@ -43,7 +43,7 @@ public void JsonTest_WrongJson()
Assert.ThrowsException(() => JObject.Parse(json));
json = @"{""length"":99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999}";
- Assert.ThrowsException(() => JObject.Parse(json));
+ Assert.ThrowsException(() => JObject.Parse(json));
}
[TestMethod]
@@ -91,10 +91,10 @@ public void JsonTest_Numbers()
Assert.AreEqual("[1,-2,3.5]", parsed.ToString());
- json = "[200.500000E+005,200.500000e+5,-1.1234e-100,9.05E+28]";
+ json = "[200.500000E+005,200.500000e+5,-1.1234e-100,9.05E+8]";
parsed = JObject.Parse(json);
- Assert.AreEqual("[20050000,20050000,-1.1234E-100,9.05E+28]", parsed.ToString());
+ Assert.AreEqual("[20050000,20050000,-1.1234E-100,905000000]", parsed.ToString());
json = "[-]";
Assert.ThrowsException(() => JObject.Parse(json));
@@ -216,7 +216,7 @@ public void Serialize_EmptyObject()
public void Serialize_Number()
{
var entry = new VM.Types.Array { 1, 9007199254740992 };
- Assert.ThrowsException(() => JsonSerializer.Serialize(entry));
+ Assert.ThrowsException(() => JsonSerializer.Serialize(entry));
}
[TestMethod]
@@ -303,7 +303,7 @@ public void Serialize_Array_Bool_Str_Num()
public void Deserialize_Array_Bool_Str_Num()
{
ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, null, null, ProtocolSettings.Default);
- var items = JsonSerializer.Deserialize(engine, JObject.Parse("[true,\"test\",123,9.05E+28]"), ExecutionEngineLimits.Default);
+ var items = JsonSerializer.Deserialize(engine, JObject.Parse("[true,\"test\",123,1.05E+4]"), ExecutionEngineLimits.Default);
Assert.IsInstanceOfType(items, typeof(VM.Types.Array));
Assert.AreEqual(((VM.Types.Array)items).Count, 4);
@@ -313,7 +313,7 @@ public void Deserialize_Array_Bool_Str_Num()
Assert.IsTrue(array[0].GetBoolean());
Assert.AreEqual(array[1].GetString(), "test");
Assert.AreEqual(array[2].GetInteger(), 123);
- Assert.AreEqual(array[3].GetInteger(), BigInteger.Parse("90500000000000000000000000000"));
+ Assert.AreEqual(array[3].GetInteger(), BigInteger.Parse("10500"));
}
[TestMethod]