diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 9c2b014ec0..72fbb48ec9 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1503,7 +1503,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private void configureNativeLibs() { if (unstableNativeLibraryOptions.getNativeAltbn128() - && AbstractAltBnPrecompiledContract.isNative()) { + && AbstractAltBnPrecompiledContract.maybeEnableNative()) { logger.info("Using the native implementation of alt bn128"); } else { AbstractAltBnPrecompiledContract.disableNative(); @@ -1519,7 +1519,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { } if (unstableNativeLibraryOptions.getNativeSecp() - && SignatureAlgorithmFactory.getInstance().isNative()) { + && SignatureAlgorithmFactory.getInstance().maybeEnableNative()) { logger.info("Using the native implementation of the signature algorithm"); } else { SignatureAlgorithmFactory.getInstance().disableNative(); diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java index 27495cd97c..b10e654626 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java @@ -397,7 +397,9 @@ public abstract class AbstractSECP256 implements SignatureAlgorithm { final Bytes32 dataHash, final SECPSignature signature) { final BigInteger publicKeyBI = recoverFromSignature(signature.getRecId(), signature.getR(), signature.getS(), dataHash); - return Optional.of(SECPPublicKey.create(publicKeyBI, ALGORITHM)); + return publicKeyBI == null + ? Optional.empty() + : Optional.of(SECPPublicKey.create(publicKeyBI, ALGORITHM)); } @Override diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java index ad4fe7ddb1..d458a4c43d 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/Blake2bfMessageDigest.java @@ -44,9 +44,10 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable /** * Implementation of the `F` compression function of the Blake2b cryptographic hash function. * - *
RFC - https://tools.ietf.org/html/rfc7693 + *
RFC - ... * - *
Adapted from - https://github.com/keep-network/blake2b/blob/master/compression/f.go + *
Adapted from - ... * *
Optimized for 64-bit platforms */ @@ -93,12 +94,7 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable private static boolean useNative; static { - try { - useNative = LibBlake2bf.ENABLED; - } catch (UnsatisfiedLinkError ule) { - LOG.info("blake2bf native precompile not available: {}", ule.getMessage()); - useNative = false; - } + maybeEnableNative(); } /** Instantiates a new Blake2bf digest. */ @@ -130,6 +126,21 @@ public class Blake2bfMessageDigest extends BCMessageDigest implements Cloneable return cloned; } + /** + * Attempt to enable the native libreary + * + * @return true if the native library was successfully enabled. + */ + public static boolean maybeEnableNative() { + try { + useNative = LibBlake2bf.ENABLED; + } catch (UnsatisfiedLinkError | NoClassDefFoundError e) { + LOG.info("blake2bf native precompile not available: {}", e.getMessage()); + useNative = false; + } + return useNative; + } + /** Disable native. */ public static void disableNative() { useNative = false; diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256K1.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256K1.java index 8d7ba7924f..2ec0470e54 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256K1.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256K1.java @@ -72,6 +72,7 @@ public class SECP256K1 extends AbstractSECP256 { * * @return true if the native library was enabled. */ + @Override public boolean maybeEnableNative() { try { useNative = LibSecp256k1.CONTEXT != null; diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java index 384723a239..812d776219 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SECP256R1.java @@ -61,6 +61,22 @@ public class SECP256R1 extends AbstractSECP256 { return useNative; } + /** + * Attempt to enable the native library for secp256r1 + * + * @return true if the native library was enabled. + */ + @Override + public boolean maybeEnableNative() { + try { + useNative = BesuNativeEC.INSTANCE != null; + } catch (UnsatisfiedLinkError | NoClassDefFoundError e) { + LOG.info("Native secp256r1 not available - {}", e.getMessage()); + useNative = false; + } + return useNative; + } + /** * SECP256R1 is using the non-deterministic implementation of K calculation (standard) * diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java index 408b1d423a..db9565d18d 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java @@ -31,6 +31,13 @@ public interface SignatureAlgorithm { /** Disable native. */ void disableNative(); + /** + * Attempt to enable the native library. + * + * @return true if the native library was enabled + */ + boolean maybeEnableNative(); + /** * Is native enabled. * diff --git a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/SECP256R1Test.java b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/SECP256R1Test.java index 7112d36294..22bd5c7651 100644 --- a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/SECP256R1Test.java +++ b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/SECP256R1Test.java @@ -127,7 +127,7 @@ public class SECP256R1Test { } @Test - public void recoverPublicKeyFromSignature() { + void recoverPublicKeyFromSignature() { final SECPPrivateKey privateKey = secp256R1.createPrivateKey( new BigInteger("c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", 16)); @@ -139,20 +139,20 @@ public class SECP256R1Test { final SECPPublicKey recoveredPublicKey = secp256R1.recoverPublicKeyFromSignature(dataHash, signature).get(); - assertThat(recoveredPublicKey.toString()).isEqualTo(keyPair.getPublicKey().toString()); + assertThat(recoveredPublicKey).hasToString(keyPair.getPublicKey().toString()); } @Test - public void signatureGenerationVerificationAndPubKeyRecovery() { + void signatureGenerationVerificationAndPubKeyRecovery() { signTestVectors.forEach( signTestVector -> { final SECPPrivateKey privateKey = - secp256R1.createPrivateKey(new BigInteger(signTestVector.getPrivateKey(), 16)); - final BigInteger publicKeyBigInt = new BigInteger(signTestVector.getPublicKey(), 16); + secp256R1.createPrivateKey(new BigInteger(signTestVector.privateKey(), 16)); + final BigInteger publicKeyBigInt = new BigInteger(signTestVector.publicKey(), 16); final SECPPublicKey publicKey = secp256R1.createPublicKey(publicKeyBigInt); final KeyPair keyPair = secp256R1.createKeyPair(privateKey); - final Bytes32 dataHash = keccak256(Bytes.wrap(signTestVector.getData().getBytes(UTF_8))); + final Bytes32 dataHash = keccak256(Bytes.wrap(signTestVector.data().getBytes(UTF_8))); final SECPSignature signature = secp256R1.sign(dataHash, keyPair); assertThat(secp256R1.verify(dataHash, signature, publicKey)).isTrue(); @@ -165,44 +165,22 @@ public class SECP256R1Test { } @Test - public void invalidFileThrowsInvalidKeyPairException() throws Exception { + void invalidFileThrowsInvalidKeyPairException() throws Exception { final File tempFile = Files.createTempFile(suiteName(), ".keypair").toFile(); tempFile.deleteOnExit(); - Files.write(tempFile.toPath(), "not valid".getBytes(UTF_8)); + Files.writeString(tempFile.toPath(), "not valid"); assertThatThrownBy(() -> KeyPairUtil.load(tempFile)) .isInstanceOf(IllegalArgumentException.class); } @Test - public void invalidMultiLineFileThrowsInvalidIdException() throws Exception { + void invalidMultiLineFileThrowsInvalidIdException() throws Exception { final File tempFile = Files.createTempFile(suiteName(), ".keypair").toFile(); tempFile.deleteOnExit(); - Files.write(tempFile.toPath(), "not\n\nvalid".getBytes(UTF_8)); + Files.writeString(tempFile.toPath(), "not\n\nvalid"); assertThatThrownBy(() -> KeyPairUtil.load(tempFile)) .isInstanceOf(IllegalArgumentException.class); } - private static class SignTestVector { - private final String privateKey; - private final String publicKey; - private final String data; - - public SignTestVector(final String privateKey, final String publicKey, final String data) { - this.privateKey = privateKey; - this.publicKey = publicKey; - this.data = data; - } - - public String getPrivateKey() { - return privateKey; - } - - public String getPublicKey() { - return publicKey; - } - - public String getData() { - return data; - } - } + private record SignTestVector(String privateKey, String publicKey, String data) {} } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContractTest.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContractTest.java index af78f975b7..5b7b470a8a 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContractTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/AltBN128PairingPrecompiledContractTest.java @@ -22,6 +22,8 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -36,8 +38,14 @@ class AltBN128PairingPrecompiledContractTest { private final AltBN128PairingPrecompiledContract istanbulContract = AltBN128PairingPrecompiledContract.istanbul(gasCalculator); - @Test - void compute_validPoints() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void compute_validPoints(final boolean useNative) { + if (useNative) { + AbstractAltBnPrecompiledContract.maybeEnableNative(); + } else { + AbstractAltBnPrecompiledContract.disableNative(); + } final Bytes input = validPointBytes(); final Bytes result = byzantiumContract.computePrecompile(input, messageFrame).getOutput(); assertThat(result).isEqualTo(AltBN128PairingPrecompiledContract.TRUE); @@ -80,8 +88,14 @@ class AltBN128PairingPrecompiledContractTest { return Bytes.concatenate(g1Point0, g2Point0, g1Point1, g2Point1); } - @Test - void compute_invalidPointsOutsideSubgroupG2() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void compute_invalidPointsOutsideSubgroupG2(final boolean useNative) { + if (useNative) { + AbstractAltBnPrecompiledContract.maybeEnableNative(); + } else { + AbstractAltBnPrecompiledContract.disableNative(); + } final Bytes g1Point0 = Bytes.concatenate( Bytes.fromHexString( @@ -122,11 +136,13 @@ class AltBN128PairingPrecompiledContractTest { @Test void gasPrice_byzantium() { + // gas calculation is java only assertThat(byzantiumContract.gasRequirement(validPointBytes())).isEqualTo(260_000L); } @Test void gasPrice_istanbul() { + // gas calculation is java only assertThat(istanbulContract.gasRequirement(validPointBytes())).isEqualTo(113_000L); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/BLAKE2BFPrecompileContractTest.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/BLAKE2BFPrecompileContractTest.java index 381e64c015..742edc8548 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/BLAKE2BFPrecompileContractTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/BLAKE2BFPrecompileContractTest.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.evm.precompile; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import org.hyperledger.besu.crypto.Blake2bfMessageDigest.Blake2bfDigest; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; @@ -67,9 +68,24 @@ class BLAKE2BFPrecompileContractTest { @ParameterizedTest @MethodSource("parameters") - void shouldRunFCompression( + void shouldRunFCompressionNative( final String inputString, final String expectedResult, final long expectedGasUsed) { + Blake2bfDigest.maybeEnableNative(); + testFCompression(inputString, expectedResult, expectedGasUsed); + } + + @ParameterizedTest + @MethodSource("parameters") + void shouldRunFCompressionJava( + final String inputString, final String expectedResult, final long expectedGasUsed) { + Blake2bfDigest.disableNative(); + + testFCompression(inputString, expectedResult, expectedGasUsed); + } + + private void testFCompression( + final String inputString, final String expectedResult, final long expectedGasUsed) { final Bytes input = Bytes.fromHexString(inputString); final Bytes expectedComputation = expectedResult == null ? null : Bytes.fromHexString(expectedResult); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/ECRECPrecompiledContractTest.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/ECRECPrecompiledContractTest.java index ac248b947b..7d2c52efca 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/ECRECPrecompiledContractTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/ECRECPrecompiledContractTest.java @@ -349,8 +349,18 @@ class ECRECPrecompiledContractTest { @ParameterizedTest @MethodSource("parameters") - void shouldRecoverAddress(final String inputString, final String expectedResult) { + void shouldRecoverAddressNative(final String inputString, final String expectedResult) { + contract.signatureAlgorithm.maybeEnableNative(); + final Bytes input = Bytes.fromHexString(inputString); + final Bytes expected = + expectedResult == null ? Bytes.EMPTY : Bytes32.fromHexString(expectedResult); + assertThat(contract.computePrecompile(input, messageFrame).getOutput()).isEqualTo(expected); + } + @ParameterizedTest + @MethodSource("parameters") + void shouldRecoverAddressJava(final String inputString, final String expectedResult) { + contract.signatureAlgorithm.disableNative(); final Bytes input = Bytes.fromHexString(inputString); final Bytes expected = expectedResult == null ? Bytes.EMPTY : Bytes32.fromHexString(expectedResult); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/MODEXPPrecompiledContractTest.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/MODEXPPrecompiledContractTest.java index ac4fd9041a..61e0f63cfb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/MODEXPPrecompiledContractTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/MODEXPPrecompiledContractTest.java @@ -155,12 +155,27 @@ class MODEXPPrecompiledContractTest { @ParameterizedTest @MethodSource("parameters") - void testPrecompiledContract( + void testPrecompiledContractNative( final String inputString, final String precompiledResult, final Long eip198Gas, final Long eip2565Gas) { + BigIntegerModularExponentiationPrecompiledContract.maybeEnableNative(); + testComputation(inputString, precompiledResult); + } + + @ParameterizedTest + @MethodSource("parameters") + void testPrecompiledContractJava( + final String inputString, + final String precompiledResult, + final Long eip198Gas, + final Long eip2565Gas) { + BigIntegerModularExponentiationPrecompiledContract.disableNative(); + testComputation(inputString, precompiledResult); + } + private void testComputation(final String inputString, final String precompiledResult) { assumeThat(precompiledResult).isNotNull(); final Bytes input = Bytes.fromHexString(inputString); final Bytes expected = Bytes.fromHexString(precompiledResult);