Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speedup modexp #4780

Merged
merged 19 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Changelog
## 22.10.3

### Additions and Improvements
- Implement Eth/68 sub-protocol [#4715](https://github.com/hyperledger/besu/issues/4715)

### Breaking Changes
- Added `--rpc-max-logs-range` CLI option to allow limiting the number of blocks queried by `eth_getLogs` RPC API. Default value: 1000 [#4597](https://github.com/hyperledger/besu/pull/4597)
- The `graalvm` docker variant no longer meets the performance requirements for Ethereum Mainnet. The `openjdk-11` and `openjdk-latest` variants are recommended in its place.

### Additions and Improvements
- Implement Eth/68 sub-protocol [#4715](https://github.com/hyperledger/besu/issues/4715)
- Increase the speed of modexp gas execution and execution. [#4780](https://github.com/hyperledger/besu/pull/4780)

### Bug Fixes

Expand All @@ -18,8 +20,8 @@ This is a hotfix release to resolve a race condition that results in segfaults,
- bugfix for async operations on Snashot worldstates [#4767](https://github.com/hyperledger/besu/pull/4767)

### Download Links
https://hyperledger.jfrog.io/hyperledger/besu-binaries/besu/22.10.2/besu-22.10.2.tar.gz / sha256: TBA
https://hyperledger.jfrog.io/hyperledger/besu-binaries/besu/22.10.2/besu-22.10.2.zip / sha256: TBA
https://hyperledger.jfrog.io/hyperledger/besu-binaries/besu/22.10.2/besu-22.10.2.tar.gz / sha256: cdb36141e3cba6379d35016e0a2de2edba579d4786124b5f7257b1e4a68867a2
https://hyperledger.jfrog.io/hyperledger/besu-binaries/besu/22.10.2/besu-22.10.2.zip / sha256: 4c9208f684762670cb4f2c6ebfb6930e05e339a7c3c586fe8caa9f26462830aa


## 22.10.1
Expand Down
9 changes: 9 additions & 0 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract;
import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.MetricCategoryRegistryImpl;
import org.hyperledger.besu.metrics.MetricsProtocol;
Expand Down Expand Up @@ -1740,6 +1741,14 @@ private void configureNativeLibs() {
logger.info("Using the Java implementation of alt bn128");
}

if (unstableNativeLibraryOptions.getNativeModExp()
&& BigIntegerModularExponentiationPrecompiledContract.isNative()) {
logger.info("Using the native implementation of modexp");
} else {
BigIntegerModularExponentiationPrecompiledContract.disableNative();
logger.info("Using the Java implementation of modexp");
}

if (unstableNativeLibraryOptions.getNativeSecp()
&& SignatureAlgorithmFactory.getInstance().isNative()) {
logger.info("Using the native implementation of the signature algorithm");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ public class NativeLibraryOptions {
arity = "1")
private final Boolean nativeBlake2bf = Boolean.TRUE;

@CommandLine.Option(
hidden = true,
names = {"--Xmodexp-native-enabled"},
description =
"Per default a native library is used for modexp. "
+ "If the Java implementation should be used instead, this option must be set to false",
arity = "1")
private final Boolean nativeModExp = Boolean.TRUE;

public static NativeLibraryOptions create() {
return new NativeLibraryOptions();
}
Expand All @@ -60,4 +69,8 @@ public Boolean getNativeAltbn128() {
public Boolean getNativeBlake2bf() {
return nativeBlake2bf;
}

public Boolean getNativeModExp() {
return nativeModExp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,16 @@ public static class Blake2bfDigest implements Digest {
private long rounds; // unsigned integer represented as long

private final long[] v;
private static boolean useNative = LibBlake2bf.ENABLED;
private static boolean useNative;

static {
try {
useNative = LibBlake2bf.ENABLED;
} catch (UnsatisfiedLinkError ule) {
LOG.info("blake2bf native precompile not available: {}", ule.getMessage());
useNative = false;
}
}

Blake2bfDigest() {
if (!useNative) {
Expand Down
13 changes: 12 additions & 1 deletion crypto/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.hyperledger.besu.nativelib.secp256r1.LibSECP256R1;
import org.hyperledger.besu.nativelib.secp256r1.Signature;
import org.hyperledger.besu.nativelib.secp256r1.besuNativeEC.BesuNativeEC;

import java.math.BigInteger;
import java.util.Optional;
Expand All @@ -25,15 +26,25 @@
import org.bouncycastle.crypto.signers.DSAKCalculator;
import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SECP256R1 extends AbstractSECP256 {

private static final Logger LOG = LoggerFactory.getLogger(SECP256R1.class);

public static final String CURVE_NAME = "secp256r1";
private boolean useNative = true;
private boolean useNative;
private final LibSECP256R1 libSECP256R1 = new LibSECP256R1();

public SECP256R1() {
super(CURVE_NAME, SecP256R1Curve.q);
try {
useNative = BesuNativeEC.INSTANCE != null;
} catch (UnsatisfiedLinkError ule) {
LOG.info("secp256r1 native precompile not available: {}", ule.getMessage());
useNative = false;
}
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion docker/graalvm/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

FROM ghcr.io/graalvm/graalvm-ce:ol7-java11
FROM ghcr.io/graalvm/graalvm-ce:ol8-java11
ARG VERSION="dev"

RUN adduser --home /opt/besu besu && \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public StateTestVersionedTransaction(
@JsonDeserialize(using = StateTestAccessListDeserializer.class) @JsonProperty("accessLists")
final List<List<AccessListEntry>> maybeAccessLists) {

this.nonce = Long.decode(nonce);
this.nonce = Bytes.fromHexStringLenient(nonce).toLong();
this.gasPrice = Optional.ofNullable(gasPrice).map(Wei::fromHexString).orElse(null);
this.maxFeePerGas = Optional.ofNullable(maxFeePerGas).map(Wei::fromHexString).orElse(null);
this.maxPriorityFeePerGas =
Expand Down
1 change: 1 addition & 0 deletions evm/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies {
compileOnly 'com.fasterxml.jackson.core:jackson-databind'

implementation 'org.apache.tuweni:tuweni-bytes'
implementation 'org.hyperledger.besu:arithmetic'
implementation 'org.hyperledger.besu:bls12-381'
implementation 'net.java.dev.jna:jna'
implementation 'com.github.ben-manes.caffeine:caffeine'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@

import static org.hyperledger.besu.datatypes.Address.BLAKE2B_F_COMPRESSION;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import static org.hyperledger.besu.evm.internal.Words.clampedMultiply;
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;

import java.math.BigInteger;
Expand Down Expand Up @@ -218,38 +221,29 @@ public long calculateStorageRefundAmount(

@Override
public long modExpGasCost(final Bytes input) {
final BigInteger baseLength =
BigIntegerModularExponentiationPrecompiledContract.baseLength(input);
final BigInteger exponentLength =
final long baseLength = BigIntegerModularExponentiationPrecompiledContract.baseLength(input);
final long exponentLength =
BigIntegerModularExponentiationPrecompiledContract.exponentLength(input);
final BigInteger modulusLength =
final long modulusLength =
BigIntegerModularExponentiationPrecompiledContract.modulusLength(input);
final BigInteger exponentOffset =
BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET.add(baseLength);
final int firstExponentBytesCap =
exponentLength.min(ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES).intValue();
final long exponentOffset =
clampedAdd(BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET, baseLength);
final long firstExponentBytesCap =
Math.min(exponentLength, ByzantiumGasCalculator.MAX_FIRST_EXPONENT_BYTES);
final BigInteger firstExpBytes =
BigIntegerModularExponentiationPrecompiledContract.extractParameter(
input, exponentOffset, firstExponentBytesCap);
final BigInteger adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
final BigInteger multiplicationComplexity =
modulusLength
.max(baseLength)
.add(BigInteger.valueOf(7))
.divide(BigInteger.valueOf(8))
.pow(2);

final BigInteger gasRequirement =
multiplicationComplexity
.multiply(adjustedExponentLength.max(BigInteger.ONE))
.divide(BigInteger.valueOf(3));

// Gas price is so large it will not fit in a Gas type, so a
// very very very unlikely high gas price is used instead.
if (gasRequirement.bitLength() > ByzantiumGasCalculator.MAX_GAS_BITS) {
return Long.MAX_VALUE;
} else {
return Math.max(gasRequirement.longValueExact(), 200L);
input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap));
final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
long multiplicationComplexity = (Math.max(modulusLength, baseLength) + 7L) / 8L;
multiplicationComplexity =
Words.clampedMultiply(multiplicationComplexity, multiplicationComplexity);

long gasRequirement =
clampedMultiply(multiplicationComplexity, Math.max(adjustedExponentLength, 1L));
if (gasRequirement != Long.MAX_VALUE) {
gasRequirement /= 3;
}

return Math.max(gasRequirement, 200L);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,65 +15,55 @@
*/
package org.hyperledger.besu.evm.gascalculator;

import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import static org.hyperledger.besu.evm.internal.Words.clampedMultiply;
import static org.hyperledger.besu.evm.internal.Words.clampedToInt;

import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;

import java.math.BigInteger;

import org.apache.tuweni.bytes.Bytes;

public class ByzantiumGasCalculator extends SpuriousDragonGasCalculator {
private static final BigInteger GQUADDIVISOR = BigInteger.valueOf(20);
private static final BigInteger WORD_SIZE = BigInteger.valueOf(32);
private static final BigInteger BITS_IN_BYTE = BigInteger.valueOf(8);
private static final int GQUADDIVISOR = 20;
private static final int WORD_SIZE = 32;
private static final int BITS_IN_BYTE = 8;

public static final BigInteger MAX_FIRST_EXPONENT_BYTES = BigInteger.valueOf(32);
public static final int MAX_GAS_BITS = 63;
public static final int MAX_FIRST_EXPONENT_BYTES = 32;

@Override
public long modExpGasCost(final Bytes input) {
final BigInteger baseLength =
BigIntegerModularExponentiationPrecompiledContract.baseLength(input);
final BigInteger exponentLength =
final long baseLength = BigIntegerModularExponentiationPrecompiledContract.baseLength(input);
final long exponentLength =
BigIntegerModularExponentiationPrecompiledContract.exponentLength(input);
final BigInteger modulusLength =
final long modulusLength =
BigIntegerModularExponentiationPrecompiledContract.modulusLength(input);
final BigInteger exponentOffset =
BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET.add(baseLength);
final int firstExponentBytesCap = exponentLength.min(MAX_FIRST_EXPONENT_BYTES).intValue();
final long exponentOffset =
clampedAdd(BigIntegerModularExponentiationPrecompiledContract.BASE_OFFSET, baseLength);
final long firstExponentBytesCap = Math.min(exponentLength, MAX_FIRST_EXPONENT_BYTES);
final BigInteger firstExpBytes =
BigIntegerModularExponentiationPrecompiledContract.extractParameter(
input, exponentOffset, firstExponentBytesCap);
final BigInteger adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
final BigInteger multiplicationComplexity =
input, clampedToInt(exponentOffset), clampedToInt(firstExponentBytesCap));
final long adjustedExponentLength = adjustedExponentLength(exponentLength, firstExpBytes);
final long multiplicationComplexity =
BigIntegerModularExponentiationPrecompiledContract.multiplicationComplexity(
baseLength.max(modulusLength));
final BigInteger gasRequirement =
multiplicationComplexity
.multiply(adjustedExponentLength.max(BigInteger.ONE))
.divide(GQUADDIVISOR);

// Gas price is so large it will not fit in a Gas type, so a
// very very very unlikely high gas price is used instead.
if (gasRequirement.bitLength() > MAX_GAS_BITS) {
return Long.MAX_VALUE;
} else {
return gasRequirement.longValueExact();
}
Math.max(baseLength, modulusLength));
long numerator = clampedMultiply(multiplicationComplexity, Math.max(adjustedExponentLength, 1));
return (numerator == Long.MAX_VALUE) ? Long.MAX_VALUE : numerator / GQUADDIVISOR;
}

public static BigInteger adjustedExponentLength(
final BigInteger exponentLength, final BigInteger firstExpBytes) {
final BigInteger bitLength = bitLength(firstExpBytes);
if (exponentLength.compareTo(WORD_SIZE) <= 0) {
public static long adjustedExponentLength(
final long exponentLength, final BigInteger firstExpBytes) {
final int bitLength = bitLength(firstExpBytes);
if (exponentLength < WORD_SIZE) {
return bitLength;
} else {
return BITS_IN_BYTE.multiply(exponentLength.subtract(WORD_SIZE)).add(bitLength);
return clampedAdd(clampedMultiply(BITS_IN_BYTE, (exponentLength - WORD_SIZE)), bitLength);
}
}

private static BigInteger bitLength(final BigInteger n) {
return n.compareTo(BigInteger.ZERO) == 0
? BigInteger.ZERO
: BigInteger.valueOf(n.bitLength() - 1L);
private static int bitLength(final BigInteger n) {
return n.compareTo(BigInteger.ZERO) == 0 ? 0 : (n.bitLength() - 1);
}
}
Loading