diff --git a/consensus-client-it/build.sbt b/consensus-client-it/build.sbt index b446338a..5ad09114 100644 --- a/consensus-client-it/build.sbt +++ b/consensus-client-it/build.sbt @@ -7,7 +7,8 @@ import java.time.format.DateTimeFormatter description := "Consensus client integration tests" libraryDependencies ++= Seq( - "org.testcontainers" % "testcontainers" % "1.20.2" + "org.testcontainers" % "testcontainers" % "1.20.2", + "org.web3j" % "core" % "4.9.8" ).map(_ % Test) val logsDirectory = taskKey[File]("The directory for logs") // Evaluates every time, so it recreates the logs directory diff --git a/consensus-client-it/src/test/resources/logback-test.xml b/consensus-client-it/src/test/resources/logback-test.xml index a7ce880b..1a2e690a 100644 --- a/consensus-client-it/src/test/resources/logback-test.xml +++ b/consensus-client-it/src/test/resources/logback-test.xml @@ -28,6 +28,9 @@ + + + diff --git a/consensus-client-it/src/test/scala/units/network/BaseItTestSuite.scala b/consensus-client-it/src/test/scala/units/BaseItTestSuite.scala similarity index 86% rename from consensus-client-it/src/test/scala/units/network/BaseItTestSuite.scala rename to consensus-client-it/src/test/scala/units/BaseItTestSuite.scala index ec290c1c..7f3f91ae 100644 --- a/consensus-client-it/src/test/scala/units/network/BaseItTestSuite.scala +++ b/consensus-client-it/src/test/scala/units/BaseItTestSuite.scala @@ -1,7 +1,7 @@ -package units.network +package units import com.google.common.primitives.{Bytes, Ints} -import com.wavesplatform.account.{Address, AddressScheme, KeyPair, SeedKeyPair} +import com.wavesplatform.account.{AddressScheme, KeyPair, SeedKeyPair} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.crypto @@ -11,10 +11,11 @@ import org.scalatest.concurrent.Eventually import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers import org.scalatest.{BeforeAndAfterAll, EitherValues, OptionValues} +import org.web3j.crypto.Credentials import units.client.contract.HasConsensusLayerDappTxHelpers import units.client.engine.model.BlockNumber +import units.docker.{EcContainer, Networks, WavesNodeContainer} import units.eth.{EthAddress, Gwei} -import units.network.test.docker.{EcContainer, Networks, WavesNodeContainer} import units.test.CustomMatchers import java.nio.charset.StandardCharsets @@ -44,13 +45,19 @@ trait BaseItTestSuite number = 1, ip = Networks.ipForNode(3), baseSeed = "devnet-1", - chainContractAddress = Address.fromString("3FdaanzgX4roVgHevhq8L8q42E7EZL9XTQr", expectedChainId = Some('D'.toByte)).explicitGet(), + chainContractAddress = chainContractAddress, ecEngineApiUrl = s"http://${ec1.hostName}:${EcContainer.EnginePort}" ) protected val miner1Account = mkKeyPair("devnet-1", 0) protected val miner1RewardAddress = EthAddress.unsafeFrom("0x7dbcf9c6c3583b76669100f9be3caf6d722bc9f9") + protected val clRichAccount1 = mkKeyPair("devnet-0", 0) + protected val clRichAccount2 = mkKeyPair("devnet-0", 1) + + protected val elRichAccount1 = Credentials.create("8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63") + protected val elRichAccount2 = Credentials.create("ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f") + override def beforeAll(): Unit = { BaseItTestSuite.init() super.beforeAll() diff --git a/consensus-client-it/src/test/scala/units/BridgeTestSuite.scala b/consensus-client-it/src/test/scala/units/BridgeTestSuite.scala new file mode 100644 index 00000000..c6ec691b --- /dev/null +++ b/consensus-client-it/src/test/scala/units/BridgeTestSuite.scala @@ -0,0 +1,15 @@ +package units + +class BridgeTestSuite extends BaseItTestSuite { + "L2-379 Checking balances in EL->CL transfers" in { + val sendResult = ec1.elBridge.sendNative(elRichAccount1, clRichAccount1.toAddress, BigInt("1000000000000000000")) + Thread.sleep(60000) + +// waves1.api.broadcastAndWait( +// chainContract.withdraw( +// ) +// ) + } + + "L2-380 Checking balances in CL->EL transfers" in {} +} diff --git a/consensus-client-it/src/test/scala/units/network/RewardTestSuite.scala b/consensus-client-it/src/test/scala/units/RewardTestSuite.scala similarity index 98% rename from consensus-client-it/src/test/scala/units/network/RewardTestSuite.scala rename to consensus-client-it/src/test/scala/units/RewardTestSuite.scala index fbc4ada8..374728f9 100644 --- a/consensus-client-it/src/test/scala/units/network/RewardTestSuite.scala +++ b/consensus-client-it/src/test/scala/units/RewardTestSuite.scala @@ -1,4 +1,4 @@ -package units.network +package units import com.wavesplatform.common.utils.EitherExt2 import units.client.engine.model.BlockNumber diff --git a/consensus-client-it/src/test/scala/units/network/test/docker/BaseContainer.scala b/consensus-client-it/src/test/scala/units/docker/BaseContainer.scala similarity index 95% rename from consensus-client-it/src/test/scala/units/network/test/docker/BaseContainer.scala rename to consensus-client-it/src/test/scala/units/docker/BaseContainer.scala index f42e791b..098c49db 100644 --- a/consensus-client-it/src/test/scala/units/network/test/docker/BaseContainer.scala +++ b/consensus-client-it/src/test/scala/units/docker/BaseContainer.scala @@ -1,4 +1,4 @@ -package units.network.test.docker +package units.docker import com.wavesplatform.utils.LoggerFacade import org.slf4j.LoggerFactory diff --git a/consensus-client-it/src/test/scala/units/network/test/docker/EcContainer.scala b/consensus-client-it/src/test/scala/units/docker/EcContainer.scala similarity index 79% rename from consensus-client-it/src/test/scala/units/network/test/docker/EcContainer.scala rename to consensus-client-it/src/test/scala/units/docker/EcContainer.scala index 70131e03..15736a96 100644 --- a/consensus-client-it/src/test/scala/units/network/test/docker/EcContainer.scala +++ b/consensus-client-it/src/test/scala/units/docker/EcContainer.scala @@ -1,15 +1,20 @@ -package units.network.test.docker +package units.docker import com.typesafe.config.{ConfigFactory, ConfigValueFactory} import net.ceedubs.ficus.Ficus.toFicusConfig import org.testcontainers.containers.BindMode import org.testcontainers.containers.Network.NetworkImpl import org.testcontainers.utility.DockerImageName +import org.web3j.protocol.Web3j +import org.web3j.protocol.http.HttpService import sttp.client3.HttpClientSyncBackend import units.ClientConfig import units.client.engine.{HttpEngineApiClient, LoggedEngineApiClient} -import units.network.test.docker.BaseContainer.{ConfigsDir, DefaultLogsDir} -import units.network.test.docker.EcContainer.{EnginePort, RpcPort, mkConfig} +import units.docker.BaseContainer.{ConfigsDir, DefaultLogsDir} +import units.docker.EcContainer.{EnginePort, RpcPort, mkConfig} +import units.el.ElBridgeClient +import units.eth.EthAddress +import units.http.OkHttpLogger class EcContainer(network: NetworkImpl, hostName: String, ip: String) extends BaseContainer(hostName) { protected override val container = new GenericContainer(DockerImageName.parse("hyperledger/besu:latest")) @@ -35,7 +40,19 @@ class EcContainer(network: NetworkImpl, hostName: String, ip: String) extends Ba private val httpClientBackend = HttpClientSyncBackend() lazy val engineApi = new LoggedEngineApiClient(new HttpEngineApiClient(mkConfig(container.getHost, enginePort), httpClientBackend)) + lazy val web3j = Web3j.build( + new HttpService( + s"http://${container.getHost}:$rpcPort", + HttpService.getOkHttpClientBuilder + .addInterceptor(OkHttpLogger) + .build() + ) + ) + + lazy val elBridge = new ElBridgeClient(web3j, EthAddress.unsafeFrom("0x0000000000000000000000000000000000006a7e")) + override def stop(): Unit = { + web3j.shutdown() httpClientBackend.close() super.stop() } diff --git a/consensus-client-it/src/test/scala/units/network/test/docker/GenericContainer.scala b/consensus-client-it/src/test/scala/units/docker/GenericContainer.scala similarity index 89% rename from consensus-client-it/src/test/scala/units/network/test/docker/GenericContainer.scala rename to consensus-client-it/src/test/scala/units/docker/GenericContainer.scala index 7c3f4152..7704cf02 100644 --- a/consensus-client-it/src/test/scala/units/network/test/docker/GenericContainer.scala +++ b/consensus-client-it/src/test/scala/units/docker/GenericContainer.scala @@ -1,4 +1,4 @@ -package units.network.test.docker +package units.docker import org.testcontainers.containers.GenericContainer as JGenericContrainer import org.testcontainers.utility.DockerImageName diff --git a/consensus-client-it/src/test/scala/units/network/test/docker/Networks.scala b/consensus-client-it/src/test/scala/units/docker/Networks.scala similarity index 96% rename from consensus-client-it/src/test/scala/units/network/test/docker/Networks.scala rename to consensus-client-it/src/test/scala/units/docker/Networks.scala index d27b15ed..5027f624 100644 --- a/consensus-client-it/src/test/scala/units/network/test/docker/Networks.scala +++ b/consensus-client-it/src/test/scala/units/docker/Networks.scala @@ -1,4 +1,4 @@ -package units.network.test.docker +package units.docker import com.github.dockerjava.api.command.CreateNetworkCmd import com.github.dockerjava.api.model.Network.Ipam diff --git a/consensus-client-it/src/test/scala/units/network/test/docker/WavesNodeContainer.scala b/consensus-client-it/src/test/scala/units/docker/WavesNodeContainer.scala similarity index 93% rename from consensus-client-it/src/test/scala/units/network/test/docker/WavesNodeContainer.scala rename to consensus-client-it/src/test/scala/units/docker/WavesNodeContainer.scala index 819b5080..0ac5d9b4 100644 --- a/consensus-client-it/src/test/scala/units/network/test/docker/WavesNodeContainer.scala +++ b/consensus-client-it/src/test/scala/units/docker/WavesNodeContainer.scala @@ -1,4 +1,4 @@ -package units.network.test.docker +package units.docker import com.wavesplatform.account.Address import com.wavesplatform.api.NodeHttpApi @@ -8,8 +8,8 @@ import org.testcontainers.containers.Network.NetworkImpl import org.testcontainers.utility.DockerImageName import sttp.client3.{HttpClientSyncBackend, UriContext} import units.client.HttpChainContractClient -import units.network.test.docker.BaseContainer.{ConfigsDir, DefaultLogsDir} -import units.network.test.docker.WavesNodeContainer.ApiPort +import units.docker.BaseContainer.{ConfigsDir, DefaultLogsDir} +import units.docker.WavesNodeContainer.ApiPort import java.nio.charset.StandardCharsets import scala.jdk.CollectionConverters.MapHasAsJava diff --git a/consensus-client-it/src/test/scala/units/el/ElBridgeClient.scala b/consensus-client-it/src/test/scala/units/el/ElBridgeClient.scala new file mode 100644 index 00000000..99b09d19 --- /dev/null +++ b/consensus-client-it/src/test/scala/units/el/ElBridgeClient.scala @@ -0,0 +1,56 @@ +package units.el + +import com.wavesplatform.account.Address +import com.wavesplatform.utils.{EthEncoding, ScorexLogging} +import org.web3j.abi.datatypes.generated.Bytes20 +import org.web3j.abi.datatypes.{Type, Function as Web3Function} +import org.web3j.crypto.{Credentials, RawTransaction, TransactionEncoder} +import org.web3j.protocol.Web3j +import org.web3j.protocol.core.DefaultBlockParameterName +import org.web3j.protocol.core.methods.response.EthSendTransaction +import org.web3j.tx.gas.DefaultGasProvider +import org.web3j.utils.Numeric +import units.eth.EthAddress + +import java.util.concurrent.ThreadLocalRandom +import scala.jdk.CollectionConverters.SeqHasAsJava + +class ElBridgeClient(web3j: Web3j, val address: EthAddress) extends ScorexLogging { + def sendNative(sender: Credentials, recipient: Address, amountInEther: BigInt): EthSendTransaction = { + val currRequestId = ThreadLocalRandom.current().nextInt(10000, 100000).toString + + val senderAddress = sender.getAddress + log.debug(s"[$currRequestId] sendNative($senderAddress->$recipient: $amountInEther Ether)") + + val recipientAddressHex = Numeric.toHexString(recipient.publicKeyHash) + val data = org.web3j.abi.FunctionEncoder.encode( + new Web3Function( + "sendNative", + List[Type[?]](new Bytes20(EthEncoding.toBytes(recipientAddressHex))).asJava, + List.empty.asJava + ) + ) + + val ethGetTransactionCount = web3j + .ethGetTransactionCount(senderAddress, DefaultBlockParameterName.LATEST) + .send() + val nonce = ethGetTransactionCount.getTransactionCount + + val transaction = RawTransaction.createTransaction( + nonce, + DefaultGasProvider.GAS_PRICE, + DefaultGasProvider.GAS_LIMIT, + address.hex, + amountInEther.bigInteger, + data + ) + + val signedMessage = TransactionEncoder.signMessage(transaction, sender) + val hexValue = Numeric.toHexString(signedMessage) + val r = web3j.ethSendRawTransaction(hexValue).send() + + log.debug(s"[$currRequestId] txn=${r.getTransactionHash}") + r + } + +} diff --git a/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala b/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala new file mode 100644 index 00000000..360a7a0a --- /dev/null +++ b/consensus-client-it/src/test/scala/units/http/OkHttpLogger.scala @@ -0,0 +1,37 @@ +package units.http + +import com.wavesplatform.utils.ScorexLogging +import okhttp3.{Interceptor, Request, Response} + +import java.util.concurrent.ThreadLocalRandom +import scala.util.Try + +object OkHttpLogger extends Interceptor with ScorexLogging { + override def intercept(chain: Interceptor.Chain): Response = { + val currRequestId = ThreadLocalRandom.current().nextInt(10000, 100000).toString + val req = chain.request() + log.debug(s"[$currRequestId] ${req.url()} ${readRequestBody(req)}") + val res = chain.proceed(req) + log.debug(s"[$currRequestId] ${res.code()}: ${readResponseBody(res)}") + res + } + + private def readRequestBody(request: Request) = request.body() match { + case null => "null" + case body => + val buffer = new okio.Buffer() + Try { + body.writeTo(buffer) + buffer.readUtf8() + }.getOrElse("Could not read body") + } + + private def readResponseBody(response: Response) = response.body() match { + case null => "null" + case body => + val source = body.source() + source.request(Long.MaxValue) // Buffer the entire body. + val buffer = source.buffer().clone() + buffer.readUtf8() + } +}