From 1751a77f98cc6cdf218cd80f429e15ccd8e01e67 Mon Sep 17 00:00:00 2001 From: daniellehrner Date: Sat, 21 Sep 2024 13:28:01 +0200 Subject: [PATCH 01/12] 7702 validation checks v2 (#7653) * yParity is valid up to 2**256 as well Signed-off-by: Daniel Lehrner --- .../besu/crypto/AbstractSECP256.java | 2 +- .../besu/crypto/CodeDelegationSignature.java | 15 ++++++++--- .../besu/crypto/SignatureAlgorithm.java | 2 +- .../crypto/CodeDelegationSignatureTest.java | 27 +++++++++++++------ .../CodeDelegationTransactionDecoder.java | 2 +- 5 files changed, 33 insertions(+), 15 deletions(-) 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 ce376512668..bd450b206e9 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 @@ -214,7 +214,7 @@ public SECPSignature createSignature(final BigInteger r, final BigInteger s, fin @Override public CodeDelegationSignature createCodeDelegationSignature( - final BigInteger r, final BigInteger s, final long yParity) { + final BigInteger r, final BigInteger s, final BigInteger yParity) { return CodeDelegationSignature.create(r, s, yParity); } diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java index 4bb2e4653e2..06ec72bf0a9 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java @@ -42,18 +42,25 @@ public CodeDelegationSignature(final BigInteger r, final BigInteger s, final byt * @return the new CodeDelegationSignature */ public static CodeDelegationSignature create( - final BigInteger r, final BigInteger s, final long yParity) { + final BigInteger r, final BigInteger s, final BigInteger yParity) { checkNotNull(r); checkNotNull(s); if (r.compareTo(TWO_POW_256) >= 0) { - throw new IllegalArgumentException("Invalid 'r' value, should be < 2^256 but got " + r); + throw new IllegalArgumentException( + "Invalid 'r' value, should be < 2^256 but got " + r.toString(16)); } if (s.compareTo(TWO_POW_256) >= 0) { - throw new IllegalArgumentException("Invalid 's' value, should be < 2^256 but got " + s); + throw new IllegalArgumentException( + "Invalid 's' value, should be < 2^256 but got " + s.toString(16)); } - return new CodeDelegationSignature(r, s, (byte) yParity); + if (yParity.compareTo(TWO_POW_256) >= 0) { + throw new IllegalArgumentException( + "Invalid 'yParity' value, should be < 2^256 but got " + yParity.toString(16)); + } + + return new CodeDelegationSignature(r, s, yParity.byteValue()); } } 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 8e19b608544..4bf8d89c825 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 @@ -224,7 +224,7 @@ Optional recoverPublicKeyFromSignature( * @return the code delegation signature */ CodeDelegationSignature createCodeDelegationSignature( - final BigInteger r, final BigInteger s, final long yParity); + final BigInteger r, final BigInteger s, final BigInteger yParity); /** * Decode secp signature. diff --git a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java index 1cc66966a78..332aa14893f 100644 --- a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java +++ b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java @@ -29,19 +29,19 @@ class CodeDelegationSignatureTest { void testValidInputs() { BigInteger r = BigInteger.ONE; BigInteger s = BigInteger.TEN; - long yParity = 1L; + BigInteger yParity = BigInteger.ONE; CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity); assertThat(r).isEqualTo(result.getR()); assertThat(s).isEqualTo(result.getS()); - assertThat((byte) yParity).isEqualTo(result.getRecId()); + assertThat(yParity.byteValue()).isEqualTo(result.getRecId()); } @Test void testNullRValue() { BigInteger s = BigInteger.TEN; - long yParity = 0L; + BigInteger yParity = BigInteger.ZERO; assertThatExceptionOfType(NullPointerException.class) .isThrownBy(() -> CodeDelegationSignature.create(null, s, yParity)); @@ -50,7 +50,7 @@ void testNullRValue() { @Test void testNullSValue() { BigInteger r = BigInteger.ONE; - long yParity = 0L; + BigInteger yParity = BigInteger.ZERO; assertThatExceptionOfType(NullPointerException.class) .isThrownBy(() -> CodeDelegationSignature.create(r, null, yParity)); @@ -60,7 +60,7 @@ void testNullSValue() { void testRValueExceedsTwoPow256() { BigInteger r = TWO_POW_256; BigInteger s = BigInteger.TEN; - long yParity = 0L; + BigInteger yParity = BigInteger.ZERO; assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity)) @@ -71,23 +71,34 @@ void testRValueExceedsTwoPow256() { void testSValueExceedsTwoPow256() { BigInteger r = BigInteger.ONE; BigInteger s = TWO_POW_256; - long yParity = 0L; + BigInteger yParity = BigInteger.ZERO; assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity)) .withMessageContainingAll("Invalid 's' value, should be < 2^256"); } + @Test + void testYParityExceedsTwoPow256() { + BigInteger r = BigInteger.ONE; + BigInteger s = BigInteger.TWO; + BigInteger yParity = TWO_POW_256; + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity)) + .withMessageContainingAll("Invalid 'yParity' value, should be < 2^256"); + } + @Test void testValidYParityZero() { BigInteger r = BigInteger.ONE; BigInteger s = BigInteger.TEN; - long yParity = 0L; + BigInteger yParity = BigInteger.ZERO; CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity); assertThat(r).isEqualTo(result.getR()); assertThat(s).isEqualTo(result.getS()); - assertThat((byte) yParity).isEqualTo(result.getRecId()); + assertThat(yParity.byteValue()).isEqualTo(result.getRecId()); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java index d3ef60bfc41..6448940d8d5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java @@ -81,7 +81,7 @@ public static CodeDelegation decodeInnerPayload(final RLPInput input) { final Address address = Address.wrap(input.readBytes()); final long nonce = input.readLongScalar(); - final long yParity = input.readUnsignedIntScalar(); + final BigInteger yParity = input.readUInt256Scalar().toUnsignedBigInteger(); final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger(); final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); From 9d689b940134b4f7a9b80020829d506417c2065e Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Sun, 22 Sep 2024 06:45:09 +1000 Subject: [PATCH 02/12] remove integration tests related to privacy (#7645) * remove integration tests related to privacy Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane --- .../hyperledger/besu/enclave/EnclaveTest.java | 215 --------------- .../enclave/TlsCertificateDefinition.java | 52 ---- .../besu/enclave/TlsEnabledEnclaveTest.java | 144 ----------- .../enclave/TlsEnabledHttpServerFactory.java | 109 -------- .../hyperledger/besu/enclave/TlsHelpers.java | 98 ------- ...vGetPrivateTransactionIntegrationTest.java | 192 -------------- ...acyPrecompiledContractIntegrationTest.java | 244 ------------------ testutil/src/main/resources/enclave_key_0.key | 1 - 8 files changed, 1055 deletions(-) delete mode 100644 enclave/src/integration-test/java/org/hyperledger/besu/enclave/EnclaveTest.java delete mode 100644 enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java delete mode 100644 enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java delete mode 100644 enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java delete mode 100644 enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java delete mode 100644 ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/PrivGetPrivateTransactionIntegrationTest.java delete mode 100644 ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java delete mode 100644 testutil/src/main/resources/enclave_key_0.key diff --git a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/EnclaveTest.java b/enclave/src/integration-test/java/org/hyperledger/besu/enclave/EnclaveTest.java deleted file mode 100644 index b9b657e6689..00000000000 --- a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/EnclaveTest.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.enclave; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; - -import org.hyperledger.besu.enclave.types.PrivacyGroup; -import org.hyperledger.besu.enclave.types.ReceiveResponse; -import org.hyperledger.besu.enclave.types.SendResponse; -import org.hyperledger.enclave.testutil.EnclaveEncryptorType; -import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration; -import org.hyperledger.enclave.testutil.TesseraTestHarness; -import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory; - -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import com.google.common.collect.Lists; -import io.vertx.core.Vertx; -import org.awaitility.Awaitility; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -public class EnclaveTest { - - @TempDir private static Path folder; - - private static final String PAYLOAD = "a wonderful transaction"; - private static final String MOCK_KEY = "iOCzoGo5kwtZU0J41Z9xnGXHN6ZNukIa9MspvHtu3Jk="; - private Enclave enclave; - private Vertx vertx; - private EnclaveFactory factory; - - private TesseraTestHarness testHarness; - - @BeforeEach - public void setUp() throws Exception { - vertx = Vertx.vertx(); - factory = new EnclaveFactory(vertx); - - testHarness = - TesseraTestHarnessFactory.create( - "enclave", - Files.createTempDirectory(folder, "enclave"), - new EnclaveKeyConfiguration( - new String[] {"enclave_key_0.pub"}, - new String[] {"enclave_key_0.key"}, - EnclaveEncryptorType.NOOP), - Optional.empty()); - - testHarness.start(); - - enclave = factory.createVertxEnclave(testHarness.clientUrl()); - } - - @AfterEach - public void tearDown() { - testHarness.close(); - vertx.close(); - } - - @Test - public void testUpCheck() { - assertThat(enclave.upCheck()).isTrue(); - } - - @Test - public void testReceiveThrowsWhenPayloadDoesNotExist() { - final String publicKey = testHarness.getDefaultPublicKey(); - - final Throwable t = catchThrowable(() -> enclave.receive(MOCK_KEY, publicKey)); - - assertThat(t.getMessage()).isEqualTo("Message with hash was not found"); - } - - @Test - public void testSendAndReceive() { - final List publicKeys = testHarness.getPublicKeys(); - - final SendResponse sr = - enclave.send(PAYLOAD, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0))); - - final ReceiveResponse rr = enclave.receive(sr.getKey(), publicKeys.get(0)); - assertThat(rr).isNotNull(); - assertThat(new String(rr.getPayload(), UTF_8)).isEqualTo(PAYLOAD); - assertThat(rr.getPrivacyGroupId()).isNotNull(); - } - - @Test - public void testSendWithPrivacyGroupAndReceive() { - final List publicKeys = testHarness.getPublicKeys(); - - final PrivacyGroup privacyGroupResponse = - enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), "", ""); - - final SendResponse sr = - enclave.send(PAYLOAD, publicKeys.get(0), privacyGroupResponse.getPrivacyGroupId()); - - final ReceiveResponse rr = enclave.receive(sr.getKey(), publicKeys.get(0)); - assertThat(rr).isNotNull(); - assertThat(new String(rr.getPayload(), UTF_8)).isEqualTo(PAYLOAD); - assertThat(rr.getPrivacyGroupId()).isNotNull(); - } - - @Test - public void testCreateAndDeletePrivacyGroup() { - final List publicKeys = testHarness.getPublicKeys(); - final String name = "testName"; - final String description = "testDesc"; - - final PrivacyGroup privacyGroupResponse = - enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull(); - assertThat(privacyGroupResponse.getName()).isEqualTo(name); - assertThat(privacyGroupResponse.getDescription()).isEqualTo(description); - assertThat(privacyGroupResponse.getType()).isEqualByComparingTo(PrivacyGroup.Type.PANTHEON); - - final String response = - enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0)); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response); - } - - @Test - public void testCreateFindDeleteFindPrivacyGroup() { - final List publicKeys = testHarness.getPublicKeys(); - final String name = "name"; - final String description = "desc"; - - final PrivacyGroup privacyGroupResponse = - enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull(); - assertThat(privacyGroupResponse.getName()).isEqualTo(name); - assertThat(privacyGroupResponse.getDescription()).isEqualTo(description); - assertThat(privacyGroupResponse.getType()).isEqualTo(PrivacyGroup.Type.PANTHEON); - - Awaitility.await() - .atMost(5, TimeUnit.SECONDS) - .untilAsserted( - () -> { - final PrivacyGroup[] findPrivacyGroupResponse = enclave.findPrivacyGroup(publicKeys); - - assertThat(findPrivacyGroupResponse.length).isEqualTo(1); - assertThat(findPrivacyGroupResponse[0].getPrivacyGroupId()) - .isEqualTo(privacyGroupResponse.getPrivacyGroupId()); - }); - - final String response = - enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0)); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response); - - Awaitility.await() - .atMost(5, TimeUnit.SECONDS) - .untilAsserted( - () -> { - final PrivacyGroup[] findPrivacyGroupResponse = enclave.findPrivacyGroup(publicKeys); - - assertThat(findPrivacyGroupResponse.length).isEqualTo(0); - }); - } - - @Test - public void testCreateDeleteRetrievePrivacyGroup() { - final List publicKeys = testHarness.getPublicKeys(); - final String name = "name"; - final String description = "desc"; - - final PrivacyGroup privacyGroupResponse = - enclave.createPrivacyGroup(publicKeys, publicKeys.get(0), name, description); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isNotNull(); - assertThat(privacyGroupResponse.getName()).isEqualTo(name); - assertThat(privacyGroupResponse.getDescription()).isEqualTo(description); - assertThat(privacyGroupResponse.getType()).isEqualTo(PrivacyGroup.Type.PANTHEON); - - final PrivacyGroup retrievePrivacyGroup = - enclave.retrievePrivacyGroup(privacyGroupResponse.getPrivacyGroupId()); - - assertThat(retrievePrivacyGroup).usingRecursiveComparison().isEqualTo(privacyGroupResponse); - - final String response = - enclave.deletePrivacyGroup(privacyGroupResponse.getPrivacyGroupId(), publicKeys.get(0)); - - assertThat(privacyGroupResponse.getPrivacyGroupId()).isEqualTo(response); - } - - @Test - public void upcheckReturnsFalseIfNoResponseReceived() throws URISyntaxException { - assertThat(factory.createVertxEnclave(new URI("http://8.8.8.8:65535")).upCheck()).isFalse(); - } -} diff --git a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java b/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java deleted file mode 100644 index ad1271e9200..00000000000 --- a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsCertificateDefinition.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.enclave; - -import java.io.File; -import java.net.URL; -import java.nio.file.Path; - -import com.google.common.io.Resources; - -public class TlsCertificateDefinition { - - private final File pkcs12File; - private final String password; - - public static TlsCertificateDefinition loadFromResource( - final String resourcePath, final String password) { - try { - final URL sslCertificate = Resources.getResource(resourcePath); - final Path keystorePath = Path.of(sslCertificate.getPath()); - - return new TlsCertificateDefinition(keystorePath.toFile(), password); - } catch (final Exception e) { - throw new RuntimeException("Failed to load TLS certificates", e); - } - } - - public TlsCertificateDefinition(final File pkcs12File, final String password) { - this.pkcs12File = pkcs12File; - this.password = password; - } - - public File getPkcs12File() { - return pkcs12File; - } - - public String getPassword() { - return password; - } -} diff --git a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java b/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java deleted file mode 100644 index b5947793060..00000000000 --- a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledEnclaveTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.enclave; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.Optional; - -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class TlsEnabledEnclaveTest { - - private TlsEnabledHttpServerFactory serverFactory; - private Vertx vertx; - - final TlsCertificateDefinition httpServerCert = - TlsCertificateDefinition.loadFromResource("tls/cert1.pfx", "password"); - final TlsCertificateDefinition besuCert = - TlsCertificateDefinition.loadFromResource("tls/cert2.pfx", "password2"); - - public void shutdown() { - vertx.close(); - } - - @BeforeEach - public void setup() { - serverFactory = new TlsEnabledHttpServerFactory(); - this.vertx = Vertx.vertx(); - } - - @AfterEach - public void cleanup() { - serverFactory.shutdown(); - this.shutdown(); - } - - private Enclave createEnclave( - final int httpServerPort, final Path workDir, final boolean tlsEnabled) throws IOException { - - final Path serverFingerprintFile = workDir.resolve("server_known_clients"); - final Path besuCertPasswordFile = workDir.resolve("password_file"); - try { - populateFingerprintFile(serverFingerprintFile, httpServerCert, Optional.of(httpServerPort)); - Files.write(besuCertPasswordFile, besuCert.getPassword().getBytes(Charset.defaultCharset())); - - final EnclaveFactory factory = new EnclaveFactory(vertx); - if (tlsEnabled) { - final URI httpServerUri = new URI("https://localhost:" + httpServerPort); - return factory.createVertxEnclave( - httpServerUri, - besuCert.getPkcs12File().toPath(), - besuCertPasswordFile, - serverFingerprintFile); - } else { - return factory.createVertxEnclave(new URI("http://localhost:" + httpServerPort)); - } - } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) { - fail("unable to populate fingerprint file"); - return null; - } catch (URISyntaxException e) { - fail("unable to create URI"); - return null; - } - } - - @Test - public void nonTlsEnclaveCannotConnectToTlsServer() throws IOException { - - Path workDir = Files.createTempDirectory("test-certs"); - - // Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up". - final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true); - - final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false); - - assertThat(enclave.upCheck()).isEqualTo(false); - } - - @Test - public void nonTlsEnclaveCanConnectToNonTlsServer() throws IOException { - - Path workDir = Files.createTempDirectory("test-certs"); - - // Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up". - final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false); - - final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, false); - - assertThat(enclave.upCheck()).isEqualTo(true); - } - - @Test - public void tlsEnclaveCannotConnectToNonTlsServer() throws IOException { - - Path workDir = Files.createTempDirectory("test-certs"); - - // Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up!". - final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, false); - - final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true); - - assertThat(enclave.upCheck()).isEqualTo(false); - } - - @Test - public void tlsEnclaveCanConnectToTlsServer() throws IOException { - - Path workDir = Files.createTempDirectory("test-certs"); - - // Note: the HttpServer always responds with a JsonRpcSuccess, result="I'm up". - final HttpServer httpServer = serverFactory.create(httpServerCert, besuCert, workDir, true); - - final Enclave enclave = createEnclave(httpServer.actualPort(), workDir, true); - - assertThat(enclave.upCheck()).isEqualTo(true); - } -} diff --git a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java b/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java deleted file mode 100644 index 7c67aeb7b9e..00000000000 --- a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsEnabledHttpServerFactory.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.enclave; - -import static org.hyperledger.besu.enclave.TlsHelpers.populateFingerprintFile; - -import java.io.IOException; -import java.nio.file.Path; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import com.google.common.collect.Lists; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.vertx.core.Vertx; -import io.vertx.core.http.ClientAuth; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.net.PfxOptions; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import org.apache.tuweni.net.tls.VertxTrustOptions; - -class TlsEnabledHttpServerFactory { - - private final Vertx vertx; - private final List serversCreated = Lists.newArrayList(); - - TlsEnabledHttpServerFactory() { - this.vertx = Vertx.vertx(); - } - - void shutdown() { - serversCreated.forEach(HttpServer::close); - vertx.close(); - } - - HttpServer create( - final TlsCertificateDefinition serverCert, - final TlsCertificateDefinition acceptedClientCerts, - final Path workDir, - final boolean tlsEnabled) { - try { - - final Path serverFingerprintFile = workDir.resolve("server_known_clients"); - populateFingerprintFile(serverFingerprintFile, acceptedClientCerts, Optional.empty()); - - final HttpServerOptions web3HttpServerOptions = new HttpServerOptions(); - web3HttpServerOptions.setPort(0); - if (tlsEnabled) { - web3HttpServerOptions.setSsl(true); - web3HttpServerOptions.setClientAuth(ClientAuth.REQUIRED); - web3HttpServerOptions.setTrustOptions( - VertxTrustOptions.allowlistClients(serverFingerprintFile)); - web3HttpServerOptions.setPfxKeyCertOptions( - new PfxOptions() - .setPath(serverCert.getPkcs12File().toString()) - .setPassword(serverCert.getPassword())); - } - final Router router = Router.router(vertx); - router - .route(HttpMethod.GET, "/upcheck") - .produces(HttpHeaderValues.APPLICATION_JSON.toString()) - .handler(TlsEnabledHttpServerFactory::handleRequest); - - final HttpServer mockOrionHttpServer = vertx.createHttpServer(web3HttpServerOptions); - - final CompletableFuture serverConfigured = new CompletableFuture<>(); - mockOrionHttpServer.requestHandler(router).listen(result -> serverConfigured.complete(true)); - - serverConfigured.get(); - - serversCreated.add(mockOrionHttpServer); - return mockOrionHttpServer; - } catch (final KeyStoreException - | NoSuchAlgorithmException - | CertificateException - | IOException - | ExecutionException - | InterruptedException e) { - throw new RuntimeException("Failed to construct a TLS Enabled Server", e); - } - } - - private static void handleRequest(final RoutingContext context) { - final HttpServerResponse response = context.response(); - if (!response.closed()) { - response.end("I'm up!"); - } - } -} diff --git a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java b/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java deleted file mode 100644 index 09002f81151..00000000000 --- a/enclave/src/integration-test/java/org/hyperledger/besu/enclave/TlsHelpers.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.enclave; - -import org.hyperledger.besu.crypto.MessageDigestFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Enumeration; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.StringJoiner; - -import com.google.common.collect.Lists; - -public class TlsHelpers { - - private TlsHelpers() {} - - private static KeyStore loadP12KeyStore(final File pkcsFile, final String password) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException { - final KeyStore store = KeyStore.getInstance("pkcs12"); - try (final InputStream keystoreStream = new FileInputStream(pkcsFile)) { - store.load(keystoreStream, password.toCharArray()); - } catch (IOException e) { - throw new RuntimeException("Unable to load keystore.", e); - } - return store; - } - - public static void populateFingerprintFile( - final Path knownClientsPath, - final TlsCertificateDefinition certDef, - final Optional serverPortToAppendToHostname) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { - - final List certs = getCertsFromPkcs12(certDef); - final StringBuilder fingerprintsToAdd = new StringBuilder(); - final String portFragment = serverPortToAppendToHostname.map(port -> ":" + port).orElse(""); - for (final X509Certificate cert : certs) { - final String fingerprint = generateFingerprint(cert); - fingerprintsToAdd.append(String.format("localhost%s %s%n", portFragment, fingerprint)); - fingerprintsToAdd.append(String.format("127.0.0.1%s %s%n", portFragment, fingerprint)); - } - Files.writeString(knownClientsPath, fingerprintsToAdd.toString()); - } - - @SuppressWarnings("JdkObsolete") // java.util.Enumeration is baked into the Keystore API - public static List getCertsFromPkcs12(final TlsCertificateDefinition certDef) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException { - final List results = Lists.newArrayList(); - - final KeyStore p12 = loadP12KeyStore(certDef.getPkcs12File(), certDef.getPassword()); - final Enumeration aliases = p12.aliases(); - while (aliases.hasMoreElements()) { - results.add((X509Certificate) p12.getCertificate(aliases.nextElement())); - } - return results; - } - - private static String generateFingerprint(final X509Certificate cert) - throws NoSuchAlgorithmException, CertificateEncodingException { - final MessageDigest md = MessageDigestFactory.create(MessageDigestFactory.SHA256_ALG); - md.update(cert.getEncoded()); - final byte[] digest = md.digest(); - - final StringJoiner joiner = new StringJoiner(":"); - for (final byte b : digest) { - joiner.add(String.format("%02X", b)); - } - - return joiner.toString().toLowerCase(Locale.ROOT); - } -} diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/PrivGetPrivateTransactionIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/PrivGetPrivateTransactionIntegrationTest.java deleted file mode 100644 index 9bb10629f31..00000000000 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/PrivGetPrivateTransactionIntegrationTest.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.ethereum.api.jsonrpc.methods.fork.frontier; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateMarkerTransaction; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import org.hyperledger.besu.crypto.KeyPair; -import org.hyperledger.besu.crypto.SignatureAlgorithm; -import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.enclave.Enclave; -import org.hyperledger.besu.enclave.EnclaveFactory; -import org.hyperledger.besu.enclave.types.SendResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacyIdProvider; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetPrivateTransaction; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.privacy.PrivateTransactionLegacyResult; -import org.hyperledger.besu.ethereum.chain.Blockchain; -import org.hyperledger.besu.ethereum.chain.TransactionLocation; -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.privacy.PrivacyController; -import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.RestrictedDefaultPrivacyController; -import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; -import org.hyperledger.besu.plugin.data.Restriction; -import org.hyperledger.enclave.testutil.EnclaveEncryptorType; -import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration; -import org.hyperledger.enclave.testutil.TesseraTestHarness; -import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory; - -import java.math.BigInteger; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Optional; - -import com.google.common.collect.Lists; -import io.vertx.core.Vertx; -import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -public class PrivGetPrivateTransactionIntegrationTest { - - @TempDir private static Path folder; - private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; - - private final PrivacyIdProvider privacyIdProvider = (user) -> ENCLAVE_PUBLIC_KEY; - private final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); - private final Blockchain blockchain = mock(Blockchain.class); - - private final Address sender = - Address.fromHexString("0x0000000000000000000000000000000000000003"); - - private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); - - private final KeyPair KEY_PAIR = - signatureAlgorithm.createKeyPair( - signatureAlgorithm.createPrivateKey( - new BigInteger( - "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); - - private final PrivateTransaction privateTransaction = - PrivateTransaction.builder() - .nonce(0) - .gasPrice(Wei.of(1000)) - .gasLimit(3000000) - .to(null) - .value(Wei.ZERO) - .payload( - Bytes.fromHexString( - "0x608060405234801561001057600080fd5b5060d08061001f60003960" - + "00f3fe60806040526004361060485763ffffffff7c01000000" - + "00000000000000000000000000000000000000000000000000" - + "60003504166360fe47b18114604d5780636d4ce63c14607557" - + "5b600080fd5b348015605857600080fd5b5060736004803603" - + "6020811015606d57600080fd5b50356099565b005b34801560" - + "8057600080fd5b506087609e565b6040805191825251908190" - + "0360200190f35b600055565b6000549056fea165627a7a7230" - + "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6" - + "daa4f6b2f003d1b0180029")) - .sender(sender) - .chainId(BigInteger.valueOf(2018)) - .privateFrom(Bytes.wrap(ENCLAVE_PUBLIC_KEY.getBytes(UTF_8))) - .privateFor( - Lists.newArrayList( - Bytes.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))) - .restriction(Restriction.RESTRICTED) - .signAndBuild(KEY_PAIR); - - private Vertx vertx = Vertx.vertx(); - private TesseraTestHarness testHarness; - private Enclave enclave; - private PrivacyController privacyController; - - @BeforeEach - public void setUp() throws Exception { - vertx = Vertx.vertx(); - - testHarness = - TesseraTestHarnessFactory.create( - "enclave", - Files.createTempDirectory(folder, "enclave"), - new EnclaveKeyConfiguration( - new String[] {"enclave_key_0.pub"}, - new String[] {"enclave_key_0.key"}, - EnclaveEncryptorType.NOOP), - Optional.empty()); - - testHarness.start(); - - final EnclaveFactory factory = new EnclaveFactory(vertx); - enclave = factory.createVertxEnclave(testHarness.clientUrl()); - - privacyController = - new RestrictedDefaultPrivacyController( - blockchain, privateStateStorage, enclave, null, null, null, null, null); - } - - @AfterEach - public void tearDown() { - testHarness.close(); - vertx.close(); - } - - @Test - public void returnsStoredPrivateTransaction() { - final PrivGetPrivateTransaction privGetPrivateTransaction = - new PrivGetPrivateTransaction(privacyController, privacyIdProvider); - - final Hash blockHash = Hash.ZERO; - final Transaction pmt = spy(privateMarkerTransaction()); - when(blockchain.getTransactionByHash(eq(pmt.getHash()))).thenReturn(Optional.of(pmt)); - when(blockchain.getTransactionLocation(eq(pmt.getHash()))) - .thenReturn(Optional.of(new TransactionLocation(blockHash, 0))); - - final BlockHeader blockHeader = mock(BlockHeader.class); - when(blockHeader.getHash()).thenReturn(blockHash); - when(blockchain.getBlockHeader(eq(blockHash))).thenReturn(Optional.of(blockHeader)); - - final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); - privateTransaction.writeTo(bvrlp); - - final String payload = Base64.getEncoder().encodeToString(bvrlp.encoded().toArrayUnsafe()); - final ArrayList to = Lists.newArrayList("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="); - final SendResponse sendResponse = enclave.send(payload, ENCLAVE_PUBLIC_KEY, to); - - final Bytes hexKey = Bytes.fromBase64String(sendResponse.getKey()); - when(pmt.getPayload()).thenReturn(hexKey); - - final Object[] params = new Object[] {pmt.getHash()}; - - final JsonRpcRequestContext request = - new JsonRpcRequestContext(new JsonRpcRequest("1", "priv_getPrivateTransaction", params)); - - final JsonRpcSuccessResponse response = - (JsonRpcSuccessResponse) privGetPrivateTransaction.response(request); - final PrivateTransactionLegacyResult result = - (PrivateTransactionLegacyResult) response.getResult(); - - assertThat(new PrivateTransactionLegacyResult(this.privateTransaction)) - .usingRecursiveComparison() - .isEqualTo(result); - } -} diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java deleted file mode 100644 index a5ee5f068ee..00000000000 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.ethereum.mainnet.precompiles.privacy; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.enclave.Enclave; -import org.hyperledger.besu.enclave.EnclaveFactory; -import org.hyperledger.besu.enclave.types.SendResponse; -import org.hyperledger.besu.ethereum.core.Block; -import org.hyperledger.besu.ethereum.core.BlockDataGenerator; -import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture; -import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; -import org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils; -import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator; -import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; -import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; -import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; -import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; -import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; -import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; -import org.hyperledger.besu.evm.precompile.PrecompiledContract; -import org.hyperledger.besu.evm.tracing.OperationTracer; -import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.enclave.testutil.EnclaveEncryptorType; -import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration; -import org.hyperledger.enclave.testutil.TesseraTestHarness; -import org.hyperledger.enclave.testutil.TesseraTestHarnessFactory; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import com.google.common.collect.Lists; -import io.vertx.core.Vertx; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -public class PrivacyPrecompiledContractIntegrationTest { - - // this tempDir is deliberately static - @TempDir private static Path folder; - - private static final Bytes VALID_PRIVATE_TRANSACTION_RLP = - Bytes.fromHexString( - "0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87" - + "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - + "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d" - + "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94" - + "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267" - + "5062572f776a3561784470573958386c393153476f3df85aac41316156744d" - + "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838" - + "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f" - + "6e766966746a69697a706a52742b4854754642733d8a726573747269637465" - + "64"); - private static final String DEFAULT_OUTPUT = "0x01"; - - private static Enclave enclave; - private static MessageFrame messageFrame; - - private static TesseraTestHarness testHarness; - private static WorldStateArchive worldStateArchive; - private static PrivateStateStorage privateStateStorage; - private static final Vertx vertx = Vertx.vertx(); - - private PrivateTransactionProcessor mockPrivateTxProcessor() { - final PrivateTransactionProcessor mockPrivateTransactionProcessor = - mock(PrivateTransactionProcessor.class); - final TransactionProcessingResult result = - TransactionProcessingResult.successful( - null, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null); - when(mockPrivateTransactionProcessor.processTransaction( - nullable(WorldUpdater.class), - nullable(WorldUpdater.class), - nullable(ProcessableBlockHeader.class), - nullable(Hash.class), - nullable(PrivateTransaction.class), - nullable(Address.class), - nullable(OperationTracer.class), - nullable(BlockHashLookup.class), - nullable(Bytes.class))) - .thenReturn(result); - - return mockPrivateTransactionProcessor; - } - - @BeforeAll - public static void setUpOnce() throws Exception { - - testHarness = - TesseraTestHarnessFactory.create( - "enclave", - Files.createTempDirectory(folder, "enclave"), - new EnclaveKeyConfiguration( - new String[] {"enclave_key_0.pub"}, - new String[] {"enclave_key_1.key"}, - EnclaveEncryptorType.NOOP), - Optional.empty()); - - testHarness.start(); - - final EnclaveFactory factory = new EnclaveFactory(vertx); - enclave = factory.createVertxEnclave(testHarness.clientUrl()); - messageFrame = mock(MessageFrame.class); - final BlockDataGenerator blockGenerator = new BlockDataGenerator(); - final Block genesis = blockGenerator.genesisBlock(); - final Block block = - blockGenerator.block( - new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash())); - when(messageFrame.getBlockValues()).thenReturn(block.getHeader()); - final PrivateMetadataUpdater privateMetadataUpdater = mock(PrivateMetadataUpdater.class); - when(privateMetadataUpdater.getPrivateBlockMetadata(any())).thenReturn(null); - when(privateMetadataUpdater.getPrivacyGroupHeadBlockMap()) - .thenReturn(PrivacyGroupHeadBlockMap.empty()); - when(messageFrame.getContextVariable( - eq(PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE), anyBoolean())) - .thenReturn(false); - when(messageFrame.getContextVariable(eq(PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER))) - .thenReturn(privateMetadataUpdater); - when(messageFrame.hasContextVariable(eq(PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER))) - .thenReturn(true); - - worldStateArchive = mock(WorldStateArchive.class); - final MutableWorldState mutableWorldState = mock(MutableWorldState.class); - when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class)); - when(worldStateArchive.getMutable()).thenReturn(mutableWorldState); - when(worldStateArchive.getMutable(any(), any())).thenReturn(Optional.of(mutableWorldState)); - - privateStateStorage = mock(PrivateStateStorage.class); - final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class); - when(privateStateStorage.getPrivacyGroupHeadBlockMap(any())) - .thenReturn(Optional.of(PrivacyGroupHeadBlockMap.empty())); - when(storageUpdater.putPrivateBlockMetadata( - nullable(Bytes32.class), nullable(Bytes32.class), any())) - .thenReturn(storageUpdater); - when(storageUpdater.putTransactionReceipt( - nullable(Bytes32.class), nullable(Bytes32.class), any())) - .thenReturn(storageUpdater); - when(privateStateStorage.updater()).thenReturn(storageUpdater); - } - - @AfterAll - public static void tearDownOnce() { - testHarness.stop(); - vertx.close(); - } - - @Test - public void testUpCheck() { - assertThat(enclave.upCheck()).isTrue(); - } - - @Test - public void testSendAndReceive() { - final List publicKeys = testHarness.getPublicKeys(); - - final PrivateTransaction privateTransaction = - PrivateTransactionDataFixture.privateContractDeploymentTransactionBesu(publicKeys.get(0)); - final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput(); - privateTransaction.writeTo(bytesValueRLPOutput); - - final String s = bytesValueRLPOutput.encoded().toBase64String(); - final SendResponse sr = - enclave.send(s, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0))); - - final PrivacyPrecompiledContract privacyPrecompiledContract = - new PrivacyPrecompiledContract( - new SpuriousDragonGasCalculator(), - enclave, - worldStateArchive, - new PrivateStateRootResolver(privateStateStorage), - new PrivateStateGenesisAllocator( - false, (privacyGroupId, blockNumber) -> Collections::emptyList), - false, - "IntegrationTest"); - - privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); - - final PrecompiledContract.PrecompileContractResult result = - privacyPrecompiledContract.computePrecompile( - Bytes.fromBase64String(sr.getKey()), messageFrame); - final Bytes actual = result.getOutput(); - - assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT)); - } - - @Test - public void testNoPrivateKeyError() throws RuntimeException { - final List publicKeys = testHarness.getPublicKeys(); - publicKeys.add("noPrivateKey"); - - final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String(); - - final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys)); - - assertThat(thrown).hasMessageContaining("Index 9 out of bounds for length 9"); - } - - @Test - public void testWrongPrivateKeyError() throws RuntimeException { - final List publicKeys = testHarness.getPublicKeys(); - publicKeys.add("noPrivateKenoPrivateKenoPrivateKenoPrivateK"); - - final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String(); - - final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys)); - - assertThat(thrown).hasMessageContaining("Recipient not found for key:"); - } -} diff --git a/testutil/src/main/resources/enclave_key_0.key b/testutil/src/main/resources/enclave_key_0.key deleted file mode 100644 index eaae9b0867c..00000000000 --- a/testutil/src/main/resources/enclave_key_0.key +++ /dev/null @@ -1 +0,0 @@ -{"data":{"bytes":"hBsuQsGJzx4QHmFmBkNoI7YGnTmaZP4P+wBOdu56ljk="},"type":"unlocked"} \ No newline at end of file From 874cba016db3ef3c64df9d2ccf0b2b4a32d5d0f8 Mon Sep 17 00:00:00 2001 From: Matt Whitehead Date: Mon, 23 Sep 2024 11:58:47 +0100 Subject: [PATCH 03/12] Update protobuf to 32.25.5 to resolve CVE-2024-7254 (#7664) Signed-off-by: Matthew Whitehead --- gradle/verification-metadata.xml | 36 ++++++++++++++++++++++++++++++++ gradle/versions.gradle | 2 ++ 2 files changed, 38 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 3778a2d60ad..7f6a58b66e7 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1023,6 +1023,11 @@ + + + + + @@ -1033,6 +1038,11 @@ + + + + + @@ -1049,6 +1059,14 @@ + + + + + + + + @@ -1062,6 +1080,14 @@ + + + + + + + + @@ -1080,6 +1106,11 @@ + + + + + @@ -1090,6 +1121,11 @@ + + + + + diff --git a/gradle/versions.gradle b/gradle/versions.gradle index b7a286e69a3..634c29b1e13 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -25,6 +25,8 @@ dependencyManagement { dependency 'com.github.ben-manes.caffeine:caffeine:3.1.8' + dependency 'com.google.protobuf:protobuf-java:3.25.5' + dependency 'com.github.oshi:oshi-core:6.6.3' dependency 'com.google.auto.service:auto-service:1.1.1' From 0d6395515890280c29ee2402c03b1ee81bde3bab Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Mon, 23 Sep 2024 14:34:20 +0200 Subject: [PATCH 04/12] Docker: Only switch user if the current user is root (#7654) * Update entrypoint script for Dockerfile to only switch user if its running as root Signed-off-by: Rafael Matias * make root user check at the beginning Signed-off-by: Rafael Matias --------- Signed-off-by: Rafael Matias --- besu/src/main/scripts/besu-entry.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/besu/src/main/scripts/besu-entry.sh b/besu/src/main/scripts/besu-entry.sh index ed3687b2291..ee11bfbffc2 100755 --- a/besu/src/main/scripts/besu-entry.sh +++ b/besu/src/main/scripts/besu-entry.sh @@ -14,6 +14,14 @@ ## SPDX-License-Identifier: Apache-2.0 ## +# Construct the command as a single string +COMMAND="/opt/besu/bin/besu $@" + +# Check if current user is not root. If not, run the command as is. +if [ "$(id -u)" -ne 0 ]; then + exec /bin/bash -c "$COMMAND" +fi + # Run Besu first to get paths needing permission adjustment output=$(/opt/besu/bin/besu --print-paths-and-exit $BESU_USER_NAME "$@") @@ -41,9 +49,5 @@ echo "$output" | while IFS=: read -r prefix path accessType; do fi done -# Finally, run Besu with the actual arguments passed to the container -# Construct the command as a single string -COMMAND="/opt/besu/bin/besu $@" - # Switch to the besu user and execute the command -exec su -s /bin/bash $BESU_USER_NAME -c "$COMMAND" +exec su -s /bin/bash "$BESU_USER_NAME" -c "$COMMAND" From 4f07e76a6c26b294bcc8f50deefefc78438636dd Mon Sep 17 00:00:00 2001 From: Matilda-Clerke Date: Tue, 24 Sep 2024 13:04:03 +1000 Subject: [PATCH 05/12] 7311: Add feature toggle for enabling use of the peertask system where available (#7633) Signed-off-by: Matilda Clerke --- .../options/unstable/SynchronizerOptions.java | 18 +++++++++++++++++- besu/src/test/resources/everything_config.toml | 1 + .../eth/sync/SynchronizerConfiguration.java | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java index 95bbe0a2b1f..816d9df00a1 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/SynchronizerOptions.java @@ -314,6 +314,13 @@ public void parseBlockPropagationRange(final String arg) { description = "Snap sync enabled for BFT chains (default: ${DEFAULT-VALUE})") private Boolean snapsyncBftEnabled = SnapSyncConfiguration.DEFAULT_SNAP_SYNC_BFT_ENABLED; + @CommandLine.Option( + names = {"--Xpeertask-system-enabled"}, + hidden = true, + description = + "Temporary feature toggle to enable using the new peertask system (default: ${DEFAULT-VALUE})") + private final Boolean isPeerTaskSystemEnabled = false; + private SynchronizerOptions() {} /** @@ -334,6 +341,15 @@ public boolean isSnapSyncBftEnabled() { return snapsyncBftEnabled; } + /** + * Flag to indicate whether the peer task system should be used where available + * + * @return true if the peer task system should be used where available + */ + public boolean isPeerTaskSystemEnabled() { + return isPeerTaskSystemEnabled; + } + /** * Create synchronizer options. * @@ -420,7 +436,7 @@ public SynchronizerConfiguration.Builder toDomainObject() { .isSnapSyncBftEnabled(snapsyncBftEnabled) .build()); builder.checkpointPostMergeEnabled(checkpointPostMergeSyncEnabled); - + builder.isPeerTaskSystemEnabled(isPeerTaskSystemEnabled); return builder; } diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index e3d7a3f28d3..d28152b47cd 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -226,6 +226,7 @@ Xsecp256k1-native-enabled=false Xaltbn128-native-enabled=false Xsnapsync-server-enabled=true Xbonsai-full-flat-db-enabled=true +Xpeertask-system-enabled=false # compatibility flags compatibility-eth64-forkid-enabled=false diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SynchronizerConfiguration.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SynchronizerConfiguration.java index d46da85dc48..d72f76c213c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SynchronizerConfiguration.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SynchronizerConfiguration.java @@ -85,6 +85,7 @@ public class SynchronizerConfiguration { private final int maxTrailingPeers; private final long worldStateMinMillisBeforeStalling; private final long propagationManagerGetBlockTimeoutMillis; + private final boolean isPeerTaskSystemEnabled; private SynchronizerConfiguration( final int syncPivotDistance, @@ -108,7 +109,8 @@ private SynchronizerConfiguration( final int computationParallelism, final int maxTrailingPeers, final long propagationManagerGetBlockTimeoutMillis, - final boolean checkpointPostMergeEnabled) { + final boolean checkpointPostMergeEnabled, + final boolean isPeerTaskSystemEnabled) { this.syncPivotDistance = syncPivotDistance; this.fastSyncFullValidationRate = fastSyncFullValidationRate; this.syncMinimumPeerCount = syncMinimumPeerCount; @@ -131,6 +133,7 @@ private SynchronizerConfiguration( this.maxTrailingPeers = maxTrailingPeers; this.propagationManagerGetBlockTimeoutMillis = propagationManagerGetBlockTimeoutMillis; this.checkpointPostMergeEnabled = checkpointPostMergeEnabled; + this.isPeerTaskSystemEnabled = isPeerTaskSystemEnabled; } public static Builder builder() { @@ -256,6 +259,10 @@ public long getPropagationManagerGetBlockTimeoutMillis() { return propagationManagerGetBlockTimeoutMillis; } + public boolean isPeerTaskSystemEnabled() { + return isPeerTaskSystemEnabled; + } + public static class Builder { private SyncMode syncMode = SyncMode.FULL; private int syncMinimumPeerCount = DEFAULT_SYNC_MINIMUM_PEERS; @@ -280,6 +287,7 @@ public static class Builder { DEFAULT_WORLD_STATE_MAX_REQUESTS_WITHOUT_PROGRESS; private long worldStateMinMillisBeforeStalling = DEFAULT_WORLD_STATE_MIN_MILLIS_BEFORE_STALLING; private int worldStateTaskCacheSize = DEFAULT_WORLD_STATE_TASK_CACHE_SIZE; + private boolean isPeerTaskSystemEnabled = false; private long propagationManagerGetBlockTimeoutMillis = DEFAULT_PROPAGATION_MANAGER_GET_BLOCK_TIMEOUT_MILLIS; @@ -406,6 +414,11 @@ public Builder checkpointPostMergeEnabled(final boolean checkpointPostMergeEnabl return this; } + public Builder isPeerTaskSystemEnabled(final boolean isPeerTaskSystemEnabled) { + this.isPeerTaskSystemEnabled = isPeerTaskSystemEnabled; + return this; + } + public SynchronizerConfiguration build() { return new SynchronizerConfiguration( syncPivotDistance, @@ -429,7 +442,8 @@ public SynchronizerConfiguration build() { computationParallelism, maxTrailingPeers, propagationManagerGetBlockTimeoutMillis, - checkpointPostMergeEnabled); + checkpointPostMergeEnabled, + isPeerTaskSystemEnabled); } } } From 04ba15aa922659a1516589612c6057301804c4d5 Mon Sep 17 00:00:00 2001 From: Glory Agatevure Date: Tue, 24 Sep 2024 06:06:16 +0100 Subject: [PATCH 06/12] Add consolidationRequestContract in jsonGenesisConfig (#7647) * Include consolidationRequestContract in jsonGenesisConfigOptions Signed-off-by: gconnect * Update changelog and ran spotlessApply Signed-off-by: gconnect * Rename consolidationRequestPredeployAddress to consolidationRequestContractAddress Signed-off-by: gconnect * Create request contract addresses class Signed-off-by: gconnect * Update method calls Signed-off-by: gconnect * Refactor RequestContractAddresses class and update method calls and test Signed-off-by: gconnect --------- Signed-off-by: gconnect Co-authored-by: Gabriel-Trintinalia --- CHANGELOG.md | 2 + .../besu/config/GenesisConfigOptions.java | 7 +++ .../besu/config/JsonGenesisConfigOptions.java | 11 ++++ .../besu/config/StubGenesisConfigOptions.java | 5 ++ .../besu/config/GenesisConfigOptionsTest.java | 27 ++++++++ .../mainnet/MainnetProtocolSpecs.java | 16 ++--- .../ConsolidationRequestProcessor.java | 9 ++- .../requests/MainnetRequestsValidator.java | 21 ++++--- .../requests/RequestContractAddresses.java | 61 +++++++++++++++++++ .../mainnet/PragueRequestsValidatorTest.java | 11 +++- 10 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestContractAddresses.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 96e1511eead..5ce29955d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +- Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647) + ### Upcoming Breaking Changes - k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release diff --git a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java index d6323ee9bfb..07ddd0d7eac 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java @@ -539,4 +539,11 @@ default boolean isConsensusMigration() { * @return the deposit address */ Optional
getDepositContractAddress(); + + /** + * The consolidation request contract address + * + * @return the consolidation request contract address + */ + Optional
getConsolidationRequestContractAddress(); } diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index 67114b29bf3..83b1f48fb48 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -52,6 +52,8 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { private static final String WITHDRAWAL_REQUEST_CONTRACT_ADDRESS_KEY = "withdrawalrequestcontractaddress"; private static final String DEPOSIT_CONTRACT_ADDRESS_KEY = "depositcontractaddress"; + private static final String CONSOLIDATION_REQUEST_CONTRACT_ADDRESS_KEY = + "consolidationrequestcontractaddress"; private final ObjectNode configRoot; private final Map configOverrides = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); @@ -453,6 +455,13 @@ public Optional
getDepositContractAddress() { return inputAddress.map(Address::fromHexString); } + @Override + public Optional
getConsolidationRequestContractAddress() { + Optional inputAddress = + JsonUtil.getString(configRoot, CONSOLIDATION_REQUEST_CONTRACT_ADDRESS_KEY); + return inputAddress.map(Address::fromHexString); + } + @Override public Map asMap() { final ImmutableMap.Builder builder = ImmutableMap.builder(); @@ -504,6 +513,8 @@ public Map asMap() { getWithdrawalRequestContractAddress() .ifPresent(l -> builder.put("withdrawalRequestContractAddress", l)); getDepositContractAddress().ifPresent(l -> builder.put("depositContractAddress", l)); + getConsolidationRequestContractAddress() + .ifPresent(l -> builder.put("consolidationRequestContractAddress", l)); if (isClique()) { builder.put("clique", getCliqueConfigOptions().asMap()); diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index efe56a086d0..ee9584fd3a4 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -467,6 +467,11 @@ public Optional
getDepositContractAddress() { return Optional.empty(); } + @Override + public Optional
getConsolidationRequestContractAddress() { + return Optional.empty(); + } + /** * Homestead block stub genesis config options. * diff --git a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java index 219ea4fcf8a..69494a9c9c4 100644 --- a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java @@ -382,6 +382,33 @@ void asMapIncludesDepositContractAddress() { .containsValue(Address.ZERO); } + @Test + void shouldGetConsolidationRequestContractAddress() { + final GenesisConfigOptions config = + fromConfigOptions( + singletonMap( + "consolidationRequestContractAddress", + "0x00000000219ab540356cbb839cbe05303d7705fa")); + assertThat(config.getConsolidationRequestContractAddress()) + .hasValue(Address.fromHexString("0x00000000219ab540356cbb839cbe05303d7705fa")); + } + + @Test + void shouldNotHaveConsolidationRequestContractAddressWhenEmpty() { + final GenesisConfigOptions config = fromConfigOptions(emptyMap()); + assertThat(config.getConsolidationRequestContractAddress()).isEmpty(); + } + + @Test + void asMapIncludesConsolidationRequestContractAddress() { + final GenesisConfigOptions config = + fromConfigOptions(Map.of("consolidationRequestContractAddress", "0x0")); + + assertThat(config.asMap()) + .containsOnlyKeys("consolidationRequestContractAddress") + .containsValue(Address.ZERO); + } + private GenesisConfigOptions fromConfigOptions(final Map configOptions) { final ObjectNode rootNode = JsonUtil.createEmptyObjectNode(); final ObjectNode options = JsonUtil.objectNodeFromMap(configOptions); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index e6d7a88cbdf..b5b2f678d67 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -14,10 +14,8 @@ */ package org.hyperledger.besu.ethereum.mainnet; -import static org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestProcessor.DEFAULT_DEPOSIT_CONTRACT_ADDRESS; import static org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsValidator.pragueRequestsProcessors; import static org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsValidator.pragueRequestsValidator; -import static org.hyperledger.besu.ethereum.mainnet.requests.WithdrawalRequestProcessor.DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.PowAlgorithm; @@ -41,6 +39,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.parallelization.MainnetParallelBlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.requests.RequestContractAddresses; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; @@ -767,12 +766,8 @@ static ProtocolSpecBuilder pragueDefinition( final boolean isParallelTxProcessingEnabled, final MetricsSystem metricsSystem) { - final Address withdrawalRequestContractAddress = - genesisConfigOptions - .getWithdrawalRequestContractAddress() - .orElse(DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS); - final Address depositContractAddress = - genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS); + RequestContractAddresses requestContractAddresses = + RequestContractAddresses.fromGenesis(genesisConfigOptions); return cancunDefinition( chainId, @@ -794,10 +789,9 @@ static ProtocolSpecBuilder pragueDefinition( .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::prague) // EIP-7002 Withdrawals / EIP-6610 Deposits / EIP-7685 Requests - .requestsValidator(pragueRequestsValidator(depositContractAddress)) + .requestsValidator(pragueRequestsValidator(requestContractAddresses)) // EIP-7002 Withdrawals / EIP-6610 Deposits / EIP-7685 Requests - .requestProcessorCoordinator( - pragueRequestsProcessors(withdrawalRequestContractAddress, depositContractAddress)) + .requestProcessorCoordinator(pragueRequestsProcessors(requestContractAddresses)) // change to accept EIP-7702 transactions .transactionValidatorFactoryBuilder( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ConsolidationRequestProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ConsolidationRequestProcessor.java index 0a48d8278c9..641720670fb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ConsolidationRequestProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ConsolidationRequestProcessor.java @@ -22,13 +22,18 @@ public class ConsolidationRequestProcessor extends AbstractSystemCallRequestProcessor { - public static final Address CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = + public static final Address CONSOLIDATION_REQUEST_CONTRACT_ADDRESS = Address.fromHexString("0x00b42dbF2194e931E80326D950320f7d9Dbeac02"); private static final int ADDRESS_BYTES = 20; private static final int PUBLIC_KEY_BYTES = 48; private static final int CONSOLIDATION_REQUEST_BYTES_SIZE = ADDRESS_BYTES + PUBLIC_KEY_BYTES + PUBLIC_KEY_BYTES; + private final Address consolidationRequestContractAddress; + + public ConsolidationRequestProcessor(final Address consolidationRequestContractAddress) { + this.consolidationRequestContractAddress = consolidationRequestContractAddress; + } /** * Gets the call address for consolidation requests. @@ -37,7 +42,7 @@ public class ConsolidationRequestProcessor */ @Override protected Address getCallAddress() { - return CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; + return consolidationRequestContractAddress; } /** diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java index 6e61a0343c3..9c86d18f7ad 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java @@ -14,27 +14,34 @@ */ package org.hyperledger.besu.ethereum.mainnet.requests; -import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.RequestType; public class MainnetRequestsValidator { public static RequestsValidatorCoordinator pragueRequestsValidator( - final Address depositContractAddress) { + final RequestContractAddresses requestContractAddresses) { return new RequestsValidatorCoordinator.Builder() .addValidator(RequestType.WITHDRAWAL, new WithdrawalRequestValidator()) .addValidator(RequestType.CONSOLIDATION, new ConsolidationRequestValidator()) - .addValidator(RequestType.DEPOSIT, new DepositRequestValidator(depositContractAddress)) + .addValidator( + RequestType.DEPOSIT, + new DepositRequestValidator(requestContractAddresses.getDepositContractAddress())) .build(); } public static RequestProcessorCoordinator pragueRequestsProcessors( - final Address withdrawalRequestContractAddress, final Address depositContractAddress) { + final RequestContractAddresses requestContractAddresses) { return new RequestProcessorCoordinator.Builder() .addProcessor( RequestType.WITHDRAWAL, - new WithdrawalRequestProcessor(withdrawalRequestContractAddress)) - .addProcessor(RequestType.CONSOLIDATION, new ConsolidationRequestProcessor()) - .addProcessor(RequestType.DEPOSIT, new DepositRequestProcessor(depositContractAddress)) + new WithdrawalRequestProcessor( + requestContractAddresses.getWithdrawalRequestContractAddress())) + .addProcessor( + RequestType.CONSOLIDATION, + new ConsolidationRequestProcessor( + requestContractAddresses.getConsolidationRequestContractAddress())) + .addProcessor( + RequestType.DEPOSIT, + new DepositRequestProcessor(requestContractAddresses.getDepositContractAddress())) .build(); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestContractAddresses.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestContractAddresses.java new file mode 100644 index 00000000000..b75677dda79 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestContractAddresses.java @@ -0,0 +1,61 @@ +/* + * Copyright contributors to Hyperledger 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.ethereum.mainnet.requests; + +import static org.hyperledger.besu.ethereum.mainnet.requests.ConsolidationRequestProcessor.CONSOLIDATION_REQUEST_CONTRACT_ADDRESS; +import static org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestProcessor.DEFAULT_DEPOSIT_CONTRACT_ADDRESS; +import static org.hyperledger.besu.ethereum.mainnet.requests.WithdrawalRequestProcessor.DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS; + +import org.hyperledger.besu.config.GenesisConfigOptions; +import org.hyperledger.besu.datatypes.Address; + +public class RequestContractAddresses { + private final Address withdrawalRequestContractAddress; + private final Address depositContractAddress; + private final Address consolidationRequestContractAddress; + + public RequestContractAddresses( + final Address withdrawalRequestContractAddress, + final Address depositContractAddress, + final Address consolidationRequestContractAddress) { + this.withdrawalRequestContractAddress = withdrawalRequestContractAddress; + this.depositContractAddress = depositContractAddress; + this.consolidationRequestContractAddress = consolidationRequestContractAddress; + } + + public static RequestContractAddresses fromGenesis( + final GenesisConfigOptions genesisConfigOptions) { + return new RequestContractAddresses( + genesisConfigOptions + .getWithdrawalRequestContractAddress() + .orElse(DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS), + genesisConfigOptions.getDepositContractAddress().orElse(DEFAULT_DEPOSIT_CONTRACT_ADDRESS), + genesisConfigOptions + .getConsolidationRequestContractAddress() + .orElse(CONSOLIDATION_REQUEST_CONTRACT_ADDRESS)); + } + + public Address getWithdrawalRequestContractAddress() { + return withdrawalRequestContractAddress; + } + + public Address getDepositContractAddress() { + return depositContractAddress; + } + + public Address getConsolidationRequestContractAddress() { + return consolidationRequestContractAddress; + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueRequestsValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueRequestsValidatorTest.java index 6c325b70a86..6158ba44c37 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueRequestsValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueRequestsValidatorTest.java @@ -17,8 +17,10 @@ import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode.NONE; +import static org.hyperledger.besu.ethereum.mainnet.requests.ConsolidationRequestProcessor.CONSOLIDATION_REQUEST_CONTRACT_ADDRESS; import static org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestProcessor.DEFAULT_DEPOSIT_CONTRACT_ADDRESS; import static org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsValidator.pragueRequestsValidator; +import static org.hyperledger.besu.ethereum.mainnet.requests.WithdrawalRequestProcessor.DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; @@ -31,6 +33,7 @@ import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.Request; import org.hyperledger.besu.ethereum.core.WithdrawalRequest; +import org.hyperledger.besu.ethereum.mainnet.requests.RequestContractAddresses; import org.hyperledger.besu.ethereum.mainnet.requests.RequestsValidatorCoordinator; import org.hyperledger.besu.evm.log.LogsBloomFilter; @@ -52,9 +55,13 @@ class PragueRequestsValidatorTest { @Mock private ProtocolSchedule protocolSchedule; @Mock private ProtocolSpec protocolSpec; @Mock private WithdrawalsValidator withdrawalsValidator; + private final RequestContractAddresses requestContractAddresses = + new RequestContractAddresses( + DEFAULT_WITHDRAWAL_REQUEST_CONTRACT_ADDRESS, + DEFAULT_DEPOSIT_CONTRACT_ADDRESS, + CONSOLIDATION_REQUEST_CONTRACT_ADDRESS); - RequestsValidatorCoordinator requestValidator = - pragueRequestsValidator(DEFAULT_DEPOSIT_CONTRACT_ADDRESS); + RequestsValidatorCoordinator requestValidator = pragueRequestsValidator(requestContractAddresses); @BeforeEach public void setUp() { From 2aa3848950c86bbe720531539bdb990ab1fcaa7b Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 24 Sep 2024 11:49:17 +0200 Subject: [PATCH 07/12] Enable logging during unit tests (#7672) Signed-off-by: Fabio Di Fabio --- testutil/src/main/resources/{log4j2.xml => log4j2-test.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename testutil/src/main/resources/{log4j2.xml => log4j2-test.xml} (100%) diff --git a/testutil/src/main/resources/log4j2.xml b/testutil/src/main/resources/log4j2-test.xml similarity index 100% rename from testutil/src/main/resources/log4j2.xml rename to testutil/src/main/resources/log4j2-test.xml From e0518c6d94bf1ae4f0e3742a9e3e51aac7e15fb3 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 24 Sep 2024 20:50:19 +1000 Subject: [PATCH 08/12] Force besu to stop on plugin initialization errors (#7662) Signed-off-by: Gabriel-Trintinalia --- CHANGELOG.md | 1 + .../dsl/node/ThreadBesuNodeRunner.java | 3 +- .../acceptance/plugins/TestPicoCLIPlugin.java | 51 +++- .../services/BesuPluginContextImplTest.java | 266 +++++++++++++++--- .../org/hyperledger/besu/cli/BesuCommand.java | 6 +- .../besu/cli/DefaultCommandValues.java | 3 + .../stable/PluginsConfigurationOptions.java | 30 +- .../besu/services/BesuPluginContextImpl.java | 91 ++++-- .../besu/cli/PluginsOptionsTest.java | 51 +++- .../src/test/resources/everything_config.toml | 3 +- .../core/plugins/PluginConfiguration.java | 18 +- 11 files changed, 424 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce29955d12..bb867af945e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release ### Breaking Changes +- Besu will now fail to start if any plugins encounter errors during initialization. To allow Besu to continue running despite plugin errors, use the `--plugin-continue-on-error` option. [#7662](https://github.com/hyperledger/besu/pull/7662) ### Additions and Improvements - Remove privacy test classes support [#7569](https://github.com/hyperledger/besu/pull/7569) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 5d78f1460c4..90de0b0e952 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -503,8 +503,9 @@ public BesuPluginContextImpl providePluginContext( besuPluginContext.addService(PermissioningService.class, permissioningService); besuPluginContext.addService(PrivacyPluginService.class, new PrivacyPluginServiceImpl()); - besuPluginContext.registerPlugins( + besuPluginContext.initialize( new PluginConfiguration.Builder().pluginsDir(pluginsPath).build()); + besuPluginContext.registerPlugins(); commandLine.parseArgs(extraCLIOptions.toArray(new String[0])); // register built-in plugins diff --git a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestPicoCLIPlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestPicoCLIPlugin.java index 0db5281537c..375fbd490ec 100644 --- a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestPicoCLIPlugin.java +++ b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestPicoCLIPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger 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 @@ -32,16 +32,25 @@ public class TestPicoCLIPlugin implements BesuPlugin { private static final Logger LOG = LoggerFactory.getLogger(TestPicoCLIPlugin.class); + private static final String UNSET = "UNSET"; + private static final String FAIL_REGISTER = "FAILREGISTER"; + private static final String FAIL_BEFORE_EXTERNAL_SERVICES = "FAILBEFOREEXTERNALSERVICES"; + private static final String FAIL_START = "FAILSTART"; + private static final String FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP = + "FAILAFTEREXTERNALSERVICEPOSTMAINLOOP"; + private static final String FAIL_STOP = "FAILSTOP"; + private static final String PLUGIN_LIFECYCLE_PREFIX = "pluginLifecycle."; + @Option( names = {"--Xplugin-test-option"}, hidden = true, - defaultValue = "UNSET") + defaultValue = UNSET) String testOption = System.getProperty("testPicoCLIPlugin.testOption"); @Option( names = {"--plugin-test-stable-option"}, hidden = true, - defaultValue = "UNSET") + defaultValue = UNSET) String stableOption = ""; private String state = "uninited"; @@ -52,7 +61,7 @@ public void register(final BesuContext context) { LOG.info("Registering. Test Option is '{}'", testOption); state = "registering"; - if ("FAILREGISTER".equals(testOption)) { + if (FAIL_REGISTER.equals(testOption)) { state = "failregister"; throw new RuntimeException("I was told to fail at registration"); } @@ -66,12 +75,26 @@ public void register(final BesuContext context) { state = "registered"; } + @Override + public void beforeExternalServices() { + LOG.info("Before external services. Test Option is '{}'", testOption); + state = "beforeExternalServices"; + + if (FAIL_BEFORE_EXTERNAL_SERVICES.equals(testOption)) { + state = "failbeforeExternalServices"; + throw new RuntimeException("I was told to fail before external services"); + } + + writeSignal("beforeExternalServices"); + state = "beforeExternalServicesFinished"; + } + @Override public void start() { LOG.info("Starting. Test Option is '{}'", testOption); state = "starting"; - if ("FAILSTART".equals(testOption)) { + if (FAIL_START.equals(testOption)) { state = "failstart"; throw new RuntimeException("I was told to fail at startup"); } @@ -80,12 +103,26 @@ public void start() { state = "started"; } + @Override + public void afterExternalServicePostMainLoop() { + LOG.info("After external services post main loop. Test Option is '{}'", testOption); + state = "afterExternalServicePostMainLoop"; + + if (FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP.equals(testOption)) { + state = "failafterExternalServicePostMainLoop"; + throw new RuntimeException("I was told to fail after external services post main loop"); + } + + writeSignal("afterExternalServicePostMainLoop"); + state = "afterExternalServicePostMainLoopFinished"; + } + @Override public void stop() { LOG.info("Stopping. Test Option is '{}'", testOption); state = "stopping"; - if ("FAILSTOP".equals(testOption)) { + if (FAIL_STOP.equals(testOption)) { state = "failstop"; throw new RuntimeException("I was told to fail at stop"); } @@ -103,7 +140,7 @@ public String getState() { @SuppressWarnings("ResultOfMethodCallIgnored") private void writeSignal(final String signal) { try { - final File callbackFile = new File(callbackDir, "pluginLifecycle." + signal); + final File callbackFile = new File(callbackDir, PLUGIN_LIFECYCLE_PREFIX + signal); if (!callbackFile.getParentFile().exists()) { callbackFile.getParentFile().mkdirs(); callbackFile.getParentFile().deleteOnExit(); diff --git a/acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java b/acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java index 7e277041776..a6d167665ff 100644 --- a/acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java +++ b/acceptance-tests/test-plugins/src/test/java/org/hyperledger/besu/services/BesuPluginContextImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright contributors to Hyperledger 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 @@ -40,8 +40,31 @@ public class BesuPluginContextImplTest { private static final Path DEFAULT_PLUGIN_DIRECTORY = Paths.get("."); + private static final String TEST_PICO_CLI_PLUGIN = "TestPicoCLIPlugin"; + private static final String TEST_PICO_CLI_PLUGIN_TEST_OPTION = "testPicoCLIPlugin.testOption"; + private static final String FAIL_REGISTER = "FAILREGISTER"; + private static final String FAIL_START = "FAILSTART"; + private static final String FAIL_STOP = "FAILSTOP"; + private static final String FAIL_BEFORE_EXTERNAL_SERVICES = "FAILBEFOREEXTERNALSERVICES"; + private static final String FAIL_BEFORE_MAIN_LOOP = "FAILBEFOREMAINLOOP"; + private static final String FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP = + "FAILAFTEREXTERNALSERVICEPOSTMAINLOOP"; + private static final String NON_EXISTENT_PLUGIN = "NonExistentPlugin"; + private static final String REGISTERED = "registered"; + private static final String STARTED = "started"; + private static final String STOPPED = "stopped"; + private static final String FAIL_START_STATE = "failstart"; + private static final String FAIL_STOP_STATE = "failstop"; + private BesuPluginContextImpl contextImpl; + private static final PluginConfiguration DEFAULT_CONFIGURATION = + PluginConfiguration.builder() + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .externalPluginsEnabled(true) + .continueOnPluginError(true) + .build(); + @BeforeAll public static void createFakePluginDir() throws IOException { if (System.getProperty("besu.plugins.dir") == null) { @@ -53,7 +76,7 @@ public static void createFakePluginDir() throws IOException { @AfterEach public void clearTestPluginState() { - System.clearProperty("testPicoCLIPlugin.testOption"); + System.clearProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION); } @BeforeEach @@ -64,31 +87,31 @@ void setup() { @Test public void verifyEverythingGoesSmoothly() { assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins( - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build()); + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); assertThat(contextImpl.getRegisteredPlugins()).isNotEmpty(); final Optional testPluginOptional = findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class); assertThat(testPluginOptional).isPresent(); final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED); contextImpl.beforeExternalServices(); contextImpl.startPlugins(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("started"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(STARTED); contextImpl.stopPlugins(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("stopped"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(STOPPED); } @Test public void registrationErrorsHandledSmoothly() { - System.setProperty("testPicoCLIPlugin.testOption", "FAILREGISTER"); + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER); assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins( - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build()); + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); contextImpl.beforeExternalServices(); @@ -103,11 +126,11 @@ public void registrationErrorsHandledSmoothly() { @Test public void startErrorsHandledSmoothly() { - System.setProperty("testPicoCLIPlugin.testOption", "FAILSTART"); + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_START); assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins( - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build()); + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); assertThat(contextImpl.getRegisteredPlugins()) .extracting("class") .contains(TestPicoCLIPlugin.class); @@ -116,11 +139,11 @@ public void startErrorsHandledSmoothly() { findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class); assertThat(testPluginOptional).isPresent(); final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED); contextImpl.beforeExternalServices(); contextImpl.startPlugins(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("failstart"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(FAIL_START_STATE); assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); contextImpl.stopPlugins(); @@ -129,11 +152,11 @@ public void startErrorsHandledSmoothly() { @Test public void stopErrorsHandledSmoothly() { - System.setProperty("testPicoCLIPlugin.testOption", "FAILSTOP"); + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_STOP); assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins( - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build()); + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); assertThat(contextImpl.getRegisteredPlugins()) .extracting("class") .contains(TestPicoCLIPlugin.class); @@ -142,22 +165,20 @@ public void stopErrorsHandledSmoothly() { findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class); assertThat(testPluginOptional).isPresent(); final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED); contextImpl.beforeExternalServices(); contextImpl.startPlugins(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("started"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(STARTED); contextImpl.stopPlugins(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("failstop"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(FAIL_STOP_STATE); } @Test public void lifecycleExceptions() throws Throwable { - final ThrowableAssert.ThrowingCallable registerPlugins = - () -> - contextImpl.registerPlugins( - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build()); + contextImpl.initialize(DEFAULT_CONFIGURATION); + final ThrowableAssert.ThrowingCallable registerPlugins = () -> contextImpl.registerPlugins(); assertThatExceptionOfType(IllegalStateException.class).isThrownBy(contextImpl::startPlugins); assertThatExceptionOfType(IllegalStateException.class).isThrownBy(contextImpl::stopPlugins); @@ -179,30 +200,27 @@ public void lifecycleExceptions() throws Throwable { @Test public void shouldRegisterAllPluginsWhenNoPluginsOption() { - final PluginConfiguration config = - PluginConfiguration.builder().pluginsDir(DEFAULT_PLUGIN_DIRECTORY).build(); - assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins(config); + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); final Optional testPluginOptional = findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class); assertThat(testPluginOptional).isPresent(); final TestPicoCLIPlugin testPicoCLIPlugin = testPluginOptional.get(); - assertThat(testPicoCLIPlugin.getState()).isEqualTo("registered"); + assertThat(testPicoCLIPlugin.getState()).isEqualTo(REGISTERED); } @Test public void shouldRegisterOnlySpecifiedPluginWhenPluginsOptionIsSet() { - final PluginConfiguration config = createConfigurationForSpecificPlugin("TestPicoCLIPlugin"); - assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins(config); + contextImpl.initialize(createConfigurationForSpecificPlugin(TEST_PICO_CLI_PLUGIN)); + contextImpl.registerPlugins(); final Optional requestedPlugin = findTestPlugin(contextImpl.getRegisteredPlugins(), TestPicoCLIPlugin.class); assertThat(requestedPlugin).isPresent(); - assertThat(requestedPlugin.get().getState()).isEqualTo("registered"); + assertThat(requestedPlugin.get().getState()).isEqualTo(REGISTERED); final Optional nonRequestedPlugin = findTestPlugin(contextImpl.getRegisteredPlugins(), TestBesuEventsPlugin.class); @@ -212,9 +230,9 @@ public void shouldRegisterOnlySpecifiedPluginWhenPluginsOptionIsSet() { @Test public void shouldNotRegisterUnspecifiedPluginsWhenPluginsOptionIsSet() { - final PluginConfiguration config = createConfigurationForSpecificPlugin("TestPicoCLIPlugin"); assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); - contextImpl.registerPlugins(config); + contextImpl.initialize(createConfigurationForSpecificPlugin(TEST_PICO_CLI_PLUGIN)); + contextImpl.registerPlugins(); final Optional nonRequestedPlugin = findTestPlugin(contextImpl.getRegisteredPlugins(), TestBesuEventsPlugin.class); @@ -223,13 +241,12 @@ public void shouldNotRegisterUnspecifiedPluginsWhenPluginsOptionIsSet() { @Test void shouldThrowExceptionIfExplicitlySpecifiedPluginNotFound() { - PluginConfiguration config = createConfigurationForSpecificPlugin("NonExistentPlugin"); - + contextImpl.initialize(createConfigurationForSpecificPlugin(NON_EXISTENT_PLUGIN)); String exceptionMessage = - assertThrows(NoSuchElementException.class, () -> contextImpl.registerPlugins(config)) + assertThrows(NoSuchElementException.class, () -> contextImpl.registerPlugins()) .getMessage(); final String expectedMessage = - "The following requested plugins were not found: NonExistentPlugin"; + "The following requested plugins were not found: " + NON_EXISTENT_PLUGIN; assertThat(exceptionMessage).isEqualTo(expectedMessage); assertThat(contextImpl.getRegisteredPlugins()).isEmpty(); } @@ -241,19 +258,180 @@ void shouldNotRegisterAnyPluginsIfExternalPluginsDisabled() { .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) .externalPluginsEnabled(false) .build(); - contextImpl.registerPlugins(config); + contextImpl.initialize(config); + contextImpl.registerPlugins(); assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isTrue(); } @Test void shouldRegisterPluginsIfExternalPluginsEnabled() { + contextImpl.initialize(DEFAULT_CONFIGURATION); + contextImpl.registerPlugins(); + assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isFalse(); + } + + @Test + void shouldHaltOnRegisterErrorWhenFlagIsFalse() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER); + PluginConfiguration config = PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) - .externalPluginsEnabled(true) + .continueOnPluginError(false) .build(); - contextImpl.registerPlugins(config); - assertThat(contextImpl.getRegisteredPlugins().isEmpty()).isFalse(); + + contextImpl.initialize(config); + + String errorMessage = + String.format("Error registering plugin of type %s", TestPicoCLIPlugin.class.getName()); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> contextImpl.registerPlugins()) + .withMessageContaining(errorMessage); + } + + @Test + void shouldNotHaltOnRegisterErrorWhenFlagIsTrue() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_REGISTER); + + PluginConfiguration config = + PluginConfiguration.builder() + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(true) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); + } + + @Test + void shouldHaltOnBeforeExternalServicesErrorWhenFlagIsFalse() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_EXTERNAL_SERVICES); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(false) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + + String errorMessage = + String.format( + "Error calling `beforeExternalServices` on plugin of type %s", + TestPicoCLIPlugin.class.getName()); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> contextImpl.beforeExternalServices()) + .withMessageContaining(errorMessage); + } + + @Test + void shouldNotHaltOnBeforeExternalServicesErrorWhenFlagIsTrue() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_EXTERNAL_SERVICES); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(true) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + contextImpl.beforeExternalServices(); + + assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); + } + + @Test + void shouldHaltOnBeforeMainLoopErrorWhenFlagIsFalse() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_START); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(false) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + contextImpl.beforeExternalServices(); + + String errorMessage = + String.format("Error starting plugin of type %s", TestPicoCLIPlugin.class.getName()); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> contextImpl.startPlugins()) + .withMessageContaining(errorMessage); + } + + @Test + void shouldNotHaltOnBeforeMainLoopErrorWhenFlagIsTrue() { + System.setProperty(TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_BEFORE_MAIN_LOOP); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(true) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + contextImpl.beforeExternalServices(); + contextImpl.startPlugins(); + + assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); + } + + @Test + void shouldHaltOnAfterExternalServicePostMainLoopErrorWhenFlagIsFalse() { + System.setProperty( + TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(false) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + contextImpl.beforeExternalServices(); + contextImpl.startPlugins(); + + String errorMessage = + String.format( + "Error calling `afterExternalServicePostMainLoop` on plugin of type %s", + TestPicoCLIPlugin.class.getName()); + assertThatExceptionOfType(RuntimeException.class) + .isThrownBy(() -> contextImpl.afterExternalServicesMainLoop()) + .withMessageContaining(errorMessage); + } + + @Test + void shouldNotHaltOnAfterExternalServicePostMainLoopErrorWhenFlagIsTrue() { + System.setProperty( + TEST_PICO_CLI_PLUGIN_TEST_OPTION, FAIL_AFTER_EXTERNAL_SERVICE_POST_MAIN_LOOP); + + PluginConfiguration config = + PluginConfiguration.builder() + .requestedPlugins(List.of(new PluginInfo(TEST_PICO_CLI_PLUGIN))) + .pluginsDir(DEFAULT_PLUGIN_DIRECTORY) + .continueOnPluginError(true) + .build(); + + contextImpl.initialize(config); + contextImpl.registerPlugins(); + contextImpl.beforeExternalServices(); + contextImpl.startPlugins(); + contextImpl.afterExternalServicesMainLoop(); + + assertThat(contextImpl.getRegisteredPlugins()).isNotInstanceOfAny(TestPicoCLIPlugin.class); } private PluginConfiguration createConfigurationForSpecificPlugin(final String pluginName) { 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 79879538bd5..e93518cad43 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -118,7 +118,6 @@ import org.hyperledger.besu.ethereum.core.MiningParametersMetrics; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.VersionMetadata; -import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; @@ -1080,9 +1079,8 @@ private IExecutionStrategy createExecuteTask(final IExecutionStrategy nextStep) private IExecutionStrategy createPluginRegistrationTask(final IExecutionStrategy nextStep) { return parseResult -> { - PluginConfiguration configuration = - PluginsConfigurationOptions.fromCommandLine(parseResult.commandSpec().commandLine()); - besuPluginContext.registerPlugins(configuration); + besuPluginContext.initialize(PluginsConfigurationOptions.fromCommandLine(commandLine)); + besuPluginContext.registerPlugins(); commandLine.setExecutionStrategy(nextStep); return commandLine.execute(parseResult.originalArgs().toArray(new String[0])); }; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java index ba05f455246..8f83c71a787 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java @@ -128,6 +128,9 @@ public interface DefaultCommandValues { /** The constant DEFAULT_PLUGINS_OPTION_NAME. */ String DEFAULT_PLUGINS_OPTION_NAME = "--plugins"; + /** The constant DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME. */ + String DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME = "--plugin-continue-on-error"; + /** The constant DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME. */ String DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME = "--Xplugins-external-enabled"; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PluginsConfigurationOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PluginsConfigurationOptions.java index 47df831c577..25893fff895 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PluginsConfigurationOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PluginsConfigurationOptions.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.cli.options.stable; +import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_CONTINUE_ON_PLUGIN_ERROR_OPTION_NAME; import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_PLUGINS_EXTERNAL_ENABLED_OPTION_NAME; import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_PLUGINS_OPTION_NAME; @@ -27,7 +28,7 @@ import picocli.CommandLine; -/** The Plugins Options options. */ +/** The Plugins options. */ public class PluginsConfigurationOptions implements CLIOptions { @CommandLine.Option( @@ -44,9 +45,17 @@ public class PluginsConfigurationOptions implements CLIOptions