Feature/required besu native (#8418)

* implement NativeRequirements for named networks

Signed-off-by: garyschulte <garyschulte@gmail.com>
This commit is contained in:
garyschulte
2025-03-17 16:54:04 -07:00
committed by GitHub
parent 54aaf3d2fc
commit c924e7686f
8 changed files with 243 additions and 58 deletions

View File

@@ -3,8 +3,13 @@
## Unreleased
### Breaking Changes
NOTE: This release breaks native Windows compatibility for mainnet ethereum configurations. As the prague(pectra) hardfork require
BLS12-381 precompiles and besu does not currently have a pure java implementation of bls12-381, only platforms which
have support in besu-native can run mainnet ethereum configurations. Windows support via WSL should still continue to work.
- k8s (KUBERNETES) Nat method is removed. Use docker or none instead. [#8289](https://github.com/hyperledger/besu/pull/8289)
- Change `Invalid block, unable to parse RLP` RPC error message to `Invalid block param (block not found)` [#8328](https://github.com/hyperledger/besu/pull/8328)
- Mainnet ethereum now REQUIRES native crypto libraries, so only linux and macos(darwin) are supported mainnet configurations [#8418](https://github.com/hyperledger/besu/pull/8418)
### Upcoming Breaking Changes
- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge`

View File

@@ -35,6 +35,7 @@ import org.hyperledger.besu.chainimport.Era1BlockImporter;
import org.hyperledger.besu.chainimport.JsonBlockImporter;
import org.hyperledger.besu.chainimport.RlpBlockImporter;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates;
import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
@@ -974,7 +975,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
// explicitly enabled, perform compatibility check
VersionMetadata.versionCompatibilityChecks(versionCompatibilityProtection, dataDir());
configureNativeLibs();
configureNativeLibs(Optional.ofNullable(network));
besuController = buildController();
besuPluginContext.beforeExternalServices();
@@ -994,7 +995,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
runner.awaitStop();
} catch (final Exception e) {
logger.error("Failed to start Besu", e);
logger.error("Failed to start Besu: {}", e.getMessage());
logger.debug("Startup failure cause", e);
throw new ParameterException(this.commandLine, e.getMessage(), e);
}
}
@@ -1388,7 +1390,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return Optional.ofNullable(colorEnabled);
}
private void configureNativeLibs() {
@VisibleForTesting
void configureNativeLibs(final Optional<NetworkName> configuredNetwork) {
if (unstableNativeLibraryOptions.getNativeAltbn128()
&& AbstractAltBnPrecompiledContract.maybeEnableNative()) {
logger.info("Using the native implementation of alt bn128");
@@ -1435,6 +1438,37 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
this.commandLine,
"--kzg-trusted-setup can only be specified on networks with data blobs enabled");
}
// assert required native libraries have been loaded
if (genesisFile == null && configuredNetwork.isPresent()) {
checkRequiredNativeLibraries(configuredNetwork.get());
}
}
@VisibleForTesting
void checkRequiredNativeLibraries(final NetworkName configuredNetwork) {
if (configuredNetwork == null) {
return;
}
// assert native library requirements for named networks:
List<NativeRequirementResult> failedNativeReqs =
configuredNetwork.getNativeRequirements().stream().filter(r -> !r.present()).toList();
if (!failedNativeReqs.isEmpty()) {
String failures =
failedNativeReqs.stream()
.map(r -> r.libname() + " " + r.errorMessage())
.collect(Collectors.joining("\n\t"));
throw new UnsupportedOperationException(
String.format(
"Failed to load required native libraries for network %s. "
+ "Verify whether your platform %s and arch %s are supported by besu. "
+ "Failures loading: \n%s",
configuredNetwork.name(),
System.getProperty("os.name"),
System.getProperty("os.arch"),
failures));
}
}
private void validateOptions() {

View File

@@ -0,0 +1,69 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.config;
import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.evm.precompile.AltBN128PairingPrecompiledContract;
import org.hyperledger.besu.evm.precompile.BLS12PairingPrecompiledContract;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
/** Encapsulates the native library requirements of given networks. */
public interface NativeRequirement {
/**
* Record type to encapsulate the result of native library loading
*
* @param present boolean indicating library loading present or failure.
* @param libname string indicating the required library name.
* @param errorMessage Optional error message suitable to log.
*/
record NativeRequirementResult(Boolean present, String libname, Optional<String> errorMessage) {}
/** Ethereum mainnet-like performance requirements: */
Supplier<List<NativeRequirementResult>> MAINNET =
() -> {
List<NativeRequirementResult> requirements = new ArrayList<>();
var secp256k1 = new SECP256K1();
requirements.add(
new NativeRequirementResult(
secp256k1.maybeEnableNative(),
"secp256k1",
secp256k1.maybeEnableNative()
? Optional.empty()
: Optional.of("secp256k1: Native secp256k1 failed to load")));
requirements.add(
new NativeRequirementResult(
AltBN128PairingPrecompiledContract.isNative(),
"alt_bn128",
AltBN128PairingPrecompiledContract.isNative()
? Optional.empty()
: Optional.of("alt_bn128: EC native library failed to load")));
requirements.add(
new NativeRequirementResult(
BLS12PairingPrecompiledContract.isAvailable(),
"bls12-381",
BLS12PairingPrecompiledContract.isAvailable()
? Optional.empty()
: Optional.of("bls12-381: EC native library failed to load")));
return requirements;
};
}

View File

@@ -14,31 +14,34 @@
*/
package org.hyperledger.besu.cli.config;
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
/** The enum Network name. */
public enum NetworkName {
/** Mainnet network name. */
MAINNET("/mainnet.json", BigInteger.valueOf(1)),
MAINNET("/mainnet.json", BigInteger.valueOf(1), true, NativeRequirement.MAINNET),
/** Sepolia network name. */
SEPOLIA("/sepolia.json", BigInteger.valueOf(11155111)),
SEPOLIA("/sepolia.json", BigInteger.valueOf(11155111), true, NativeRequirement.MAINNET),
/** Holešky network name. */
HOLESKY("/holesky.json", BigInteger.valueOf(17000)),
HOLESKY("/holesky.json", BigInteger.valueOf(17000), true, NativeRequirement.MAINNET),
/** Hoodi network name. */
HOODI("/hoodi.json", BigInteger.valueOf(560048)),
/** LUKSO mainnet network name. */
LUKSO("/lukso.json", BigInteger.valueOf(42)),
HOODI("/hoodi.json", BigInteger.valueOf(560048), true, NativeRequirement.MAINNET),
/**
* EPHEMERY network name. The actual networkId used is calculated based on this default value and
* the current time. https://ephemery.dev/
*/
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135)),
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135), true, NativeRequirement.MAINNET),
/** LUKSO mainnet network name. */
LUKSO("/lukso.json", BigInteger.valueOf(42)),
/** Dev network name. */
DEV("/dev.json", BigInteger.valueOf(2018), false),
/** Future EIPs network name. */
@@ -54,17 +57,27 @@ public enum NetworkName {
private final BigInteger networkId;
private final boolean canSnapSync;
private final String deprecationDate;
private final Supplier<List<NativeRequirementResult>> nativeRequirements;
NetworkName(final String genesisFile, final BigInteger networkId) {
this(genesisFile, networkId, true);
}
NetworkName(final String genesisFile, final BigInteger networkId, final boolean canSnapSync) {
this(genesisFile, networkId, canSnapSync, Collections::emptyList);
}
NetworkName(
final String genesisFile,
final BigInteger networkId,
final boolean canSnapSync,
final Supplier<List<NativeRequirementResult>> nativeRequirements) {
this.genesisFile = genesisFile;
this.networkId = networkId;
this.canSnapSync = canSnapSync;
// no deprecations planned
this.deprecationDate = null;
this.nativeRequirements = nativeRequirements;
}
/**
@@ -120,4 +133,13 @@ public enum NetworkName {
public Optional<String> getDeprecationDate() {
return Optional.ofNullable(deprecationDate);
}
/**
* Gets native requirements for this network.
*
* @return result of native library requirements defined for this network, as a list.
*/
public List<NativeRequirementResult> getNativeRequirements() {
return this.nativeRequirements.get();
}
}

View File

@@ -16,6 +16,8 @@ package org.hyperledger.besu.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.hyperledger.besu.cli.config.NetworkName.CLASSIC;
import static org.hyperledger.besu.cli.config.NetworkName.DEV;
import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;
@@ -42,11 +44,15 @@ import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.BesuInfo;
import org.hyperledger.besu.cli.config.EthNetworkConfig;
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
import org.hyperledger.besu.cli.config.NetworkName;
import org.hyperledger.besu.config.GenesisConfig;
import org.hyperledger.besu.config.MergeConfiguration;
@@ -2564,6 +2570,46 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void assertUnnamedNetworkNativeRequirements_Met() throws IOException {
final Path genesisFile =
createFakeGenesisFile(new JsonObject().put("config", new JsonObject()));
parseCommand("--genesis-file", genesisFile.toString());
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
}
@Test
public void assertNativeRequirements_UnMet() throws IOException {
BesuCommand mockCmd = parseCommand("--network=mainnet");
NetworkName spyMainnet = spy(NetworkName.MAINNET);
when(spyMainnet.getNativeRequirements())
.thenReturn(
List.of(new NativeRequirementResult(false, "MOCKLIB", Optional.of("Mock error"))));
assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> mockCmd.checkRequiredNativeLibraries(spyMainnet))
.withMessageContaining("MOCKLIB")
.withMessageContaining("Mock error")
.withMessageContaining(System.getProperty("os.arch"))
.withMessageContaining(System.getProperty("os.name"));
}
@Test
public void assertNativeRequirements_UnMetForUnnamedNetwork() throws IOException {
final Path fakeGenesisFile = createFakeGenesisFile(GENESIS_VALID_JSON);
BesuCommand mockCmd = parseCommand("--genesis-file=" + fakeGenesisFile.toString());
NetworkName spyMainnet = spy(NetworkName.MAINNET);
// assert no error output
assertThat(commandOutput.toString(UTF_8)).isEmpty();
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
// assert no exception
assertThatNoException().isThrownBy(() -> mockCmd.configureNativeLibs(Optional.of(spyMainnet)));
// assert we didn't check for native requirements for a custom-genesis
verify(spyMainnet, times(0)).getNativeRequirements();
}
@Test
public void bonsaiFlatDbShouldBeEnabledByDefault() {
final TestBesuCommand besuCommand = parseCommand();

View File

@@ -44,7 +44,7 @@ public class SECP256R1 extends AbstractSECP256 {
public SECP256R1() {
super(CURVE_NAME, SecP256R1Curve.q);
try {
useNative = BesuNativeEC.INSTANCE != null;
useNative = BesuNativeEC.ENABLED;
} catch (UnsatisfiedLinkError ule) {
LOG.info("secp256r1 native precompile not available: {}", ule.getMessage());
useNative = false;
@@ -69,7 +69,7 @@ public class SECP256R1 extends AbstractSECP256 {
@Override
public boolean maybeEnableNative() {
try {
useNative = BesuNativeEC.INSTANCE != null;
useNative = BesuNativeEC.ENABLED;
} catch (UnsatisfiedLinkError | NoClassDefFoundError e) {
LOG.info("Native secp256r1 not available - {}", e.getMessage());
useNative = false;

View File

@@ -4387,12 +4387,12 @@
<sha256 value="e08aed198642584fe427117f1183f5304071accc91cc91631ff6ada2478d2a86" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="arithmetic" version="1.1.2">
<artifact name="arithmetic-1.1.2.jar">
<sha256 value="8701d14f8eda5594c548505d0d4285e4f36fde3402ad33b094754f16b8b58c99" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="arithmetic" version="1.3.0">
<artifact name="arithmetic-1.3.0.jar">
<sha256 value="e6e10ac419e3f06c0178bcc3ec8a990d952836beccfe7f312ffffbae5c66b92f" origin="Generated by Gradle"/>
</artifact>
<artifact name="arithmetic-1.1.2.module">
<sha256 value="ef3174381e5fd7bef147031b93bd673850fad8b974f1f07d7b5d5148e51e09fe" origin="Generated by Gradle"/>
<artifact name="arithmetic-1.3.0.module">
<sha256 value="671df2b96e1b3897744758c0f83ff0ba322c1b3961601497eb4d2de9f31db990" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="besu-errorprone-checks" version="1.0.0">
@@ -4403,44 +4403,52 @@
<sha256 value="c273525c9f23a0bd5b9cf6830b4bebd9d81e355b7f2ed3a22f23f76c2a2313d5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="blake2bf" version="1.1.2">
<artifact name="blake2bf-1.1.2.jar">
<sha256 value="b1a5728396fbb91c6941d1c0eba0b4b37fbc14e2e0f38b635ae11c1c287b5008" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="besu-native-common" version="1.3.0">
<artifact name="besu-native-common-1.3.0.jar">
<sha256 value="17accb3a91a70ea2437f9ce5d8ebb26db796856524f6aa904e408474c7d00439" origin="Generated by Gradle"/>
</artifact>
<artifact name="blake2bf-1.1.2.module">
<sha256 value="ee399441ddb250fa8ac9c1135ae7b1f331e9d59ffdba838fdbcab59f3cbbdb96" origin="Generated by Gradle"/>
<artifact name="besu-native-common-1.3.0.module">
<sha256 value="51cb1a7c6002b4db973b51cc16c90adcc492c42f6484e937ddecd5580381aebe" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="gnark" version="1.1.2">
<artifact name="gnark-1.1.2.jar">
<sha256 value="4ab855fb11acb48849dd6b2b4ab0f31b180a5856b19cbca5b29675731c8cbee5" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="blake2bf" version="1.3.0">
<artifact name="blake2bf-1.3.0.jar">
<sha256 value="ffd8915fd19f77600c80db4df2c80a0c81659278de925f8cdd75405c4e0f35b7" origin="Generated by Gradle"/>
</artifact>
<artifact name="gnark-1.1.2.module">
<sha256 value="d0aef11ff927f6382dd307158a6da6ac205d5684d84aab8988b35caea313c098" origin="Generated by Gradle"/>
<artifact name="blake2bf-1.3.0.module">
<sha256 value="e4f470d7d3518eb690667a714d698f4c7d0aa5ae298dd5d2c2c12a5f3d18be38" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="ipa-multipoint" version="1.1.2">
<artifact name="ipa-multipoint-1.1.2.jar">
<sha256 value="3d5b3029b81205b70cb5a192618ad0decf4d5b87e71b90f99824819bba47f2d0" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="gnark" version="1.3.0">
<artifact name="gnark-1.3.0.jar">
<sha256 value="4272c441eb078541ee665a8d0374a2901f8226834075dde88be010bda703ee79" origin="Generated by Gradle"/>
</artifact>
<artifact name="ipa-multipoint-1.1.2.module">
<sha256 value="6949e4d8c89f9e5321d03c87c566d52b6140cefed45387bff3b0f6e7bf68251c" origin="Generated by Gradle"/>
<artifact name="gnark-1.3.0.module">
<sha256 value="e4c04f0699b5079e4b1644643fb664359961a3f1e07180750dfc4bc0bb9d7fd1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="secp256k1" version="1.1.2">
<artifact name="secp256k1-1.1.2.jar">
<sha256 value="9af8a3a93ce674dbaa56e1be877125a321b80f4626fbcf60a7ba26152caf6525" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="ipa-multipoint" version="1.3.0">
<artifact name="ipa-multipoint-1.3.0.jar">
<sha256 value="4c995b6fd603f766f129b9fde4a0b1514cfc746c0571f6f05d64c3caa136ce67" origin="Generated by Gradle"/>
</artifact>
<artifact name="secp256k1-1.1.2.module">
<sha256 value="446de2dcf4262da9443f9d904beaaf98b7b4dd9d31ff08cf7b38632ab6721692" origin="Generated by Gradle"/>
<artifact name="ipa-multipoint-1.3.0.module">
<sha256 value="524cfa88b4dd62043d75b181d053b03952ac9592bb287e0e10c1ff5b4069124d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="secp256r1" version="1.1.2">
<artifact name="secp256r1-1.1.2.jar">
<sha256 value="9876147c58d1f55b7836006e74f4d57ec7f79b4023b85d299e084012dee0c2ad" origin="Generated by Gradle"/>
<component group="org.hyperledger.besu" name="secp256k1" version="1.3.0">
<artifact name="secp256k1-1.3.0.jar">
<sha256 value="65b8d6594fc5137c95fa82f9400eadac843dce5bd62c222e73e07501a5a2aa96" origin="Generated by Gradle"/>
</artifact>
<artifact name="secp256r1-1.1.2.module">
<sha256 value="575d68e453134ed46c823e7aa96c877714898dc4fbc856d1b99a88c7ed6a1f75" origin="Generated by Gradle"/>
<artifact name="secp256k1-1.3.0.module">
<sha256 value="7fb6508d26bc7092505ffa5d4e3dea9b6aed73e660375de49899a057e7c9b054" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.hyperledger.besu" name="secp256r1" version="1.3.0">
<artifact name="secp256r1-1.3.0.jar">
<sha256 value="12e36e6f72c5145d8501a2a3cae2bef05d1f1c11d9d5c1fb4a907e4068022304" origin="Generated by Gradle"/>
</artifact>
<artifact name="secp256r1-1.3.0.module">
<sha256 value="99496cb18066d3d5493d38cd332cb5cdbca47e358f5e6c11b37ebb33378062a5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.immutables" name="bom" version="2.10.1">
@@ -4664,6 +4672,14 @@
<sha256 value="f2e2a6573b5b5265c412d5c581fbf8ea00c62b10b9c92d5674c9e97f2f3335b3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.0.21">
<artifact name="kotlin-stdlib-2.0.21.jar">
<sha256 value="f31cc53f105a7e48c093683bbd5437561d1233920513774b470805641bedbc09" origin="Generated by Gradle"/>
</artifact>
<artifact name="kotlin-stdlib-2.0.21.module">
<sha256 value="81fd6d181012487ee3246eff4e2bacb64b58c46e5b5aa72971a4ddf1bd1541ed" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.1.0">
<artifact name="kotlin-stdlib-2.1.0.jar">
<sha256 value="d6f91b7b0f306cca299fec74fb7c34e4874d6f5ec5b925a0b4de21901e119c3f" origin="Generated by Gradle"/>
@@ -4675,14 +4691,6 @@
<sha256 value="134e48c17796c1c1027ecbe6c9f1c75c792f535987ab89e1e1dda43f8c366f5e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.0.21">
<artifact name="kotlin-stdlib-2.0.21.jar">
<sha256 value="f31cc53f105a7e48c093683bbd5437561d1233920513774b470805641bedbc09" origin="Generated by Gradle"/>
</artifact>
<artifact name="kotlin-stdlib-2.0.21.module">
<sha256 value="81fd6d181012487ee3246eff4e2bacb64b58c46e5b5aa72971a4ddf1bd1541ed" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.8.21">
<artifact name="kotlin-stdlib-common-1.8.21.jar">
<sha256 value="6a44c9ecc9d7754d9e943fb1e3588c74d4a3f1785be51074f49d6c5723682a73" origin="Generated by Gradle"/>

View File

@@ -139,12 +139,13 @@ dependencies {
api 'org.hibernate.validator:hibernate-validator:8.0.2.Final'
api 'org.hyperledger.besu:arithmetic:1.1.2'
api 'org.hyperledger.besu:blake2bf:1.1.2'
api 'org.hyperledger.besu:gnark:1.1.2'
api 'org.hyperledger.besu:ipa-multipoint:1.1.2'
api 'org.hyperledger.besu:secp256k1:1.1.2'
api 'org.hyperledger.besu:secp256r1:1.1.2'
api 'org.hyperledger.besu:besu-native-common:1.3.0'
api 'org.hyperledger.besu:arithmetic:1.3.0'
api 'org.hyperledger.besu:blake2bf:1.3.0'
api 'org.hyperledger.besu:gnark:1.3.0'
api 'org.hyperledger.besu:ipa-multipoint:1.3.0'
api 'org.hyperledger.besu:secp256k1:1.3.0'
api 'org.hyperledger.besu:secp256r1:1.3.0'
api 'org.hyperledger.besu:besu-errorprone-checks:1.0.0'