diff --git a/src/main/scala/units/ELUpdater.scala b/src/main/scala/units/ELUpdater.scala index 7b1ed903..e6488025 100644 --- a/src/main/scala/units/ELUpdater.scala +++ b/src/main/scala/units/ELUpdater.scala @@ -25,7 +25,6 @@ import io.netty.channel.group.DefaultChannelGroup import monix.execution.cancelables.SerialCancelable import monix.execution.{CancelableFuture, Scheduler} import play.api.libs.json.* -import units.ELUpdater.FullValidationState.{Continue, Stop} import units.ELUpdater.State.* import units.ELUpdater.State.ChainStatus.{FollowingChain, Mining, WaitForNewChain} import units.client.L2BlockLike @@ -165,7 +164,7 @@ class ELUpdater( } } - private def callContract(fc: FUNCTION_CALL, blockData: EcBlock, invoker: KeyPair): Job[Unit] = { + private def callContract(fc: FUNCTION_CALL, blockData: EcBlock, invoker: KeyPair): JobResult[Unit] = { val extraFee = if (blockchain.hasPaidVerifier(invoker.toAddress)) ScriptExtraFee else 0 val tx = InvokeScriptTransaction( @@ -258,7 +257,7 @@ class ELUpdater( } } - private def rollbackTo(prevState: Working[ChainStatus], target: L2BlockLike, finalizedBlock: ContractBlock): Job[Working[ChainStatus]] = { + private def rollbackTo(prevState: Working[ChainStatus], target: L2BlockLike, finalizedBlock: ContractBlock): JobResult[Working[ChainStatus]] = { val targetHash = target.hash for { rollbackBlock <- mkRollbackBlock(targetHash) @@ -298,7 +297,7 @@ class ELUpdater( lastElWithdrawalIndex: WithdrawalIndex, chainContractOptions: ChainContractOptions, prevEpochMinerRewardAddress: Option[EthAddress] - ): Job[MiningData] = { + ): JobResult[MiningData] = { val firstElWithdrawalIndex = lastElWithdrawalIndex + 1 val startC2ETransferIndex = lastC2ETransferIndex + 1 @@ -933,7 +932,7 @@ class ELUpdater( logger.debug(s"Unexpected state on sync: $other") }) - private def validateRandao(block: EcBlock, epochNumber: Int): Job[Unit] = + private def validateRandao(block: EcBlock, epochNumber: Int): JobResult[Unit] = blockchain.vrf(epochNumber) match { case None => ClientError(s"VRF of $epochNumber epoch is empty").asLeft case Some(vrf) => @@ -945,7 +944,7 @@ class ELUpdater( ) } - private def validateMiner(block: NetworkL2Block, epochInfo: Option[EpochInfo]): Job[Unit] = { + private def validateMiner(block: NetworkL2Block, epochInfo: Option[EpochInfo]): JobResult[Unit] = { epochInfo match { case Some(epochMeta) => for { @@ -969,7 +968,7 @@ class ELUpdater( } } - private def validateTimestamp(newNetworkBlock: NetworkL2Block, parentEcBlock: EcBlock): Job[Unit] = { + private def validateTimestamp(newNetworkBlock: NetworkL2Block, parentEcBlock: EcBlock): JobResult[Unit] = { val minAppendTs = parentEcBlock.timestamp + config.blockDelay.toSeconds Either.cond( newNetworkBlock.timestamp >= minAppendTs, @@ -985,7 +984,7 @@ class ELUpdater( networkBlock: NetworkL2Block, parentBlock: EcBlock, epochInfo: Option[EpochInfo] - ): Job[Unit] = { + ): JobResult[Unit] = { for { _ <- validateTimestamp(networkBlock, parentBlock) _ <- validateMiner(networkBlock, epochInfo) @@ -993,7 +992,7 @@ class ELUpdater( } yield () } - private def getAltChainReferenceBlock(nodeChainInfo: ChainInfo, lastContractBlock: ContractBlock): Job[ContractBlock] = { + private def getAltChainReferenceBlock(nodeChainInfo: ChainInfo, lastContractBlock: ContractBlock): JobResult[ContractBlock] = { if (nodeChainInfo.isMain) { for { lastEpoch <- chainContractClient @@ -1160,7 +1159,7 @@ class ELUpdater( } } - private def mkRollbackBlock(rollbackTargetBlockId: BlockHash): Job[RollbackBlock] = for { + private def mkRollbackBlock(rollbackTargetBlockId: BlockHash): JobResult[RollbackBlock] = for { targetBlockFromContract <- Right(chainContractClient.getBlock(rollbackTargetBlockId)) targetBlockOpt <- targetBlockFromContract match { case None => engineApiClient.getBlockByHash(rollbackTargetBlockId) @@ -1179,7 +1178,7 @@ class ELUpdater( Withdrawal(index, x.destElAddress, Bridge.clToGweiNativeTokenAmount(x.amount)) } - private def getLastWithdrawalIndex(hash: BlockHash): Job[WithdrawalIndex] = + private def getLastWithdrawalIndex(hash: BlockHash): JobResult[WithdrawalIndex] = engineApiClient.getBlockByHash(hash).flatMap { case None => Left(ClientError(s"Can't find $hash block on EC during withdrawal search")) case Some(ecBlock) => @@ -1191,7 +1190,7 @@ class ELUpdater( } } - private def getE2CTransfersRootHash(hash: BlockHash, elBridgeAddress: EthAddress): Job[Digest] = + private def getE2CTransfersRootHash(hash: BlockHash, elBridgeAddress: EthAddress): JobResult[Digest] = for { elRawLogs <- engineApiClient.getLogs(hash, elBridgeAddress, Bridge.ElSentNativeEventTopic) rootHash <- { @@ -1209,49 +1208,43 @@ class ELUpdater( } } yield rootHash + private def skipFinalizedBlocksValidation(curState: Working[ChainStatus]) = { + if (curState.finalizedBlock.height > curState.fullValidationStatus.lastValidatedBlock.height) { + val newState = curState.copy(fullValidationStatus = FullValidationStatus(curState.finalizedBlock, None)) + setState("4", newState) + newState + } else curState + } + private def validateAppliedBlocks(): Unit = { state match { case w: Working[ChainStatus] => - val blocksToValidate = getContractBlocksForValidation(w.lastContractBlock, w.fullValidationStatus.lastValidatedBlock) - blocksToValidate.foldLeft[FullValidationState](Continue(w)) { - case (Continue(curState), contractBlock) => - getBlockForValidation(contractBlock, curState.lastEcBlock, curState.finalizedBlock) - .map { - case BlockForValidation.Found(contractBlock, ecBlock) => - logger.debug(s"Trying to validate applied block ${contractBlock.hash}") - validateAppliedBlock(contractBlock, ecBlock, curState) match { - case Right(updatedState) => - logger.debug(s"Block ${contractBlock.hash} was successfully validated") - Continue(updatedState) - case Left(err) => - logger.debug(s"Validation of applied block ${contractBlock.hash} failed: ${err.message}") - processInvalidBlock(contractBlock, curState, None) - Stop - } - case BlockForValidation.SkippedFinalized(block) => - logger.debug(s"Validation of applied block ${block.hash} skipped, because of finalization") - val newState = - curState.copy(fullValidationStatus = curState.fullValidationStatus.copy(lastValidatedBlock = block, lastElWithdrawalIndex = None)) - setState("4", newState) - Continue(newState) - case BlockForValidation.NotFound => Stop - } - .fold[FullValidationState]( - err => { - logger.warn(s"Validation of applied blocks error: ${err.message}") - Stop - }, - identity - ) - case _ => Stop - } + val startState = skipFinalizedBlocksValidation(w) + getContractBlocksForValidation(startState).fold[Unit]( + err => logger.error(s"Validation of applied blocks error: ${err.message}"), + blocksToValidate => + blocksToValidate.foldLeft[JobResult[Working[ChainStatus]]](Right(startState)) { + case (Right(curState), block) => + logger.debug(s"Trying to validate applied block ${block.hash}") + validateAppliedBlock(block.contractBlock, block.ecBlock, curState) match { + case Right(updatedState) => + logger.debug(s"Block ${block.hash} was successfully validated") + Right(updatedState) + case Left(err) => + logger.debug(s"Validation of applied block ${block.hash} failed: ${err.message}") + processInvalidBlock(block.contractBlock, curState, None) + Left(err) + } + case (err, _) => err + } + ) case other => logger.debug(s"Skipping validation of applied blocks: $other") Either.unit } } - private def validateE2CTransfers(contractBlock: ContractBlock, elBridgeAddress: EthAddress): Job[Unit] = + private def validateE2CTransfers(contractBlock: ContractBlock, elBridgeAddress: EthAddress): JobResult[Unit] = getE2CTransfersRootHash(contractBlock.hash, elBridgeAddress).flatMap { elRootHash => // elRootHash is the source of true if (java.util.Arrays.equals(contractBlock.e2cTransfersRootHash, elRootHash)) Either.unit @@ -1270,7 +1263,7 @@ class ELUpdater( ecBlock: EcBlock, fullValidationStatus: FullValidationStatus, chainContractOptions: ChainContractOptions - ): Job[Option[WithdrawalIndex]] = { + ): JobResult[Option[WithdrawalIndex]] = { val blockEpoch = chainContractClient .getEpochMeta(contractBlock.epoch) .getOrElse(throw new RuntimeException(s"Can't find an epoch ${contractBlock.epoch} data of block ${contractBlock.hash} on chain contract")) @@ -1308,7 +1301,7 @@ class ELUpdater( contractBlock: ContractBlock, parentBlock: EcBlock, prevState: Working[ChainStatus] - ): Job[Working[ChainStatus]] = { + ): JobResult[Working[ChainStatus]] = { logger.debug(s"Trying to do full validation of block ${networkBlock.hash}") for { _ <- preValidateBlock(networkBlock, parentBlock, None) @@ -1322,7 +1315,7 @@ class ELUpdater( contractBlock: ContractBlock, ecBlock: EcBlock, prevState: Working[ChainStatus] - ): Job[Working[ChainStatus]] = { + ): JobResult[Working[ChainStatus]] = { val validationResult = for { _ <- Either.cond( @@ -1388,49 +1381,33 @@ class ELUpdater( } } - private def getContractBlocksForValidation( - lastContractBlock: ContractBlock, - lastValidatedBlock: ContractBlock - ): List[ContractBlock] = { + private def getContractBlocksForValidation(curState: Working[ChainStatus]): JobResult[List[BlockForValidation]] = { @tailrec - def loop(curBlock: ContractBlock, acc: List[ContractBlock]): List[ContractBlock] = { - if (curBlock.parentHash == lastValidatedBlock.hash) { - curBlock :: acc + def loop(curBlock: ContractBlock, acc: List[BlockForValidation]): JobResult[List[BlockForValidation]] = { + if (curBlock.height <= curState.fullValidationStatus.lastValidatedBlock.height || curBlock.height <= curState.finalizedBlock.height) { + Right(acc) } else { chainContractClient.getBlock(curBlock.parentHash) match { - case Some(block) => - loop(block, curBlock :: acc) + case Some(parentBlock) => + if (curBlock.height > curState.lastEcBlock.height) { + loop(parentBlock, acc) + } else { + engineApiClient.getBlockByHash(curBlock.hash) match { + case Right(Some(ecBlock)) => + loop(parentBlock, BlockForValidation(curBlock, ecBlock) :: acc) + case Right(None) => + Left(ClientError(s"Block ${curBlock.hash} not found on EC client for full validation")) + case Left(err) => + Left(ClientError(s"Can't get EC block ${curBlock.hash} for full validation: ${err.message}")) + } + } case _ => - logger.error(s"Block ${curBlock.parentHash} not found at contract during full validation") - List.empty + Left(ClientError(s"Block ${curBlock.parentHash} not found at contract during full validation")) } } } - if (lastContractBlock.height <= lastValidatedBlock.height) { - List.empty - } else { - loop(lastContractBlock, List.empty) - } - } - - private def getBlockForValidation( - contractBlock: ContractBlock, - lastEcBlock: EcBlock, - finalizedBlock: ContractBlock - ): Job[BlockForValidation] = { - if (contractBlock.height <= lastEcBlock.height) { - if (contractBlock.height <= finalizedBlock.height) Right(BlockForValidation.SkippedFinalized(contractBlock)) - else - engineApiClient.getBlockByHash(contractBlock.hash).map { - case Some(ecBlock) => BlockForValidation.Found(contractBlock, ecBlock) - case None => - logger.debug(s"Can't find a block ${contractBlock.hash} on EC client for a full validation") - BlockForValidation.NotFound - } - } else { - Right(BlockForValidation.NotFound) - } + loop(curState.lastContractBlock, List.empty) } private def validateC2ETransfers( @@ -1494,12 +1471,12 @@ class ELUpdater( } .map(_ => ()) - private def confirmBlock(block: L2BlockLike, finalizedBlock: L2BlockLike): Job[String] = { + private def confirmBlock(block: L2BlockLike, finalizedBlock: L2BlockLike): JobResult[String] = { val finalizedBlockHash = if (finalizedBlock.height > block.height) block.hash else finalizedBlock.hash engineApiClient.forkChoiceUpdate(block.hash, finalizedBlockHash) } - private def confirmBlock(hash: BlockHash, finalizedBlockHash: BlockHash): Job[String] = + private def confirmBlock(hash: BlockHash, finalizedBlockHash: BlockHash): JobResult[String] = engineApiClient.forkChoiceUpdate(hash, finalizedBlockHash) private def confirmBlockAndStartMining( @@ -1509,7 +1486,7 @@ class ELUpdater( suggestedFeeRecipient: EthAddress, prevRandao: String, withdrawals: Vector[Withdrawal] - ): Job[String] = { + ): JobResult[String] = { val finalizedBlockHash = if (finalizedBlock.height > lastBlock.height) lastBlock.hash else finalizedBlock.hash engineApiClient .forkChoiceUpdateWithPayloadId( @@ -1607,17 +1584,8 @@ object ELUpdater { private case class MiningData(payloadId: PayloadId, nextBlockUnixTs: Long, lastC2ETransferIndex: Long, lastElWithdrawalIndex: WithdrawalIndex) - private sealed trait BlockForValidation - private object BlockForValidation { - case class Found(contractBlock: ContractBlock, ecBlock: EcBlock) extends BlockForValidation - case class SkippedFinalized(block: ContractBlock) extends BlockForValidation - case object NotFound extends BlockForValidation - } - - sealed trait FullValidationState - object FullValidationState { - case class Continue(state: Working[ChainStatus]) extends FullValidationState - case object Stop extends FullValidationState + private case class BlockForValidation(contractBlock: ContractBlock, ecBlock: EcBlock) { + val hash: BlockHash = contractBlock.hash } case class FullValidationStatus(lastValidatedBlock: ContractBlock, lastElWithdrawalIndex: Option[WithdrawalIndex]) { diff --git a/src/main/scala/units/client/contract/ContractFunction.scala b/src/main/scala/units/client/contract/ContractFunction.scala index 0bc9966c..6375fe35 100644 --- a/src/main/scala/units/client/contract/ContractFunction.scala +++ b/src/main/scala/units/client/contract/ContractFunction.scala @@ -8,10 +8,10 @@ import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BYTESTR, CONST_LONG, CONST_STRING, EVALUATED, FUNCTION_CALL} import org.web3j.utils.Numeric.cleanHexPrefix import units.util.HexBytesConverter.toHexNoPrefix -import units.{BlockHash, ClientError, Job} +import units.{BlockHash, ClientError, JobResult} abstract class ContractFunction(name: String, reference: BlockHash, extraArgs: Either[CommonError, List[EVALUATED]]) { - def toFunctionCall(blockHash: BlockHash, transfersRootHash: Digest, lastC2ETransferIndex: Long): Job[FUNCTION_CALL] = (for { + def toFunctionCall(blockHash: BlockHash, transfersRootHash: Digest, lastC2ETransferIndex: Long): JobResult[FUNCTION_CALL] = (for { hash <- CONST_STRING(cleanHexPrefix(blockHash)) ref <- CONST_STRING(cleanHexPrefix(reference)) trh <- CONST_STRING(toHexNoPrefix(transfersRootHash)) diff --git a/src/main/scala/units/client/engine/EngineApiClient.scala b/src/main/scala/units/client/engine/EngineApiClient.scala index 200b7e4e..9731a530 100644 --- a/src/main/scala/units/client/engine/EngineApiClient.scala +++ b/src/main/scala/units/client/engine/EngineApiClient.scala @@ -4,10 +4,10 @@ import play.api.libs.json.* import units.client.engine.EngineApiClient.PayloadId import units.client.engine.model.* import units.eth.EthAddress -import units.{BlockHash, Job} +import units.{BlockHash, JobResult} trait EngineApiClient { - def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): Job[String] // TODO Replace String with an appropriate type + def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): JobResult[String] // TODO Replace String with an appropriate type def forkChoiceUpdateWithPayloadId( lastBlockHash: BlockHash, @@ -16,25 +16,25 @@ trait EngineApiClient { suggestedFeeRecipient: EthAddress, prevRandao: String, withdrawals: Vector[Withdrawal] = Vector.empty - ): Job[PayloadId] + ): JobResult[PayloadId] - def getPayload(payloadId: PayloadId): Job[JsObject] + def getPayload(payloadId: PayloadId): JobResult[JsObject] - def applyNewPayload(payload: JsObject): Job[Option[BlockHash]] + def applyNewPayload(payload: JsObject): JobResult[Option[BlockHash]] - def getPayloadBodyByHash(hash: BlockHash): Job[Option[JsObject]] + def getPayloadBodyByHash(hash: BlockHash): JobResult[Option[JsObject]] - def getBlockByNumber(number: BlockNumber): Job[Option[EcBlock]] + def getBlockByNumber(number: BlockNumber): JobResult[Option[EcBlock]] - def getBlockByHash(hash: BlockHash): Job[Option[EcBlock]] + def getBlockByHash(hash: BlockHash): JobResult[Option[EcBlock]] - def getBlockByHashJson(hash: BlockHash): Job[Option[JsObject]] + def getBlockByHashJson(hash: BlockHash): JobResult[Option[JsObject]] - def getLastExecutionBlock: Job[EcBlock] + def getLastExecutionBlock: JobResult[EcBlock] - def blockExists(hash: BlockHash): Job[Boolean] + def blockExists(hash: BlockHash): JobResult[Boolean] - def getLogs(hash: BlockHash, address: EthAddress, topic: String): Job[List[GetLogsResponseEntry]] + def getLogs(hash: BlockHash, address: EthAddress, topic: String): JobResult[List[GetLogsResponseEntry]] } object EngineApiClient { diff --git a/src/main/scala/units/client/engine/HttpEngineApiClient.scala b/src/main/scala/units/client/engine/HttpEngineApiClient.scala index 810af2a7..e5bd48e0 100644 --- a/src/main/scala/units/client/engine/HttpEngineApiClient.scala +++ b/src/main/scala/units/client/engine/HttpEngineApiClient.scala @@ -11,7 +11,7 @@ import units.client.engine.HttpEngineApiClient.* import units.client.engine.model.* import units.client.engine.model.ForkChoiceUpdatedRequest.ForkChoiceAttributes import units.eth.EthAddress -import units.{BlockHash, ClientConfig, ClientError, Job} +import units.{BlockHash, ClientConfig, ClientError, JobResult} import scala.concurrent.duration.{DurationInt, FiniteDuration} @@ -19,7 +19,7 @@ class HttpEngineApiClient(val config: ClientConfig, val backend: SttpBackend[Ide val apiUrl: Uri = uri"${config.executionClientAddress}" - def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): Job[String] = { + def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): JobResult[String] = { sendEngineRequest[ForkChoiceUpdatedRequest, ForkChoiceUpdatedResponse]( ForkChoiceUpdatedRequest(blockHash, finalizedBlockHash, None), BlockExecutionTimeout @@ -39,7 +39,7 @@ class HttpEngineApiClient(val config: ClientConfig, val backend: SttpBackend[Ide suggestedFeeRecipient: EthAddress, prevRandao: String, withdrawals: Vector[Withdrawal] - ): Job[PayloadId] = { + ): JobResult[PayloadId] = { sendEngineRequest[ForkChoiceUpdatedRequest, ForkChoiceUpdatedResponse]( ForkChoiceUpdatedRequest( lastBlockHash, @@ -59,11 +59,11 @@ class HttpEngineApiClient(val config: ClientConfig, val backend: SttpBackend[Ide } } - def getPayload(payloadId: PayloadId): Job[JsObject] = { + def getPayload(payloadId: PayloadId): JobResult[JsObject] = { sendEngineRequest[GetPayloadRequest, GetPayloadResponse](GetPayloadRequest(payloadId), NonBlockExecutionTimeout).map(_.executionPayload) } - def applyNewPayload(payload: JsObject): Job[Option[BlockHash]] = { + def applyNewPayload(payload: JsObject): JobResult[Option[BlockHash]] = { sendEngineRequest[NewPayloadRequest, PayloadStatus](NewPayloadRequest(payload), BlockExecutionTimeout).flatMap { case PayloadStatus(_, _, Some(validationError)) => Left(ClientError(s"Payload validation error: $validationError")) case PayloadStatus(status, Some(latestValidHash), _) if status == "VALID" => Right(Some(latestValidHash)) @@ -73,47 +73,47 @@ class HttpEngineApiClient(val config: ClientConfig, val backend: SttpBackend[Ide } } - def getPayloadBodyByHash(hash: BlockHash): Job[Option[JsObject]] = { + def getPayloadBodyByHash(hash: BlockHash): JobResult[Option[JsObject]] = { sendEngineRequest[GetPayloadBodyByHash, JsArray](GetPayloadBodyByHash(hash), NonBlockExecutionTimeout) .map(_.value.headOption.flatMap(_.asOpt[JsObject])) } - def getBlockByNumber(number: BlockNumber): Job[Option[EcBlock]] = { + def getBlockByNumber(number: BlockNumber): JobResult[Option[EcBlock]] = { for { json <- getBlockByNumberJson(number.str) blockMeta <- json.traverse(parseJson[EcBlock](_)) } yield blockMeta } - def getBlockByHash(hash: BlockHash): Job[Option[EcBlock]] = { + def getBlockByHash(hash: BlockHash): JobResult[Option[EcBlock]] = { sendRequest[GetBlockByHashRequest, EcBlock](GetBlockByHashRequest(hash)) .leftMap(err => ClientError(s"Error getting block by hash $hash: $err")) } - def getBlockByHashJson(hash: BlockHash): Job[Option[JsObject]] = { + def getBlockByHashJson(hash: BlockHash): JobResult[Option[JsObject]] = { sendRequest[GetBlockByHashRequest, JsObject](GetBlockByHashRequest(hash)) .leftMap(err => ClientError(s"Error getting block json by hash $hash: $err")) } - def getLastExecutionBlock: Job[EcBlock] = for { + def getLastExecutionBlock: JobResult[EcBlock] = for { lastEcBlockOpt <- getBlockByNumber(BlockNumber.Latest) lastEcBlock <- Either.fromOption(lastEcBlockOpt, ClientError("Impossible: EC doesn't have blocks")) } yield lastEcBlock - def blockExists(hash: BlockHash): Job[Boolean] = + def blockExists(hash: BlockHash): JobResult[Boolean] = getBlockByHash(hash).map(_.isDefined) - private def getBlockByNumberJson(number: String): Job[Option[JsObject]] = { + private def getBlockByNumberJson(number: String): JobResult[Option[JsObject]] = { sendRequest[GetBlockByNumberRequest, JsObject](GetBlockByNumberRequest(number)) .leftMap(err => ClientError(s"Error getting block by number $number: $err")) } - override def getLogs(hash: BlockHash, address: EthAddress, topic: String): Job[List[GetLogsResponseEntry]] = + override def getLogs(hash: BlockHash, address: EthAddress, topic: String): JobResult[List[GetLogsResponseEntry]] = sendRequest[GetLogsRequest, List[GetLogsResponseEntry]](GetLogsRequest(hash, address, List(topic))) .leftMap(err => ClientError(s"Error getting block logs by hash $hash: $err")) .map(_.getOrElse(List.empty)) - private def sendEngineRequest[A: Writes, B: Reads](request: A, timeout: FiniteDuration): Job[B] = { + private def sendEngineRequest[A: Writes, B: Reads](request: A, timeout: FiniteDuration): JobResult[B] = { sendRequest(request, timeout) match { case Right(response) => response.toRight(ClientError(s"Unexpected engine API empty response")) case Left(err) => Left(ClientError(s"Engine API request error: $err")) diff --git a/src/main/scala/units/client/engine/LoggedEngineApiClient.scala b/src/main/scala/units/client/engine/LoggedEngineApiClient.scala index 0a183309..8e297439 100644 --- a/src/main/scala/units/client/engine/LoggedEngineApiClient.scala +++ b/src/main/scala/units/client/engine/LoggedEngineApiClient.scala @@ -7,7 +7,7 @@ import units.client.engine.EngineApiClient.PayloadId import units.client.engine.LoggedEngineApiClient.excludedJsonFields import units.client.engine.model.* import units.eth.EthAddress -import units.{BlockHash, Job} +import units.{BlockHash, JobResult} import java.util.concurrent.ThreadLocalRandom import scala.util.chaining.scalaUtilChainingOps @@ -15,7 +15,7 @@ import scala.util.chaining.scalaUtilChainingOps class LoggedEngineApiClient(underlying: EngineApiClient) extends EngineApiClient { protected val log = LoggerFacade(LoggerFactory.getLogger(underlying.getClass)) - override def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): Job[String] = + override def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): JobResult[String] = wrap(s"forkChoiceUpdate($blockHash, f=$finalizedBlockHash)", underlying.forkChoiceUpdate(blockHash, finalizedBlockHash)) override def forkChoiceUpdateWithPayloadId( @@ -25,40 +25,40 @@ class LoggedEngineApiClient(underlying: EngineApiClient) extends EngineApiClient suggestedFeeRecipient: EthAddress, prevRandao: String, withdrawals: Vector[Withdrawal] - ): Job[PayloadId] = wrap( + ): JobResult[PayloadId] = wrap( s"forkChoiceUpdateWithPayloadId(l=$lastBlockHash, f=$finalizedBlockHash, ts=$unixEpochSeconds, m=$suggestedFeeRecipient, " + s"r=$prevRandao, w={${withdrawals.mkString(", ")}}", underlying.forkChoiceUpdateWithPayloadId(lastBlockHash, finalizedBlockHash, unixEpochSeconds, suggestedFeeRecipient, prevRandao, withdrawals) ) - override def getPayload(payloadId: PayloadId): Job[JsObject] = + override def getPayload(payloadId: PayloadId): JobResult[JsObject] = wrap(s"getPayload($payloadId)", underlying.getPayload(payloadId), filteredJson) - override def applyNewPayload(payload: JsObject): Job[Option[BlockHash]] = + override def applyNewPayload(payload: JsObject): JobResult[Option[BlockHash]] = wrap(s"applyNewPayload(${filteredJson(payload)})", underlying.applyNewPayload(payload), _.fold("None")(_.toString)) - override def getPayloadBodyByHash(hash: BlockHash): Job[Option[JsObject]] = + override def getPayloadBodyByHash(hash: BlockHash): JobResult[Option[JsObject]] = wrap(s"getPayloadBodyByHash($hash)", underlying.getPayloadBodyByHash(hash), _.fold("None")(filteredJson)) - override def getBlockByNumber(number: BlockNumber): Job[Option[EcBlock]] = + override def getBlockByNumber(number: BlockNumber): JobResult[Option[EcBlock]] = wrap(s"getBlockByNumber($number)", underlying.getBlockByNumber(number), _.fold("None")(_.toString)) - override def getBlockByHash(hash: BlockHash): Job[Option[EcBlock]] = + override def getBlockByHash(hash: BlockHash): JobResult[Option[EcBlock]] = wrap(s"getBlockByHash($hash)", underlying.getBlockByHash(hash), _.fold("None")(_.toString)) - override def getBlockByHashJson(hash: BlockHash): Job[Option[JsObject]] = + override def getBlockByHashJson(hash: BlockHash): JobResult[Option[JsObject]] = wrap(s"getBlockByHashJson($hash)", underlying.getBlockByHashJson(hash), _.fold("None")(filteredJson)) - override def getLastExecutionBlock: Job[EcBlock] = + override def getLastExecutionBlock: JobResult[EcBlock] = wrap("getLastExecutionBlock", underlying.getLastExecutionBlock) - override def blockExists(hash: BlockHash): Job[Boolean] = + override def blockExists(hash: BlockHash): JobResult[Boolean] = wrap(s"blockExists($hash)", underlying.blockExists(hash)) - override def getLogs(hash: BlockHash, address: EthAddress, topic: String): Job[List[GetLogsResponseEntry]] = + override def getLogs(hash: BlockHash, address: EthAddress, topic: String): JobResult[List[GetLogsResponseEntry]] = wrap(s"getLogs($hash, a=$address, t=$topic)", underlying.getLogs(hash, address, topic), _.view.map(_.data).mkString("{", ", ", "}")) - protected def wrap[R](method: String, f: => Job[R], toMsg: R => String = (_: R).toString): Job[R] = { + protected def wrap[R](method: String, f: => JobResult[R], toMsg: R => String = (_: R).toString): JobResult[R] = { val currRequestId = ThreadLocalRandom.current().nextInt(10000, 100000).toString log.debug(s"[$currRequestId] $method") diff --git a/src/main/scala/units/package.scala b/src/main/scala/units/package.scala index a437e4fb..b8b13ff3 100644 --- a/src/main/scala/units/package.scala +++ b/src/main/scala/units/package.scala @@ -1,4 +1,4 @@ package object units { - type BlockHash = BlockHash.Type - type Job[A] = Either[ClientError, A] + type BlockHash = BlockHash.Type + type JobResult[A] = Either[ClientError, A] } diff --git a/src/test/scala/units/ExtensionDomain.scala b/src/test/scala/units/ExtensionDomain.scala index 8ba72c47..894a6dfa 100644 --- a/src/test/scala/units/ExtensionDomain.scala +++ b/src/test/scala/units/ExtensionDomain.scala @@ -54,17 +54,17 @@ import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.reflect.ClassTag class ExtensionDomain( - rdb: RDB, - blockchainUpdater: BlockchainUpdaterImpl, - rocksDBWriter: RocksDBWriter, - settings: WavesSettings, - override val elBridgeAddress: EthAddress, - elMinerDefaultReward: Gwei - ) extends Domain(rdb, blockchainUpdater, rocksDBWriter, settings) - with HasConsensusLayerDappTxHelpers - with CustomMatchers - with AutoCloseable - with ScorexLogging { self => + rdb: RDB, + blockchainUpdater: BlockchainUpdaterImpl, + rocksDBWriter: RocksDBWriter, + settings: WavesSettings, + override val elBridgeAddress: EthAddress, + elMinerDefaultReward: Gwei +) extends Domain(rdb, blockchainUpdater, rocksDBWriter, settings) + with HasConsensusLayerDappTxHelpers + with CustomMatchers + with AutoCloseable + with ScorexLogging { self => override val chainContractAccount: KeyPair = KeyPair("chain-contract".getBytes(StandardCharsets.UTF_8)) override val stakingContractAccount: KeyPair = KeyPair("staking-contract".getBytes(StandardCharsets.UTF_8)) @@ -137,10 +137,10 @@ class ExtensionDomain( val defaultInterval = ClChangedProcessingDelay def waitForWorking( - title: String = "", - maxTimeout: FiniteDuration = defaultMaxTimeout, - interval: FiniteDuration = defaultInterval - )(f: Working[?] => Unit): Unit = { + title: String = "", + maxTimeout: FiniteDuration = defaultMaxTimeout, + interval: FiniteDuration = defaultInterval + )(f: Working[?] => Unit): Unit = { val logPrefix = if (title.isEmpty) "waitForWorking" else s"waitForWorking($title)" log.trace(logPrefix) val maxAttempts = (maxTimeout / interval).toInt @@ -233,12 +233,12 @@ class ExtensionDomain( // Useful for debugging purposes def evaluateExtendAltChain( - minerAccount: KeyPair, - chainId: Long, - block: L2BlockLike, - epoch: Long, - e2CTransfersRootHashHex: String = EmptyE2CTransfersRootHashHex - ): Either[String, JsObject] = { + minerAccount: KeyPair, + chainId: Long, + block: L2BlockLike, + epoch: Long, + e2CTransfersRootHashHex: String = EmptyE2CTransfersRootHashHex + ): Either[String, JsObject] = { val r = evaluate( chainContractAddress, FunctionCallPart( @@ -372,4 +372,4 @@ object ExtensionDomain { } implicit val functionCallPartWrites: Writes[FunctionCallPart] = Json.writes -} \ No newline at end of file +} diff --git a/src/test/scala/units/HasJobLogging.scala b/src/test/scala/units/HasJobLogging.scala index f562860b..c3fcc229 100644 --- a/src/test/scala/units/HasJobLogging.scala +++ b/src/test/scala/units/HasJobLogging.scala @@ -7,7 +7,7 @@ import java.util.concurrent.ThreadLocalRandom import scala.util.chaining.scalaUtilChainingOps trait HasJobLogging extends ScorexLogging { - protected def wrap[A](method: String, f: => Job[A], toMsg: A => String = (_: A).toString): Job[A] = { + protected def wrap[A](method: String, f: => JobResult[A], toMsg: A => String = (_: A).toString): JobResult[A] = { val currRequestId = ThreadLocalRandom.current().nextInt(10000, 100000).toString log.debug(s"[$currRequestId] $method") @@ -17,5 +17,5 @@ trait HasJobLogging extends ScorexLogging { } } - protected def l(text: String): Job[Unit] = log.debug(text).asRight + protected def l(text: String): JobResult[Unit] = log.debug(text).asRight } diff --git a/src/test/scala/units/client/TestEcClients.scala b/src/test/scala/units/client/TestEcClients.scala index f998944c..c402a3f6 100644 --- a/src/test/scala/units/client/TestEcClients.scala +++ b/src/test/scala/units/client/TestEcClients.scala @@ -12,7 +12,7 @@ import units.client.engine.model.* import units.client.engine.{EngineApiClient, LoggedEngineApiClient} import units.collections.ListOps.* import units.eth.EthAddress -import units.{BlockHash, Job, NetworkL2Block} +import units.{BlockHash, JobResult, NetworkL2Block} class TestEcClients private ( knownBlocks: Atomic[Map[BlockHash, ChainId]], @@ -64,7 +64,7 @@ class TestEcClients private ( val engineApi = new LoggedEngineApiClient( new EngineApiClient { - override def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): Job[String] = { + override def forkChoiceUpdate(blockHash: BlockHash, finalizedBlockHash: BlockHash): JobResult[String] = { knownBlocks.get().get(blockHash) match { case Some(cid) => currChainIdValue.set(cid) @@ -86,7 +86,7 @@ class TestEcClients private ( suggestedFeeRecipient: EthAddress, prevRandao: String, withdrawals: Vector[Withdrawal] - ): Job[PayloadId] = + ): JobResult[PayloadId] = forgingBlocks .get() .collectFirst { case fb if fb.testBlock.parentHash == lastBlockHash => fb } match { @@ -98,7 +98,7 @@ class TestEcClients private ( fb.payloadId.asRight } - override def getPayload(payloadId: PayloadId): Job[JsObject] = + override def getPayload(payloadId: PayloadId): JobResult[JsObject] = forgingBlocks.transformAndExtract(_.withoutFirst { fb => fb.payloadId == payloadId }) match { case Some(fb) => TestEcBlocks.toPayload(fb.testBlock, fb.testBlock.prevRandao).asRight case None => @@ -107,7 +107,7 @@ class TestEcClients private ( ) } - override def applyNewPayload(payload: JsObject): Job[Option[BlockHash]] = { + override def applyNewPayload(payload: JsObject): JobResult[Option[BlockHash]] = { val newBlock = NetworkL2Block(payload).explicitGet().toEcBlock knownBlocks.get().get(newBlock.parentHash) match { case Some(cid) => @@ -128,16 +128,16 @@ class TestEcClients private ( Some(newBlock.hash) }.asRight - override def getPayloadBodyByHash(hash: BlockHash): Job[Option[JsObject]] = + override def getPayloadBodyByHash(hash: BlockHash): JobResult[Option[JsObject]] = getBlockByHashJson(hash) - override def getBlockByNumber(number: BlockNumber): Job[Option[EcBlock]] = + override def getBlockByNumber(number: BlockNumber): JobResult[Option[EcBlock]] = number match { case BlockNumber.Latest => currChain.headOption.asRight case BlockNumber.Number(n) => currChain.find(_.height == n).asRight } - override def getBlockByHash(hash: BlockHash): Job[Option[EcBlock]] = { + override def getBlockByHash(hash: BlockHash): JobResult[Option[EcBlock]] = { for { cid <- knownBlocks.get().get(hash) c <- chains.get().get(cid) @@ -145,14 +145,14 @@ class TestEcClients private ( } yield b }.asRight - override def getBlockByHashJson(hash: BlockHash): Job[Option[JsObject]] = + override def getBlockByHashJson(hash: BlockHash): JobResult[Option[JsObject]] = notImplementedMethodJob("getBlockByHashJson") - override def getLastExecutionBlock: Job[EcBlock] = currChain.head.asRight + override def getLastExecutionBlock: JobResult[EcBlock] = currChain.head.asRight - override def blockExists(hash: BlockHash): Job[Boolean] = notImplementedMethodJob("blockExists") + override def blockExists(hash: BlockHash): JobResult[Boolean] = notImplementedMethodJob("blockExists") - override def getLogs(hash: BlockHash, address: EthAddress, topic: String): Job[List[GetLogsResponseEntry]] = { + override def getLogs(hash: BlockHash, address: EthAddress, topic: String): JobResult[List[GetLogsResponseEntry]] = { val request = GetLogsRequest(hash, address, List(topic)) getLogsCalls.transform(_ + hash) logs.get().getOrElse(request, throw notImplementedCase("call setBlockLogs")) @@ -160,7 +160,7 @@ class TestEcClients private ( } ) - protected def notImplementedMethodJob[A](text: String): Job[A] = throw new NotImplementedMethod(text) + protected def notImplementedMethodJob[A](text: String): JobResult[A] = throw new NotImplementedMethod(text) protected def notImplementedCase(text: String): Throwable = new NotImplementedCase(text) }