From 122915751ad68fc8cc3cf0a518986ed612c224ad Mon Sep 17 00:00:00 2001 From: Vyatcheslav Suharnikov Date: Thu, 14 Nov 2024 19:13:42 +0400 Subject: [PATCH] Choose an execution client in tests --- .../scala/units/BaseDockerTestSuite.scala | 18 +++-- .../test/scala/units/SyncingTestSuite.scala | 7 +- .../scala/units/docker/BesuContainer.scala | 52 ++++++++++++++ .../scala/units/docker/DockerImages.scala | 5 +- .../test/scala/units/docker/EcContainer.scala | 51 +++----------- .../scala/units/docker/GethContainer.scala | 69 +++++++++++++++++++ .../test/scala/units/http/OkHttpLogger.scala | 2 +- .../scala/units/test/TestEnvironment.scala | 1 + 8 files changed, 152 insertions(+), 53 deletions(-) create mode 100644 consensus-client-it/src/test/scala/units/docker/BesuContainer.scala create mode 100644 consensus-client-it/src/test/scala/units/docker/GethContainer.scala diff --git a/consensus-client-it/src/test/scala/units/BaseDockerTestSuite.scala b/consensus-client-it/src/test/scala/units/BaseDockerTestSuite.scala index 68f3bdbf..8b67e56b 100644 --- a/consensus-client-it/src/test/scala/units/BaseDockerTestSuite.scala +++ b/consensus-client-it/src/test/scala/units/BaseDockerTestSuite.scala @@ -15,11 +15,11 @@ import units.BaseDockerTestSuite.generateWavesGenesisConfig import units.client.HttpChainContractClient import units.client.contract.HasConsensusLayerDappTxHelpers import units.client.engine.model.BlockNumber -import units.docker.{EcContainer, Networks, WavesNodeContainer} +import units.docker.* import units.el.ElBridgeClient import units.eth.Gwei import units.test.TestEnvironment.* -import units.test.{CustomMatchers, HasRetry} +import units.test.{CustomMatchers, HasRetry, TestEnvironment} import java.io.PrintStream import java.nio.file.{Files, Path} @@ -46,11 +46,15 @@ trait BaseDockerTestSuite private implicit val httpClientBackend: SttpBackend[Identity, Any] = HttpClientSyncBackend() - protected lazy val ec1: EcContainer = new EcContainer( - network = network, - number = 1, - ip = Networks.ipForNode(2) // ipForNode(1) is assigned to Ryuk - ) + protected lazy val ec1: EcContainer = { + val constructor = TestEnvironment.ExecutionClient match { + case "besu" => new BesuContainer(_, _, _) + case "geth" => new GethContainer(_, _, _) + case x => throw new RuntimeException(s"Unknown execution client: $x. Only 'geth' or 'besu' supported") + } + + constructor(network, 1, Networks.ipForNode(2) /* ipForNode(1) is assigned to Ryuk */ ) + } protected lazy val waves1: WavesNodeContainer = new WavesNodeContainer( network = network, diff --git a/consensus-client-it/src/test/scala/units/SyncingTestSuite.scala b/consensus-client-it/src/test/scala/units/SyncingTestSuite.scala index 7b07e126..e26a66a0 100644 --- a/consensus-client-it/src/test/scala/units/SyncingTestSuite.scala +++ b/consensus-client-it/src/test/scala/units/SyncingTestSuite.scala @@ -6,6 +6,7 @@ import org.web3j.protocol.core.DefaultBlockParameter import org.web3j.protocol.core.methods.response.{EthSendTransaction, TransactionReceipt} import org.web3j.tx.gas.DefaultGasProvider import org.web3j.utils.Convert +import units.docker.EcContainer import java.math.BigInteger @@ -65,11 +66,13 @@ class SyncingTestSuite extends BaseDockerTestSuite { private def sendTxn(nonce: Long): EthSendTransaction = { val rawTransaction = RawTransaction.createEtherTransaction( + EcContainer.ChainId, BigInteger.valueOf(nonce), - DefaultGasProvider.GAS_PRICE, DefaultGasProvider.GAS_LIMIT, "0x0000000000000000000000000000000000000000", - amount + amount, + BigInteger.ZERO, + DefaultGasProvider.GAS_PRICE ) val signedTransaction = EthEncoding.toHexString(TransactionEncoder.signMessage(rawTransaction, elSender)) ec1.web3j.ethSendRawTransaction(signedTransaction).send() diff --git a/consensus-client-it/src/test/scala/units/docker/BesuContainer.scala b/consensus-client-it/src/test/scala/units/docker/BesuContainer.scala new file mode 100644 index 00000000..61022170 --- /dev/null +++ b/consensus-client-it/src/test/scala/units/docker/BesuContainer.scala @@ -0,0 +1,52 @@ +package units.docker + +import org.testcontainers.containers.BindMode +import org.testcontainers.containers.Network.NetworkImpl +import org.web3j.protocol.Web3j +import org.web3j.protocol.http.HttpService +import sttp.client3.{Identity, SttpBackend} +import units.client.JsonRpcClient +import units.client.engine.{EngineApiClient, HttpEngineApiClient, LoggedEngineApiClient} +import units.docker.EcContainer.{EnginePort, RpcPort} +import units.http.OkHttpLogger +import units.test.TestEnvironment.ConfigsDir + +import scala.concurrent.duration.DurationInt + +class BesuContainer(network: NetworkImpl, number: Int, ip: String)(implicit httpClientBackend: SttpBackend[Identity, Any]) + extends EcContainer(number) { + protected override val container = new GenericContainer(DockerImages.BesuExecutionClient) + .withNetwork(network) + .withExposedPorts(RpcPort, EnginePort) + .withEnv("LOG4J_CONFIGURATION_FILE", "/config/log4j2.xml") + .withEnv("ROOT_LOG_FILE_LEVEL", "TRACE") + .withFileSystemBind(s"$ConfigsDir/ec-common/genesis.json", "/genesis.json", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/besu", "/config", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/besu/run-besu.sh", "/tmp/run.sh", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/ec-common/p2p-key-$number.hex", "/etc/secrets/p2p-key", BindMode.READ_ONLY) + .withFileSystemBind(s"$logFile", "/opt/besu/logs/besu.log", BindMode.READ_WRITE) + .withCreateContainerCmdModifier { cmd => + cmd + .withName(s"${network.getName}-$hostName") + .withHostName(hostName) + .withIpv4Address(ip) + .withEntrypoint("/tmp/run.sh") + .withStopTimeout(5) + } + + override lazy val engineApi: EngineApiClient = new LoggedEngineApiClient( + new HttpEngineApiClient( + JsonRpcClient.Config(apiUrl = s"http://${container.getHost}:$enginePort", apiRequestRetries = 5, apiRequestRetryWaitTime = 1.second), + httpClientBackend + ) + ) + + override lazy val web3j = Web3j.build( + new HttpService( + s"http://${container.getHost}:$rpcPort", + HttpService.getOkHttpClientBuilder + .addInterceptor(OkHttpLogger) + .build() + ) + ) +} diff --git a/consensus-client-it/src/test/scala/units/docker/DockerImages.scala b/consensus-client-it/src/test/scala/units/docker/DockerImages.scala index 5aaddb62..3806cfa5 100644 --- a/consensus-client-it/src/test/scala/units/docker/DockerImages.scala +++ b/consensus-client-it/src/test/scala/units/docker/DockerImages.scala @@ -4,6 +4,7 @@ import org.testcontainers.utility.DockerImageName.parse import units.test.TestEnvironment.WavesDockerImage object DockerImages { - val WavesNode = parse(WavesDockerImage) - val ExecutionClient = parse("hyperledger/besu:latest") + val WavesNode = parse(WavesDockerImage) + val BesuExecutionClient = parse("hyperledger/besu:latest") + val GethExecutionClient = parse("ethereum/client-go:stable") } diff --git a/consensus-client-it/src/test/scala/units/docker/EcContainer.scala b/consensus-client-it/src/test/scala/units/docker/EcContainer.scala index b795e887..46b97368 100644 --- a/consensus-client-it/src/test/scala/units/docker/EcContainer.scala +++ b/consensus-client-it/src/test/scala/units/docker/EcContainer.scala @@ -1,63 +1,31 @@ package units.docker import com.google.common.io.Files -import org.testcontainers.containers.BindMode -import org.testcontainers.containers.Network.NetworkImpl import org.web3j.protocol.Web3j -import org.web3j.protocol.http.HttpService -import sttp.client3.{Identity, SttpBackend} import units.client.JsonRpcClient -import units.client.engine.{HttpEngineApiClient, LoggedEngineApiClient} +import units.client.engine.EngineApiClient import units.docker.EcContainer.{EnginePort, RpcPort} -import units.http.OkHttpLogger import units.test.TestEnvironment.* import java.io.File import scala.concurrent.duration.DurationInt -class EcContainer(network: NetworkImpl, number: Int, ip: String)(implicit httpClientBackend: SttpBackend[Identity, Any]) - extends BaseContainer(s"ec-$number") { - private val logFile = new File(s"$DefaultLogsDir/besu-$number.log") +abstract class EcContainer(number: Int) extends BaseContainer(s"ec-$number") { + protected val logFile = new File(s"$DefaultLogsDir/ec-$number.log") Files.touch(logFile) - protected override val container = new GenericContainer(DockerImages.ExecutionClient) - .withNetwork(network) - .withExposedPorts(RpcPort, EnginePort) - .withEnv("LOG4J_CONFIGURATION_FILE", "/config/log4j2.xml") - .withEnv("ROOT_LOG_FILE_LEVEL", "TRACE") - .withFileSystemBind(s"$ConfigsDir/ec-common/genesis.json", "/genesis.json", BindMode.READ_ONLY) - .withFileSystemBind(s"$ConfigsDir/besu", "/config", BindMode.READ_ONLY) - .withFileSystemBind(s"$ConfigsDir/besu/run-besu.sh", "/tmp/run.sh", BindMode.READ_ONLY) - .withFileSystemBind(s"$ConfigsDir/ec-common/p2p-key-$number.hex", "/etc/secrets/p2p-key", BindMode.READ_ONLY) - .withFileSystemBind(s"$logFile", "/opt/besu/logs/besu.log", BindMode.READ_WRITE) - .withCreateContainerCmdModifier { cmd => - cmd - .withName(s"${network.getName}-$hostName") - .withHostName(hostName) - .withIpv4Address(ip) - .withEntrypoint("/tmp/run.sh") - .withStopTimeout(5) - } - lazy val rpcPort = container.getMappedPort(RpcPort) lazy val enginePort = container.getMappedPort(EnginePort) lazy val engineApiDockerUrl = s"http://$hostName:${EcContainer.EnginePort}" - lazy val engineApi = new LoggedEngineApiClient( - new HttpEngineApiClient( - JsonRpcClient.Config(apiUrl = s"http://${container.getHost}:$enginePort", apiRequestRetries = 5, apiRequestRetryWaitTime = 1.second), - httpClientBackend - ) + lazy val engineApiConfig = JsonRpcClient.Config( + apiUrl = s"http://${container.getHost}:$enginePort", + apiRequestRetries = 5, + apiRequestRetryWaitTime = 1.second ) - lazy val web3j = Web3j.build( - new HttpService( - s"http://${container.getHost}:$rpcPort", - HttpService.getOkHttpClientBuilder - .addInterceptor(OkHttpLogger) - .build() - ) - ) + def engineApi: EngineApiClient + def web3j: Web3j override def stop(): Unit = { web3j.shutdown() @@ -70,4 +38,5 @@ class EcContainer(network: NetworkImpl, number: Int, ip: String)(implicit httpCl object EcContainer { val RpcPort = 8545 val EnginePort = 8551 + val ChainId = 1337L // from genesis.json } diff --git a/consensus-client-it/src/test/scala/units/docker/GethContainer.scala b/consensus-client-it/src/test/scala/units/docker/GethContainer.scala new file mode 100644 index 00000000..88735ee8 --- /dev/null +++ b/consensus-client-it/src/test/scala/units/docker/GethContainer.scala @@ -0,0 +1,69 @@ +package units.docker + +import okhttp3.Interceptor +import org.testcontainers.containers.BindMode +import org.testcontainers.containers.Network.NetworkImpl +import org.web3j.protocol.Web3j +import org.web3j.protocol.http.HttpService +import pdi.jwt.{JwtAlgorithm, JwtClaim, JwtJson} +import sttp.client3.{Identity, SttpBackend} +import units.client.JwtAuthenticationBackend +import units.client.engine.{EngineApiClient, HttpEngineApiClient, LoggedEngineApiClient} +import units.docker.EcContainer.{EnginePort, RpcPort} +import units.http.OkHttpLogger +import units.test.TestEnvironment.ConfigsDir + +import java.time.Clock +import scala.io.Source + +class GethContainer(network: NetworkImpl, number: Int, ip: String)(implicit httpClientBackend: SttpBackend[Identity, Any]) + extends EcContainer(number) { + protected override val container = new GenericContainer(DockerImages.GethExecutionClient) + .withNetwork(network) + .withExposedPorts(RpcPort, EnginePort) + .withFileSystemBind(s"$ConfigsDir/ec-common/genesis.json", "/tmp/genesis.json", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/geth/run-geth.sh", "/tmp/run.sh", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/ec-common/p2p-key-$number.hex", "/etc/secrets/p2p-key", BindMode.READ_ONLY) + .withFileSystemBind(s"$ConfigsDir/ec-common/jwt-secret-$number.hex", "/etc/secrets/jwtsecret", BindMode.READ_ONLY) + .withFileSystemBind(s"$logFile", "/root/logs/log", BindMode.READ_WRITE) + .withCreateContainerCmdModifier { cmd => + cmd + .withName(s"${network.getName}-$hostName") + .withHostName(hostName) + .withIpv4Address(ip) + .withEntrypoint("/tmp/run.sh") + .withStopTimeout(5) + } + + lazy val jwtSecretKey = { + val src = Source.fromFile(s"$ConfigsDir/ec-common/jwt-secret-$number.hex") + try src.getLines().next() + finally src.close() + } + + override lazy val engineApi: EngineApiClient = new LoggedEngineApiClient( + new HttpEngineApiClient( + engineApiConfig, + new JwtAuthenticationBackend(jwtSecretKey, httpClientBackend) + ) + ) + + override lazy val web3j = Web3j.build( + new HttpService( + s"http://${container.getHost}:$rpcPort", + HttpService.getOkHttpClientBuilder + .addInterceptor { (chain: Interceptor.Chain) => + val orig = chain.request() + val jwtToken = JwtJson.encode(JwtClaim().issuedNow(Clock.systemUTC), jwtSecretKey, JwtAlgorithm.HS256) + val request = orig + .newBuilder() + .header("Authorization", s"Bearer $jwtToken") + .build() + + chain.proceed(request) + } + .addInterceptor(OkHttpLogger) + .build() + ) + ) +} diff --git a/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala b/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala index 9845d076..a779644a 100644 --- a/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala +++ b/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala @@ -35,6 +35,6 @@ object OkHttpLogger extends Interceptor with ScorexLogging { val source = body.source() source.request(Long.MaxValue) // Buffer the entire body. val buffer = source.getBuffer.clone() - buffer.readUtf8() + buffer.readUtf8().trim } } diff --git a/consensus-client-it/src/test/scala/units/test/TestEnvironment.scala b/consensus-client-it/src/test/scala/units/test/TestEnvironment.scala index 44bd8059..f5ef1377 100644 --- a/consensus-client-it/src/test/scala/units/test/TestEnvironment.scala +++ b/consensus-client-it/src/test/scala/units/test/TestEnvironment.scala @@ -8,4 +8,5 @@ object TestEnvironment { Files.createDirectories(DefaultLogsDir) val WavesDockerImage: String = System.getProperty("cc.it.docker.image") + val ExecutionClient: String = System.getProperty("cc.it.ec", "besu") // | geth }