diff --git a/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Scalar8x32Tests.cs b/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Scalar8x32Tests.cs
index 6a37ce3..d54fb4b 100644
--- a/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Scalar8x32Tests.cs
+++ b/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Scalar8x32Tests.cs
@@ -457,12 +457,12 @@ public void GetHashCodeTest()
private const int Count = 64;
// random_scalar_order_test
- private static Scalar8x32 CreateRandom(Rand rng)
+ private static Scalar8x32 CreateRandom(TestRNG rng)
{
byte[] b32 = new byte[32];
do
{
- rng.secp256k1_testrand256_test(b32);
+ rng.Rand256Test(b32);
Scalar8x32 result = new(b32, out bool overflow);
if (!overflow && !result.IsZero)
{
@@ -472,7 +472,7 @@ private static Scalar8x32 CreateRandom(Rand rng)
}
// scalar_test(void)
- private static unsafe void ScalarTest(Rand rng)
+ private static unsafe void ScalarTest(TestRNG rng)
{
// Set 's' to a random scalar, with value 'snum'.
Scalar8x32 s = CreateRandom(rng);
@@ -509,7 +509,7 @@ private static unsafe void ScalarTest(Rand rng)
int i = 0;
while (i < 256)
{
- int now = (int)(rng.secp256k1_testrand_int(15) + 1);
+ int now = (int)(rng.RandInt(15) + 1);
if (now + i > 256)
{
now = 256 - i;
@@ -534,7 +534,7 @@ private static unsafe void ScalarTest(Rand rng)
{
// Test add_bit
- uint bit = (uint)rng.secp256k1_testrand_bits(8);
+ uint bit = (uint)rng.RandBits(8);
Scalar8x32 b = new(1);
Assert.True(b.IsOne);
for (int i = 0; i < bit; i++)
@@ -623,7 +623,7 @@ private static unsafe void ScalarTest(Rand rng)
[Fact]
public void Libsecp256k1Tests() // run_scalar_tests
{
- Rand rng = new();
+ TestRNG rng = new();
rng.Init(null);
for (int i = 0; i < 128 * Count; i++)
diff --git a/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Rand.cs b/Src/Tests/Bitcoin/Cryptography/EllipticCurve/TestRNG.cs
similarity index 54%
rename from Src/Tests/Bitcoin/Cryptography/EllipticCurve/Rand.cs
rename to Src/Tests/Bitcoin/Cryptography/EllipticCurve/TestRNG.cs
index 39f2943..71e69f7 100644
--- a/Src/Tests/Bitcoin/Cryptography/EllipticCurve/Rand.cs
+++ b/Src/Tests/Bitcoin/Cryptography/EllipticCurve/TestRNG.cs
@@ -6,21 +6,27 @@
using Autarkysoft.Bitcoin;
using Autarkysoft.Bitcoin.Cryptography.Hashing;
using System;
+using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Tests.Bitcoin.Cryptography.EllipticCurve
{
///
- /// xoshiro PRNG https://prng.di.unimi.it/
- /// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand.h
- /// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand_impl.h
+ /// A non-cryptographic RNG used only for test infrastructure
+ /// xoshiro PRNG https://prng.di.unimi.it/
+ /// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand.h
+ /// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand_impl.h
///
- internal class Rand
+ public class TestRNG
{
private readonly ulong[] state = new ulong[4];
- private void secp256k1_testrand_seed(byte[] seed16)
+ ///
+ /// Seed the pseudorandom number generator for testing
+ ///
+ /// secp256k1_testrand_seed
+ private void Seed(byte[] seed16)
{
Assert.Equal(16, seed16.Length);
byte[] PREFIX = Encoding.UTF8.GetBytes("secp256k1 test init");
@@ -41,65 +47,85 @@ private void secp256k1_testrand_seed(byte[] seed16)
}
}
- private static ulong rotl(ulong x, int k) => (x << k) | (x >> (64 - k));
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ulong ROTL(ulong x, int k) => (x << k) | (x >> (64 - k));
- private ulong secp256k1_testrand64()
+ ///
+ /// Generate a pseudorandom number in the range [0..2**64-1].
+ ///
+ /// secp256k1_testrand64
+ public ulong Rand64()
{
- /* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */
- ulong result = rotl(state[0] + state[3], 23) + state[0];
+ // Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/
+ ulong result = ROTL(state[0] + state[3], 23) + state[0];
ulong t = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= t;
- state[3] = rotl(state[3], 45);
+ state[3] = ROTL(state[3], 45);
return result;
}
///
/// Generate a pseudorandom number in the range [0..2**bits-1].
///
+ /// secp256k1_testrand_bits
/// Bits must be 1 or more
- ///
- internal ulong secp256k1_testrand_bits(int bits)
+ public ulong RandBits(int bits)
{
- if (bits == 0) return 0;
- return secp256k1_testrand64() >> (64 - bits);
+ return bits == 0 ? 0 : Rand64() >> (64 - bits);
}
- uint secp256k1_testrand32()
+ ///
+ /// Generate a pseudorandom number in the range [0..2**32-1]
+ ///
+ /// secp256k1_testrand32
+ public uint Rand32()
{
- return (uint)(secp256k1_testrand64() >> 32);
+ return (uint)(Rand64() >> 32);
}
- internal uint secp256k1_testrand_int(uint range)
+ ///
+ /// Generate a pseudorandom number in the range [0..range-1]
+ ///
+ /// secp256k1_testrand_int
+ public uint RandInt(uint range)
{
uint mask = 0;
uint range_copy;
- /* Reduce range by 1, changing its meaning to "maximum value". */
+ // Reduce range by 1, changing its meaning to "maximum value".
Assert.True(range != 0);
range -= 1;
- /* Count the number of bits in range. */
+ // Count the number of bits in range.
range_copy = range;
while (range_copy != 0)
{
mask = (mask << 1) | 1U;
range_copy >>= 1;
}
- /* Generation loop. */
+ // Generation loop.
while (true)
{
- uint val = (uint)(secp256k1_testrand64() & mask);
- if (val <= range) return val;
+ uint val = (uint)(Rand64() & mask);
+ if (val <= range)
+ {
+ return val;
+ }
}
}
- private void secp256k1_testrand256(byte[] b32)
+ ///
+ /// Generate a pseudorandom 32-byte array.
+ ///
+ /// secp256k1_testrand256
+ public void Rand256(Span b32)
{
+ Assert.True(b32.Length == 32);
for (int i = 0; i < b32.Length; i += 8)
{
- ulong val = secp256k1_testrand64();
+ ulong val = Rand64();
b32[i + 0] = (byte)val;
b32[i + 1] = (byte)(val >> 8);
b32[i + 2] = (byte)(val >> 16);
@@ -111,7 +137,11 @@ private void secp256k1_testrand256(byte[] b32)
}
}
- private void secp256k1_testrand_bytes_test(byte[] bytes, int len)
+ ///
+ /// Generate pseudorandom bytes with long sequences of zero and one bits.
+ ///
+ /// secp256k1_testrand_bytes_test
+ public void RandBytesTest(Span bytes, int len)
{
for (int i = 0; i < len; i++)
{
@@ -121,8 +151,8 @@ private void secp256k1_testrand_bytes_test(byte[] bytes, int len)
int bits = 0;
while (bits < len * 8)
{
- int now = (int)(1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31);
- uint val = (uint)secp256k1_testrand_bits(1);
+ int now = (int)(1 + (RandBits(6) * RandBits(5) + 16) / 31);
+ uint val = (uint)RandBits(1);
while (now > 0 && bits < len * 8)
{
bytes[bits / 8] |= (byte)(val << (bits % 8));
@@ -132,27 +162,42 @@ private void secp256k1_testrand_bytes_test(byte[] bytes, int len)
}
}
- internal void secp256k1_testrand256_test(byte[] b32)
+ ///
+ /// Generate a pseudorandom 32-byte array with long sequences of zero and one bits.
+ ///
+ /// secp256k1_testrand256_test
+ public void Rand256Test(Span b32)
{
- secp256k1_testrand_bytes_test(b32, 32);
+ Assert.True(b32.Length == 32);
+ RandBytesTest(b32, 32);
}
- internal void secp256k1_testrand256_test(ushort[] arr16)
+ public void Rand256Test(Span arr16)
{
+ Assert.True(arr16.Length == 16);
byte[] b32 = new byte[32];
- secp256k1_testrand_bytes_test(b32, 32);
+ RandBytesTest(b32, 32);
for (int i = 0, j = 0; i < b32.Length && j < arr16.Length; i += 2, j++)
{
arr16[j] = (ushort)(b32[i] | b32[i + 1] << 8);
}
}
- private void secp256k1_testrand_flip(byte[] b, int len)
+ ///
+ /// Flip a single random bit in a byte array
+ ///
+ /// secp256k1_testrand_flip
+ public void RandFlip(Span b, uint len)
{
- b[secp256k1_testrand_int((uint)len)] ^= (byte)(1 << (int)secp256k1_testrand_bits(3));
+ b[(int)RandInt(len)] ^= (byte)(1 << (int)RandBits(3));
}
- internal void Init(string hexseed)
+
+ ///
+ /// Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL.
+ ///
+ /// secp256k1_testrand_init
+ public void Init(string hexseed)
{
byte[] seed16 = new byte[16];
if (!string.IsNullOrEmpty(hexseed))
@@ -172,17 +217,12 @@ internal void Init(string hexseed)
RandomNumberGenerator.Fill(seed16);
}
- secp256k1_testrand_seed(seed16);
+ Seed(seed16);
}
- private void secp256k1_testrand_finish()
- {
- byte[] run32 = new byte[32];
- secp256k1_testrand256(run32);
- }
- internal void RunXoshiro256ppTests()
+ public void RunXoshiro256ppTests()
{
// Sanity check that we run before the actual seeding.
for (int i = 0; i < state.Length; i++)
@@ -198,10 +238,10 @@ internal void RunXoshiro256ppTests()
0x4C, 0xCC, 0xC1, 0x18, 0xB2, 0xD8, 0x8F, 0xEF,
0x43, 0x26, 0x15, 0x57, 0x37, 0x00, 0xEF, 0x30,
};
- secp256k1_testrand_seed(seed16);
+ Seed(seed16);
for (int i = 0; i < 17; i++)
{
- secp256k1_testrand256(buf32);
+ Rand256(buf32);
}
Assert.Equal(buf32_expected, buf32);
}