Skip to content

Commit aa39868

Browse files
cschuchardt88shargonWi1l-B0tvncoelhoJimmy
authored
[Add] RandomNumberFactory Class (#3987)
* Updated `RandomExtensions.NextBigIneger` to use `RandomNumberGenerator` * Added `RandomNumberFactory` class to generate random numbers. * changed namespace * Fixed bugs * fixed more bugs * Fixed up tests and add more * Update src/Neo.Extensions/Factories/RandomNumberFactory.cs Co-authored-by: Shargon <[email protected]> * Fixed per @shargon feedback * Bug fix per @vncoelho * Added more tests * Bug Fix * Fixed bugs in `NextBigInteger` unit tests * Updated `RandomExtensions.NextBigIneger` to use `RandomNumberGenerator` * Added `RandomNumberFactory` class to generate random numbers. * changed namespace * Fixed bugs * fixed more bugs * Fixed up tests and add more * Fixed per @shargon feedback * Update src/Neo.Extensions/Factories/RandomNumberFactory.cs Co-authored-by: Shargon <[email protected]> * Bug fix per @vncoelho * Added more tests * Bug Fix * Fixed bugs in `NextBigInteger` unit tests * Added more tests and add `NexrBigInteger` minmax * Bug fixes * `BigInteger` now uses negative numbers. * Fixes to `NextBigInteger` * Fixed `BigNextInteger(MaxValue)` to calulate correctly. * Added @vncoelho suggestions * Fixed `NextInteger` for faster resolve. --------- Co-authored-by: Shargon <[email protected]> Co-authored-by: Will <[email protected]> Co-authored-by: Vitor Nazário Coelho <[email protected]> Co-authored-by: Jimmy <[email protected]> Co-authored-by: NGD Admin <[email protected]>
1 parent c3dd619 commit aa39868

File tree

8 files changed

+593
-88
lines changed

8 files changed

+593
-88
lines changed

src/Neo.Extensions/BigIntegerExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ public static BigInteger Sqrt(this BigInteger value)
160160
return z;
161161
}
162162

