From c5ebd5007935b799c721bf5d3e23f62cb965b9b4 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 30 May 2025 23:03:41 -0400 Subject: [PATCH 01/35] Add GetRandom With ranges to stdlib --- src/Neo/SmartContract/Native/StdLib.cs | 50 +++++++++++++++++++ .../SmartContract/Native/UT_StdLib.cs | 22 ++++++++ 2 files changed, 72 insertions(+) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 8a1421c113..13f094593a 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -263,5 +263,55 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } + + [ContractMethod(CpuFee = 1 << 10)] + public static ulong GetRandom(ApplicationEngine engine, ulong minValue, ulong maxValue) + { + if (minValue > maxValue) + throw new ArgumentOutOfRangeException(nameof(minValue)); + + return GetRandom(engine, maxValue - minValue) + minValue; + } + + [ContractMethod(CpuFee = 1 << 10)] + private static ulong GetRandom(ApplicationEngine engine, ulong maxValue) + { + var randomProduct = BigMul(maxValue, (ulong)(engine.GetRandom() & ulong.MaxValue), out var lowPart); + + if (lowPart < maxValue) + { + var remainder = (0ul - maxValue) % maxValue; + + while (lowPart < remainder) + { + randomProduct = BigMul(maxValue, (ulong)(engine.GetRandom() & ulong.MaxValue), out lowPart); + } + } + + return randomProduct; + + static ulong BigMul(ulong a, ulong b, out ulong low) + { + // Adaptation of algorithm for multiplication + // of 32-bit unsigned integers described + // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 + // Basically, it's an optimized version of FOIL method applied to + // low and high dwords of each operand + + // Use 32-bit uints to optimize the fallback for 32-bit platforms. + var al = (uint)a; + var ah = (uint)(a >> 32); + var bl = (uint)b; + var bh = (uint)(b >> 32); + + var mull = ((ulong)al) * bl; + var t = ((ulong)ah) * bl + (mull >> 32); + var tl = ((ulong)al) * bh + (uint)t; + + low = tl << 32 | (uint)mull; + + return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); + } + } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 8d745997ba..b9633ce611 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -427,5 +427,27 @@ public void TestBase64Url() Assert.AreEqual("U3ViamVjdD10ZXN0QGV4YW1wbGUuY29tJklzc3Vlcj1odHRwczovL2V4YW1wbGUuY29t", engine.ResultStack.Pop().GetString()); } } + + [TestMethod] + public void TestGetRandomRanges() + { + var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + using (var script = new ScriptBuilder()) + { + // Test encoding + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000, 1_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 100_000_000_000_00000000, 100_000_000_000_00000000); + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestProtocolSettings.Default); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(3, engine.ResultStack.Count); + Assert.AreEqual(100_000_000_000_00000000, engine.ResultStack.Pop()); + Assert.AreEqual(1_00000000, engine.ResultStack.Pop()); + Assert.AreEqual(0_00000000, engine.ResultStack.Pop()); + } + } } } From 68cea76bd019e4711953778bdd91ecdbbd4fcdb5 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 6 Jun 2025 23:33:10 -0400 Subject: [PATCH 02/35] GetRandom only does min and max value with BigInteger, no limits --- src/Neo/SmartContract/Native/StdLib.cs | 79 ++++++++----------- .../SmartContract/Native/UT_StdLib.cs | 22 ++++-- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 13f094593a..6ba0e1d250 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -264,54 +264,43 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(CpuFee = 1 << 10)] - public static ulong GetRandom(ApplicationEngine engine, ulong minValue, ulong maxValue) + [ContractMethod(CpuFee = 1 << 7)] + private static BigInteger GetRandom(ApplicationEngine engine, BigInteger minValue, BigInteger maxValue) { - if (minValue > maxValue) - throw new ArgumentOutOfRangeException(nameof(minValue)); - - return GetRandom(engine, maxValue - minValue) + minValue; - } - - [ContractMethod(CpuFee = 1 << 10)] - private static ulong GetRandom(ApplicationEngine engine, ulong maxValue) - { - var randomProduct = BigMul(maxValue, (ulong)(engine.GetRandom() & ulong.MaxValue), out var lowPart); - - if (lowPart < maxValue) - { - var remainder = (0ul - maxValue) % maxValue; + // Another way + // minValue + (engine.GetRandom() % (maxValue - minValue + BigInteger.One)); - while (lowPart < remainder) - { - randomProduct = BigMul(maxValue, (ulong)(engine.GetRandom() & ulong.MaxValue), out lowPart); - } - } - - return randomProduct; - - static ulong BigMul(ulong a, ulong b, out ulong low) - { - // Adaptation of algorithm for multiplication - // of 32-bit unsigned integers described - // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 - // Basically, it's an optimized version of FOIL method applied to - // low and high dwords of each operand - - // Use 32-bit uints to optimize the fallback for 32-bit platforms. - var al = (uint)a; - var ah = (uint)(a >> 32); - var bl = (uint)b; - var bh = (uint)(b >> 32); - - var mull = ((ulong)al) * bl; - var t = ((ulong)ah) * bl + (mull >> 32); - var tl = ((ulong)al) * bh + (uint)t; - - low = tl << 32 | (uint)mull; + if (minValue >= maxValue) + throw new ArgumentOutOfRangeException(nameof(minValue)); - return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); - } + // The total possible range is [0, 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936). + // Subtract one to account for zero being an actual possibility. + var range = maxValue - minValue - BigInteger.One; + + // If there is only one possible choice, nothing random will actually happen, so return + // the only possibility. + if (range == 0) + return minValue; + + // Create a mask for the bits that we care about for the range. The other bits will be + // masked away. + var mask = range; + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + mask |= mask >> 32; + mask |= mask >> 64; + mask |= mask >> 128; + + BigInteger result; + + do + result = mask & engine.GetRandom(); + while (result > range); + + return result + minValue; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index b9633ce611..ce02605efd 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -435,18 +435,26 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000, 1_00000000); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 100_000_000_000_00000000, 100_000_000_000_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 100_000_000_000_00000000, 100_000_000_001_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000, 2_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1, 2); using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestProtocolSettings.Default); engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(3, engine.ResultStack.Count); - Assert.AreEqual(100_000_000_000_00000000, engine.ResultStack.Pop()); - Assert.AreEqual(1_00000000, engine.ResultStack.Pop()); - Assert.AreEqual(0_00000000, engine.ResultStack.Pop()); + + var actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.AreEqual(BigInteger.One, actualValue); // This doesn't do calculation + + actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue <= 2_00000000); + Assert.IsTrue(actualValue > 1_00000000); + + actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue <= 100_000_000_001_00000000); + Assert.IsTrue(actualValue > 100_000_000_000_00000000); } } } From 92d641cac7b6f685de50801db2ebd5211f2d480a Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Thu, 19 Jun 2025 00:01:31 -0400 Subject: [PATCH 03/35] Changed for @roman-khimov --- src/Neo/SmartContract/Native/StdLib.cs | 39 ++----------------- .../SmartContract/Native/UT_StdLib.cs | 17 ++++---- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 0b5ec1f712..b83f5b21aa 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -265,42 +265,11 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) } [ContractMethod(CpuFee = 1 << 7)] - private static BigInteger GetRandom(ApplicationEngine engine, BigInteger minValue, BigInteger maxValue) + private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { - // Another way - // minValue + (engine.GetRandom() % (maxValue - minValue + BigInteger.One)); - - if (minValue >= maxValue) - throw new ArgumentOutOfRangeException(nameof(minValue)); - - // The total possible range is [0, 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936). - // Subtract one to account for zero being an actual possibility. - var range = maxValue - minValue - BigInteger.One; - - // If there is only one possible choice, nothing random will actually happen, so return - // the only possibility. - if (range == 0) - return minValue; - - // Create a mask for the bits that we care about for the range. The other bits will be - // masked away. - var mask = range; - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; - mask |= mask >> 32; - mask |= mask >> 64; - mask |= mask >> 128; - - BigInteger result; - - do - result = mask & engine.GetRandom(); - while (result > range); - - return result + minValue; + if (maxValue.Sign < 0) + throw new ArgumentOutOfRangeException(nameof(maxValue), "Value cannot be negative."); + return engine.GetRandom() % (maxValue + BigInteger.One); } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 927ed5be81..02df65a563 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -438,9 +438,9 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 100_000_000_000_00000000, 100_000_000_001_00000000); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000, 2_00000000); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1, 2); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000000000000000000000000000000000")); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestProtocolSettings.Default); engine.LoadScript(script.ToArray()); @@ -449,15 +449,16 @@ public void TestGetRandomRanges() Assert.AreEqual(3, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.AreEqual(BigInteger.One, actualValue); // This doesn't do calculation + Assert.IsTrue(actualValue <= 1_00000000); + Assert.IsTrue(actualValue > BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= 2_00000000); - Assert.IsTrue(actualValue > 1_00000000); + Assert.IsTrue(actualValue <= BigInteger.Parse("100000000000000000000000")); + Assert.IsTrue(actualValue > BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= 100_000_000_001_00000000); - Assert.IsTrue(actualValue > 100_000_000_000_00000000); + Assert.IsTrue(actualValue <= BigInteger.Parse("10000000000000000000000000000000000000000000000000000000000")); + Assert.IsTrue(actualValue > BigInteger.Zero); } } } From 7779416799b6fc6023c37d81369a81ec5eef6fe5 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Fri, 20 Jun 2025 23:17:06 -0400 Subject: [PATCH 04/35] Added @roman-khimov feedback --- src/Neo/SmartContract/Native/StdLib.cs | 22 ++++++++++++++++- .../SmartContract/Native/UT_StdLib.cs | 24 +++++++++++++------ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index b83f5b21aa..28e3409ea6 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -269,7 +269,27 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu { if (maxValue.Sign < 0) throw new ArgumentOutOfRangeException(nameof(maxValue), "Value cannot be negative."); - return engine.GetRandom() % (maxValue + BigInteger.One); + + var randomProduct = maxValue * engine.GetRandom(); + var randomProductBits = randomProduct.GetByteCount() * 8; + + var maxValueBits = maxValue.GetByteCount() * 8; + var maxValueSize = BigInteger.Pow(2, maxValueBits); + + var lowPart = randomProduct & maxValue; + + if (lowPart < maxValue) + { + var remainder = (maxValueSize - maxValue) % maxValue; + + while (lowPart < remainder) + { + randomProduct = maxValue * engine.GetRandom(); + lowPart = randomProduct & maxValue; + } + } + + return randomProduct >> (randomProductBits - maxValueBits); } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 02df65a563..45209d269b 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -10,6 +10,7 @@ // modifications are permitted. using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Builders; using Neo.Extensions; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -438,27 +439,36 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000000000000000000000000000000000")); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshotCache, settings: TestProtocolSettings.Default); + var tx = TransactionBuilder.CreateEmpty() + .Nonce((uint)Random.Shared.Next()) + .Build(); + + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default); engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(3, engine.ResultStack.Count); + Assert.AreEqual(4, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue <= 10); + Assert.IsTrue(actualValue >= BigInteger.Zero); + + actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue <= 1_00000000); - Assert.IsTrue(actualValue > BigInteger.Zero); + Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue <= BigInteger.Parse("100000000000000000000000")); - Assert.IsTrue(actualValue > BigInteger.Zero); + Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= BigInteger.Parse("10000000000000000000000000000000000000000000000000000000000")); - Assert.IsTrue(actualValue > BigInteger.Zero); + Assert.IsTrue(actualValue <= BigInteger.Parse("10000000000000000000000000000")); + Assert.IsTrue(actualValue >= BigInteger.Zero); } } } From 889b467873be2c08a98e70a09d2b02f29d828834 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Thu, 26 Jun 2025 08:44:54 -0400 Subject: [PATCH 05/35] Made fixes to `GetRandom(maxValue)` for the calulation. --- src/Neo/SmartContract/Native/StdLib.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 28e3409ea6..6d2066097b 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -264,19 +264,20 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(CpuFee = 1 << 7)] + [ContractMethod(CpuFee = 1 << 17)] private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { if (maxValue.Sign < 0) - throw new ArgumentOutOfRangeException(nameof(maxValue), "Value cannot be negative."); - - var randomProduct = maxValue * engine.GetRandom(); - var randomProductBits = randomProduct.GetByteCount() * 8; + throw new ArgumentOutOfRangeException(nameof(maxValue)); var maxValueBits = maxValue.GetByteCount() * 8; var maxValueSize = BigInteger.Pow(2, maxValueBits); - var lowPart = randomProduct & maxValue; + var nthMask = (BigInteger.One << maxValueBits) - 1; + var randomProduct = maxValue * (engine.GetRandom() & nthMask); + var randomProductBits = randomProduct.GetByteCount() * 8; + + var lowPart = randomProduct & BigInteger.MinusOne; if (lowPart < maxValue) { @@ -284,8 +285,9 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu while (lowPart < remainder) { - randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct & maxValue; + randomProduct = maxValue * (engine.GetRandom() & nthMask); + randomProductBits = randomProduct.GetByteCount() * 8; + lowPart = randomProduct & BigInteger.MinusOne; } } From f6bb2ce4f07bd10ea79d38f40a64fcc4d3f84346 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 7 Jul 2025 22:55:23 -0400 Subject: [PATCH 06/35] Fixed the method for `GetRandom(MaxValue)` --- src/Neo.Extensions/BigIntegerExtensions.cs | 6 ++++ src/Neo/SmartContract/Native/StdLib.cs | 36 +++++++++++-------- .../SmartContract/Native/UT_StdLib.cs | 9 ++++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/Neo.Extensions/BigIntegerExtensions.cs b/src/Neo.Extensions/BigIntegerExtensions.cs index ebf78f5c87..43a1e7bfa1 100644 --- a/src/Neo.Extensions/BigIntegerExtensions.cs +++ b/src/Neo.Extensions/BigIntegerExtensions.cs @@ -138,6 +138,12 @@ public static BigInteger Sqrt(this BigInteger value) return z; } + public static BigInteger GetLowPart(this BigInteger value, int bitCount) + { + var mask = (BigInteger.One << bitCount) - 1; + return value & mask; + } + /// /// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit. /// Note: This method is imprecise and might not work as expected with integers larger than 256 bits if less than .NET5. diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 6d2066097b..33d60094e3 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -13,6 +13,7 @@ using Microsoft.IdentityModel.Tokens; using Neo.Cryptography; +using Neo.Extensions; using Neo.Json; using Neo.VM.Types; using System; @@ -264,7 +265,7 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(CpuFee = 1 << 17)] + [ContractMethod(CpuFee = 1 << 1)] private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { if (maxValue.Sign < 0) @@ -273,25 +274,32 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu var maxValueBits = maxValue.GetByteCount() * 8; var maxValueSize = BigInteger.Pow(2, maxValueBits); - var nthMask = (BigInteger.One << maxValueBits) - 1; - var randomProduct = maxValue * (engine.GetRandom() & nthMask); - var randomProductBits = randomProduct.GetByteCount() * 8; + var candidate = maxValue; - var lowPart = randomProduct & BigInteger.MinusOne; - - if (lowPart < maxValue) + do { - var remainder = (maxValueSize - maxValue) % maxValue; + var randomProduct = maxValue * engine.GetRandom().GetLowPart(maxValueBits); + var randomProductBits = randomProduct.GetByteCount() * 8; + + var lowPart = randomProduct.GetLowPart(maxValueBits); - while (lowPart < remainder) + if (lowPart < maxValue) { - randomProduct = maxValue * (engine.GetRandom() & nthMask); - randomProductBits = randomProduct.GetByteCount() * 8; - lowPart = randomProduct & BigInteger.MinusOne; + var remainder = (maxValueSize - maxValue) % maxValue; + + while (lowPart < remainder) + { + randomProduct = maxValue * engine.GetRandom().GetLowPart(maxValueBits); + randomProductBits = randomProduct.GetByteCount() * 8; + lowPart = randomProduct.GetLowPart(maxValueBits); + } } - } - return randomProduct >> (randomProductBits - maxValueBits); + candidate = (randomProduct >> (randomProductBits - maxValueBits)).GetLowPart(maxValueBits) + BigInteger.One; + + } while (candidate > maxValue); + + return candidate; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 45209d269b..26d0100f68 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -18,6 +18,7 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Globalization; using System.Numerics; using Array = System.Array; @@ -436,9 +437,11 @@ public void TestBase64Url() public void TestGetRandomRanges() { var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + using (var script = new ScriptBuilder()) { // Test encoding + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); @@ -452,7 +455,7 @@ public void TestGetRandomRanges() engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(4, engine.ResultStack.Count); + Assert.AreEqual(5, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue <= 10); @@ -469,6 +472,10 @@ public void TestGetRandomRanges() actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue <= BigInteger.Parse("10000000000000000000000000000")); Assert.IsTrue(actualValue >= BigInteger.Zero); + + actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue <= BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); + Assert.IsTrue(actualValue >= BigInteger.Zero); } } } From c4a2381dbb071095bab4f2950f749c71b41d34a8 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 7 Jul 2025 23:02:12 -0400 Subject: [PATCH 07/35] Fixed to be `A 32-bit signed integer that is greater than or equal to 0 and less than .` --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 33d60094e3..c327808a99 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -295,7 +295,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu } } - candidate = (randomProduct >> (randomProductBits - maxValueBits)).GetLowPart(maxValueBits) + BigInteger.One; + candidate = (randomProduct >> (randomProductBits - maxValueBits)).GetLowPart(maxValueBits); } while (candidate > maxValue); From 97646c086a1c44d37b2fb29ef81b19b92d765a2d Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 7 Jul 2025 23:02:33 -0400 Subject: [PATCH 08/35] A 32-bit signed integer that is greater than or equal to 0 and less than . --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index c327808a99..48d656a9e4 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -297,7 +297,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu candidate = (randomProduct >> (randomProductBits - maxValueBits)).GetLowPart(maxValueBits); - } while (candidate > maxValue); + } while (candidate >= maxValue); return candidate; } From ec3195377eabaed80ff37d14d438e29e549c5373 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 7 Jul 2025 23:03:24 -0400 Subject: [PATCH 09/35] fixed tests --- tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 26d0100f68..86286cbe55 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -458,23 +458,23 @@ public void TestGetRandomRanges() Assert.AreEqual(5, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= 10); + Assert.IsTrue(actualValue < 10); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= 1_00000000); + Assert.IsTrue(actualValue < 1_00000000); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= BigInteger.Parse("100000000000000000000000")); + Assert.IsTrue(actualValue < BigInteger.Parse("100000000000000000000000")); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= BigInteger.Parse("10000000000000000000000000000")); + Assert.IsTrue(actualValue < BigInteger.Parse("10000000000000000000000000000")); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue <= BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); + Assert.IsTrue(actualValue < BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); Assert.IsTrue(actualValue >= BigInteger.Zero); } } From e251a4e69682530692ecaa89668b413cc1745a32 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 13 Jul 2025 23:22:04 -0400 Subject: [PATCH 10/35] Fixed `GetRandom` for faster resolve. --- src/Neo/SmartContract/Native/StdLib.cs | 35 +++-- .../SmartContract/Native/UT_StdLib.cs | 128 +++++++++++++++++- 2 files changed, 140 insertions(+), 23 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 48d656a9e4..ce8e555684 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -274,32 +274,31 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu var maxValueBits = maxValue.GetByteCount() * 8; var maxValueSize = BigInteger.Pow(2, maxValueBits); - var candidate = maxValue; + var randomProduct = maxValue * engine.GetRandom(); + var randomProductBits = randomProduct.GetByteCount() * 8; - do - { - var randomProduct = maxValue * engine.GetRandom().GetLowPart(maxValueBits); - var randomProductBits = randomProduct.GetByteCount() * 8; + var lowPart = randomProduct.GetLowPart(maxValueBits); - var lowPart = randomProduct.GetLowPart(maxValueBits); + if (lowPart < maxValue) + { + var remainder = (maxValueSize - maxValue) % maxValue; - if (lowPart < maxValue) + while (lowPart < remainder) { - var remainder = (maxValueSize - maxValue) % maxValue; - - while (lowPart < remainder) - { - randomProduct = maxValue * engine.GetRandom().GetLowPart(maxValueBits); - randomProductBits = randomProduct.GetByteCount() * 8; - lowPart = randomProduct.GetLowPart(maxValueBits); - } + randomProduct = maxValue * engine.GetRandom(); + randomProductBits = randomProduct.GetByteCount() * 8; + lowPart = randomProduct.GetLowPart(maxValueBits); } + } - candidate = (randomProduct >> (randomProductBits - maxValueBits)).GetLowPart(maxValueBits); + var result = randomProduct >> (randomProductBits - maxValueBits); - } while (candidate >= maxValue); + // Since BigInteger doesn't have a max value or bit size + // anything over 'maxValue' return zero + if (result >= maxValue) + return BigInteger.Zero; - return candidate; + return result; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 86286cbe55..f94766fd8a 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -441,10 +441,128 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); + //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); + //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); + //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); + //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); var tx = TransactionBuilder.CreateEmpty() @@ -455,7 +573,7 @@ public void TestGetRandomRanges() engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(5, engine.ResultStack.Count); + //Assert.AreEqual(5, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue < 10); From aa42a91d20ab58c1a397cdda77f8ca3dc449bf76 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 14 Jul 2025 00:20:26 -0400 Subject: [PATCH 11/35] Fixed unit test --- .../SmartContract/Native/UT_StdLib.cs | 128 +----------------- 1 file changed, 5 insertions(+), 123 deletions(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index f94766fd8a..86286cbe55 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -441,128 +441,10 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); - //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); - //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); - //script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); var tx = TransactionBuilder.CreateEmpty() @@ -573,7 +455,7 @@ public void TestGetRandomRanges() engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - //Assert.AreEqual(5, engine.ResultStack.Count); + Assert.AreEqual(5, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue < 10); From 0d81431fcd74f832e7f21e07c3929e0ae491416d Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Tue, 29 Jul 2025 00:28:57 -0400 Subject: [PATCH 12/35] Made better fast return --- src/Neo/SmartContract/Native/StdLib.cs | 3 +++ tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index ce8e555684..b08745e31e 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -271,6 +271,9 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue.Sign < 0) throw new ArgumentOutOfRangeException(nameof(maxValue)); + if (maxValue == BigInteger.Zero) + return BigInteger.Zero; + var maxValueBits = maxValue.GetByteCount() * 8; var maxValueSize = BigInteger.Pow(2, maxValueBits); diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 86286cbe55..05fa79b455 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -446,6 +446,7 @@ public void TestGetRandomRanges() script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Zero); var tx = TransactionBuilder.CreateEmpty() .Nonce((uint)Random.Shared.Next()) @@ -455,9 +456,13 @@ public void TestGetRandomRanges() engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(5, engine.ResultStack.Count); + Assert.AreEqual(6, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue <= 0); + Assert.IsTrue(actualValue >= BigInteger.Zero); + + actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue < 10); Assert.IsTrue(actualValue >= BigInteger.Zero); From 9d3cf99ff8cbd39ee0a75c100d278ba426da8b05 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 30 Jul 2025 01:04:33 -0700 Subject: [PATCH 13/35] Update src/Neo/SmartContract/Native/StdLib.cs Co-authored-by: Owen <38493437+superboyiii@users.noreply.github.com> --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index b08745e31e..a8d538ba4b 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -265,7 +265,7 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(CpuFee = 1 << 1)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 12, RequiredCallFlags = CallFlags.ReadStates)] private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { if (maxValue.Sign < 0) From 1890a245cacc2ee061b7f1941b95869d61f1d041 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 31 Jul 2025 10:24:52 +0200 Subject: [PATCH 14/35] Fix UT --- tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 032fe78c18..248f3e329f 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -41,7 +41,7 @@ public void TestSetup() _nativeStates = new Dictionary { {"ContractManagement", """{"id":-1,"updatecounter":0,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":3581846399},"manifest":{"name":"ContractManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","offset":0,"safe":false},{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","offset":7,"safe":false},{"name":"destroy","parameters":[],"returntype":"Void","offset":14,"safe":false},{"name":"getContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","offset":21,"safe":true},{"name":"getContractById","parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getContractHashes","parameters":[],"returntype":"InteropInterface","offset":35,"safe":true},{"name":"getMinimumDeploymentFee","parameters":[],"returntype":"Integer","offset":42,"safe":true},{"name":"hasMethod","parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","offset":49,"safe":true},{"name":"isContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Boolean","offset":56,"safe":true},{"name":"setMinimumDeploymentFee","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":63,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","offset":70,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}""" }, - {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2681632925},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":77,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":84,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":91,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":98,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":105,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":112,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":119,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":126,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":133,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":140,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":147,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":154,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1262861563},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"getRandom","parameters":[{"name":"maxValue","type":"Integer"}],"returntype":"Integer","offset":77,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":84,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":91,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":98,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":105,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":112,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":119,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":126,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":133,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":140,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":147,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":154,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":161,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":174904780},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":77,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":84,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"LedgerContract", """{"id":-4,"updatecounter":0,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"currentHash","parameters":[],"returntype":"Hash256","offset":0,"safe":true},{"name":"currentIndex","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getBlock","parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","offset":14,"safe":true},{"name":"getTransaction","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":21,"safe":true},{"name":"getTransactionFromBlock","parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getTransactionHeight","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":35,"safe":true},{"name":"getTransactionSigners","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":42,"safe":true},{"name":"getTransactionVMState","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":49,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"NeoToken", """{"id":-5,"updatecounter":0,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","groups":[],"features":{},"supportedstandards":["NEP-17","NEP-27"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getAccountState","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","offset":14,"safe":true},{"name":"getAllCandidates","parameters":[],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"getCandidateVote","parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","offset":28,"safe":true},{"name":"getCandidates","parameters":[],"returntype":"Array","offset":35,"safe":true},{"name":"getCommittee","parameters":[],"returntype":"Array","offset":42,"safe":true},{"name":"getCommitteeAddress","parameters":[],"returntype":"Hash160","offset":49,"safe":true},{"name":"getGasPerBlock","parameters":[],"returntype":"Integer","offset":56,"safe":true},{"name":"getNextBlockValidators","parameters":[],"returntype":"Array","offset":63,"safe":true},{"name":"getRegisterPrice","parameters":[],"returntype":"Integer","offset":70,"safe":true},{"name":"onNEP17Payment","parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false},{"name":"registerCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":84,"safe":false},{"name":"setGasPerBlock","parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","offset":91,"safe":false},{"name":"setRegisterPrice","parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","offset":98,"safe":false},{"name":"symbol","parameters":[],"returntype":"String","offset":105,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":112,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":119,"safe":false},{"name":"unclaimedGas","parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","offset":126,"safe":true},{"name":"unregisterCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":133,"safe":false},{"name":"vote","parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","offset":140,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, From 699412bf0b22e522fc8f517f9d9d2438bfca049e Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Sun, 3 Aug 2025 07:33:06 -0400 Subject: [PATCH 15/35] Update src/Neo/SmartContract/Native/StdLib.cs Co-authored-by: Shargon --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index a8d538ba4b..010c7b6d1c 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -265,7 +265,7 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 12, RequiredCallFlags = CallFlags.ReadStates)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 13, RequiredCallFlags = CallFlags.None)] private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { if (maxValue.Sign < 0) From 087e2e34b9a95fee6f26f5fdcde12110bc68c723 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 4 Aug 2025 09:32:16 -0700 Subject: [PATCH 16/35] Update src/Neo/SmartContract/Native/StdLib.cs Co-authored-by: Anna Shaleva --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 010c7b6d1c..86515c5d6d 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -265,7 +265,7 @@ private static int StrLen([MaxLength(MaxInputLength)] string str) return count; } - [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 13, RequiredCallFlags = CallFlags.None)] + [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 13)] private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValue) { if (maxValue.Sign < 0) From 684ef497ccf1932de330b80fe221509588e74aa8 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 4 Aug 2025 22:58:32 -0400 Subject: [PATCH 17/35] Update src/Neo/SmartContract/Native/StdLib.cs Co-authored-by: Owen <38493437+superboyiii@users.noreply.github.com> --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 86515c5d6d..4208b02ee2 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -299,7 +299,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu // Since BigInteger doesn't have a max value or bit size // anything over 'maxValue' return zero if (result >= maxValue) - return BigInteger.Zero; + return result % maxValue; return result; } From 23c66448ce53865a06d8e6741669fff9498a1c4c Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Fri, 22 Aug 2025 01:46:52 -0400 Subject: [PATCH 18/35] Fixed `GetRandom` --- src/Neo/SmartContract/Native/StdLib.cs | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 4208b02ee2..a1a6615f51 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -13,7 +13,6 @@ using Microsoft.IdentityModel.Tokens; using Neo.Cryptography; -using Neo.Extensions; using Neo.Json; using Neo.VM.Types; using System; @@ -275,33 +274,24 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxValueSize = BigInteger.Pow(2, maxValueBits); + var maxValueSize = BigInteger.Pow(2, maxValueBits) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); - var randomProductBits = randomProduct.GetByteCount() * 8; - var lowPart = randomProduct.GetLowPart(maxValueBits); + var lowPart = randomProduct >> maxValueBits; if (lowPart < maxValue) { - var remainder = (maxValueSize - maxValue) % maxValue; + var remainder = maxValueSize % maxValue; while (lowPart < remainder) { randomProduct = maxValue * engine.GetRandom(); - randomProductBits = randomProduct.GetByteCount() * 8; - lowPart = randomProduct.GetLowPart(maxValueBits); + lowPart = randomProduct >> maxValueBits; } } - var result = randomProduct >> (randomProductBits - maxValueBits); - - // Since BigInteger doesn't have a max value or bit size - // anything over 'maxValue' return zero - if (result >= maxValue) - return result % maxValue; - - return result; + return randomProduct >> 128; } } } From 239ac4825fd952bccfedbaa3342d2123607ac228 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 1 Sep 2025 00:14:54 -0400 Subject: [PATCH 19/35] Fixed unit test --- tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 40026d9783..16b5da1e9e 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -41,7 +41,7 @@ public void TestSetup() _nativeStates = new Dictionary { {"ContractManagement", """{"id":-1,"updatecounter":0,"hash":"0xfffdc93764dbaddd97c48f252a53ea4643faa3fd","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":3581846399},"manifest":{"name":"ContractManagement","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Array","offset":0,"safe":false},{"name":"deploy","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Array","offset":7,"safe":false},{"name":"destroy","parameters":[],"returntype":"Void","offset":14,"safe":false},{"name":"getContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Array","offset":21,"safe":true},{"name":"getContractById","parameters":[{"name":"id","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getContractHashes","parameters":[],"returntype":"InteropInterface","offset":35,"safe":true},{"name":"getMinimumDeploymentFee","parameters":[],"returntype":"Integer","offset":42,"safe":true},{"name":"hasMethod","parameters":[{"name":"hash","type":"Hash160"},{"name":"method","type":"String"},{"name":"pcount","type":"Integer"}],"returntype":"Boolean","offset":49,"safe":true},{"name":"isContract","parameters":[{"name":"hash","type":"Hash160"}],"returntype":"Boolean","offset":56,"safe":true},{"name":"setMinimumDeploymentFee","parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","offset":63,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"}],"returntype":"Void","offset":70,"safe":false},{"name":"update","parameters":[{"name":"nefFile","type":"ByteArray"},{"name":"manifest","type":"ByteArray"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false}],"events":[{"name":"Deploy","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Update","parameters":[{"name":"Hash","type":"Hash160"}]},{"name":"Destroy","parameters":[{"name":"Hash","type":"Hash160"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}""" }, - {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":2426471238},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"hexDecode","parameters":[{"name":"str","type":"String"}],"returntype":"ByteArray","offset":77,"safe":true},{"name":"hexEncode","parameters":[{"name":"bytes","type":"ByteArray"}],"returntype":"String","offset":84,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":91,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":98,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":105,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":112,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":119,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":126,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":133,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":140,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":147,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":154,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":161,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":168,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, + {"StdLib", """{"id":-2,"updatecounter":0,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2703728828},"manifest":{"name":"StdLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"atoi","parameters":[{"name":"value","type":"String"}],"returntype":"Integer","offset":0,"safe":true},{"name":"atoi","parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","offset":7,"safe":true},{"name":"base58CheckDecode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":14,"safe":true},{"name":"base58CheckEncode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":21,"safe":true},{"name":"base58Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":28,"safe":true},{"name":"base58Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":35,"safe":true},{"name":"base64Decode","parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"base64Encode","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","offset":49,"safe":true},{"name":"base64UrlDecode","parameters":[{"name":"s","type":"String"}],"returntype":"String","offset":56,"safe":true},{"name":"base64UrlEncode","parameters":[{"name":"data","type":"String"}],"returntype":"String","offset":63,"safe":true},{"name":"deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","offset":70,"safe":true},{"name":"getRandom","parameters":[{"name":"maxValue","type":"Integer"}],"returntype":"Integer","offset":77,"safe":true},{"name":"hexDecode","parameters":[{"name":"str","type":"String"}],"returntype":"ByteArray","offset":84,"safe":true},{"name":"hexEncode","parameters":[{"name":"bytes","type":"ByteArray"}],"returntype":"String","offset":91,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"}],"returntype":"String","offset":98,"safe":true},{"name":"itoa","parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","offset":105,"safe":true},{"name":"jsonDeserialize","parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","offset":112,"safe":true},{"name":"jsonSerialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":119,"safe":true},{"name":"memoryCompare","parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","offset":126,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","offset":133,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","offset":140,"safe":true},{"name":"memorySearch","parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","offset":147,"safe":true},{"name":"serialize","parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","offset":154,"safe":true},{"name":"strLen","parameters":[{"name":"str","type":"String"}],"returntype":"Integer","offset":161,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","offset":168,"safe":true},{"name":"stringSplit","parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","offset":175,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"CryptoLib", """{"id":-3,"updatecounter":0,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":174904780},"manifest":{"name":"CryptoLib","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"bls12381Add","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","offset":0,"safe":true},{"name":"bls12381Deserialize","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","offset":7,"safe":true},{"name":"bls12381Equal","parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","offset":14,"safe":true},{"name":"bls12381Mul","parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"bls12381Pairing","parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","offset":28,"safe":true},{"name":"bls12381Serialize","parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","offset":35,"safe":true},{"name":"keccak256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":42,"safe":true},{"name":"murmur32","parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","offset":49,"safe":true},{"name":"recoverSecp256K1","parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","offset":56,"safe":true},{"name":"ripemd160","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":63,"safe":true},{"name":"sha256","parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","offset":70,"safe":true},{"name":"verifyWithECDsa","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","offset":77,"safe":true},{"name":"verifyWithEd25519","parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","offset":84,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"LedgerContract", """{"id":-4,"updatecounter":0,"hash":"0xda65b600f7124ce6c79950c1772a36403104f2be","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":1110259869},"manifest":{"name":"LedgerContract","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"currentHash","parameters":[],"returntype":"Hash256","offset":0,"safe":true},{"name":"currentIndex","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getBlock","parameters":[{"name":"indexOrHash","type":"ByteArray"}],"returntype":"Array","offset":14,"safe":true},{"name":"getTransaction","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":21,"safe":true},{"name":"getTransactionFromBlock","parameters":[{"name":"blockIndexOrHash","type":"ByteArray"},{"name":"txIndex","type":"Integer"}],"returntype":"Array","offset":28,"safe":true},{"name":"getTransactionHeight","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":35,"safe":true},{"name":"getTransactionSigners","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Array","offset":42,"safe":true},{"name":"getTransactionVMState","parameters":[{"name":"hash","type":"Hash256"}],"returntype":"Integer","offset":49,"safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, {"NeoToken", """{"id":-5,"updatecounter":0,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","groups":[],"features":{},"supportedstandards":["NEP-17","NEP-27"],"abi":{"methods":[{"name":"balanceOf","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","offset":0,"safe":true},{"name":"decimals","parameters":[],"returntype":"Integer","offset":7,"safe":true},{"name":"getAccountState","parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","offset":14,"safe":true},{"name":"getAllCandidates","parameters":[],"returntype":"InteropInterface","offset":21,"safe":true},{"name":"getCandidateVote","parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","offset":28,"safe":true},{"name":"getCandidates","parameters":[],"returntype":"Array","offset":35,"safe":true},{"name":"getCommittee","parameters":[],"returntype":"Array","offset":42,"safe":true},{"name":"getCommitteeAddress","parameters":[],"returntype":"Hash160","offset":49,"safe":true},{"name":"getGasPerBlock","parameters":[],"returntype":"Integer","offset":56,"safe":true},{"name":"getNextBlockValidators","parameters":[],"returntype":"Array","offset":63,"safe":true},{"name":"getRegisterPrice","parameters":[],"returntype":"Integer","offset":70,"safe":true},{"name":"onNEP17Payment","parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","offset":77,"safe":false},{"name":"registerCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":84,"safe":false},{"name":"setGasPerBlock","parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","offset":91,"safe":false},{"name":"setRegisterPrice","parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","offset":98,"safe":false},{"name":"symbol","parameters":[],"returntype":"String","offset":105,"safe":true},{"name":"totalSupply","parameters":[],"returntype":"Integer","offset":112,"safe":true},{"name":"transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","offset":119,"safe":false},{"name":"unclaimedGas","parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","offset":126,"safe":true},{"name":"unregisterCandidate","parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","offset":133,"safe":false},{"name":"vote","parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","offset":140,"safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}}"""}, From cd46b38baaa4a6b6d0b21d4ea1fb5a224327816b Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sat, 6 Sep 2025 06:50:04 -0400 Subject: [PATCH 20/35] Changed `GetRandom` system call to use 256-bits integers Changed `GetRandom` StdLib call to use 256-bits --- .../ApplicationEngine.Runtime.cs | 12 +++++++- src/Neo/SmartContract/Native/StdLib.cs | 12 ++++---- .../SmartContract/Native/UT_StdLib.cs | 30 +++++++++++-------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index bbdef2e7d4..8af3c3f067 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.Extensions; using Neo.Network.P2P.Payloads; @@ -312,7 +313,16 @@ protected internal BigInteger GetRandom() byte[] buffer; // In the unit of datoshi, 1 datoshi = 1e-8 GAS long price; - if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) + if (IsHardforkEnabled(Hardfork.HF_Faun)) + { + buffer = [ + .. nonceData, + .. BitConverter.GetBytes(ProtocolSettings.Network + random_times++) + ]; + buffer = buffer.Sha256(); + price = 1 << 13; + } + else if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) { buffer = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network + random_times++); price = 1 << 13; diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index ff669814c8..011bb8ebaf 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -286,25 +286,25 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue == BigInteger.Zero) return BigInteger.Zero; - var maxValueBits = maxValue.GetByteCount() * 8; - var maxValueSize = BigInteger.Pow(2, maxValueBits) - BigInteger.One; + var maxValueBits = (maxValue.GetByteCount() * 8) - 1; + var maxValueModulus = (BigInteger.One << maxValueBits) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); - var lowPart = randomProduct >> maxValueBits; + var lowPart = randomProduct >> 256; if (lowPart < maxValue) { - var remainder = maxValueSize % maxValue; + var remainder = (maxValueModulus - lowPart) % maxValue; while (lowPart < remainder) { randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct >> maxValueBits; + lowPart = randomProduct >> 256; } } - return randomProduct >> 128; + return randomProduct >> 256; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 805d011eb3..d4ad624ea5 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -18,7 +18,6 @@ using Neo.VM.Types; using System; using System.Collections.Generic; -using System.Globalization; using System.Numerics; using Array = System.Array; @@ -464,45 +463,50 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("10000000000000000000000000000")); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Parse("100000000000000000000000")); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 1_00000000); - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 255) - BigInteger.One); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 127) - BigInteger.One); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 63) - BigInteger.One); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 31) - BigInteger.One); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 15) - BigInteger.One); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 7) - BigInteger.One); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", BigInteger.Zero); var tx = TransactionBuilder.CreateEmpty() .Nonce((uint)Random.Shared.Next()) .Build(); - using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default); + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default, gas: long.MaxValue); engine.LoadScript(script.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(6, engine.ResultStack.Count); + Assert.AreEqual(7, engine.ResultStack.Count); var actualValue = engine.ResultStack.Pop().GetInteger(); Assert.IsTrue(actualValue <= 0); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue < 10); + Assert.IsTrue(actualValue < (BigInteger.One << 7) - BigInteger.One); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue < 1_00000000); + Assert.IsTrue(actualValue < (BigInteger.One << 15) - BigInteger.One); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue < BigInteger.Parse("100000000000000000000000")); + Assert.IsTrue(actualValue < (BigInteger.One << 31) - BigInteger.One); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue < BigInteger.Parse("10000000000000000000000000000")); + Assert.IsTrue(actualValue < (BigInteger.One << 63) - BigInteger.One); Assert.IsTrue(actualValue >= BigInteger.Zero); actualValue = engine.ResultStack.Pop().GetInteger(); - Assert.IsTrue(actualValue < BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber)); + Assert.IsTrue(actualValue < (BigInteger.One << 127) - BigInteger.One); + Assert.IsTrue(actualValue >= BigInteger.Zero); + + actualValue = engine.ResultStack.Pop().GetInteger(); + Assert.IsTrue(actualValue < (BigInteger.One << 255) - BigInteger.One); Assert.IsTrue(actualValue >= BigInteger.Zero); } } From 71421bee01ed4917fb88d97d6ca6f9b5509b4e79 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sat, 6 Sep 2025 07:04:27 -0400 Subject: [PATCH 21/35] Fixed GetRandom syscall tests --- .../UT_ApplicationEngine.Runtime.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs index 7c619ff564..7d0e44a6c9 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs @@ -118,11 +118,11 @@ public void TestGetRandomSameBlock() var rand_9 = engine_2.GetRandom(); var rand_10 = engine_2.GetRandom(); - Assert.AreEqual(BigInteger.Parse("271339657438512451304577787170704246350"), rand_1); - Assert.AreEqual(BigInteger.Parse("98548189559099075644778613728143131367"), rand_2); - Assert.AreEqual(BigInteger.Parse("247654688993873392544380234598471205121"), rand_3); - Assert.AreEqual(BigInteger.Parse("291082758879475329976578097236212073607"), rand_4); - Assert.AreEqual(BigInteger.Parse("247152297361212656635216876565962360375"), rand_5); + Assert.AreEqual(BigInteger.Parse("14137547953482507529787531174726728043575192644771421434672954709228789229484"), rand_1); + Assert.AreEqual(BigInteger.Parse("69041174077957018274220681211053021553180194393930421796159707285471439930896"), rand_2); + Assert.AreEqual(BigInteger.Parse("86652300146654203963319756238534975146678258076385506807048849596188931607815"), rand_3); + Assert.AreEqual(BigInteger.Parse("50496764884756063422584719912836053887682141355224860059151777430503142747434"), rand_4); + Assert.AreEqual(BigInteger.Parse("38661511749709063695278724931879319836342931787293543816633165541555766668070"), rand_5); Assert.AreEqual(rand_6, rand_1); Assert.AreEqual(rand_7, rand_2); @@ -165,11 +165,11 @@ public void TestGetRandomDifferentBlock() var rand_9 = engine_2.GetRandom(); var rand_10 = engine_2.GetRandom(); - Assert.AreEqual(BigInteger.Parse("271339657438512451304577787170704246350"), rand_1); - Assert.AreEqual(BigInteger.Parse("98548189559099075644778613728143131367"), rand_2); - Assert.AreEqual(BigInteger.Parse("247654688993873392544380234598471205121"), rand_3); - Assert.AreEqual(BigInteger.Parse("291082758879475329976578097236212073607"), rand_4); - Assert.AreEqual(BigInteger.Parse("247152297361212656635216876565962360375"), rand_5); + Assert.AreEqual(BigInteger.Parse("14137547953482507529787531174726728043575192644771421434672954709228789229484"), rand_1); + Assert.AreEqual(BigInteger.Parse("69041174077957018274220681211053021553180194393930421796159707285471439930896"), rand_2); + Assert.AreEqual(BigInteger.Parse("86652300146654203963319756238534975146678258076385506807048849596188931607815"), rand_3); + Assert.AreEqual(BigInteger.Parse("50496764884756063422584719912836053887682141355224860059151777430503142747434"), rand_4); + Assert.AreEqual(BigInteger.Parse("38661511749709063695278724931879319836342931787293543816633165541555766668070"), rand_5); Assert.AreNotEqual(rand_6, rand_1); Assert.AreNotEqual(rand_7, rand_2); From 8defdea3ba2de5c64b90daf14d2005c1f9a39587 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 8 Sep 2025 23:04:02 -0400 Subject: [PATCH 22/35] made srand function for syscall `GetRandom` --- .../ApplicationEngine.Runtime.cs | 13 ++++++- src/Neo/SmartContract/Native/StdLib.cs | 6 +-- .../SmartContract/Native/UT_StdLib.cs | 37 +++++++++++++++++++ .../UT_ApplicationEngine.Runtime.cs | 24 +++++++----- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index 8af3c3f067..ca7959d94c 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -315,12 +315,21 @@ protected internal BigInteger GetRandom() long price; if (IsHardforkEnabled(Hardfork.HF_Faun)) { + var A = new BigInteger(BitConverter.GetBytes(ProtocolSettings.Network), isUnsigned: true); + var C = new BigInteger(BitConverter.GetBytes(ProtocolSettings.TimePerBlock.TotalMilliseconds), isUnsigned: true); + var M = (BigInteger.One << 255) - BigInteger.One; + buffer = [ .. nonceData, - .. BitConverter.GetBytes(ProtocolSettings.Network + random_times++) + .. BitConverter.GetBytes(random_times++), ]; - buffer = buffer.Sha256(); + + var seed = new BigInteger(buffer.Sha256(), isUnsigned: true); + var state = (A * seed + C) % M; price = 1 << 13; + + AddFee(price * ExecFeeFactor); + return state; } else if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) { diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 011bb8ebaf..0c045b4242 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -291,7 +291,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu var randomProduct = maxValue * engine.GetRandom(); - var lowPart = randomProduct >> 256; + var lowPart = (randomProduct >> 255) & maxValueModulus; if (lowPart < maxValue) { @@ -300,11 +300,11 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu while (lowPart < remainder) { randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct >> 256; + lowPart = (randomProduct >> 255) & maxValueModulus; } } - return randomProduct >> 256; + return randomProduct >> 255 & maxValueModulus; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index d4ad624ea5..9363958c36 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -510,5 +510,42 @@ public void TestGetRandomRanges() Assert.IsTrue(actualValue >= BigInteger.Zero); } } + + [TestMethod] + public void MyTestMethod() + { + var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + + using (var script = new ScriptBuilder()) + { + // Test encoding + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); + + var tx = TransactionBuilder.CreateEmpty() + .Nonce((uint)Random.Shared.Next(int.MaxValue)) + .Build(); + + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default, gas: long.MaxValue); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(7, engine.ResultStack.Count); + + var actualValue0 = (BigInteger.One << 255) - BigInteger.One; + var actualValue1 = engine.ResultStack.Pop().GetInteger(); + var actualValue2 = engine.ResultStack.Pop().GetInteger(); + var actualValue3 = engine.ResultStack.Pop().GetInteger(); + var actualValue4 = engine.ResultStack.Pop().GetInteger(); + var actualValue5 = engine.ResultStack.Pop().GetInteger(); + var actualValue6 = engine.ResultStack.Pop().GetInteger(); + var actualValue7 = engine.ResultStack.Pop().GetInteger(); + } + } } } diff --git a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs index 7d0e44a6c9..b64de255a5 100644 --- a/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs +++ b/tests/Neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs @@ -118,11 +118,13 @@ public void TestGetRandomSameBlock() var rand_9 = engine_2.GetRandom(); var rand_10 = engine_2.GetRandom(); - Assert.AreEqual(BigInteger.Parse("14137547953482507529787531174726728043575192644771421434672954709228789229484"), rand_1); - Assert.AreEqual(BigInteger.Parse("69041174077957018274220681211053021553180194393930421796159707285471439930896"), rand_2); - Assert.AreEqual(BigInteger.Parse("86652300146654203963319756238534975146678258076385506807048849596188931607815"), rand_3); - Assert.AreEqual(BigInteger.Parse("50496764884756063422584719912836053887682141355224860059151777430503142747434"), rand_4); - Assert.AreEqual(BigInteger.Parse("38661511749709063695278724931879319836342931787293543816633165541555766668070"), rand_5); + var actualValue0 = (BigInteger.One << 255) - BigInteger.One; + + Assert.IsTrue(rand_1 < actualValue0); + Assert.IsTrue(rand_2 < actualValue0); + Assert.IsTrue(rand_3 < actualValue0); + Assert.IsTrue(rand_4 < actualValue0); + Assert.IsTrue(rand_5 < actualValue0); Assert.AreEqual(rand_6, rand_1); Assert.AreEqual(rand_7, rand_2); @@ -165,11 +167,13 @@ public void TestGetRandomDifferentBlock() var rand_9 = engine_2.GetRandom(); var rand_10 = engine_2.GetRandom(); - Assert.AreEqual(BigInteger.Parse("14137547953482507529787531174726728043575192644771421434672954709228789229484"), rand_1); - Assert.AreEqual(BigInteger.Parse("69041174077957018274220681211053021553180194393930421796159707285471439930896"), rand_2); - Assert.AreEqual(BigInteger.Parse("86652300146654203963319756238534975146678258076385506807048849596188931607815"), rand_3); - Assert.AreEqual(BigInteger.Parse("50496764884756063422584719912836053887682141355224860059151777430503142747434"), rand_4); - Assert.AreEqual(BigInteger.Parse("38661511749709063695278724931879319836342931787293543816633165541555766668070"), rand_5); + var actualValue0 = (BigInteger.One << 255) - BigInteger.One; + + Assert.IsTrue(rand_1 < actualValue0); + Assert.IsTrue(rand_2 < actualValue0); + Assert.IsTrue(rand_3 < actualValue0); + Assert.IsTrue(rand_4 < actualValue0); + Assert.IsTrue(rand_5 < actualValue0); Assert.AreNotEqual(rand_6, rand_1); Assert.AreNotEqual(rand_7, rand_2); From 578440f1ce42cd8ca72714498fe54e7e98de7a8a Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 8 Sep 2025 23:18:02 -0400 Subject: [PATCH 23/35] Removed unused code and fix `GetRandom` syscall --- .../ApplicationEngine.Runtime.cs | 9 ++--- .../SmartContract/Native/UT_StdLib.cs | 37 ------------------- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index ca7959d94c..cb3878883f 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -315,8 +315,8 @@ protected internal BigInteger GetRandom() long price; if (IsHardforkEnabled(Hardfork.HF_Faun)) { - var A = new BigInteger(BitConverter.GetBytes(ProtocolSettings.Network), isUnsigned: true); - var C = new BigInteger(BitConverter.GetBytes(ProtocolSettings.TimePerBlock.TotalMilliseconds), isUnsigned: true); + var A = (BigInteger)ProtocolSettings.Network; + var C = (BigInteger)ProtocolSettings.TimePerBlock.TotalMilliseconds; var M = (BigInteger.One << 255) - BigInteger.One; buffer = [ @@ -326,10 +326,9 @@ .. BitConverter.GetBytes(random_times++), var seed = new BigInteger(buffer.Sha256(), isUnsigned: true); var state = (A * seed + C) % M; - price = 1 << 13; - AddFee(price * ExecFeeFactor); - return state; + buffer = state.ToByteArray(); + price = 1 << 13; } else if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) { diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 9363958c36..d4ad624ea5 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -510,42 +510,5 @@ public void TestGetRandomRanges() Assert.IsTrue(actualValue >= BigInteger.Zero); } } - - [TestMethod] - public void MyTestMethod() - { - var snapshotCache = TestBlockchain.GetTestSnapshotCache(); - - using (var script = new ScriptBuilder()) - { - // Test encoding - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - script.EmitSysCall(ApplicationEngine.System_Runtime_GetRandom); - - var tx = TransactionBuilder.CreateEmpty() - .Nonce((uint)Random.Shared.Next(int.MaxValue)) - .Build(); - - using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default, gas: long.MaxValue); - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(VMState.HALT, engine.Execute()); - Assert.AreEqual(7, engine.ResultStack.Count); - - var actualValue0 = (BigInteger.One << 255) - BigInteger.One; - var actualValue1 = engine.ResultStack.Pop().GetInteger(); - var actualValue2 = engine.ResultStack.Pop().GetInteger(); - var actualValue3 = engine.ResultStack.Pop().GetInteger(); - var actualValue4 = engine.ResultStack.Pop().GetInteger(); - var actualValue5 = engine.ResultStack.Pop().GetInteger(); - var actualValue6 = engine.ResultStack.Pop().GetInteger(); - var actualValue7 = engine.ResultStack.Pop().GetInteger(); - } - } } } From 67d982473a5f7b51459e3c3ea08302ed804b9a0a Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 10 Sep 2025 08:03:25 -0400 Subject: [PATCH 24/35] fixed the spread of the numbers --- .../ApplicationEngine.Runtime.cs | 40 ++++++++++++------- src/Neo/SmartContract/Native/StdLib.cs | 9 ++--- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs index cb3878883f..6923327bf2 100644 --- a/src/Neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/Neo/SmartContract/ApplicationEngine.Runtime.cs @@ -9,7 +9,6 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.Extensions; using Neo.Network.P2P.Payloads; @@ -21,6 +20,7 @@ using System.IO; using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using Array = Neo.VM.Types.Array; namespace Neo.SmartContract @@ -310,25 +310,37 @@ protected internal int GetInvocationCounter() /// The next random number. protected internal BigInteger GetRandom() { - byte[] buffer; + Span buffer; // In the unit of datoshi, 1 datoshi = 1e-8 GAS long price; if (IsHardforkEnabled(Hardfork.HF_Faun)) { - var A = (BigInteger)ProtocolSettings.Network; - var C = (BigInteger)ProtocolSettings.TimePerBlock.TotalMilliseconds; - var M = (BigInteger.One << 255) - BigInteger.One; - - buffer = [ - .. nonceData, - .. BitConverter.GetBytes(random_times++), - ]; - - var seed = new BigInteger(buffer.Sha256(), isUnsigned: true); - var state = (A * seed + C) % M; + buffer = Cryptography.Helper.Murmur128(nonceData, ProtocolSettings.Network + random_times++); + var s0 = BitConverter.ToUInt64(buffer[0..8]); + var s1 = BitConverter.ToUInt64(buffer[8..16]); + + buffer = Cryptography.Helper.Murmur128(buffer, ProtocolSettings.Network + random_times++); + var s2 = BitConverter.ToUInt64(buffer[0..8]); + var s3 = BitConverter.ToUInt64(buffer[8..16]); + + // Update PRNG state. + ulong t = s1 << 17; + s2 ^= s0; + s3 ^= s1; + s1 ^= s2; + s0 ^= s3; + s2 ^= t; + s3 = BitOperations.RotateLeft(s3, 45); + + buffer = new byte[32]; + MemoryMarshal.Write(buffer[0..8], s0); + MemoryMarshal.Write(buffer[8..16], s1); + MemoryMarshal.Write(buffer[16..24], s2); + MemoryMarshal.Write(buffer[24..32], s3); - buffer = state.ToByteArray(); price = 1 << 13; + AddFee(price * ExecFeeFactor); + return new BigInteger(buffer, isUnsigned: true) % (BigInteger.One << 255) - BigInteger.One; } else if (IsHardforkEnabled(Hardfork.HF_Aspidochelone)) { diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 0c045b4242..435cc6c151 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -286,12 +286,11 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue == BigInteger.Zero) return BigInteger.Zero; - var maxValueBits = (maxValue.GetByteCount() * 8) - 1; + var maxValueBits = maxValue.GetByteCount() * 8; var maxValueModulus = (BigInteger.One << maxValueBits) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); - - var lowPart = (randomProduct >> 255) & maxValueModulus; + var lowPart = randomProduct & maxValueModulus; if (lowPart < maxValue) { @@ -300,11 +299,11 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu while (lowPart < remainder) { randomProduct = maxValue * engine.GetRandom(); - lowPart = (randomProduct >> 255) & maxValueModulus; + lowPart = randomProduct & maxValueModulus; } } - return randomProduct >> 255 & maxValueModulus; + return randomProduct >> 255; } } } From f80ac483820b3bbcfab365b7a6de5ee678419064 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sat, 20 Sep 2025 12:32:41 -0400 Subject: [PATCH 25/35] fixed remainder --- src/Neo/SmartContract/Native/StdLib.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 435cc6c151..82c0cf8972 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -287,14 +287,15 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxValueModulus = (BigInteger.One << maxValueBits) - BigInteger.One; + var maxValueModulus = BigInteger.One << maxValueBits; var randomProduct = maxValue * engine.GetRandom(); var lowPart = randomProduct & maxValueModulus; if (lowPart < maxValue) { - var remainder = (maxValueModulus - lowPart) % maxValue; + var underflowMaxValue = (-maxValue + maxValueModulus) % maxValueModulus; + var remainder = underflowMaxValue % maxValue; while (lowPart < remainder) { From f57cd44fc21a28efade5104bc2dcae6f9793ef80 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 21 Sep 2025 05:34:16 -0400 Subject: [PATCH 26/35] optimized --- src/Neo/SmartContract/Native/StdLib.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 82c0cf8972..57fa82a626 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -287,20 +287,19 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxValueModulus = BigInteger.One << maxValueBits; + var maxMaxValue = BigInteger.One << maxValueBits; var randomProduct = maxValue * engine.GetRandom(); - var lowPart = randomProduct & maxValueModulus; + var lowPart = randomProduct & maxMaxValue; if (lowPart < maxValue) { - var underflowMaxValue = (-maxValue + maxValueModulus) % maxValueModulus; - var remainder = underflowMaxValue % maxValue; + var threshold = maxMaxValue - maxValue % maxValue; - while (lowPart < remainder) + while (lowPart < threshold) { randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct & maxValueModulus; + lowPart = randomProduct & maxMaxValue; } } From a8328c710f6382f410f11ccac5e2acf96988ecc1 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 21 Sep 2025 06:20:43 -0400 Subject: [PATCH 27/35] fixed bug --- src/Neo/SmartContract/Native/StdLib.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 57fa82a626..3aac70a9ea 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -287,14 +287,14 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxMaxValue = BigInteger.One << maxValueBits; + var maxMaxValue = (BigInteger.One << maxValueBits) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); var lowPart = randomProduct & maxMaxValue; if (lowPart < maxValue) { - var threshold = maxMaxValue - maxValue % maxValue; + var threshold = (maxMaxValue - maxValue) % maxValue; while (lowPart < threshold) { From 09ba01803002e61a8d2e937110308c8a0223fe2a Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 21 Sep 2025 06:26:18 -0400 Subject: [PATCH 28/35] bug --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 3aac70a9ea..1c30a1cc3e 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -287,7 +287,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxMaxValue = (BigInteger.One << maxValueBits) - BigInteger.One; + var maxMaxValue = (BigInteger.One << maxValueBits - 1) - BigInteger.One; // maxValueBits - 1 for signed integers var randomProduct = maxValue * engine.GetRandom(); var lowPart = randomProduct & maxMaxValue; From 19fea5d19622aa00f6e6d219f3e7c34a4a551971 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 22 Sep 2025 11:14:54 -0400 Subject: [PATCH 29/35] bug fix --- src/Neo/SmartContract/Native/StdLib.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 1c30a1cc3e..3aac70a9ea 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -287,7 +287,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxMaxValue = (BigInteger.One << maxValueBits - 1) - BigInteger.One; // maxValueBits - 1 for signed integers + var maxMaxValue = (BigInteger.One << maxValueBits) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); var lowPart = randomProduct & maxMaxValue; From cc5dfd2aa62a67fa034ab6f2b052421d2afcd686 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 22 Sep 2025 22:07:35 -0400 Subject: [PATCH 30/35] fixed a problem --- src/Neo/SmartContract/Native/StdLib.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 3aac70a9ea..02a5de670d 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -286,8 +286,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue == BigInteger.Zero) return BigInteger.Zero; - var maxValueBits = maxValue.GetByteCount() * 8; - var maxMaxValue = (BigInteger.One << maxValueBits) - BigInteger.One; + var maxMaxValue = (BigInteger.One << 255) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); var lowPart = randomProduct & maxMaxValue; From 61fc499c57313fe12234a4cb2393cfee0b1a4e60 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 22 Sep 2025 22:29:48 -0400 Subject: [PATCH 31/35] LowPart maxvalue fixed --- src/Neo/SmartContract/Native/StdLib.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index 02a5de670d..bc1326828a 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -289,7 +289,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu var maxMaxValue = (BigInteger.One << 255) - BigInteger.One; var randomProduct = maxValue * engine.GetRandom(); - var lowPart = randomProduct & maxMaxValue; + var lowPart = randomProduct % UInt128.MaxValue; if (lowPart < maxValue) { @@ -298,7 +298,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu while (lowPart < threshold) { randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct & maxMaxValue; + lowPart = randomProduct % UInt128.MaxValue; } } From 6cec1933f853f1201becf215a961266a292ad292 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 22 Sep 2025 23:03:58 -0400 Subject: [PATCH 32/35] made function dynamic --- src/Neo/SmartContract/Native/StdLib.cs | 13 +++---- .../SmartContract/Native/UT_StdLib.cs | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index bc1326828a..d8f1381c00 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -286,10 +286,11 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue == BigInteger.Zero) return BigInteger.Zero; - var maxMaxValue = (BigInteger.One << 255) - BigInteger.One; + var maxValueBits = maxValue.GetByteCount() * 8; + var maxMaxValue = (BigInteger.One << maxValueBits - 1) - BigInteger.One; - var randomProduct = maxValue * engine.GetRandom(); - var lowPart = randomProduct % UInt128.MaxValue; + var randomProduct = maxValue * (engine.GetRandom() % maxMaxValue); + var lowPart = randomProduct % maxMaxValue; if (lowPart < maxValue) { @@ -297,12 +298,12 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu while (lowPart < threshold) { - randomProduct = maxValue * engine.GetRandom(); - lowPart = randomProduct % UInt128.MaxValue; + randomProduct = maxValue * (engine.GetRandom() % maxMaxValue); + lowPart = randomProduct % maxMaxValue; } } - return randomProduct >> 255; + return randomProduct >> maxValueBits - 1; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index d4ad624ea5..97ac421752 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -18,6 +18,7 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.IO; using System.Numerics; using Array = System.Array; @@ -463,6 +464,7 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 255) - BigInteger.One); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 127) - BigInteger.One); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 63) - BigInteger.One); @@ -510,5 +512,37 @@ public void TestGetRandomRanges() Assert.IsTrue(actualValue >= BigInteger.Zero); } } + + + [TestMethod] + public void TestGetRandomRanges2() + { + var snapshotCache = TestBlockchain.GetTestSnapshotCache(); + + using (var script = new ScriptBuilder()) + { + // Test encoding + for (var i = 0; i < 2000; i++) + script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); + + var tx = TransactionBuilder.CreateEmpty() + .Nonce((uint)Random.Shared.Next()) + .Build(); + + using var engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshotCache, settings: TestProtocolSettings.Default, gas: long.MaxValue); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(2000, engine.ResultStack.Count); + + for (var i = 0; i < engine.ResultStack.Count; i++) + { + var actualValue = engine.ResultStack.Pop().GetInteger(); + File.AppendAllLines(@"D:\tmp\demo.txt", [$"{actualValue}"]); + Assert.IsTrue(actualValue < 10); + Assert.IsTrue(actualValue >= BigInteger.Zero); + } + } + } } } From 20ebbfd81d2ddd6e3eace7eb8830b4828413a42f Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Mon, 22 Sep 2025 23:04:54 -0400 Subject: [PATCH 33/35] removed a un-needed line --- tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 97ac421752..1e358ab874 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -18,7 +18,6 @@ using Neo.VM.Types; using System; using System.Collections.Generic; -using System.IO; using System.Numerics; using Array = System.Array; @@ -538,7 +537,6 @@ public void TestGetRandomRanges2() for (var i = 0; i < engine.ResultStack.Count; i++) { var actualValue = engine.ResultStack.Pop().GetInteger(); - File.AppendAllLines(@"D:\tmp\demo.txt", [$"{actualValue}"]); Assert.IsTrue(actualValue < 10); Assert.IsTrue(actualValue >= BigInteger.Zero); } From 8db7e474c38311068fc13609e6385d3b338ce071 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 24 Sep 2025 20:12:16 -0400 Subject: [PATCH 34/35] Fix unit tests and `GetRandom` --- src/Neo/SmartContract/Native/StdLib.cs | 6 +++--- tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Neo/SmartContract/Native/StdLib.cs b/src/Neo/SmartContract/Native/StdLib.cs index d8f1381c00..67955c4486 100644 --- a/src/Neo/SmartContract/Native/StdLib.cs +++ b/src/Neo/SmartContract/Native/StdLib.cs @@ -283,11 +283,11 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu if (maxValue.Sign < 0) throw new ArgumentOutOfRangeException(nameof(maxValue)); - if (maxValue == BigInteger.Zero) + if (maxValue <= BigInteger.One) return BigInteger.Zero; var maxValueBits = maxValue.GetByteCount() * 8; - var maxMaxValue = (BigInteger.One << maxValueBits - 1) - BigInteger.One; + var maxMaxValue = BigInteger.One << maxValueBits; var randomProduct = maxValue * (engine.GetRandom() % maxMaxValue); var lowPart = randomProduct % maxMaxValue; @@ -303,7 +303,7 @@ private static BigInteger GetRandom(ApplicationEngine engine, BigInteger maxValu } } - return randomProduct >> maxValueBits - 1; + return randomProduct >> maxValueBits; } } } diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 1e358ab874..8c75574dff 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -463,7 +463,6 @@ public void TestGetRandomRanges() using (var script = new ScriptBuilder()) { // Test encoding - script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", 10); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 255) - BigInteger.One); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 127) - BigInteger.One); script.EmitDynamicCall(NativeContract.StdLib.Hash, "getRandom", (BigInteger.One << 63) - BigInteger.One); From 104d719c61f348bb3501a48a8621341bc785e90e Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Fri, 26 Sep 2025 20:02:21 -0400 Subject: [PATCH 35/35] Fixed syntax --- tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 6fc03f69e9..62e0eb87de 100644 --- a/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/Neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -540,6 +540,7 @@ public void TestGetRandomRanges2() Assert.IsTrue(actualValue >= BigInteger.Zero); } } + } public void TestMemorySearch() {