mirror of https://github.com/hyperledger/besu
commit
f2ac53ecfa
@ -0,0 +1,7 @@ |
|||||||
|
--- |
||||||
|
# runtime docker tests for interfaces & ports |
||||||
|
port: |
||||||
|
tcp:30303: |
||||||
|
listening: true |
||||||
|
ip: |
||||||
|
- 0.0.0.0 |
@ -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<String> 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<String> 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<String> 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<String> 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<String> 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(); |
|
||||||
} |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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<HttpServer> 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<Boolean> 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!"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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<Integer> serverPortToAppendToHostname) |
|
||||||
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { |
|
||||||
|
|
||||||
final List<X509Certificate> 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<X509Certificate> getCertsFromPkcs12(final TlsCertificateDefinition certDef) |
|
||||||
throws KeyStoreException, NoSuchAlgorithmException, CertificateException { |
|
||||||
final List<X509Certificate> results = Lists.newArrayList(); |
|
||||||
|
|
||||||
final KeyStore p12 = loadP12KeyStore(certDef.getPkcs12File(), certDef.getPassword()); |
|
||||||
final Enumeration<String> 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); |
|
||||||
} |
|
||||||
} |
|
@ -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<String> 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); |
|
||||||
} |
|
||||||
} |
|
@ -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<String> 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<String> 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<String> 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:"); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,26 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
public enum BodyValidationMode { |
||||||
|
/** No Validation. data must be pre-validated */ |
||||||
|
NONE, |
||||||
|
|
||||||
|
/** Skip receipts and transactions root validation */ |
||||||
|
LIGHT, |
||||||
|
|
||||||
|
/** Fully validate the body */ |
||||||
|
FULL; |
||||||
|
} |
@ -1 +0,0 @@ |
|||||||
{"data":{"bytes":"hBsuQsGJzx4QHmFmBkNoI7YGnTmaZP4P+wBOdu56ljk="},"type":"unlocked"} |
|
Loading…
Reference in new issue