package com.xsn.explorer.services import javax.inject.Inject import com.alexitc.playsonify.core.FutureApplicationResult import com.alexitc.playsonify.core.FutureOr.Implicits.{FutureOps, OrOps} import com.xsn.explorer.errors.BlockNotFoundError import com.xsn.explorer.models._ import com.xsn.explorer.models.rpc.{Block, TransactionVIN} import com.xsn.explorer.services.logic.{BlockLogic, TransactionLogic} import org.scalactic.Good import scala.concurrent.{ExecutionContext, Future} class BlockService @Inject() ( xsnService: XSNService, blockLogic: BlockLogic, transactionLogic: TransactionLogic)( implicit ec: ExecutionContext) { def getDetails(blockhashString: String): FutureApplicationResult[BlockDetails] = { val result = for { blockhash <- blockLogic .getBlockhash(blockhashString) .toFutureOr block <- xsnService .getBlock(blockhash) .toFutureOr rewards <- getBlockRewards(block).toFutureOr } yield BlockDetails(block, rewards) result.toFuture } def getLatestBlocks(): FutureApplicationResult[List[Block]] = { /** * Temporal workaround to retrieve the latest blocks, they * will be retrieved from the database once available. */ val result = for { a <- xsnService.getLatestBlock().toFutureOr b <- xsnService.getBlock(a.previousBlockhash.get).toFutureOr c <- xsnService.getBlock(b.previousBlockhash.get).toFutureOr d <- xsnService.getBlock(c.previousBlockhash.get).toFutureOr e <- xsnService.getBlock(d.previousBlockhash.get).toFutureOr f <- xsnService.getBlock(e.previousBlockhash.get).toFutureOr g <- xsnService.getBlock(f.previousBlockhash.get).toFutureOr h <- xsnService.getBlock(g.previousBlockhash.get).toFutureOr i <- xsnService.getBlock(h.previousBlockhash.get).toFutureOr j <- xsnService.getBlock(i.previousBlockhash.get).toFutureOr } yield List(a, b, c, d, e, f, g, h, i, j) result.toFuture } private def getBlockRewards(block: Block): FutureApplicationResult[BlockRewards] = { if (block.isPoW) { getPoWBlockRewards(block) } else if (block.isPoS) { getPoSBlockRewards(block) } else { getTPoSBlockRewards(block) } } private def getPoWBlockRewards(block: Block): FutureApplicationResult[PoWBlockRewards] = { val result = for { txid <- blockLogic.getPoWTransactionId(block).toFutureOr // TODO: handle tx not found tx <- xsnService.getTransaction(txid).toFutureOr vout <- transactionLogic.getVOUT(0, tx, BlockNotFoundError).toFutureOr address <- transactionLogic.getAddress(vout, BlockNotFoundError).toFutureOr } yield PoWBlockRewards(BlockReward(address, vout.value)) result.toFuture } private def getPoSBlockRewards(block: Block): FutureApplicationResult[PoSBlockRewards] = { val result = for { coinstakeTxId <- blockLogic .getCoinstakeTransactionId(block) .toFutureOr coinstakeTx <- xsnService .getTransaction(coinstakeTxId) .toFutureOr coinstakeTxVIN <- transactionLogic .getVIN(coinstakeTx, BlockNotFoundError) .toFutureOr previousToCoinstakeTx <- xsnService .getTransaction(coinstakeTxVIN.txid) .toFutureOr previousToCoinstakeVOUT <- transactionLogic .getVOUT(coinstakeTxVIN, previousToCoinstakeTx, BlockNotFoundError) .toFutureOr coinstakeAddress <- transactionLogic .getAddress(previousToCoinstakeVOUT, BlockNotFoundError) .toFutureOr rewards <- blockLogic .getPoSRewards(coinstakeTx, coinstakeAddress, previousToCoinstakeVOUT.value) .toFutureOr } yield rewards result.toFuture } private def getTPoSBlockRewards(block: Block): FutureApplicationResult[BlockRewards] = { val result = for { coinstakeTxId <- blockLogic .getCoinstakeTransactionId(block) .toFutureOr coinstakeTx <- xsnService .getTransaction(coinstakeTxId) .toFutureOr coinstakeTxVIN <- transactionLogic .getVIN(coinstakeTx, BlockNotFoundError) .toFutureOr coinstakeInput <- getCoinstakeInput(coinstakeTxVIN).toFutureOr tposTxId <- blockLogic .getTPoSTransactionId(block) .toFutureOr tposTx <- xsnService .getTransaction(tposTxId) .toFutureOr addresses <- blockLogic .getTPoSAddresses(tposTx) .toFutureOr (ownerAddress, merchantAddress) = addresses rewards <- blockLogic .getTPoSRewards(coinstakeTx, ownerAddress, merchantAddress, coinstakeInput) .toFutureOr } yield rewards result.toFuture } private def getCoinstakeInput(coinstakeTxVIN: TransactionVIN): FutureApplicationResult[BigDecimal] = { def loadFromTx = { val result = for { previousToCoinstakeTx <- xsnService .getTransaction(coinstakeTxVIN.txid) .toFutureOr previousToCoinstakeVOUT <- transactionLogic .getVOUT(coinstakeTxVIN, previousToCoinstakeTx, BlockNotFoundError) .toFutureOr } yield previousToCoinstakeVOUT.value result.toFuture } coinstakeTxVIN .value .map(Good(_)) .map(Future.successful) .getOrElse(loadFromTx) } }