163+
internal static BigInteger GetLowPart(this BigInteger value, int bitCount)
164+
{
165+
var mask = (BigInteger.One << bitCount) - 1;
166+
return value & mask;
167+
}
168+
163169
/// <summary>
164170
/// Gets the number of bits required for shortest two's complement representation of the current instance without the sign bit.
165171
/// Note: This method is imprecise and might not work as expected with integers larger than 256 bits if less than .NET5.
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// Copyright (C) 2015-2025 The Neo Project.
2+
//
3+
// RandomNumberFactory.cs file belongs to the neo project and is free
4+
// software distributed under the MIT software license, see the
5+
// accompanying file LICENSE in the main directory of the
6+
// repository or http://www.opensource.org/licenses/mit-license.php
7+
// for more details.
8+
//
9+
// Redistribution and use in source and binary forms with or without
10+
// modifications are permitted.
11+
12+
using System;
13+
using System.Buffers.Binary;
14+
using System.Numerics;
15+
using System.Security.Cryptography;
16+
17+
namespace Neo.Extensions.Factories
18+
{
19+
public static class RandomNumberFactory
20+
{
21+
public static sbyte NextSByte() =>
22+
NextSByte(0, sbyte.MaxValue);
23+
24+
public static sbyte NextSByte(sbyte maxValue)
25+
{
26+
if (maxValue < 0)
27+
throw new ArgumentOutOfRangeException(nameof(maxValue));
28+
29+
return NextSByte(0, maxValue);
30+
}
31+
32+
public static sbyte NextSByte(sbyte minValue, sbyte maxValue)
33+
{
34+
if (minValue == maxValue) return maxValue;
35+
36+
if (minValue > maxValue)
37+
throw new ArgumentOutOfRangeException(nameof(minValue));
38+
39+
return (sbyte)(NextUInt32((uint)(maxValue - minValue)) + minValue);
40+
}
41+
42+
public static byte NextByte() =>
43+
NextByte(0, byte.MaxValue);
44+
45+
public static byte NextByte(byte maxValue) =>
46+
NextByte(0, maxValue);
47+
48+
public static byte NextByte(byte minValue, byte maxValue)
49+
{
50+
if (minValue == maxValue) return maxValue;
51+
52+
if (minValue > maxValue)
53+
throw new ArgumentOutOfRangeException(nameof(minValue));
54+
55+
return (byte)(NextUInt32((uint)(maxValue - minValue)) + minValue);
56+
}
57+
58+
public static short NextInt16() =>
59+
NextInt16(0, short.MaxValue);
60+
61+
public static short NextInt16(short maxValue)
62+
{
63+
if (maxValue < 0)
64+
throw new ArgumentOutOfRangeException(nameof(maxValue));
65+
66+
return NextInt16(0, maxValue);
67+
}
68+
69+
public static short NextInt16(short minValue, short maxValue)
70+
{
71+
if (minValue == maxValue) return maxValue;
72+
73+
if (minValue > maxValue)
74+
throw new ArgumentOutOfRangeException(nameof(minValue));
75+
76+
return (short)(NextUInt32((uint)(maxValue - minValue)) + minValue);
77+
}
78+
79+
public static ushort NextUInt16() =>
80+
NextUInt16(0, ushort.MaxValue);
81+
82+
public static ushort NextUInt16(ushort maxValue) =>
83+
NextUInt16(0, maxValue);
84+
85+
public static ushort NextUInt16(ushort minValue, ushort maxValue)
86+
{
87+
if (minValue == maxValue) return maxValue;
88+
89+
if (minValue > maxValue)
90+
throw new ArgumentOutOfRangeException(nameof(minValue));
91+
92+
return (ushort)(NextUInt32((uint)(maxValue - minValue)) + minValue);
93+
}
94+
95+
public static int NextInt32() =>
96+
NextInt32(0, int.MaxValue);
97+
98+
public static int NextInt32(int maxValue)
99+
{
100+
if (maxValue < 0)
101+
throw new ArgumentOutOfRangeException(nameof(maxValue));
102+
103+
return NextInt32(0, maxValue);
104+
}
105+
106+
public static int NextInt32(int minValue, int maxValue)
107+
{
108+
if (minValue == maxValue) return maxValue;
109+
110+
if (minValue > maxValue)
111+
throw new ArgumentOutOfRangeException(nameof(minValue));
112+
113+
return (int)NextUInt32((uint)(maxValue - minValue)) + minValue;
114+
}
115+
116+
public static uint NextUInt32()
117+
{
118+
Span<byte> longBytes = stackalloc byte[4];
119+
RandomNumberGenerator.Fill(longBytes);
120+
return BinaryPrimitives.ReadUInt32LittleEndian(longBytes);
121+
}
122+
123+
public static uint NextUInt32(uint maxValue)
124+
{
125+
var randomProduct = (ulong)maxValue * NextUInt32();
126+
var lowPart = (uint)randomProduct;
127+
128+
if (lowPart < maxValue)
129+
{
130+
var remainder = (0u - maxValue) % maxValue;
131+
132+
while (lowPart < remainder)
133+
{
134+
randomProduct = (ulong)maxValue * NextUInt32();
135+
lowPart = (uint)randomProduct;
136+
}
137+
}
138+
139+
return (uint)(randomProduct >> 32);
140+
}
141+
142+
public static uint NextUInt32(uint minValue, uint maxValue)
143+
{
144+
if (minValue == maxValue) return maxValue;
145+
146+
if (minValue > maxValue)
147+
throw new ArgumentOutOfRangeException(nameof(minValue));
148+
149+
return NextUInt32(maxValue - minValue) + minValue;
150+
}
151+
152+
public static long NextInt64() =>
153+
NextInt64(0L, long.MaxValue);
154+
155+
public static long NextInt64(long maxValue)
156+
{
157+
return NextInt64(0L, maxValue);
158+
}
159+
160+
public static long NextInt64(long minValue, long maxValue)
161+
{
162+
if (minValue == maxValue) return maxValue;
163+
164+
if (minValue > maxValue)
165+
throw new ArgumentOutOfRangeException(nameof(minValue));
166+
167+
return (long)NextUInt64((ulong)(maxValue - minValue)) + minValue;
168+
}
169+
170+
public static ulong NextUInt64()
171+
{
172+
Span<byte> longBytes = stackalloc byte[8];
173+
RandomNumberGenerator.Fill(longBytes);
174+
return BinaryPrimitives.ReadUInt64LittleEndian(longBytes);
175+
}
176+
177+
public static ulong NextUInt64(ulong maxValue)
178+
{
179+
var randomProduct = BigMul(maxValue, NextUInt64(), out var lowPart);
180+
181+
if (lowPart < maxValue)
182+
{
183+
var remainder = (0ul - maxValue) % maxValue;
184+
185+
while (lowPart < remainder)
186+
{
187+
randomProduct = BigMul(maxValue, NextUInt64(), out lowPart);
188+
}
189+
}
190+
191+
return randomProduct;
192+
}
193+
194+
public static ulong NextUInt64(ulong minValue, ulong maxValue)
195+
{
196+
if (minValue == maxValue) return maxValue;
197+
198+
if (minValue > maxValue)
199+
throw new ArgumentOutOfRangeException(nameof(minValue));
200+
201+
return NextUInt64(maxValue - minValue) + minValue;
202+
}
203+
204+
public static BigInteger NextBigInteger(BigInteger minValue, BigInteger maxValue)
205+
{
206+
if (minValue == maxValue) return maxValue;
207+
208+
if (minValue > maxValue)
209+
throw new ArgumentOutOfRangeException(nameof(minValue));
210+
211+
return NextBigInteger(maxValue - minValue) + minValue;
212+
}
213+
214+
public static BigInteger NextBigInteger(BigInteger maxValue)
215+
{
216+
if (maxValue.Sign < 0)
217+
throw new ArgumentOutOfRangeException(nameof(maxValue));
218+
219+
var maxValueBits = maxValue.GetByteCount() * 8;
220+
var maxValueSize = BigInteger.Pow(2, maxValueBits);
221+
222+
var randomProduct = maxValue * NextBigInteger(maxValueBits);
223+
var randomProductBits = randomProduct.GetByteCount() * 8;
224+
225+
var lowPart = randomProduct.GetLowPart(maxValueBits);
226+
227+
if (lowPart < maxValue)
228+
{
229+
var remainder = (maxValueSize - maxValue) % maxValue;
230+
231+
while (lowPart < remainder)
232+
{
233+
randomProduct = maxValue * NextBigInteger(maxValueBits);
234+
randomProductBits = randomProduct.GetByteCount() * 8;
235+
lowPart = randomProduct.GetLowPart(maxValueBits);
236+
}
237+
}
238+
239+
var result = randomProduct >> (randomProductBits - maxValueBits);
240+
241+
// Since BigInteger doesn't have a max value or bit size
242+
// anything over 'maxValue' return zero
243+
if (result >= maxValue)
244+
return BigInteger.Zero;
245+
246+
return result;
247+
}
248+
249+
public static BigInteger NextBigInteger(int sizeInBits)
250+
{
251+
if (sizeInBits < 0)
252+
throw new ArgumentException("sizeInBits must be non-negative.");
253+
254+
if (sizeInBits == 0)
255+
return BigInteger.Zero;
256+
257+
Span<byte> b = stackalloc byte[sizeInBits / 8 + 1];
258+
RandomNumberGenerator.Fill(b);
259+
260+
if (sizeInBits % 8 == 0)
261+
b[^1] = 0;
262+
else
263+
b[^1] &= (byte)((1 << sizeInBits % 8) - 1);
264+
265+
return new BigInteger(b);
266+
}
267+
268+
private static ulong BigMul(ulong a, ulong b, out ulong low)
269+
{
270+
// Adaptation of algorithm for multiplication
271+
// of 32-bit unsigned integers described
272+
// in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8
273+
// Basically, it's an optimized version of FOIL method applied to
274+
// low and high dwords of each operand
275+
276+
// Use 32-bit uints to optimize the fallback for 32-bit platforms.
277+
var al = (uint)a;
278+
var ah = (uint)(a >> 32);
279+
var bl = (uint)b;
280+
var bh = (uint)(b >> 32);
281+
282+
var mull = ((ulong)al) * bl;
283+
var t = ((ulong)ah) * bl + (mull >> 32);
284+
var tl = ((ulong)al) * bh + (uint)t;
285+
286+
low = (tl << 32) | (uint)mull;
287+
288+
return ((ulong)ah) * bh + (t >> 32) + (tl >> 32);
289+
}
290+
}
291+
}

src/Neo.Extensions/RandomExtensions.cs

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/Neo/Cryptography/ECC/ECFieldElement.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#nullable enable
1313

1414
using Neo.Extensions;
15+
using Neo.Extensions.Factories;
1516
using System;
1617
using System.Numerics;
1718

@@ -121,11 +122,10 @@ public override int GetHashCode()
121122
BigInteger U, V;
122123
do
123124
{
124-
Random rand = new();
125-
BigInteger P;
125+
var P = BigInteger.Zero;
126126
do
127127
{
128-
P = rand.NextBigInteger((int)_curve.Q.GetBitLength());
128+
P = RandomNumberFactory.NextBigInteger((int)_curve.Q.GetBitLength());
129129
}
130130
while (P >= _curve.Q || BigInteger.ModPow(P * P - fourQ, legendreExponent, _curve.Q) != qMinusOne);
131131
var result = FastLucasSequence(_curve.Q, P, Q, k);

0 commit comments

Comments
 (0)