Skip to content

Commit 21a3e6a

Browse files
ADD: SMod operation with UInt256
Signed-off-by: Thomas Zamojski <thomas.zamojski@quadratic-labs.com>
1 parent 7a90b37 commit 21a3e6a

File tree

4 files changed

+317
-25
lines changed

4 files changed

+317
-25
lines changed

ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/SModOperationBenchmark.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,113 @@
1818
import org.hyperledger.besu.evm.operation.Operation;
1919
import org.hyperledger.besu.evm.operation.SModOperation;
2020

21+
import java.math.BigInteger;
22+
import java.util.concurrent.ThreadLocalRandom;
23+
24+
import org.apache.tuweni.bytes.Bytes;
25+
import org.openjdk.jmh.annotations.Level;
26+
import org.openjdk.jmh.annotations.Param;
27+
import org.openjdk.jmh.annotations.Setup;
28+
2129
public class SModOperationBenchmark extends BinaryOperationBenchmark {
30+
// Benches for a % b
31+
32+
// Define available scenarios
33+
public enum Case {
34+
SMOD_32_32(1, 1),
35+
SMOD_64_32(2, 1),
36+
SMOD_64_64(2, 2),
37+
SMOD_128_32(4, 1),
38+
SMOD_128_64(4, 2),
39+
SMOD_128_128(4, 4),
40+
SMOD_192_32(6, 1),
41+
SMOD_192_64(6, 2),
42+
SMOD_192_128(6, 4),
43+
SMOD_192_192(6, 6),
44+
SMOD_256_32(8, 1),
45+
SMOD_256_64(8, 2),
46+
SMOD_256_128(8, 4),
47+
SMOD_256_192(8, 6),
48+
SMOD_256_256(8, 8),
49+
LARGER_SMOD_64_128(2, 4),
50+
LARGER_SMOD_192_256(6, 8),
51+
ZERO_SMOD_128_0(4, 0),
52+
FULL_RANDOM(-1, -1);
53+
54+
final int divSize;
55+
final int modSize;
56+
57+
Case(final int divSize, final int modSize) {
58+
this.divSize = divSize;
59+
this.modSize = modSize;
60+
}
61+
}
62+
63+
@Param({
64+
"SMOD_32_32",
65+
"SMOD_64_32",
66+
"SMOD_64_64",
67+
"SMOD_128_32",
68+
"SMOD_128_64",
69+
"SMOD_128_128",
70+
"SMOD_192_32",
71+
"SMOD_192_64",
72+
"SMOD_192_128",
73+
"SMOD_192_192",
74+
"SMOD_256_32",
75+
"SMOD_256_64",
76+
"SMOD_256_128",
77+
"SMOD_256_192",
78+
"SMOD_256_256",
79+
"LARGER_SMOD_64_128",
80+
"LARGER_SMOD_192_256",
81+
"ZERO_SMOD_128_0",
82+
"FULL_RANDOM"
83+
})
84+
private String caseName;
85+
86+
@Setup(Level.Iteration)
87+
@Override
88+
public void setUp() {
89+
frame = BenchmarkHelper.createMessageCallFrame();
90+
91+
Case scenario = Case.valueOf(caseName);
92+
aPool = new Bytes[SAMPLE_SIZE];
93+
bPool = new Bytes[SAMPLE_SIZE];
94+
95+
final ThreadLocalRandom random = ThreadLocalRandom.current();
96+
int aSize;
97+
int bSize;
98+
99+
for (int i = 0; i < SAMPLE_SIZE; i++) {
100+
if (scenario.divSize < 0) aSize = random.nextInt(1, 33);
101+
else aSize = scenario.divSize * 4;
102+
if (scenario.modSize < 0) bSize = random.nextInt(1, 33);
103+
else bSize = scenario.modSize * 4;
104+
105+
final byte[] a = new byte[aSize];
106+
final byte[] b = new byte[bSize];
107+
random.nextBytes(a);
108+
random.nextBytes(b);
109+
110+
// Swap a and b if necessary
111+
if ((scenario.divSize != scenario.modSize)) {
112+
aPool[i] = Bytes.wrap(a);
113+
bPool[i] = Bytes.wrap(b);
114+
} else {
115+
BigInteger aInt = new BigInteger(a);
116+
BigInteger bInt = new BigInteger(b);
117+
if ((aInt.abs().compareTo(bInt.abs()) < 0)) {
118+
aPool[i] = Bytes.wrap(b);
119+
bPool[i] = Bytes.wrap(a);
120+
} else {
121+
aPool[i] = Bytes.wrap(a);
122+
bPool[i] = Bytes.wrap(b);
123+
}
124+
}
125+
}
126+
index = 0;
127+
}
22128

23129
@Override
24130
protected Operation.OperationResult invoke(final MessageFrame frame) {

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

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
*/
1515
package org.hyperledger.besu.evm;
1616

17+
import java.util.Arrays;
18+
1719
/**
1820
* 256-bits wide unsigned integer class.
1921
*
@@ -141,6 +143,45 @@ public static UInt256 fromBytesBE(final byte[] bytes) {
141143
return new UInt256(limbs, len);
142144
}
143145

146+
/**
147+
* Instantiates a new UInt256 from byte[].
148+
*
149+
* @param bytes raw bytes in BigEndian order.
150+
* @return Big-endian UInt256 represented by the bytes.
151+
*/
152+
public static UInt256 fromSignedBytesBE(final byte[] bytes) {
153+
int nBytes = bytes.length;
154+
if (nBytes == 0) return ZERO;
155+
int len = (nBytes + 3) / 4;
156+
int[] limbs = new int[len];
157+
// int[] limbs = new int[N_LIMBS];
158+
159+
int i;
160+
int base;
161+
// Up to most significant limb take 4 bytes.
162+
for (i = 0, base = bytes.length - 4; i < len - 1; ++i, base = base - 4) {
163+
limbs[i] =
164+
(bytes[base] << 24)
165+
| ((bytes[base + 1] & 0xFF) << 16)
166+
| ((bytes[base + 2] & 0xFF) << 8)
167+
| ((bytes[base + 3] & 0xFF));
168+
}
169+
// Last effective limb
170+
limbs[i] =
171+
switch (nBytes - i * 4) {
172+
case 1 -> ((bytes[0]));
173+
case 2 -> (((bytes[0]) << 8) | (bytes[1] & 0xFF));
174+
case 3 -> (((bytes[0]) << 16) | ((bytes[1] & 0xFF) << 8) | (bytes[2] & 0xFF));
175+
case 4 ->
176+
((bytes[0] << 24)
177+
| ((bytes[1] & 0xFF) << 16)
178+
| ((bytes[2] & 0xFF) << 8)
179+
| (bytes[3] & 0xFF));
180+
default -> throw new IllegalStateException("Unexpected value");
181+
};
182+
return new UInt256(limbs, len);
183+
}
184+
144185
/**
145186
* Instantiates a new UInt256 from an int.
146187
*
@@ -168,7 +209,6 @@ public static UInt256 fromLong(final long value) {
168209

169210
// region Conversions
170211
// --------------------------------------------------------------------------
171-
172212
/**
173213
* Convert to int.
174214
*
@@ -214,6 +254,43 @@ public byte[] toBytesBE() {
214254
return out;
215255
}
216256

257+
/**
258+
* Convert to BigEndian byte array.
259+
*
260+
* @return Big-endian ordered bytes for this UInt256 value.
261+
*/
262+
public byte[] toSignedBytesBE() {
263+
byte[] out = new byte[32];
264+
if (length > 0 && limbs[length - 1] < 0) Arrays.fill(out, (byte) 0xFF);
265+
for (int i = 0, offset = 28; i < length; i++, offset -= 4) {
266+
int v = limbs[i];
267+
out[offset] = (byte) (v >>> 24);
268+
out[offset + 1] = (byte) (v >>> 16);
269+
out[offset + 2] = (byte) (v >>> 8);
270+
out[offset + 3] = (byte) v;
271+
}
272+
return out;
273+
}
274+
275+
/**
276+
* Convert to BigEndian byte array.
277+
*
278+
* @param sign if sign is negative, pad with 1s, else pad with 0s.
279+
* @return Big-endian ordered bytes for this UInt256 value.
280+
*/
281+
public byte[] toSignedBytesBE(final int sign) {
282+
byte[] out = new byte[32];
283+
if (sign < 0) Arrays.fill(out, (byte) 0xFF);
284+
for (int i = 0, offset = 28; i < length; i++, offset -= 4) {
285+
int v = limbs[i];
286+
out[offset] = (byte) (v >>> 24);
287+
out[offset + 1] = (byte) (v >>> 16);
288+
out[offset + 2] = (byte) (v >>> 8);
289+
out[offset + 3] = (byte) v;
290+
}
291+
return out;
292+
}
293+
217294
@Override
218295
public String toString() {
219296
StringBuilder sb = new StringBuilder("0x");
@@ -231,6 +308,33 @@ public String toString() {
231308
return sb.toString();
232309
}
233310

311+
/**
312+
* Convert to hexstring according to sign.
313+
*
314+
* <p>If sign is negative, pad with 1s, else pad with 0s.
315+
* </p>
316+
*
317+
* @param sign padding with 0s or 1s whether sign is non-negative.
318+
* @return HexString
319+
*/
320+
public String toString(final int sign) {
321+
if (sign >= 0) return toString();
322+
StringBuilder sb = new StringBuilder("0x");
323+
byte[] out = new byte[length * 4];
324+
Arrays.fill(out, (byte) 0xFF);
325+
for (int i = 0, offset = 4 * (length - 1); i < length; i++, offset -= 4) {
326+
int v = limbs[i];
327+
out[offset] = (byte) (v >>> 24);
328+
out[offset + 1] = (byte) (v >>> 16);
329+
out[offset + 2] = (byte) (v >>> 8);
330+
out[offset + 3] = (byte) v;
331+
}
332+
for (byte b : out) {
333+
sb.append(String.format("%02x", b));
334+
}
335+
return sb.toString();
336+
}
337+
234338
// --------------------------------------------------------------------------
235339
// endregion
236340

@@ -330,6 +434,17 @@ public UInt256 shiftRight(final int shift) {
330434
// region Arithmetic Operations
331435
// --------------------------------------------------------------------------
332436

437+
/**
438+
* (Signed) absolute value
439+
*
440+
* @return The absolute value of this signed integer.
441+
*/
442+
public UInt256 abs() {
443+
int[] newLimbs = Arrays.copyOf(limbs, length);
444+
absInplace(newLimbs);
445+
return new UInt256(newLimbs, length);
446+
}
447+
333448
/**
334449
* Addition (modulo 2**256).
335450
*
@@ -356,7 +471,7 @@ public UInt256 mul(final UInt256 other) {
356471
}
357472

358473
/**
359-
* Reduce modulo modulus.
474+
* Unsigned modulo reduction.
360475
*
361476
* @param modulus The modulus of the reduction
362477
* @return The remainder modulo {@code modulus}.
@@ -368,6 +483,28 @@ public UInt256 mod(final UInt256 modulus) {
368483
return new UInt256(knuthRemainder(this.limbs, modulus.limbs));
369484
}
370485

486+
/**
487+
* Signed modulo reduction.
488+
*
489+
* @param modulus The modulus of the reduction
490+
* @return The remainder modulo {@code modulus}.
491+
*/
492+
public UInt256 signedMod(final UInt256 modulus) {
493+
if (this.isZero() || modulus.isZero()) return ZERO;
494+
int[] x = new int[this.length];
495+
int[] y = new int[modulus.length];
496+
absInto(x, this.limbs);
497+
absInto(y, modulus.limbs);
498+
int[] r = knuthRemainder(x, y);
499+
if (isNegative(this.limbs)) {
500+
int[] s = new int[N_LIMBS];
501+
System.arraycopy(r, 0, s, 0, r.length);
502+
negate(s);
503+
return new UInt256(s);
504+
}
505+
return new UInt256(r);
506+
}
507+
371508
/**
372509
* Modular addition.
373510
*
@@ -404,6 +541,27 @@ public UInt256 mulMod(final UInt256 other, final UInt256 modulus) {
404541

405542
// region Support (private) Algorithms
406543
// --------------------------------------------------------------------------
544+
private static boolean isNegative(final int[] x) {
545+
return x[x.length - 1] < 0;
546+
}
547+
548+
private static void negate(final int[] x) {
549+
int carry = 1;
550+
for (int i = 0; i < x.length; i++) {
551+
x[i] = ~x[i] + carry;
552+
carry = (x[i] == 0 && carry == 1) ? 1 : 0;
553+
}
554+
}
555+
556+
private static void absInplace(final int[] x) {
557+
if (isNegative(x)) negate(x);
558+
}
559+
560+
private static void absInto(final int[] dst, final int[] src) {
561+
System.arraycopy(src, 0, dst, 0, Math.min(dst.length, src.length));
562+
absInplace(dst);
563+
}
564+
407565
private static int numberOfLeadingZeros(final int[] x, final int limit) {
408566
int leadingIndex = limit - 1;
409567
while ((leadingIndex >= 0) && (x[leadingIndex] == 0)) leadingIndex--;
@@ -529,6 +687,7 @@ private static int[] knuthRemainder(final int[] dividend, final int[] modulus) {
529687
int n = modulus.length - limbShift;
530688
if (n == 0) return new int[0];
531689
if (n == 1) {
690+
if (dividend.length == 1) return (new int[] {dividend[0] % modulus[0]});
532691
long d = modulus[0] & MASK_L;
533692
long rem = 0;
534693
// Process from most significant limb downwards

0 commit comments

Comments
 (0)