Skip to content

Commit 62c7946

Browse files
committed
Add parameterised tests for UInt256
Fix bugs found by tests Signed-off-by: Simon Dudley <[email protected]>
1 parent af9125a commit 62c7946

File tree

2 files changed

+336
-6
lines changed

2 files changed

+336
-6
lines changed

evm/src/main/java/org/hyperledger/besu/evm/UInt256.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,11 @@ public int hashCode() {
403403
* @return Shifted UInt256 value.
404404
*/
405405
public UInt256 shiftLeft(final int shift) {
406-
if (shift >= length * 32) return ZERO;
406+
if (shift >= 256) return ZERO;
407407
if (shift < 0) return shiftRight(-shift);
408408
if (shift == 0 || isZero()) return this;
409409
int nDiffBits = shift - numberOfLeadingZeros(this.limbs, this.length);
410-
int size = this.length + (nDiffBits + 31) / 32;
410+
int size = Math.min(this.length + (nDiffBits + 31) / 32, N_LIMBS);
411411
int[] shifted = new int[size];
412412
shiftLeftInto(shifted, this.limbs, shift);
413413
return new UInt256(shifted, size);
@@ -576,7 +576,7 @@ private static void shiftLeftInto(final int[] result, final int[] x, final int s
576576
int limbShift = shift / 32;
577577
int bitShift = shift % 32;
578578
int nLimbs = Math.min(x.length, result.length - limbShift);
579-
if (shift > 32 * nLimbs) return;
579+
if (limbShift >= result.length) return;
580580
if (bitShift == 0) {
581581
System.arraycopy(x, 0, result, limbShift, nLimbs);
582582
return;
@@ -588,15 +588,15 @@ private static void shiftLeftInto(final int[] result, final int[] x, final int s
588588
result[j] = (x[i] << bitShift) | carry;
589589
carry = x[i] >>> (32 - bitShift);
590590
}
591-
if (carry != 0) result[j] = carry; // last carry
591+
if (carry != 0 && j < result.length) result[j] = carry; // last carry
592592
}
593593

594594
private static void shiftRightInto(final int[] result, final int[] x, final int shift) {
595595
int limbShift = shift / 32;
596596
int bitShift = shift % 32;
597597
int nLimbs = Math.min(x.length - limbShift, result.length);
598598

599-
if (shift > 32 * nLimbs) return;
599+
if (limbShift >= x.length) return;
600600
if (bitShift == 0) {
601601
System.arraycopy(x, limbShift, result, 0, nLimbs);
602602
return;
@@ -686,7 +686,8 @@ private static int[] knuthRemainder(final int[] dividend, final int[] modulus) {
686686
int n = modulus.length - limbShift;
687687
if (n == 0) return new int[0];
688688
if (n == 1) {
689-
if (dividend.length == 1) return (new int[] {dividend[0] % modulus[0]});
689+
if (dividend.length == 1)
690+
return (new int[] {Integer.remainderUnsigned(dividend[0], modulus[0])});
690691
long d = modulus[0] & MASK_L;
691692
long rem = 0;
692693
// Process from most significant limb downwards
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.evm;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
import java.math.BigInteger;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Random;
23+
import java.util.stream.Stream;
24+
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.Arguments;
27+
import org.junit.jupiter.params.provider.MethodSource;
28+
29+
public class UInt256ParameterisedTest {
30+
31+
// Test constants
32+
private static final BigInteger TWO_TO_64 = BigInteger.TWO.pow(64);
33+
private static final BigInteger TWO_TO_128 = BigInteger.TWO.pow(128);
34+
private static final BigInteger TWO_TO_192 = BigInteger.TWO.pow(192);
35+
private static final BigInteger TWO_TO_256 = BigInteger.TWO.pow(256);
36+
private static final BigInteger UINT256_MAX = TWO_TO_256.subtract(BigInteger.ONE);
37+
38+
private static final int RANDOM_TEST_COUNT = 6;
39+
40+
// region Test Data Providers
41+
42+
/** Provides unary test cases (single BigInteger values). */
43+
static Stream<BigInteger> provideUnaryTestCases() {
44+
List<BigInteger> cases = new ArrayList<>();
45+
46+
// Basic values
47+
cases.add(BigInteger.ZERO);
48+
cases.add(BigInteger.ONE);
49+
cases.add(BigInteger.TWO);
50+
cases.add(BigInteger.valueOf(3));
51+
52+
// Boundary values
53+
cases.add(BigInteger.valueOf(Short.MAX_VALUE));
54+
cases.add(BigInteger.valueOf(0xFFFF - 1)); // UnsignedShort.MAX_VALUE - 1
55+
cases.add(BigInteger.valueOf(0xFFFF)); // UnsignedShort.MAX_VALUE
56+
cases.add(BigInteger.valueOf(0xFFFF + 1)); // UnsignedShort.MAX_VALUE + 1
57+
cases.add(BigInteger.valueOf(Integer.MAX_VALUE));
58+
cases.add(BigInteger.valueOf(0xFFFFFFFFL - 1)); // UnsignedInteger.MAX_VALUE - 1
59+
cases.add(BigInteger.valueOf(0xFFFFFFFFL)); // UnsignedInteger.MAX_VALUE
60+
cases.add(BigInteger.valueOf(0xFFFFFFFFL + 1)); // UnsignedInteger.MAX_VALUE + 1
61+
cases.add(BigInteger.valueOf(Long.MAX_VALUE));
62+
63+
// Large values
64+
cases.add(new BigInteger("FFFFFFFFFFFFFFFE", 16)); // UnsignedLong.MAX_VALUE - 1
65+
cases.add(new BigInteger("FFFFFFFFFFFFFFFF", 16)); // UnsignedLong.MAX_VALUE
66+
cases.add(new BigInteger("080000000000000008000000000000001", 16));
67+
cases.add(TWO_TO_64);
68+
cases.add(TWO_TO_128);
69+
cases.add(TWO_TO_192);
70+
cases.add(TWO_TO_128.subtract(BigInteger.ONE)); // UInt128Max
71+
cases.add(TWO_TO_192.subtract(BigInteger.ONE)); // UInt192Max
72+
cases.add(UINT256_MAX);
73+
74+
// Add random values
75+
cases.addAll(generateRandomUnsigned(RANDOM_TEST_COUNT));
76+
77+
return cases.stream();
78+
}
79+
80+
/** Provides binary test cases (pairs of BigInteger values). */
81+
static Stream<Arguments> provideBinaryTestCases() {
82+
List<BigInteger> unary = provideUnaryTestCases().toList();
83+
List<Arguments> binary = new ArrayList<>();
84+
85+
for (BigInteger a : unary) {
86+
for (BigInteger b : unary) {
87+
binary.add(Arguments.of(a, b));
88+
}
89+
}
90+
91+
return binary.stream();
92+
}
93+
94+
/** Provides ternary test cases (triples of BigInteger values). */
95+
static Stream<Arguments> provideTernaryTestCases() {
96+
List<Arguments> binary = provideBinaryTestCases().toList();
97+
List<BigInteger> unary = provideUnaryTestCases().toList();
98+
List<Arguments> ternary = new ArrayList<>();
99+
100+
for (Arguments binArgs : binary) {
101+
BigInteger a = (BigInteger) binArgs.get()[0];
102+
BigInteger b = (BigInteger) binArgs.get()[1];
103+
for (BigInteger c : unary) {
104+
ternary.add(Arguments.of(a, b, c));
105+
}
106+
}
107+
108+
return ternary.stream();
109+
}
110+
111+
/** Provides shift test cases (BigInteger value and int shift amount). */
112+
static Stream<Arguments> provideShiftTestCases() {
113+
List<BigInteger> unary = provideUnaryTestCases().toList();
114+
List<Arguments> shifts = new ArrayList<>();
115+
116+
for (BigInteger value : unary) {
117+
for (int shift = 0; shift <= 256; shift++) {
118+
shifts.add(Arguments.of(value, shift));
119+
}
120+
}
121+
122+
return shifts.stream();
123+
}
124+
125+
// endregion
126+
127+
// region Helper Methods
128+
129+
/** Converts BigInteger to UInt256, wrapping to 256-bit range. */
130+
private static UInt256 toUInt256(final BigInteger value) {
131+
BigInteger wrapped = value.mod(TWO_TO_256);
132+
return fromBigInteger(wrapped);
133+
}
134+
135+
/**
136+
* Create UInt256 from BigInteger.
137+
*
138+
* @param value BigInteger value to convert (must be non-negative and <= 2^256-1)
139+
* @return UInt256 representation of the BigInteger value.
140+
* @throws IllegalArgumentException if value is negative or exceeds 256 bits
141+
*/
142+
private static UInt256 fromBigInteger(final java.math.BigInteger value) {
143+
if (value.signum() < 0) {
144+
throw new IllegalArgumentException("UInt256 cannot represent negative values");
145+
}
146+
if (value.bitLength() > 256) {
147+
throw new IllegalArgumentException("Value exceeds 256 bits");
148+
}
149+
if (value.equals(java.math.BigInteger.ZERO)) return UInt256.ZERO;
150+
151+
byte[] bytes = value.toByteArray();
152+
// Remove sign byte if present
153+
int offset = 0;
154+
if (bytes.length > 32 || (bytes.length > 0 && bytes[0] == 0)) {
155+
offset = bytes.length - 32;
156+
if (offset < 0) {
157+
// Need to pad with zeros
158+
byte[] padded = new byte[32];
159+
System.arraycopy(bytes, 0, padded, 32 - bytes.length, bytes.length);
160+
return UInt256.fromBytesBE(padded);
161+
}
162+
}
163+
164+
return UInt256.fromBytesBE(java.util.Arrays.copyOfRange(bytes, offset, bytes.length));
165+
}
166+
167+
private static BigInteger toBigInteger(final UInt256 value) {
168+
if (value.isZero()) return java.math.BigInteger.ZERO;
169+
byte[] bytes = value.toBytesBE();
170+
return new java.math.BigInteger(1, bytes);
171+
}
172+
173+
/** Generates random unsigned 256-bit BigInteger values. */
174+
private static List<BigInteger> generateRandomUnsigned(final int count) {
175+
List<BigInteger> randoms = new ArrayList<>();
176+
Random rand = new Random(12345);
177+
byte[] data = new byte[32];
178+
179+
for (int i = 0; i < count; i++) {
180+
rand.nextBytes(data);
181+
data[data.length - 1] &= 0x7F; // Clear sign bit to ensure positive
182+
randoms.add(new BigInteger(1, data));
183+
}
184+
185+
return randoms;
186+
}
187+
188+
/** Wraps result to 256-bit range. */
189+
private static BigInteger wrap256(final BigInteger value) {
190+
return value.mod(TWO_TO_256);
191+
}
192+
193+
// endregion
194+
195+
// region Arithmetic Operations Tests
196+
197+
@ParameterizedTest
198+
@MethodSource("provideBinaryTestCases")
199+
void testAdd(final BigInteger a, final BigInteger b) {
200+
BigInteger expected = wrap256(a.add(b));
201+
202+
UInt256 uint256a = toUInt256(a);
203+
UInt256 uint256b = toUInt256(b);
204+
UInt256 result = uint256a.add(uint256b);
205+
206+
assertThat(toBigInteger(result)).isEqualTo(expected);
207+
}
208+
209+
@ParameterizedTest
210+
@MethodSource("provideBinaryTestCases")
211+
void testMul(final BigInteger a, final BigInteger b) {
212+
BigInteger expected = wrap256(a.multiply(b));
213+
214+
UInt256 uint256a = toUInt256(a);
215+
UInt256 uint256b = toUInt256(b);
216+
UInt256 result = uint256a.mul(uint256b);
217+
218+
assertThat(toBigInteger(result)).isEqualTo(expected);
219+
}
220+
221+
@ParameterizedTest
222+
@MethodSource("provideBinaryTestCases")
223+
void testMod(final BigInteger a, final BigInteger b) {
224+
if (b.equals(BigInteger.ZERO)) {
225+
return; // Skip division by zero
226+
}
227+
228+
BigInteger expected = wrap256(a.mod(b));
229+
230+
UInt256 uint256a = toUInt256(a);
231+
UInt256 uint256b = toUInt256(b);
232+
UInt256 result = uint256a.mod(uint256b);
233+
234+
assertThat(toBigInteger(result)).isEqualTo(expected);
235+
}
236+
237+
@ParameterizedTest
238+
@MethodSource("provideTernaryTestCases")
239+
void testAddMod(final BigInteger a, final BigInteger b, final BigInteger m) {
240+
if (m.equals(BigInteger.ZERO)) {
241+
return; // Skip division by zero
242+
}
243+
244+
BigInteger expected = a.add(b).mod(m);
245+
expected = wrap256(expected);
246+
247+
UInt256 uint256a = toUInt256(a);
248+
UInt256 uint256b = toUInt256(b);
249+
UInt256 uint256m = toUInt256(m);
250+
251+
UInt256 result = uint256a.addMod(uint256b, uint256m);
252+
assertThat(toBigInteger(result)).isEqualTo(expected);
253+
}
254+
255+
@ParameterizedTest
256+
@MethodSource("provideTernaryTestCases")
257+
void testMulMod(final BigInteger a, final BigInteger b, final BigInteger m) {
258+
if (m.equals(BigInteger.ZERO)) {
259+
return; // Skip division by zero
260+
}
261+
262+
BigInteger expected = a.multiply(b).mod(m);
263+
expected = wrap256(expected);
264+
265+
UInt256 uint256a = toUInt256(a);
266+
UInt256 uint256b = toUInt256(b);
267+
UInt256 uint256m = toUInt256(m);
268+
269+
UInt256 result = uint256a.mulMod(uint256b, uint256m);
270+
assertThat(toBigInteger(result)).isEqualTo(expected);
271+
}
272+
273+
// endregion
274+
275+
// region Bitwise Operations Tests
276+
277+
@ParameterizedTest
278+
@MethodSource("provideShiftTestCases")
279+
void testLeftShift(final BigInteger value, final int shift) {
280+
BigInteger expected = wrap256(value.shiftLeft(shift));
281+
282+
UInt256 uint256 = toUInt256(value);
283+
UInt256 result = uint256.shiftLeft(shift);
284+
285+
assertThat(toBigInteger(result)).isEqualTo(expected);
286+
}
287+
288+
@ParameterizedTest
289+
@MethodSource("provideShiftTestCases")
290+
void testRightShift(final BigInteger value, final int shift) {
291+
BigInteger expected = wrap256(value.shiftRight(shift));
292+
293+
UInt256 uint256 = toUInt256(value);
294+
UInt256 result = uint256.shiftRight(shift);
295+
296+
assertThat(toBigInteger(result)).isEqualTo(expected);
297+
}
298+
299+
// endregion
300+
301+
// region Comparison Tests
302+
303+
@ParameterizedTest
304+
@MethodSource("provideBinaryTestCases")
305+
void testComparisons(final BigInteger a, final BigInteger b) {
306+
UInt256 uint256a = toUInt256(a);
307+
UInt256 uint256b = toUInt256(b);
308+
309+
assertThat(UInt256.compare(uint256a, uint256b) < 0).isEqualTo(a.compareTo(b) < 0);
310+
assertThat(UInt256.compare(uint256a, uint256b) <= 0).isEqualTo(a.compareTo(b) <= 0);
311+
assertThat(UInt256.compare(uint256a, uint256b) > 0).isEqualTo(a.compareTo(b) > 0);
312+
assertThat(UInt256.compare(uint256a, uint256b) >= 0).isEqualTo(a.compareTo(b) >= 0);
313+
assertThat(uint256a.equals(uint256b)).isEqualTo(a.equals(b));
314+
}
315+
316+
// endregion
317+
318+
// region Conversion Tests
319+
320+
@ParameterizedTest
321+
@MethodSource("provideUnaryTestCases")
322+
void testToBigIntegerAndBack(final BigInteger value) {
323+
UInt256 uint256 = toUInt256(value);
324+
BigInteger result = toBigInteger(uint256);
325+
assertThat(result).isEqualTo(value);
326+
}
327+
328+
// endregion
329+
}

0 commit comments

Comments
 (0)