"container-tests" module. (#1894)

Signed-off-by: Mark Terry <mark.terry@consensys.net>
pull/2149/head
mark-terry 4 years ago committed by GitHub
parent a95a008d1d
commit 6fe29e5dd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      .circleci/config.yml
  2. 17
      container-tests/build.gradle
  3. 38
      container-tests/tests/build.gradle
  4. 297
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/ContainerTestBase.java
  5. 195
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/ContainerTests.java
  6. 162
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/helpers/ContractOperations.java
  7. 1
      container-tests/tests/src/test/resources/besu/config/besuAddress
  8. 1
      container-tests/tests/src/test/resources/besu/data/key
  9. 45
      container-tests/tests/src/test/resources/genesis.json
  10. 1
      container-tests/tests/src/test/resources/goQuorum/qdata/nodeKey
  11. 0
      container-tests/tests/src/test/resources/ipc/.empty
  12. 6
      container-tests/tests/src/test/resources/keys/key1.key
  13. 1
      container-tests/tests/src/test/resources/keys/key1.pub
  14. 6
      container-tests/tests/src/test/resources/keys/key2.key
  15. 1
      container-tests/tests/src/test/resources/keys/key2.pub
  16. 47
      container-tests/tests/src/test/resources/tessera/tesseraConfig.json
  17. 9
      container-tests/tests/src/test/solidity/TestContract.sol
  18. 1
      gradle/check-licenses.gradle
  19. 3
      gradle/versions.gradle
  20. 1
      settings.gradle

@ -26,6 +26,10 @@ executors:
environment:
GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.workers.max=4
machine_executor:
machine:
image: ubuntu-2004:202010-01
notify:
webhooks:
- url: $HUBOT_URL
@ -191,6 +195,19 @@ jobs:
- capture_test_results
- capture_test_logs
containerTests:
parallelism: 1
executor: machine_executor
steps:
- prepare
- attach_workspace:
at: ~/project
- run:
name: Container Tests
no_output_timeout: 10m
command: ./gradlew --no-daemon containerTests -i
- capture_test_results
buildDocker:
executor: besu_executor_med
steps:
@ -274,7 +291,12 @@ workflows:
requires:
- assemble
context:
- besu-dockerhub-ro
- besu-dockerhub-ro
- containerTests:
requires:
- assemble
context:
- besu-dockerhub-ro
- buildDocker:
requires:
- unitTests
@ -289,6 +311,7 @@ workflows:
requires:
- integrationTests
- unitTests
- containerTests
- acceptanceTests
- referenceTests
- buildDocker
@ -304,6 +327,7 @@ workflows:
requires:
- integrationTests
- unitTests
- containerTests
- acceptanceTests
- referenceTests
- buildDocker

@ -0,0 +1,17 @@
/*
* 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
*/
jar { enabled = false }

@ -0,0 +1,38 @@
/*
* 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
*/
jar { enabled = false }
dependencies {
testImplementation 'junit:junit'
testImplementation 'org.apache.logging.log4j:log4j-slf4j-impl'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.web3j:core'
testImplementation 'org.web3j:quorum'
}
test.enabled = false
task containerTests(type: Test) {
description = 'Runs GoQuorum <> Besu container tests.'
dependsOn(rootProject.distDocker)
def dockerBuildVersion = project.hasProperty('release.releaseVersion') ? project.property('release.releaseVersion') : "${rootProject.version}"
def imageName = "hyperledger/besu"
def image = "${imageName}:${dockerBuildVersion}"
systemProperty 'containertest.imagename', image
}

@ -0,0 +1,297 @@
/*
* 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.tests.container;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.github.dockerjava.api.command.CreateContainerCmd;
import org.awaitility.Awaitility;
import org.awaitility.core.ThrowingRunnable;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthBlockNumber;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;
import org.web3j.quorum.Quorum;
@SuppressWarnings({"rawtypes", "unchecked"})
public class ContainerTestBase {
private final String besuImage = System.getProperty("containertest.imagename");
private final String goQuorumVersion = "21.1.0";
private final String tesseraVersion = "21.1.0";
protected final String goQuorumTesseraPubKey = "3XGBIf+x8IdVQOVfIsbRnHwTYOJP/Fx84G8gMmy8qDM=";
protected final String besuTesseraPubKey = "8JJLEAbq6o9m4Kqm++v0Y1n9Z2ryAFtZTyhnxSKWgws=";
private final Network containerNetwork = Network.SHARED;
protected Quorum besuWeb3j;
protected Quorum goQuorumWeb3j;
// General config
private final String hostGenesisPath = "/genesis.json";
private final String hostKeyPath = "/keys/";
private final String containerKeyPath = "/tmp/keys/";
private final Integer networkId = 2020;
// Besu config
private final Integer besuP2pPort = 30303;
private final Integer besuRpcPort = 8545;
private final String besuContainerGenesisPath = "/opt/besu/genesis.json";
private final String besuNetworkAlias = "besuNode";
private final String hostBesuKeyPath = "/besu/data/key";
private final String containerBesuKeyPath = "/opt/besu/key";
// GoQuorum + Tessera shared config
private final String ipcBindDir = "/tmp/ipc/";
private final String ipcDirPath = "/ipc/";
private final String ipcFilePath = "test.ipc";
private final String containerIpcPath = ipcBindDir + ipcFilePath;
// Tessera config
private final String tesseraContainerPrivKey1Path = "/tmp/keys/key1.key";
private final String tesseraContainerPubKey1Path = "/tmp/keys/key1.pub";
private final String tesseraContainerPrivKey2Path = "/tmp/keys/key2.key";
private final String tesseraContainerPubKey2Path = "/tmp/keys/key2.pub";
private final String tesseraContainerConfigFilePath = "/tmp/tessera/tesseraConfig.json";
protected final int tesseraRestPort = 9081;
private final int tesseraQ2TRestPort = 9003;
private final String hostTesseraResources = "/tessera/";
private final String containerTesseraResources = "/tmp/tessera/";
private final int tesseraP2pPort = 9001;
// GoQuorum config
private final String goQuorumContainerDatadir = "/tmp/qdata/";
private final String goQuorumContainerGenesis = "/tmp/qdata/genesis.json";
private final String goQuorumContainerGethIpcPath = goQuorumContainerDatadir + "/geth.ipc";
private final Integer goQuorumP2pPort = 30303;
private final Integer goQuorumRpcPort = 8545;
@Rule public final GenericContainer besuContainer = buildBesuContainer();
@Rule
public final GenericContainer tesseraGoQuorumContainer =
buildGoQuorumTesseraContainer(
ipcDirPath,
ipcBindDir,
containerIpcPath,
tesseraContainerPrivKey1Path,
tesseraContainerPubKey1Path);
@Rule
public final GenericContainer tesseraBesuContainer =
buildBesuTesseraContainer(tesseraContainerPrivKey2Path, tesseraContainerPubKey2Path);
@Rule
public final GenericContainer goQuorumContainer =
buildGoQuorumContainer(ipcDirPath, ipcBindDir, containerIpcPath);;
@Before
public void setUp() throws IOException, InterruptedException {
besuWeb3j =
buildWeb3JQuorum(
besuContainer.getContainerIpAddress(), besuContainer.getMappedPort(besuRpcPort));
goQuorumWeb3j =
buildWeb3JQuorum(
goQuorumContainer.getContainerIpAddress(),
goQuorumContainer.getMappedPort(goQuorumRpcPort));
waitFor(10, () -> assertClientVersion(besuWeb3j, "dev"));
waitFor(10, () -> assertClientVersion(goQuorumWeb3j, goQuorumVersion));
// Tell GoQuorum to peer to Besu
goQuorumContainer.execInContainer(
"geth",
"--exec",
"admin.addPeer(\"enode://11b72d1e2fdde254a493047d4061f3b62cc2ba59f4c1b0cf41dda4ba0d77f0bfe4f932ccf9f60203b1d47753f69edf1c80e755e3159e596b1f6aa03cb0c275c4@"
+ besuNetworkAlias
+ ":"
+ besuP2pPort
+ "\")",
"attach",
goQuorumContainerGethIpcPath);
waitFor(30, () -> assertBlockHeight(besuWeb3j, 5));
waitFor(30, () -> assertBlockHeight(goQuorumWeb3j, 5));
}
@After
public void tearDown() throws IOException {
boolean fileExists = Files.deleteIfExists(Path.of(getResourcePath(ipcDirPath + ipcFilePath)));
if (!fileExists) {
System.out.println("Unable to delete tx IPC file.");
}
}
private GenericContainer buildBesuContainer() {
return new GenericContainer(besuImage)
.withNetwork(containerNetwork)
.withNetworkAliases(besuNetworkAlias)
.withExposedPorts(besuRpcPort, besuP2pPort)
.withClasspathResourceMapping(hostBesuKeyPath, containerBesuKeyPath, BindMode.READ_ONLY)
.withClasspathResourceMapping(hostGenesisPath, besuContainerGenesisPath, BindMode.READ_ONLY)
.withClasspathResourceMapping(hostKeyPath, containerKeyPath, BindMode.READ_ONLY)
.withCommand(
"--genesis-file",
besuContainerGenesisPath,
"--network-id",
networkId.toString(),
"--p2p-port",
besuP2pPort.toString(),
"--rpc-http-enabled",
"--rpc-http-port",
besuRpcPort.toString(),
"--rpc-http-api",
"ADMIN,ETH,NET,WEB3,GOQUORUM",
"--goquorum-compatibility-enabled",
"--min-gas-price",
"0",
"--privacy-public-key-file",
"/tmp/keys/key2.pub",
"--privacy-url",
"http://" + "besuTessera" + ':' + tesseraQ2TRestPort);
}
private GenericContainer buildGoQuorumTesseraContainer(
final String ipcPath,
final String ipcBindDir,
final String containerIpcPath,
final String privKeyPath,
final String pubKeyPath) {
return new GenericContainer("quorumengineering/tessera:" + tesseraVersion)
.withNetwork(containerNetwork)
.withNetworkAliases("goQuorumTessera")
.withClasspathResourceMapping(
hostTesseraResources, containerTesseraResources, BindMode.READ_ONLY)
.withClasspathResourceMapping(ipcPath, ipcBindDir, BindMode.READ_WRITE)
.withClasspathResourceMapping(hostKeyPath, containerKeyPath, BindMode.READ_ONLY)
.withCommand(
"--configfile " + tesseraContainerConfigFilePath,
"-o serverConfigs[0].serverAddress=http://localhost:" + tesseraRestPort,
"-o serverConfigs[1].serverAddress=unix:" + containerIpcPath,
"-o serverConfigs[2].serverAddress=http://" + "goQuorumTessera" + ":" + tesseraP2pPort,
"-o keys.keyData[0].privateKeyPath=" + privKeyPath,
"-o keys.keyData[0].publicKeyPath=" + pubKeyPath,
"-o peer[0].url=http://" + "goQuorumTessera" + ":" + tesseraP2pPort + "/",
"-o peer[1].url=http://" + "besuTessera" + ":" + tesseraP2pPort + "/")
.withExposedPorts(tesseraP2pPort, tesseraRestPort)
.waitingFor(Wait.forHttp("/upcheck"));
}
private GenericContainer buildBesuTesseraContainer(
final String privKeyPath, final String pubKeyPath) {
return new GenericContainer("quorumengineering/tessera:" + tesseraVersion)
.withNetwork(containerNetwork)
.withNetworkAliases("besuTessera")
.withClasspathResourceMapping(
hostTesseraResources, containerTesseraResources, BindMode.READ_ONLY)
.withClasspathResourceMapping(hostKeyPath, containerKeyPath, BindMode.READ_ONLY)
.withCommand(
"--configfile " + tesseraContainerConfigFilePath,
"-o serverConfigs[0].serverAddress=http://localhost:" + tesseraRestPort,
"-o serverConfigs[1].serverAddress=http://localhost:" + tesseraQ2TRestPort,
"-o serverConfigs[2].serverAddress=http://" + "besuTessera" + ":" + tesseraP2pPort,
"-o keys.keyData[0].privateKeyPath=" + privKeyPath,
"-o keys.keyData[0].publicKeyPath=" + pubKeyPath,
"-o peer[0].url=http://" + "besuTessera" + ":" + tesseraP2pPort + "/",
"-o peer[1].url=http://" + "goQuorumTessera" + ":" + tesseraP2pPort + "/")
.withExposedPorts(tesseraP2pPort, tesseraRestPort)
.waitingFor(Wait.forHttp("/upcheck"));
}
private GenericContainer buildGoQuorumContainer(
final String ipcPath, final String ipcBindDir, final String containerIpcPath) {
return new GenericContainer("quorumengineering/quorum:" + goQuorumVersion)
.withNetwork(containerNetwork)
.dependsOn(tesseraGoQuorumContainer)
.withExposedPorts(goQuorumRpcPort, goQuorumP2pPort)
.withClasspathResourceMapping(
"/goQuorum/qdata/", goQuorumContainerDatadir, BindMode.READ_ONLY)
.withClasspathResourceMapping(hostGenesisPath, goQuorumContainerGenesis, BindMode.READ_ONLY)
.withClasspathResourceMapping(ipcPath, ipcBindDir, BindMode.READ_WRITE)
.withEnv("PRIVATE_CONFIG", containerIpcPath)
.withCreateContainerCmdModifier(
(Consumer<CreateContainerCmd>)
cmd ->
cmd.withEntrypoint(
"/bin/ash",
"-c",
"geth init "
+ goQuorumContainerGenesis
+ " --datadir "
+ goQuorumContainerDatadir
+ " && geth --datadir "
+ goQuorumContainerDatadir
+ " --networkid "
+ networkId.toString()
+ " --rpc"
+ " --rpcaddr 0.0.0.0"
+ " --rpcport "
+ goQuorumRpcPort.toString()
+ " --rpcapi admin,eth,debug,miner,net,shh,txpool,personal,web3,quorum,quorumExtension,clique"
+ " --emitcheckpoints"
+ " --port "
+ goQuorumP2pPort.toString()
+ " --verbosity"
+ " 3"
+ " --nousb"
+ " --nodekey "
+ goQuorumContainerDatadir
+ "/nodeKey"));
}
private Quorum buildWeb3JQuorum(final String containerIpAddress, final Integer mappedPort) {
return Quorum.build(new HttpService("http://" + containerIpAddress + ":" + mappedPort));
}
private void assertClientVersion(final Web3j web3, final String clientString) throws IOException {
final Web3ClientVersion clientVersionResult = web3.web3ClientVersion().send();
assertThat(clientVersionResult.getWeb3ClientVersion()).contains(clientString);
}
private void assertBlockHeight(final Web3j web3j, final int blockHeight) throws IOException {
final EthBlockNumber blockNumberResult = web3j.ethBlockNumber().send();
assertThat(blockNumberResult.getBlockNumber().intValueExact())
.isGreaterThanOrEqualTo(blockHeight);
}
public static void waitFor(final int timeout, final ThrowingRunnable condition) {
Awaitility.await()
.ignoreExceptions()
.atMost(timeout, TimeUnit.SECONDS)
.untilAsserted(condition);
}
private String getResourcePath(final String resourceName) {
return getClass().getResource(resourceName).getPath();
}
protected Credentials loadCredentials() throws IOException, CipherException {
return Credentials.create("8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63");
}
}

@ -0,0 +1,195 @@
/*
* 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.tests.container;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.deployContractAndReturnAddress;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.generateRandomLogValue;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getTransactionLog;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.sendLogEventAndReturnTransactionHash;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okhttp3.OkHttpClient;
import org.junit.Before;
import org.junit.Test;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.quorum.enclave.Enclave;
import org.web3j.quorum.enclave.Tessera;
import org.web3j.quorum.enclave.protocol.EnclaveService;
import org.web3j.quorum.tx.QuorumTransactionManager;
import org.web3j.tx.response.PollingTransactionReceiptProcessor;
public class ContainerTests extends ContainerTestBase {
private Credentials credentials;
private Enclave besuEnclave;
private EnclaveService besuEnclaveService;
private Enclave goQuorumEnclave;
private EnclaveService goQuorumEnclaveService;
private PollingTransactionReceiptProcessor besuPollingTransactionReceiptProcessor;
private PollingTransactionReceiptProcessor goQuorumPollingTransactionReceiptProcessor;
@Before
public void testSetUp() throws IOException, CipherException {
besuEnclaveService =
new EnclaveService(
"http://" + tesseraBesuContainer.getHost(),
tesseraBesuContainer.getMappedPort(tesseraRestPort),
new OkHttpClient());
besuEnclave = new Tessera(besuEnclaveService, besuWeb3j);
besuPollingTransactionReceiptProcessor =
new PollingTransactionReceiptProcessor(besuWeb3j, 1000, 10);
goQuorumEnclaveService =
new EnclaveService(
"http://" + tesseraGoQuorumContainer.getHost(),
tesseraGoQuorumContainer.getMappedPort(tesseraRestPort),
new OkHttpClient());
goQuorumEnclave = new Tessera(goQuorumEnclaveService, goQuorumWeb3j);
goQuorumPollingTransactionReceiptProcessor =
new PollingTransactionReceiptProcessor(goQuorumWeb3j, 1000, 10);
credentials = loadCredentials();
}
@Test
public void contractShouldBeDeployedToBothNodes() throws IOException, TransactionException {
// create a GoQuorum transaction manager
final QuorumTransactionManager qtm =
new QuorumTransactionManager(
goQuorumWeb3j,
credentials,
goQuorumTesseraPubKey,
Arrays.asList(goQuorumTesseraPubKey, besuTesseraPubKey),
goQuorumEnclave);
// Get the deployed contract address
final String contractAddress =
deployContractAndReturnAddress(
goQuorumWeb3j,
credentials,
qtm,
besuPollingTransactionReceiptProcessor,
goQuorumPollingTransactionReceiptProcessor);
// Generate a random value to insert into the log
final String logValue = generateRandomLogValue();
// Send the transaction and get the transaction hash
final String transactionHash =
sendLogEventAndReturnTransactionHash(
goQuorumWeb3j,
credentials,
contractAddress,
qtm,
besuPollingTransactionReceiptProcessor,
goQuorumPollingTransactionReceiptProcessor,
logValue);
// Get the transaction logs
final String goQuorumResult = getTransactionLog(goQuorumWeb3j, transactionHash);
final String besuResult = getTransactionLog(besuWeb3j, transactionHash);
assertThat(besuResult).isEqualTo(logValue);
assertThat(goQuorumResult).isEqualTo(logValue);
}
@Test
public void contractShouldBeDeployedOnlyToGoQuorumNode()
throws IOException, TransactionException {
// create a quorum transaction manager
final QuorumTransactionManager qtm =
new QuorumTransactionManager(
goQuorumWeb3j,
credentials,
goQuorumTesseraPubKey,
List.of(goQuorumTesseraPubKey),
goQuorumEnclave);
// Get the deployed contract address
final String contractAddress =
deployContractAndReturnAddress(
goQuorumWeb3j,
credentials,
qtm,
goQuorumPollingTransactionReceiptProcessor,
besuPollingTransactionReceiptProcessor);
// Generate a random value to insert into the log
final String logValue = generateRandomLogValue();
// Send the transaction and get the transaction hash
final String transactionHash =
sendLogEventAndReturnTransactionHash(
goQuorumWeb3j,
credentials,
contractAddress,
qtm,
goQuorumPollingTransactionReceiptProcessor,
besuPollingTransactionReceiptProcessor,
logValue);
// Assert the GoQuorum node has received the log
final String quorumResult = getTransactionLog(goQuorumWeb3j, transactionHash);
assertThat(quorumResult).isEqualTo(logValue);
// Assert the Besu node has not received the log
assertThatThrownBy(() -> getTransactionLog(besuWeb3j, transactionHash))
.hasMessageContaining("No log found");
}
@Test
public void contractShouldBeDeployedOnlyToBesuNode() throws IOException, TransactionException {
// create a GoQuorum transaction manager
final QuorumTransactionManager qtm =
new QuorumTransactionManager(
besuWeb3j, credentials, besuTesseraPubKey, List.of(besuTesseraPubKey), besuEnclave);
// Get the deployed contract address
final String contractAddress =
deployContractAndReturnAddress(
besuWeb3j,
credentials,
qtm,
besuPollingTransactionReceiptProcessor,
goQuorumPollingTransactionReceiptProcessor);
// Generate a random value to insert into the log
final String logValue = generateRandomLogValue();
// Send the transaction and get the transaction hash
final String transactionHash =
sendLogEventAndReturnTransactionHash(
besuWeb3j,
credentials,
contractAddress,
qtm,
besuPollingTransactionReceiptProcessor,
goQuorumPollingTransactionReceiptProcessor,
logValue);
// Assert the Besu node has received the log
final String besuResult = getTransactionLog(besuWeb3j, transactionHash);
assertThat(besuResult).isEqualTo(logValue);
// Assert the GoQuorum node has not received the log
assertThatThrownBy(() -> getTransactionLog(goQuorumWeb3j, transactionHash))
.hasMessageContaining("No log found");
}
}

@ -0,0 +1,162 @@
/*
* 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.tests.container.helpers;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import org.web3j.abi.FunctionEncoder;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.quorum.Quorum;
import org.web3j.quorum.tx.QuorumTransactionManager;
import org.web3j.tx.response.PollingTransactionReceiptProcessor;
public class ContractOperations {
public static String deployContractAndReturnAddress(
final Quorum quorumWeb3j,
final Credentials credentials,
final QuorumTransactionManager qtm,
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessorReturn,
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessorIgnore)
throws IOException, TransactionException {
// Build smart contract transaction
final String simpleStorageContractBytecode =
"6080604052348015600f57600080fd5b5060de8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060275760003560e01c8062f88abf14602c575b600080fd5b605560048036036020811015604057600080fd5b81019080803590602001909291905050506057565b005b3373ffffffffffffffffffffffffffffffffffffffff167f748aa07c80b05bd067e3688dbb79d9f9583cd018be6a589a7c364cacd770e0a2826040518082815260200191505060405180910390a25056fea26469706673582212207df0d3ad8bced04b7bd476cc81a6233c0b575966c29b4af96450313628ee623964736f6c63430007040033";
final String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.emptyList());
final String binaryAndInitCode = simpleStorageContractBytecode + encodedConstructor;
final RawTransaction contractTransaction =
RawTransaction.createTransaction(
BigInteger.valueOf(getNonce(quorumWeb3j, credentials)),
BigInteger.ZERO,
BigInteger.valueOf(4300000),
"",
BigInteger.ZERO,
binaryAndInitCode);
// Send the signed transaction to quorum
final EthSendTransaction sendContractTransactionResult = qtm.signAndSend(contractTransaction);
assertThat(sendContractTransactionResult.hasError()).isFalse();
pollNodeForTransactionReceipt(
pollingTransactionReceiptProcessorIgnore,
sendContractTransactionResult.getTransactionHash());
// Poll for the transaction receipt
final TransactionReceipt transactionReceiptResult =
pollNodeForTransactionReceiptResult(
pollingTransactionReceiptProcessorReturn,
sendContractTransactionResult.getTransactionHash());
return transactionReceiptResult.getContractAddress();
}
public static String getTransactionLog(final Quorum web3j, final String transactionHash)
throws IOException {
final EthGetTransactionReceipt transactionReceiptResult =
web3j.ethGetTransactionReceipt(transactionHash).send();
assertThat(transactionReceiptResult.getTransactionReceipt())
.withFailMessage("Transaction for log not found.")
.isNotEmpty();
final List<Log> logs = transactionReceiptResult.getTransactionReceipt().get().getLogs();
assertThat(logs.size()).withFailMessage("No log found.").isEqualTo(1);
// Remove the 0x prefix
return logs.get(0).getData().substring(2);
}
public static String sendLogEventAndReturnTransactionHash(
final Quorum quorum,
final Credentials credentials,
final String contractAddress,
final QuorumTransactionManager qtm,
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessorReturn,
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessorIgnore,
final String param)
throws IOException, TransactionException {
final String functionSig = "0x00f88abf";
final String data = functionSig + param;
final RawTransaction logEventTransaction =
RawTransaction.createTransaction(
BigInteger.valueOf(getNonce(quorum, credentials)),
BigInteger.ZERO,
BigInteger.valueOf(4300000),
contractAddress,
BigInteger.ZERO,
data);
final EthSendTransaction sendTransactionResult = qtm.signAndSend(logEventTransaction);
assertThat(sendTransactionResult.hasError()).isFalse();
pollNodeForTransactionReceipt(
pollingTransactionReceiptProcessorIgnore, sendTransactionResult.getTransactionHash());
final TransactionReceipt logReceiptResult =
pollNodeForTransactionReceiptResult(
pollingTransactionReceiptProcessorReturn, sendTransactionResult.getTransactionHash());
return logReceiptResult.getTransactionHash();
}
public static String generateRandomLogValue() {
final StringBuilder randomValue =
new StringBuilder(Long.toHexString(((Double) (Math.random() * 100000000)).longValue()));
while (randomValue.length() < 64) {
randomValue.insert(0, '0');
}
return randomValue.toString();
}
public static int getNonce(final Quorum quorum, final Credentials credentials)
throws IOException {
final EthGetTransactionCount transactionCountResult =
quorum
.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST)
.send();
assertThat(transactionCountResult.hasError()).isFalse();
return transactionCountResult.getTransactionCount().intValueExact();
}
public static TransactionReceipt pollNodeForTransactionReceiptResult(
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessor,
final String transactionHash)
throws IOException, TransactionException {
final TransactionReceipt transactionReceiptResult =
pollingTransactionReceiptProcessor.waitForTransactionReceipt(transactionHash);
assertThat(transactionReceiptResult.isStatusOK()).isTrue();
return transactionReceiptResult;
}
public static void pollNodeForTransactionReceipt(
final PollingTransactionReceiptProcessor pollingTransactionReceiptProcessor,
final String transactionHash)
throws IOException, TransactionException {
final TransactionReceipt transactionReceiptResult =
pollingTransactionReceiptProcessor.waitForTransactionReceipt(transactionHash);
assertThat(transactionReceiptResult.isStatusOK()).isTrue();
}
}

@ -0,0 +1 @@
0x8a15eaa6e526e9b8ebdea0bfc6d4399b0ae7ba1c

@ -0,0 +1 @@
0xc86c0030a86bb894c1e10c25d48d4949a288df9c6ff736b32d977f39893fd0dc

@ -0,0 +1,45 @@
{
"config":{
"homesteadBlock": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"chainId":2020,
"eip150Block": 0,
"eip155Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip158Block": 0,
"clique":{
"blockperiodseconds":2,
"epochlength":30000,
"period": 2,
"epoch": 30000
},
"isQuorum": true
},
"coinbase":"0x0000000000000000000000000000000000000000",
"extraData":"0x00000000000000000000000000000000000000000000000000000000000000008a15eaa6e526e9b8ebdea0bfc6d4399b0ae7ba1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"alloc": {
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0xad78ebc5ac6200000"
},
"627306090abaB3A6e1400e9345bC60c78a8BEf57": {
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
},
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
}
},
"difficulty": "0x0",
"gasLimit": "0xE0000000",
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"nonce": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}

@ -0,0 +1 @@
c0b7ff2352175d2a43d72eca7c2f806cc7bd74a7a597630d62f5261834348f74

@ -0,0 +1,6 @@
{
"type" : "unlocked",
"data" : {
"bytes" : "srn2w10NWdvWyHkPMWB07W5NjIPmiAHRqESL8vH3Kyw="
}
}

@ -0,0 +1 @@
3XGBIf+x8IdVQOVfIsbRnHwTYOJP/Fx84G8gMmy8qDM=

@ -0,0 +1,6 @@
{
"type" : "unlocked",
"data" : {
"bytes" : "LWjzMnDP/MQIATegt45LTrMh1i9ISX64CFO0EzFDNhg="
}
}

@ -0,0 +1 @@
8JJLEAbq6o9m4Kqm++v0Y1n9Z2ryAFtZTyhnxSKWgws=

@ -0,0 +1,47 @@
{
"useWhiteList": false,
"jdbc": {
"username": "sa",
"password": "",
"url": "jdbc:h2:/tmp/tessera;LOCK_TIMEOUT=20000",
"autoCreateTables": true
},
"serverConfigs":[
{
"app":"ThirdParty",
"enabled": true,
"serverAddress": "http://localhost:9081",
"communicationType" : "REST"
},
{
"app":"Q2T",
"enabled": true,
"serverAddress": "unix:/tmp/test.ipc",
"communicationType" : "REST"
},
{
"app":"P2P",
"enabled": true,
"serverAddress":"http://localhost:9001",
"sslConfig": {
"tls": "OFF"
},
"communicationType" : "REST"
}
],
"peer": [
{
"url": "http://localhost:9001"
}
],
"keys": {
"passwords": [],
"keyData": [
{
"privateKeyPath": "myKey.key",
"publicKeyPath": "myKey.pub"
}
]
},
"alwaysSendTo": []
}

@ -0,0 +1,9 @@
pragma solidity >=0.6.0 < 0.8.0;
contract TestContract {
event TestEvent(address indexed _from, int val);
function logEvent(int randomNum) public {
emit TestEvent(msg.sender, randomNum);
}
}

@ -46,6 +46,7 @@ ext.acceptedLicenses = [
'Public Domain (CC0) License 1.0',
'Public Domain',
'Unicode/ICU License',
'New BSD License'
]*.toLowerCase()
/**

@ -150,6 +150,7 @@ dependencyManagement {
exclude group: 'com.github.jnr', name: 'jnr-unixsocket'
}
dependency 'org.web3j:crypto:4.5.15'
dependency 'org.web3j:quorum:4.5.15'
dependency 'org.xerial.snappy:snappy-java:1.1.8.2'
@ -162,5 +163,7 @@ dependencyManagement {
entry 'signing-secp256k1-api'
entry 'signing-secp256k1-impl'
}
dependency 'org.testcontainers:testcontainers:1.15.2'
}
}

@ -23,6 +23,7 @@ include 'consensus:common'
include 'consensus:ibft'
include 'consensus:ibftlegacy'
include 'consensus:qbft'
include 'container-tests:tests'
include 'crypto'
include 'enclave'
include 'errorprone-checks'

Loading…
Cancel
Save