Skip to content

Commit c924e76

Browse files
authored
Feature/required besu native (#8418)
* implement NativeRequirements for named networks Signed-off-by: garyschulte <[email protected]>
1 parent 54aaf3d commit c924e76

File tree

8 files changed

+243
-58
lines changed

8 files changed

+243
-58
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33
## Unreleased
44

55
### Breaking Changes
6+
NOTE: This release breaks native Windows compatibility for mainnet ethereum configurations. As the prague(pectra) hardfork require
7+
BLS12-381 precompiles and besu does not currently have a pure java implementation of bls12-381, only platforms which
8+
have support in besu-native can run mainnet ethereum configurations. Windows support via WSL should still continue to work.
9+
610
- k8s (KUBERNETES) Nat method is removed. Use docker or none instead. [#8289](https://github.com/hyperledger/besu/pull/8289)
711
- 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)
12+
- 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)
813

914
### Upcoming Breaking Changes
1015
- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge`

besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java

+37-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.hyperledger.besu.chainimport.JsonBlockImporter;
3636
import org.hyperledger.besu.chainimport.RlpBlockImporter;
3737
import org.hyperledger.besu.cli.config.EthNetworkConfig;
38+
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
3839
import org.hyperledger.besu.cli.config.NetworkName;
3940
import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates;
4041
import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
@@ -974,7 +975,7 @@ public void run() {
974975
// explicitly enabled, perform compatibility check
975976
VersionMetadata.versionCompatibilityChecks(versionCompatibilityProtection, dataDir());
976977

977-
configureNativeLibs();
978+
configureNativeLibs(Optional.ofNullable(network));
978979
besuController = buildController();
979980

980981
besuPluginContext.beforeExternalServices();
@@ -994,7 +995,8 @@ public void run() {
994995
runner.awaitStop();
995996

996997
} catch (final Exception e) {
997-
logger.error("Failed to start Besu", e);
998+
logger.error("Failed to start Besu: {}", e.getMessage());
999+
logger.debug("Startup failure cause", e);
9981000
throw new ParameterException(this.commandLine, e.getMessage(), e);
9991001
}
10001002
}
@@ -1388,7 +1390,8 @@ public static Optional<Boolean> getColorEnabled() {
13881390
return Optional.ofNullable(colorEnabled);
13891391
}
13901392

1391-
private void configureNativeLibs() {
1393+
@VisibleForTesting
1394+
void configureNativeLibs(final Optional<NetworkName> configuredNetwork) {
13921395
if (unstableNativeLibraryOptions.getNativeAltbn128()
13931396
&& AbstractAltBnPrecompiledContract.maybeEnableNative()) {
13941397
logger.info("Using the native implementation of alt bn128");
@@ -1435,6 +1438,37 @@ private void configureNativeLibs() {
14351438
this.commandLine,
14361439
"--kzg-trusted-setup can only be specified on networks with data blobs enabled");
14371440
}
1441+
// assert required native libraries have been loaded
1442+
if (genesisFile == null && configuredNetwork.isPresent()) {
1443+
checkRequiredNativeLibraries(configuredNetwork.get());
1444+
}
1445+
}
1446+
1447+
@VisibleForTesting
1448+
void checkRequiredNativeLibraries(final NetworkName configuredNetwork) {
1449+
if (configuredNetwork == null) {
1450+
return;
1451+
}
1452+
1453+
// assert native library requirements for named networks:
1454+
List<NativeRequirementResult> failedNativeReqs =
1455+
configuredNetwork.getNativeRequirements().stream().filter(r -> !r.present()).toList();
1456+
1457+
if (!failedNativeReqs.isEmpty()) {
1458+
String failures =
1459+
failedNativeReqs.stream()
1460+
.map(r -> r.libname() + " " + r.errorMessage())
1461+
.collect(Collectors.joining("\n\t"));
1462+
throw new UnsupportedOperationException(
1463+
String.format(
1464+
"Failed to load required native libraries for network %s. "
1465+
+ "Verify whether your platform %s and arch %s are supported by besu. "
1466+
+ "Failures loading: \n%s",
1467+
configuredNetwork.name(),
1468+
System.getProperty("os.name"),
1469+
System.getProperty("os.arch"),
1470+
failures));
1471+
}
14381472
}
14391473

14401474
private void validateOptions() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.cli.config;
16+
17+
import org.hyperledger.besu.crypto.SECP256K1;
18+
import org.hyperledger.besu.evm.precompile.AltBN128PairingPrecompiledContract;
19+
import org.hyperledger.besu.evm.precompile.BLS12PairingPrecompiledContract;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.Optional;
24+
import java.util.function.Supplier;
25+
26+
/** Encapsulates the native library requirements of given networks. */
27+
public interface NativeRequirement {
28+
29+
/**
30+
* Record type to encapsulate the result of native library loading
31+
*
32+
* @param present boolean indicating library loading present or failure.
33+
* @param libname string indicating the required library name.
34+
* @param errorMessage Optional error message suitable to log.
35+
*/
36+
record NativeRequirementResult(Boolean present, String libname, Optional<String> errorMessage) {}
37+
38+
/** Ethereum mainnet-like performance requirements: */
39+
Supplier<List<NativeRequirementResult>> MAINNET =
40+
() -> {
41+
List<NativeRequirementResult> requirements = new ArrayList<>();
42+
var secp256k1 = new SECP256K1();
43+
requirements.add(
44+
new NativeRequirementResult(
45+
secp256k1.maybeEnableNative(),
46+
"secp256k1",
47+
secp256k1.maybeEnableNative()
48+
? Optional.empty()
49+
: Optional.of("secp256k1: Native secp256k1 failed to load")));
50+
51+
requirements.add(
52+
new NativeRequirementResult(
53+
AltBN128PairingPrecompiledContract.isNative(),
54+
"alt_bn128",
55+
AltBN128PairingPrecompiledContract.isNative()
56+
? Optional.empty()
57+
: Optional.of("alt_bn128: EC native library failed to load")));
58+
59+
requirements.add(
60+
new NativeRequirementResult(
61+
BLS12PairingPrecompiledContract.isAvailable(),
62+
"bls12-381",
63+
BLS12PairingPrecompiledContract.isAvailable()
64+
? Optional.empty()
65+
: Optional.of("bls12-381: EC native library failed to load")));
66+
67+
return requirements;
68+
};
69+
}

besu/src/main/java/org/hyperledger/besu/cli/config/NetworkName.java

+31-9
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,34 @@
1414
*/
1515
package org.hyperledger.besu.cli.config;
1616

17+
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
18+
1719
import java.math.BigInteger;
20+
import java.util.Collections;
21+
import java.util.List;
1822
import java.util.Locale;
1923
import java.util.Optional;
24+
import java.util.function.Supplier;
2025

2126
import org.apache.commons.lang3.StringUtils;
2227

2328
/** The enum Network name. */
2429
public enum NetworkName {
2530
/** Mainnet network name. */
26-
MAINNET("/mainnet.json", BigInteger.valueOf(1)),
31+
MAINNET("/mainnet.json", BigInteger.valueOf(1), true, NativeRequirement.MAINNET),
2732
/** Sepolia network name. */
28-
SEPOLIA("/sepolia.json", BigInteger.valueOf(11155111)),
33+
SEPOLIA("/sepolia.json", BigInteger.valueOf(11155111), true, NativeRequirement.MAINNET),
2934
/** Holešky network name. */
30-
HOLESKY("/holesky.json", BigInteger.valueOf(17000)),
35+
HOLESKY("/holesky.json", BigInteger.valueOf(17000), true, NativeRequirement.MAINNET),
3136
/** Hoodi network name. */
32-
HOODI("/hoodi.json", BigInteger.valueOf(560048)),
33-
/** LUKSO mainnet network name. */
34-
LUKSO("/lukso.json", BigInteger.valueOf(42)),
35-
37+
HOODI("/hoodi.json", BigInteger.valueOf(560048), true, NativeRequirement.MAINNET),
3638
/**
3739
* EPHEMERY network name. The actual networkId used is calculated based on this default value and
3840
* the current time. https://ephemery.dev/
3941
*/
40-
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135)),
41-
42+
EPHEMERY("/ephemery.json", BigInteger.valueOf(39438135), true, NativeRequirement.MAINNET),
43+
/** LUKSO mainnet network name. */
44+
LUKSO("/lukso.json", BigInteger.valueOf(42)),
4245
/** Dev network name. */
4346
DEV("/dev.json", BigInteger.valueOf(2018), false),
4447
/** Future EIPs network name. */
@@ -54,17 +57,27 @@ public enum NetworkName {
5457
private final BigInteger networkId;
5558
private final boolean canSnapSync;
5659
private final String deprecationDate;
60+
private final Supplier<List<NativeRequirementResult>> nativeRequirements;
5761

5862
NetworkName(final String genesisFile, final BigInteger networkId) {
5963
this(genesisFile, networkId, true);
6064
}
6165

6266
NetworkName(final String genesisFile, final BigInteger networkId, final boolean canSnapSync) {
67+
this(genesisFile, networkId, canSnapSync, Collections::emptyList);
68+
}
69+
70+
NetworkName(
71+
final String genesisFile,
72+
final BigInteger networkId,
73+
final boolean canSnapSync,
74+
final Supplier<List<NativeRequirementResult>> nativeRequirements) {
6375
this.genesisFile = genesisFile;
6476
this.networkId = networkId;
6577
this.canSnapSync = canSnapSync;
6678
// no deprecations planned
6779
this.deprecationDate = null;
80+
this.nativeRequirements = nativeRequirements;
6881
}
6982

7083
/**
@@ -120,4 +133,13 @@ public boolean isDeprecated() {
120133
public Optional<String> getDeprecationDate() {
121134
return Optional.ofNullable(deprecationDate);
122135
}
136+
137+
/**
138+
* Gets native requirements for this network.
139+
*
140+
* @return result of native library requirements defined for this network, as a list.
141+
*/
142+
public List<NativeRequirementResult> getNativeRequirements() {
143+
return this.nativeRequirements.get();
144+
}
123145
}

besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java

+46
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import static java.nio.charset.StandardCharsets.UTF_8;
1818
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20+
import static org.assertj.core.api.Assertions.assertThatNoException;
1921
import static org.hyperledger.besu.cli.config.NetworkName.CLASSIC;
2022
import static org.hyperledger.besu.cli.config.NetworkName.DEV;
2123
import static org.hyperledger.besu.cli.config.NetworkName.EPHEMERY;
@@ -42,11 +44,15 @@
4244
import static org.mockito.Mockito.argThat;
4345
import static org.mockito.Mockito.atLeast;
4446
import static org.mockito.Mockito.never;
47+
import static org.mockito.Mockito.spy;
48+
import static org.mockito.Mockito.times;
4549
import static org.mockito.Mockito.verify;
4650
import static org.mockito.Mockito.verifyNoInteractions;
51+
import static org.mockito.Mockito.when;
4752

4853
import org.hyperledger.besu.BesuInfo;
4954
import org.hyperledger.besu.cli.config.EthNetworkConfig;
55+
import org.hyperledger.besu.cli.config.NativeRequirement.NativeRequirementResult;
5056
import org.hyperledger.besu.cli.config.NetworkName;
5157
import org.hyperledger.besu.config.GenesisConfig;
5258
import org.hyperledger.besu.config.MergeConfiguration;
@@ -2564,6 +2570,46 @@ public void kzgTrustedSetupFileLoadedWithCustomGenesisFile()
25642570
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
25652571
}
25662572

2573+
@Test
2574+
public void assertUnnamedNetworkNativeRequirements_Met() throws IOException {
2575+
final Path genesisFile =
2576+
createFakeGenesisFile(new JsonObject().put("config", new JsonObject()));
2577+
parseCommand("--genesis-file", genesisFile.toString());
2578+
2579+
assertThat(commandOutput.toString(UTF_8)).isEmpty();
2580+
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
2581+
}
2582+
2583+
@Test
2584+
public void assertNativeRequirements_UnMet() throws IOException {
2585+
BesuCommand mockCmd = parseCommand("--network=mainnet");
2586+
NetworkName spyMainnet = spy(NetworkName.MAINNET);
2587+
when(spyMainnet.getNativeRequirements())
2588+
.thenReturn(
2589+
List.of(new NativeRequirementResult(false, "MOCKLIB", Optional.of("Mock error"))));
2590+
assertThatExceptionOfType(UnsupportedOperationException.class)
2591+
.isThrownBy(() -> mockCmd.checkRequiredNativeLibraries(spyMainnet))
2592+
.withMessageContaining("MOCKLIB")
2593+
.withMessageContaining("Mock error")
2594+
.withMessageContaining(System.getProperty("os.arch"))
2595+
.withMessageContaining(System.getProperty("os.name"));
2596+
}
2597+
2598+
@Test
2599+
public void assertNativeRequirements_UnMetForUnnamedNetwork() throws IOException {
2600+
final Path fakeGenesisFile = createFakeGenesisFile(GENESIS_VALID_JSON);
2601+
BesuCommand mockCmd = parseCommand("--genesis-file=" + fakeGenesisFile.toString());
2602+
NetworkName spyMainnet = spy(NetworkName.MAINNET);
2603+
// assert no error output
2604+
assertThat(commandOutput.toString(UTF_8)).isEmpty();
2605+
assertThat(commandErrorOutput.toString(UTF_8)).isEmpty();
2606+
2607+
// assert no exception
2608+
assertThatNoException().isThrownBy(() -> mockCmd.configureNativeLibs(Optional.of(spyMainnet)));
2609+
// assert we didn't check for native requirements for a custom-genesis
2610+
verify(spyMainnet, times(0)).getNativeRequirements();
2611+
}
2612+
25672613
@Test
25682614
public void bonsaiFlatDbShouldBeEnabledByDefault() {
25692615
final TestBesuCommand besuCommand = parseCommand();

crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class SECP256R1 extends AbstractSECP256 {
4444
public SECP256R1() {
4545
super(CURVE_NAME, SecP256R1Curve.q);
4646
try {
47-
useNative = BesuNativeEC.INSTANCE != null;
47+
useNative = BesuNativeEC.ENABLED;
4848
} catch (UnsatisfiedLinkError ule) {
4949
LOG.info("secp256r1 native precompile not available: {}", ule.getMessage());
5050
useNative = false;
@@ -69,7 +69,7 @@ public boolean isNative() {
6969
@Override
7070
public boolean maybeEnableNative() {
7171
try {
72-
useNative = BesuNativeEC.INSTANCE != null;
72+
useNative = BesuNativeEC.ENABLED;
7373
} catch (UnsatisfiedLinkError | NoClassDefFoundError e) {
7474
LOG.info("Native secp256r1 not available - {}", e.getMessage());
7575
useNative = false;

0 commit comments

Comments
 (0)