diff --git a/app/actors/Receiver.scala b/app/actors/Receiver.scala new file mode 100644 index 0000000..e5a1c45 --- /dev/null +++ b/app/actors/Receiver.scala @@ -0,0 +1,22 @@ +package actors + +import actors.TransStorage.RemoveConfirmedTransactions +import akka.actor.{Actor, ActorRef, Props} +import javax.inject.{Inject, Named} +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction +import play.api.Logger + +class Receiver @Inject()(@Named("transStorage") transStorage: ActorRef) extends Actor { + + def receive: Receive = { + case transaction: Transaction => + transStorage ! transaction + + case confirmedTransactionIds: List[String] => + transStorage ! RemoveConfirmedTransactions(confirmedTransactionIds) + } +} + +object Receiver { + def props(transStorage: ActorRef): Props = Props(new Receiver(transStorage)) +} \ No newline at end of file diff --git a/app/actors/TransStorage.scala b/app/actors/TransStorage.scala new file mode 100644 index 0000000..2c53b73 --- /dev/null +++ b/app/actors/TransStorage.scala @@ -0,0 +1,60 @@ +package actors + +import actors.TransStorage._ +import akka.actor.{Actor, Timers} +import javax.inject.Inject +import models.FullFilledTransaction +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction +import settings.Settings + +import scala.collection.mutable +import scala.concurrent.duration._ + +class TransStorage @Inject()(settings: Settings) extends Actor with Timers { + + val transactions: mutable.LinkedHashMap[String, FullFilledTransaction] = mutable.LinkedHashMap.empty[String, FullFilledTransaction] + + object Timer + + timers.startPeriodicTimer(Timer, Tick, 10 seconds) + + def receive: Receive = { + case tx: Transaction => + transactions += tx.encodedId -> FullFilledTransaction(tx) + + case TransactionsQ(from, to) => + val fromBound = if (from >= 0) from else 0 + val toBound = if (to <= transactions.size) to else transactions.size + val fromBoundR = transactions.size - toBound + val toBoundR = transactions.size - (if (fromBound <= toBound) fromBound else toBound) + val txs = transactions.values.slice(fromBoundR, toBoundR).toList.reverse + sender ! TransactionsA(txs) + + case TransactionByIdQ(id) => + sender ! TransactionByIdA(transactions.get(id)) + + case RemoveConfirmedTransactions(txIds) => + txIds.foreach(id => transactions.remove(id)) + + case Tick => + val timestamp = System.currentTimeMillis() + + transactions.takeWhile { case (_, tx) => + timestamp - tx.transaction.timestamp > settings.trans.unconfirmedTransactionExpiredInterval.toMillis + }.foreach { case (_, tx) => + transactions.remove(tx.transaction.id) + } + } +} + +object TransStorage { + case object Tick + + case class TransactionsQ(from: Int, to: Int) + case class TransactionsA(txs: List[FullFilledTransaction]) + + case class TransactionByIdQ(id: String) + case class TransactionByIdA(tx: Option[FullFilledTransaction]) + + case class RemoveConfirmedTransactions(txIds: List[String]) +} diff --git a/app/controllers/BlockController.scala b/app/controllers/BlockController.scala index 47d6fba..2ce2527 100644 --- a/app/controllers/BlockController.scala +++ b/app/controllers/BlockController.scala @@ -1,7 +1,8 @@ package controllers import javax.inject.{Inject, Singleton} -import models._ +import models.{Block, DBTransaction, Header} +import models.dao.{HistoryDao, TransactionsDao} import play.api.libs.circe.Circe import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents} import scala.concurrent.{ExecutionContext, Future} @@ -21,7 +22,7 @@ class BlockController @Inject()(cc: ControllerComponents, def getBlock(id: String): Future[Option[Block]] = { val headerF: Future[Option[Header]] = historyDao.findHeader(id) - val payloadF: Future[List[Transaction]] = transactionsDao.transactionsByBlock(id) + val payloadF: Future[List[DBTransaction]] = transactionsDao.transactionsByBlock(id) for { headerOpt <- headerF payload <- payloadF diff --git a/app/controllers/HomeController.scala b/app/controllers/HomeController.scala index 6c0e73a..7ef6490 100644 --- a/app/controllers/HomeController.scala +++ b/app/controllers/HomeController.scala @@ -1,15 +1,15 @@ package controllers -import com.typesafe.scalalogging.StrictLogging import javax.inject.{Inject, _} -import models.{Header, HistoryDao} +import models.Header +import models.dao.HistoryDao import play.api.mvc.{Action, _} import scala.concurrent.{ExecutionContext, Future} @Singleton class HomeController @Inject()(cc: ControllerComponents, historyDao: HistoryDao) - (implicit ex: ExecutionContext) extends AbstractController(cc) with ControllerHelpers with StrictLogging { + (implicit ex: ExecutionContext) extends AbstractController(cc) with ControllerHelpers { def index(): Action[AnyContent] = Action.async { val headers: Future[List[Header]] = historyDao.lastHeaders() diff --git a/app/controllers/NodeController.scala b/app/controllers/NodeController.scala index 73acbd6..e90afc3 100644 --- a/app/controllers/NodeController.scala +++ b/app/controllers/NodeController.scala @@ -1,7 +1,8 @@ package controllers import javax.inject.{Inject, _} -import models.{HistoryDao, Node} +import models.Node +import models.dao.HistoryDao import play.api.mvc._ import scala.concurrent.ExecutionContext @@ -13,7 +14,7 @@ class NodeController @Inject()(cc: ControllerComponents, def nodes(): Action[AnyContent] = Action.async { historyDao.getAllNodes.map { case Nil => NotFound - case node: List[Node] => Ok(views.html.nodeInfo(node)) + case node: List[Node] => Ok(views.html.nodes(node)) } } } diff --git a/app/controllers/SearchController.scala b/app/controllers/SearchController.scala index 76be16a..581f4b3 100644 --- a/app/controllers/SearchController.scala +++ b/app/controllers/SearchController.scala @@ -1,11 +1,10 @@ package controllers -import models._ import javax.inject.{Inject, Singleton} -import org.encryfoundation.common.transaction.{EncryAddress, Pay2ContractHashAddress, Pay2PubKeyAddress, PubKeyLockedContract} +import models.dao.{BoxesDao, HistoryDao, TransactionsDao} +import models.{Block, Contract, DBInput, DBOutput, DBTransaction, FullFilledTransaction, Header, Wallet} import play.api.libs.circe.Circe import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents, Result} -import scorex.crypto.encode.Base16 import settings.Utils import scala.concurrent.{ExecutionContext, Future} @@ -16,41 +15,14 @@ class SearchController @Inject()(cc: ControllerComponents, boxesDao: BoxesDao) (implicit ex: ExecutionContext) extends AbstractController(cc) with Circe { - def getBlock(id: String): Future[Option[Block]] = { - val headerF: Future[Option[Header]] = historyDao.findHeader(id) - val payloadF: Future[List[Transaction]] = transactionsDao.transactionsByBlock(id) - for { - headerOpt <- headerF - payload <- payloadF - } yield headerOpt match { - case Some(header) => Some(Block(header, payload)) - case _ => None - } - } - - def getFullTransaction(id: String): Future[Option[FullFilledTransaction]] = - transactionsDao.transactionById(id).flatMap { - case Some(tx) => - val outputsF: Future[List[Output]] = transactionsDao.outputsByTransaction(tx.id) - val inputsF: Future[List[Input]] = transactionsDao.inputsByTransaction(tx.id) - val contractF: Future[List[Contract]] = transactionsDao.contractByTransaction(tx.id) - for { - outputs <- outputsF - inputs <- inputsF - contract <- contractF - } yield Some(FullFilledTransaction(tx, inputs, outputs, contract)) - - case _ => Future(Option.empty[FullFilledTransaction]) - } - def search(id: String): Action[AnyContent] = Action.async { val blockF: Future[Option[Block]] = getBlock(id) val transactionF: Future[Option[FullFilledTransaction]] = getFullTransaction(id) - val outputF: Future[Option[Output]] = transactionsDao.outputById(id) + val outputF: Future[Option[DBOutput]] = transactionsDao.outputById(id) val walletF: Future[List[Wallet]] = boxesDao.getWalletByHash(Utils.contractHashByAddress(id)) val txIdF: Future[List[String]] = boxesDao.getTxsIdByHash(Utils.contractHashByAddress(id)) - val txsF: Future[List[Transaction]] = txIdF.flatMap(x => Future.sequence(x.map(id => boxesDao.getLastTxById(id)))) + val txsF: Future[List[DBTransaction]] = txIdF.flatMap(x => Future.sequence(x.map(id => boxesDao.getLastTxById(id)))) val result = for { blockOpt <- blockF diff --git a/app/controllers/TransactionsController.scala b/app/controllers/TransactionsController.scala index 98cd4fe..150a539 100644 --- a/app/controllers/TransactionsController.scala +++ b/app/controllers/TransactionsController.scala @@ -1,36 +1,31 @@ package controllers -import models.{Contract, FullFilledTransaction, Input, Output, TransactionsDao} import javax.inject.{Inject, Singleton} +import models.dao.TransactionsDao import play.api.libs.circe.Circe import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents} -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.ExecutionContext @Singleton -class TransactionsController @Inject()(cc: ControllerComponents, - transactionsDao: TransactionsDao) - (implicit ex: ExecutionContext) extends AbstractController(cc) with Circe { +class TransactionsController @Inject()(cc: ControllerComponents, transactionsDao: TransactionsDao)(implicit ex: ExecutionContext) + extends AbstractController(cc) with Circe { - def getFullTransaction(id: String): Future[Option[FullFilledTransaction]] = - transactionsDao.transactionById(id).flatMap { - case Some(tx) => - val outputsF: Future[List[Output]] = transactionsDao.outputsByTransaction(tx.id) - val inputsF: Future[List[Input]] = transactionsDao.inputsByTransaction(tx.id) - val contractF: Future[List[Contract]] = transactionsDao.contractByTransaction(tx.id) - for { - outputs <- outputsF - inputs <- inputsF - contract <- contractF - } yield Some(FullFilledTransaction(tx, inputs, outputs, contract)) - - case _ => Future(Option.empty[FullFilledTransaction]) - } + val TRANSACTIONS_PER_PAGE = 50 def getTransaction(txId: String): Action[AnyContent] = Action.async { - getFullTransaction(txId).map { + transactionsDao.fullTransactionById(txId).map { case Some(tx) => Ok(views.html.transactionInfo(tx)) - case None => NotFound + case None => NotFound + } + } + + def getUnconfirmedTransactions(page: Int): Action[AnyContent] = Action.async { + val from = page * TRANSACTIONS_PER_PAGE + val to = (page + 1) * TRANSACTIONS_PER_PAGE + transactionsDao.unconfirmedTransactions(from, to).map { txs => + Ok(views.html.transactions(txs, page)) } } + } \ No newline at end of file diff --git a/app/controllers/WalletController.scala b/app/controllers/WalletController.scala index 4c18a19..16b52bc 100644 --- a/app/controllers/WalletController.scala +++ b/app/controllers/WalletController.scala @@ -1,24 +1,23 @@ package controllers -import com.typesafe.scalalogging.StrictLogging import javax.inject.Inject import models._ import play.api.libs.circe.Circe import scala.concurrent._ import io.circe.generic.auto._ import io.circe.syntax._ +import models.dao.BoxesDao import play.api.mvc._ import settings.Utils class WalletController @Inject()(cc: ControllerComponents, - boxesDao: BoxesDao, - transactionsDao: TransactionsDao) - (implicit ex: ExecutionContext) extends AbstractController(cc) with ControllerHelpers with Circe with StrictLogging { + boxesDao: BoxesDao) + (implicit ex: ExecutionContext) extends AbstractController(cc) with ControllerHelpers with Circe { def info(contractHash: String, from: Int, to: Int): Action[AnyContent] = Action.async { boxesDao.getBoxesByContractHash(contractHash, from: Int, to: Int).map { case Nil => NotFound - case list: List[Output] => Ok(list.asJson) + case list: List[DBOutput] => Ok(list.asJson) } } @@ -27,7 +26,7 @@ class WalletController @Inject()(cc: ControllerComponents, val walletF: Future[List[Wallet]] = boxesDao.getWalletByHash(Utils.contractHashByAddress(contractHash)) val txIdF = boxesDao.getTxsIdByHash(Utils.contractHashByAddress(contractHash)).flatMap(x => Future.sequence(x.map(id => boxesDao.getLastTxById(id)))) - val result: Future[(List[Wallet], List[Transaction])] = for { + val result: Future[(List[Wallet], List[DBTransaction])] = for { wallet <- walletF txs <- txIdF } yield (wallet, txs) diff --git a/app/loggingSystem/LoggingFilter.scala b/app/logging/LoggingFilter.scala similarity index 97% rename from app/loggingSystem/LoggingFilter.scala rename to app/logging/LoggingFilter.scala index 3c1c7f0..bc28aba 100644 --- a/app/loggingSystem/LoggingFilter.scala +++ b/app/logging/LoggingFilter.scala @@ -1,4 +1,4 @@ -package loggingSystem +package logging import akka.stream.Materializer import javax.inject.Inject diff --git a/app/models/Block.scala b/app/models/Block.scala index cae3bac..94dd268 100644 --- a/app/models/Block.scala +++ b/app/models/Block.scala @@ -1,3 +1,3 @@ package models -case class Block(header: Header, payload: List[Transaction]) \ No newline at end of file +case class Block(header: Header, payload: List[DBTransaction]) \ No newline at end of file diff --git a/app/models/Contract.scala b/app/models/Contract.scala index ccdbb94..52bd8d7 100644 --- a/app/models/Contract.scala +++ b/app/models/Contract.scala @@ -1,4 +1,3 @@ package models -case class Contract (hash: String, - contract: String) +case class Contract(hash: String) diff --git a/app/models/DBInput.scala b/app/models/DBInput.scala new file mode 100644 index 0000000..3ee8399 --- /dev/null +++ b/app/models/DBInput.scala @@ -0,0 +1,18 @@ +package models + +import org.encryfoundation.common.modifiers.mempool.transaction.Input +import org.encryfoundation.common.utils.Algos + +case class DBInput(bxId: String, txId: String, contractHash: String, proofs: String) + +object DBInput { + def apply(input: Input, txId: String): DBInput = + new DBInput( + Algos.encode(input.boxId), + txId, + input.contract.fold(c => Algos.encode(c.hash), c => Algos.encode(c.contract.hash)), + input.proofs.map(_.toString).mkString(",") + ) + + def apply(bxId: String, txId: String, contractHash: String, proofs: String): DBInput = new DBInput(bxId, txId, contractHash, proofs) +} \ No newline at end of file diff --git a/app/models/DBOutput.scala b/app/models/DBOutput.scala new file mode 100644 index 0000000..4a594bd --- /dev/null +++ b/app/models/DBOutput.scala @@ -0,0 +1,61 @@ +package models + +import models.DBOutput.Proposition +import org.encryfoundation.common.modifiers.mempool.directive.TransferDirective +import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction +import org.encryfoundation.common.modifiers.state.box.{AssetBox, DataBox} +import org.encryfoundation.common.utils.Algos +import settings.Utils + +case class DBOutput(idx: Int, + id: String, + `type`: Long, + txId: String, + value: Long, + nonce: Long, + tokenId: String, + proposition: Proposition, + data: String, + isActive: Boolean, + minerAddress: String) + +object DBOutput { + + case class Proposition(contractHash: String) + + def apply(idx: Int, + id: String, + `type`: Long, + txId: String, + value: Long, + nonce: Long, + tokenId: String, + proposition: String, + data: String, + isActive: Boolean, + minerAddress: String): DBOutput = new DBOutput(idx, id, `type`, txId, value, nonce, tokenId, Proposition(proposition), data, isActive, minerAddress) + + def apply(tx: Transaction): List[DBOutput] = { + tx.directives.toList.collect { + case TransferDirective(address, amount, tokenIdOpt) => address + } + + tx.newBoxes.toList.zipWithIndex.map { case(box, idx) => + val (amount, tokenId, data) = box match { + case box: AssetBox => + (box.amount, Algos.encode(box.tokenIdOpt.getOrElse(Utils.IntrinsicTokenId)), "") + case box: DataBox => + (0L, "", Algos.encode(box.data)) + case _ => (0L, "", "") + } + val address: Option[Address] = tx.directives(idx) match { + case d: TransferDirective => Some(d.address) + case _ => None + } + + new DBOutput(idx, Algos.encode(box.id), box.typeId, tx.encodedId, amount, box.nonce, tokenId, + Proposition(Algos.encode(box.proposition.contractHash)), data, true, address.getOrElse("")) + } + } +} \ No newline at end of file diff --git a/app/models/DBTransaction.scala b/app/models/DBTransaction.scala new file mode 100644 index 0000000..5baaca6 --- /dev/null +++ b/app/models/DBTransaction.scala @@ -0,0 +1,46 @@ +package models + +import org.encryfoundation.common.modifiers.mempool.transaction.{Proof, Transaction} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.prismlang.core.wrapped.BoxedValue._ + +case class DBTransaction(id: String, + fee: Long, + blockId: String, + isCoinbase: Boolean, + timestamp: Long, + defaultProofOpt: Option[String]) + +object DBTransaction { + + def apply(tx: Transaction, blockId: String): DBTransaction = + DBTransaction( + tx.encodedId, + tx.fee, + blockId, + tx.inputs.isEmpty, + tx.timestamp, + tx.defaultProofOpt.map { + case Proof(IntValue(value), _) => s"IntValue - ${value.toString}" + case Proof(ByteValue(value), _) => s"ByteValue - ${value.toString}" + case Proof(BoolValue(value), _) => s"BoolValue - ${value.toString}" + case Proof(StringValue(value), _) => s"StringValue - ${value.toString}" + case Proof(ByteCollectionValue(value), _) => s"ByteCollectionValue - ${Algos.encode(value.toArray)}" + case Proof(Signature25519Value(value), _) => s"Signature25519Value - ${Algos.encode(value.toArray)}" + case Proof(MultiSignatureValue(value), _) => s"MultiSignatureValue - ${Algos.encode(value.flatten.toArray)}" + } + ) +} + +case class FullFilledTransaction(transaction: DBTransaction, inputs: List[DBInput], output: List[DBOutput], contract: List[Contract]) + +object FullFilledTransaction { + + def apply(tx: Transaction): FullFilledTransaction = { + val dbTransaction = DBTransaction(tx, "") + val dbInputs = tx.inputs.map(input => DBInput(input, tx.encodedId)).toList + val dbOutputs = DBOutput(tx) + val contracts = dbInputs.map(input => Contract(input.contractHash)).distinct + FullFilledTransaction(dbTransaction, dbInputs, dbOutputs, contracts) + } +} \ No newline at end of file diff --git a/app/models/Input.scala b/app/models/Input.scala deleted file mode 100644 index f16cdb9..0000000 --- a/app/models/Input.scala +++ /dev/null @@ -1,3 +0,0 @@ -package models - -case class Input(bxId: String, txId: String, contract: String, proofs: String) \ No newline at end of file diff --git a/app/models/Output.scala b/app/models/Output.scala deleted file mode 100644 index 2e77638..0000000 --- a/app/models/Output.scala +++ /dev/null @@ -1,35 +0,0 @@ -package models - -import io.circe.generic.auto._ -import io.circe.Encoder -import io.circe.syntax._ -import models.Output.Proposition - -case class Output(idx: Int, - id: String, - `type`: Long, - txId: String, - value: Long, - nonce: Long, - tokenId: String, - proposition: Proposition, - data: String, - isActive: Boolean, - minerAddress: String) - -object Output{ - - case class Proposition(contractHash: String) - - def apply(idx: Int, - id: String, - `type`: Long, - txId: String, - value: Long, - nonce: Long, - tokenId: String, - proposition: String, - data: String, - isActive: Boolean, - minerAddress: String): Output = new Output(idx, id, `type`, txId, value, nonce, tokenId, Proposition(proposition), data, isActive, minerAddress) -} \ No newline at end of file diff --git a/app/models/Transaction.scala b/app/models/Transaction.scala deleted file mode 100644 index a459654..0000000 --- a/app/models/Transaction.scala +++ /dev/null @@ -1,10 +0,0 @@ -package models - -case class Transaction(id: String, - fee: Long, - blockId: String, - isCoinbase: Boolean, - timestamp: Long, - defaultProofOpt: Option[String]) - -case class FullFilledTransaction(transaction: Transaction, inputs: List[Input], output: List[Output], contract: List[Contract]) \ No newline at end of file diff --git a/app/models/TransactionsDao.scala b/app/models/TransactionsDao.scala deleted file mode 100644 index b7d2fdd..0000000 --- a/app/models/TransactionsDao.scala +++ /dev/null @@ -1,21 +0,0 @@ -package models - -import javax.inject.Inject -import models.database.DBService -import models.database.TransactionsQueries._ -import scala.concurrent.{ExecutionContext, Future} - -class TransactionsDao @Inject()(dBService: DBService)(implicit ec: ExecutionContext) { - - def transactionsByBlock(id: String): Future[List[Transaction]] = dBService.runAsync(getTransactionsByBlockId(id)) - - def inputsByTransaction(id: String): Future[List[Input]] = dBService.runAsync(getTransactionInputs(id)) - - def outputsByTransaction(id: String): Future[List[Output]] = dBService.runAsync(getTransactionOutputs(id)) - - def transactionById(id: String): Future[Option[Transaction]] = dBService.runAsync(getTransactionById(id)) - - def outputById(id: String): Future[Option[Output]] = dBService.runAsync(getOutput(id)) - - def contractByTransaction(id: String): Future[List[Contract]] = dBService.runAsync(getContract(id)) -} diff --git a/app/models/BoxesDao.scala b/app/models/dao/BoxesDao.scala similarity index 72% rename from app/models/BoxesDao.scala rename to app/models/dao/BoxesDao.scala index 549f94a..a5fb57c 100644 --- a/app/models/BoxesDao.scala +++ b/app/models/dao/BoxesDao.scala @@ -1,13 +1,15 @@ -package models +package models.dao import javax.inject.Inject -import models.database.{BoxesQueries, DBService, WalletQueries} +import models.{DBOutput, DBTransaction, Wallet} +import models.database.{BoxesQueries, WalletQueries} +import models.service.DBService import scala.concurrent.{ExecutionContext, Future} class BoxesDao @Inject()(dBService: DBService)(implicit ec: ExecutionContext){ - def getBoxesByContractHash(contractHash: String, from: Int, to: Int): Future[List[Output]] = + def getBoxesByContractHash(contractHash: String, from: Int, to: Int): Future[List[DBOutput]] = dBService.runAsync(BoxesQueries.getBoxesByContractHash(contractHash, from, to)) def getWalletByHash(contractHash: String): Future[List[Wallet]] = @@ -16,7 +18,7 @@ class BoxesDao @Inject()(dBService: DBService)(implicit ec: ExecutionContext){ def getTxsIdByHash(contractHash: String): Future[List[String]] = dBService.runAsync(WalletQueries.getTxsIdByHash(contractHash)) - def getLastTxById(Id: String): Future[Transaction] = + def getLastTxById(Id: String): Future[DBTransaction] = dBService.runAsync(WalletQueries.getLastTxsByHash(Id)) } diff --git a/app/models/HistoryDao.scala b/app/models/dao/HistoryDao.scala similarity index 85% rename from app/models/HistoryDao.scala rename to app/models/dao/HistoryDao.scala index 7602aac..14eccc1 100644 --- a/app/models/HistoryDao.scala +++ b/app/models/dao/HistoryDao.scala @@ -1,8 +1,10 @@ -package models +package models.dao import javax.inject.Inject -import models.database.{DBService, NodeQueries} +import models.{Header, Node} import models.database.HeaderQueries._ +import models.database.NodeQueries +import models.service.DBService import scala.concurrent.{ExecutionContext, Future} diff --git a/app/models/dao/TransactionsDao.scala b/app/models/dao/TransactionsDao.scala new file mode 100644 index 0000000..56c60ae --- /dev/null +++ b/app/models/dao/TransactionsDao.scala @@ -0,0 +1,52 @@ +package models.dao + +import javax.inject.Inject +import models.{Contract, DBInput, DBOutput, DBTransaction, FullFilledTransaction} +import models.database.TransactionsQueries._ +import models.service.{DBService, TransService} + +import scala.concurrent.{ExecutionContext, Future} + +class TransactionsDao @Inject()(dbService: DBService, transService: TransService)(implicit ec: ExecutionContext) { + + def transactionsByBlock(id: String): Future[List[DBTransaction]] = dbService.runAsync(getTransactionsByBlockId(id)) + + def inputsByTransaction(id: String): Future[List[DBInput]] = dbService.runAsync(getTransactionInputs(id)) + + def outputsByTransaction(id: String): Future[List[DBOutput]] = dbService.runAsync(getTransactionOutputs(id)) + + def transactionById(id: String): Future[Option[DBTransaction]] = { + transService.getTransactionById(id).flatMap { txOpt => + if(txOpt.isEmpty) dbService.runAsync(getTransactionById(id)) + else Future.successful(txOpt) + } + } + + private def getFullTransaction(id: String): Future[Option[FullFilledTransaction]] = + transactionById(id).flatMap { + case Some(tx) => + val outputsF: Future[List[DBOutput]] = outputsByTransaction(tx.id) + val inputsF: Future[List[DBInput]] = inputsByTransaction(tx.id) + val contractF: Future[List[Contract]] = contractByTransaction(tx.id) + for { + outputs <- outputsF + inputs <- inputsF + contract <- contractF + } yield Some(FullFilledTransaction(tx, inputs, outputs, contract)) + + case _ => Future.successful(Option.empty[FullFilledTransaction]) + } + + def fullTransactionById(id: String): Future[Option[FullFilledTransaction]] = { + transService.getFullTransactionById(id).flatMap { txOpt => + if(txOpt.isEmpty) getFullTransaction(id) + else Future.successful(txOpt) + } + } + + def outputById(id: String): Future[Option[DBOutput]] = dbService.runAsync(getOutput(id)) + + def contractByTransaction(id: String): Future[List[Contract]] = dbService.runAsync(getContract(id)) + + def unconfirmedTransactions(from: Int, to: Int): Future[List[DBTransaction]] = transService.getUnconfirmedTransactions(from, to) +} diff --git a/app/models/database/BoxesQueries.scala b/app/models/database/BoxesQueries.scala index ec97ffa..1d5a587 100644 --- a/app/models/database/BoxesQueries.scala +++ b/app/models/database/BoxesQueries.scala @@ -2,7 +2,7 @@ package models.database import doobie._ import doobie.implicits._ -import models.Output +import models.DBOutput object BoxesQueries { @@ -12,12 +12,12 @@ object BoxesQueries { def a(nodeIp: String) = sql"SELECT * FROM outputs WHERE outputsToNodes.nodeIp = $nodeIp AND outputsToNodes.outputId = outputs.id" - def getAvailableBoxByBoxId(boxId: String): ConnectionIO[Option[Output]] = - sql"SELECT * FROM outputs WHERE id = $boxId AND outputs.isActive = true".query[Output].to[List].map(_.headOption) + def getAvailableBoxByBoxId(boxId: String): ConnectionIO[Option[DBOutput]] = + sql"SELECT * FROM outputs WHERE id = $boxId AND outputs.isActive = true".query[DBOutput].to[List].map(_.headOption) - def getNumberOfAvailableBoxesByBoxId(boxId: String, numberOf: Int): ConnectionIO[Option[Output]] = - sql"SELECT * FROM outputs WHERE id = $boxId AND outputs.isActive = true".query[Output].to[List].map(_.headOption) + def getNumberOfAvailableBoxesByBoxId(boxId: String, numberOf: Int): ConnectionIO[Option[DBOutput]] = + sql"SELECT * FROM outputs WHERE id = $boxId AND outputs.isActive = true".query[DBOutput].to[List].map(_.headOption) - def getBoxesByContractHash(contractHash: String, from: Int, to: Int): ConnectionIO[List[Output]] = - sql"SELECT * FROM outputs WHERE contractHash = $contractHash AND outputs.isActive = true limit ${to - from} offset $from".query[Output].to[List] + def getBoxesByContractHash(contractHash: String, from: Int, to: Int): ConnectionIO[List[DBOutput]] = + sql"SELECT * FROM outputs WHERE contractHash = $contractHash AND outputs.isActive = true limit ${to - from} offset $from".query[DBOutput].to[List] } diff --git a/app/models/database/TransactionsQueries.scala b/app/models/database/TransactionsQueries.scala index d265eb0..3d699cc 100644 --- a/app/models/database/TransactionsQueries.scala +++ b/app/models/database/TransactionsQueries.scala @@ -1,24 +1,24 @@ package models.database import doobie.implicits._ -import models.{Contract, Input, Output, Transaction} +import models.{Contract, DBInput, DBOutput, DBTransaction} object TransactionsQueries { - def getTransactionsByBlockId(blockId: String): doobie.ConnectionIO[List[Transaction]] = - sql"SELECT * FROM transactions WHERE blockId = $blockId".query[Transaction].to[List] + def getTransactionsByBlockId(blockId: String): doobie.ConnectionIO[List[DBTransaction]] = + sql"SELECT * FROM transactions WHERE blockId = $blockId".query[DBTransaction].to[List] - def getTransactionInputs(transactionId: String): doobie.ConnectionIO[List[Input]] = - sql"SELECT * FROM inputs WHERE txId = $transactionId".query[Input].to[List] + def getTransactionInputs(transactionId: String): doobie.ConnectionIO[List[DBInput]] = + sql"SELECT * FROM inputs WHERE txId = $transactionId".query[DBInput].to[List] - def getTransactionOutputs(transactionId: String): doobie.ConnectionIO[List[Output]] = - sql"SELECT * FROM outputs WHERE txId = $transactionId".query[Output].to[List] + def getTransactionOutputs(transactionId: String): doobie.ConnectionIO[List[DBOutput]] = + sql"SELECT * FROM outputs WHERE txId = $transactionId".query[DBOutput].to[List] - def getTransactionById(transactionId: String): doobie.ConnectionIO[Option[Transaction]] = - sql"SELECT * FROM transactions WHERE id = $transactionId".query[Transaction].to[List].map(_.headOption) + def getTransactionById(transactionId: String): doobie.ConnectionIO[Option[DBTransaction]] = + sql"SELECT * FROM transactions WHERE id = $transactionId".query[DBTransaction].to[List].map(_.headOption) - def getOutput(id: String): doobie.ConnectionIO[Option[Output]] = - sql"SELECT * FROM outputs WHERE id = $id".query[Output].to[List].map(_.headOption) + def getOutput(id: String): doobie.ConnectionIO[Option[DBOutput]] = + sql"SELECT * FROM outputs WHERE id = $id".query[DBOutput].to[List].map(_.headOption) def getContract(transactionId: String): doobie.ConnectionIO[List[Contract]] = sql"select * from contracts where hash IN (select contract from inputs where txId = $transactionId)".query[Contract].to[List] diff --git a/app/models/database/WalletQueries.scala b/app/models/database/WalletQueries.scala index 013a081..e365489 100644 --- a/app/models/database/WalletQueries.scala +++ b/app/models/database/WalletQueries.scala @@ -2,8 +2,7 @@ package models.database import doobie._ import doobie.implicits._ -import models.{Transaction, Wallet} - +import models.{DBTransaction, Wallet} object WalletQueries { @@ -13,7 +12,7 @@ object WalletQueries { def getTxsIdByHash(address: String): ConnectionIO[List[String]] = sql"SELECT txId FROM outputs WHERE contractHash = $address order by idx DESC LIMIT 50".query[String].to[List] - def getLastTxsByHash(txId: String): ConnectionIO[Transaction] = - sql"SELECT * FROM transactions WHERE id = $txId ORDER BY timestamp DESC LIMIT 50".query[Transaction].unique + def getLastTxsByHash(txId: String): ConnectionIO[DBTransaction] = + sql"SELECT * FROM transactions WHERE id = $txId ORDER BY timestamp DESC LIMIT 50".query[DBTransaction].unique } diff --git a/app/models/database/DBService.scala b/app/models/service/DBService.scala similarity index 82% rename from app/models/database/DBService.scala rename to app/models/service/DBService.scala index ecc37db..5a1f54d 100644 --- a/app/models/database/DBService.scala +++ b/app/models/service/DBService.scala @@ -1,4 +1,4 @@ -package models.database +package models.service import cats.effect.IO import doobie.free.connection.ConnectionIO @@ -7,10 +7,10 @@ import doobie.implicits._ import javax.inject.Inject import play.api.Configuration import doobie.hikari.implicits._ -import settings.ExplorerSettings +import settings.Settings import scala.concurrent.Future -class DBService @Inject()(config: Configuration, settings: ExplorerSettings) { +class DBService @Inject()(config: Configuration, settings: Settings) { def runAsync[T](io: ConnectionIO[T]): Future[T] = (for { pool <- newHikariTransactor[IO]( diff --git a/app/models/service/TransService.scala b/app/models/service/TransService.scala new file mode 100644 index 0000000..90ce873 --- /dev/null +++ b/app/models/service/TransService.scala @@ -0,0 +1,28 @@ +package models.service + +import actors.TransStorage.{TransactionByIdA, TransactionByIdQ, TransactionsA, TransactionsQ} +import akka.actor.ActorRef +import akka.pattern.ask +import akka.util.Timeout +import javax.inject._ +import models.{DBTransaction, FullFilledTransaction} + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration._ + +class TransService @Inject()(@Named("transStorage") transStorage: ActorRef) { + implicit val timeout: Timeout = 5 seconds + + def getUnconfirmedTransactions(from: Int, to: Int): Future[List[DBTransaction]] = + (transStorage ? TransactionsQ(from, to)).mapTo[TransactionsA].map(_.txs.map(_.transaction)) + + def transById(id: String): Future[Option[FullFilledTransaction]] = + (transStorage ? TransactionByIdQ(id)).mapTo[TransactionByIdA].map(_.tx) + + def getTransactionById(id: String): Future[Option[DBTransaction]] = + transById(id).map(_.map(_.transaction)) + + def getFullTransactionById(id: String): Future[Option[FullFilledTransaction]] = + transById(id) +} diff --git a/app/modules/ActorsBindModule.scala b/app/modules/ActorsBindModule.scala new file mode 100644 index 0000000..2087d92 --- /dev/null +++ b/app/modules/ActorsBindModule.scala @@ -0,0 +1,13 @@ +package modules +import actors.{Receiver, TransStorage} +import com.google.inject.AbstractModule +import javax.inject.Singleton +import play.api.libs.concurrent.AkkaGuiceSupport + +@Singleton +class ActorsBindModule extends AbstractModule with AkkaGuiceSupport { + override def configure = { + bindActor[TransStorage]("transStorage") + bindActor[Receiver]("receiver") + } +} \ No newline at end of file diff --git a/app/modules/MainModule.scala b/app/modules/MainModule.scala index 83a9cb1..34db795 100644 --- a/app/modules/MainModule.scala +++ b/app/modules/MainModule.scala @@ -6,12 +6,12 @@ import play.api.Configuration import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ import net.codingwell.scalaguice.ScalaModule -import settings.ExplorerSettings +import settings.Settings class MainModule extends AbstractModule with ScalaModule { @Provides - def provideExplorerAppSettings(configuration: Configuration): ExplorerSettings = + def provideExplorerAppSettings(configuration: Configuration): Settings = ConfigFactory.load("local.conf") - .withFallback(configuration.underlying).as[ExplorerSettings]("encry") + .withFallback(configuration.underlying).as[Settings]("encry") } diff --git a/app/settings/ExplorerSettings.scala b/app/settings/ExplorerSettings.scala deleted file mode 100644 index 74202b8..0000000 --- a/app/settings/ExplorerSettings.scala +++ /dev/null @@ -1,3 +0,0 @@ -package settings - -case class ExplorerSettings(postgres: PostgresSettings) diff --git a/app/settings/PostgresSettings.scala b/app/settings/PostgresSettings.scala deleted file mode 100644 index 8036a1e..0000000 --- a/app/settings/PostgresSettings.scala +++ /dev/null @@ -1,5 +0,0 @@ -package settings - -case class PostgresSettings(host: String, - user: String, - password: String) \ No newline at end of file diff --git a/app/settings/Settings.scala b/app/settings/Settings.scala new file mode 100644 index 0000000..62782ab --- /dev/null +++ b/app/settings/Settings.scala @@ -0,0 +1,8 @@ +package settings + +import scala.concurrent.duration.FiniteDuration + +case class PostgresSettings(host: String, user: String, password: String) +case class TransSettings(unconfirmedTransactionExpiredInterval: FiniteDuration) + +case class Settings(postgres: PostgresSettings, trans: TransSettings) diff --git a/app/settings/Utils.scala b/app/settings/Utils.scala index c60b8d7..bf824d4 100644 --- a/app/settings/Utils.scala +++ b/app/settings/Utils.scala @@ -1,7 +1,7 @@ package settings -import org.encryfoundation.common.Algos -import org.encryfoundation.common.transaction.{EncryAddress, Pay2ContractHashAddress, Pay2PubKeyAddress, PubKeyLockedContract} +import org.encryfoundation.common.modifiers.mempool.transaction.{EncryAddress, Pay2ContractHashAddress, Pay2PubKeyAddress, PubKeyLockedContract} +import org.encryfoundation.common.utils.Algos import scorex.crypto.encode.Base16 object Utils { diff --git a/app/views/blockInfo.scala.html b/app/views/blockInfo.scala.html index c233f0d..67fdf05 100644 --- a/app/views/blockInfo.scala.html +++ b/app/views/blockInfo.scala.html @@ -1,429 +1,254 @@ @import java.util.Date @(block: Block) +@content_main { - - - - - - - - Encry Explorer - - - - - - - - - - - - - - - -
- - - -
- -
- -
-
-
-
-

Block

-
-
- - - - - - - - - - - - - - - - + + + + + + + + +
-
-
- Block ID -
-
-
- @block.header.id -
-
-
- Version -
-
-
- @block.header.version -
-
-
- Parent ID -
-
-
- @block.header.parentId -
-
-
- Transactions Root +
+ + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - -
+
+
+ Block ID +
+
+
+ @block.header.id +
+
+
+ Version +
+
+
+ @block.header.version +
+
+
+ Parent ID +
+
+
+ @block.header.parentId +
+
+
+ Transactions Root -
-
-
- @block.header.transactionsRoot -
-
-
- Timestamp -
-
-
- @{ - new Date(block.header.timestamp).format("yyyy-MM-dd HH:mm:ss") - } -
-
-
- Height -
-
-
- @block.header.height -
-
-
- Nonce -
-
-
- @block.header.nonce -
-
-
- Difficulty -
-
-
- @block.header.difficulty -
-
-
- EquihashSolution -
-
-
- @block.header.equihashSolution -
+ @block.header.transactionsRoot +
+
+
+ Timestamp +
+
+
+ @{ + new Date(block.header.timestamp).format("yyyy-MM-dd HH:mm:ss") + } +
+
+
+ Height +
+
+
+ @block.header.height +
+
+
+ Nonce +
+
+
+ @block.header.nonce +
+
+
+ Difficulty +
+
+
+ @block.header.difficulty +
+
+
+ EquihashSolution +
+
+
+ @block.header.equihashSolution +
-
-
- Miner Address -
-
-
- @block.header.minerAddress -
-
-
- Miner Reward -
-
-
- @block.header.minerReward -
-
-
+
+
+
+ Miner Address +
+
+
+ @block.header.minerAddress +
+
+
+ Miner Reward +
+
+
+ @block.header.minerReward +
- -
-
-
-
-

Block Transactions

-
-
- - - - - - - - - - - @for(tx <- block.payload) { - - - + + } + +
Transaction idTransaction timestampTransaction feeis CoinBase
- - @tx.id - + + + +
+
+
+
+

Block Transactions

+
+
+ + + + + + + + + + + @for(tx <- block.payload) { + + + - + - + - - } - -
Transaction idTransaction timestampTransaction feeis CoinBase
+ + @tx.id + @{ new Date(tx.timestamp).format("yyyy-MM-dd HH:mm:ss") } - + @tx.fee - + @{ if(tx.isCoinbase) "Yes" else None } -
-
-
+
- -
- - - - - - - - - \ No newline at end of file + + +
+
+} \ No newline at end of file diff --git a/app/views/content_main.scala.html b/app/views/content_main.scala.html new file mode 100644 index 0000000..0714e46 --- /dev/null +++ b/app/views/content_main.scala.html @@ -0,0 +1,192 @@ +@(content: Html) + + + + + + + + + + Encry Explorer + + + + + + + + + + + + + + + + @content + + + + + + + + + + \ No newline at end of file diff --git a/app/views/index.scala.html b/app/views/index.scala.html index ac67b63..6b10c65 100644 --- a/app/views/index.scala.html +++ b/app/views/index.scala.html @@ -1,158 +1,6 @@ @import java.util.Date @(lastBlocks: List[Header], bestHeight: Int) - - - - - - - - - Encry Explorer - - - - - - - - - - - - - - - - - - - - +@main {
- - - - - - - - - - - - - \ No newline at end of file +} \ No newline at end of file diff --git a/app/views/main.scala.html b/app/views/main.scala.html new file mode 100644 index 0000000..52c1b96 --- /dev/null +++ b/app/views/main.scala.html @@ -0,0 +1,171 @@ +@(content: Html) + + + + + + + + + Encry Explorer + + + + + + + + + + + + + + + + + + + @content + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/views/nodeInfo.scala.html b/app/views/nodeInfo.scala.html deleted file mode 100644 index d6f3139..0000000 --- a/app/views/nodeInfo.scala.html +++ /dev/null @@ -1,237 +0,0 @@ -@import java.util.Date -@(nodes: List[Node]) - - - - - - - - - Encry Explorer - - - - - - - - - - - - - - - -
- - - -
- -
- -
-
-
-
-

Node Info

-
-
- - - - - - - - - - - @for(n <- nodes) { - - - - - - - } - -
IpStatusLast Block IdLast Block Height
- @n.ip - - @{ - if (n.status) "Online" else "Offline" - } - - @n.lastFullBlock - - @n.lastFullHeight -
-
-
-
-
- - -
-
- - - - - - - - - \ No newline at end of file diff --git a/app/views/nodes.scala.html b/app/views/nodes.scala.html new file mode 100644 index 0000000..c568f34 --- /dev/null +++ b/app/views/nodes.scala.html @@ -0,0 +1,90 @@ +@import java.util.Date +@(nodes: List[Node]) +@main { +
+ + + +
+ +
+ +
+
+
+
+

Node Info

+
+
+ + + + + + + + + + + @for(n <- nodes) { + + + + + + + } + +
IpStatusLast Block IdLast Block Height
+ @n.ip + + @{ + if (n.status) "Online" else "Offline" + } + + @n.lastFullBlock + + @n.lastFullHeight +
+
+
+
+
+ + +
+
+} \ No newline at end of file diff --git a/app/views/outputInfo.scala.html b/app/views/outputInfo.scala.html index b601af9..33d6e69 100644 --- a/app/views/outputInfo.scala.html +++ b/app/views/outputInfo.scala.html @@ -1,5 +1,5 @@ @import java.util.Date -@(output: Output) +@(output: DBOutput) @@ -262,7 +262,7 @@

Output Info

- @output.proposition + @output.proposition.contractHash diff --git a/app/views/search.scala.html b/app/views/search.scala.html new file mode 100644 index 0000000..8404cc4 --- /dev/null +++ b/app/views/search.scala.html @@ -0,0 +1,50 @@ + + + + diff --git a/app/views/transactionInfo.scala.html b/app/views/transactionInfo.scala.html index 071ab41..876997b 100644 --- a/app/views/transactionInfo.scala.html +++ b/app/views/transactionInfo.scala.html @@ -1,408 +1,232 @@ @import java.util.Date @(tx: FullFilledTransaction) - - - - - - - - - Encry Explorer - - - - - - - - - - - - - - - -
- - - -
- -
- -
-
-
-
-

Transaction Info

-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
-
- Transaction id -
-
-
- @tx.transaction.id -
-
-
- Transaction fee -
-
-
- @tx.transaction.fee -
-
-
- Is CoinBase -
-
-
- @tx.transaction.isCoinbase -
-
-
- Transaction timestamp -
-
-
- @{ - new Date(tx.transaction.timestamp).format("yyyy-MM-dd HH:mm:ss") - } -
-
-
- Transaction defaultProofOpt -
-
-
- @tx.transaction.defaultProofOpt -
-
-
+
+ +
+
+
+
+

Transaction Inputs

-
- -
-
-
-
-

Transaction Inputs

-
-
- - - - - - - - - - @for(input <- tx.inputs) { - - + + } + +
Input IDInput contractInput proofs
+
+ + + + + + + + + + @for(input <- tx.inputs) { + + - + - + - - } - -
Input IDInput contract hashInput proofs
@input.bxId - + - @tx.contract.map(_.contract) + @tx.contract.map(_.hash).mkString(",") - + @input.proofs -
-
- +
- -
-
-
-
-

Transaction Outputs

-
-
- - - - - - - - - - - - - @for(output <- tx.output) { - - + + } + +
Output IDOutput valueOutput coinOutput contract hashRecipient addressOutput data
+ + + +
+
+
+
+

Transaction Outputs

+
+
+ + + + + + + + + + + + + @for(output <- tx.output) { + + - + - + - + - + - + - - } - -
Output IDOutput valueOutput coinOutput contract hashRecipient addressOutput data
@output.id - + @output.value - + @{ if(output.tokenId == "487291c237b68dd2ab213be6b5d1174666074a5afab772b600ea14e8285affab") "Encry Coin" else output.tokenId } - + - @output.proposition + @output.proposition.contractHash - + @output.minerAddress - + @output.data -
-
-
+
- -
- - - - - - - - - \ No newline at end of file + + +
+
+} \ No newline at end of file diff --git a/app/views/transactions.scala.html b/app/views/transactions.scala.html new file mode 100644 index 0000000..375ed6a --- /dev/null +++ b/app/views/transactions.scala.html @@ -0,0 +1,117 @@ +@import java.util.Date +@(txs: List[DBTransaction], page: Int) +@main { + +
+ + + +
+ +
+
+
+
+
+
+
+

Transactions

+
+ +
+
+ +
+ + + + + + + + + + @for(tx <- txs) { + + + + + + } + +
Transaction idtimestampfee
+ + @tx.id + + + @{ + new Date(tx.timestamp).format("yyyy-MM-dd HH:mm:ss") + } + + + + @tx.fee + +
+
+
+
+
+ + +
+
+ +} \ No newline at end of file diff --git a/app/views/wallet.scala.html b/app/views/wallet.scala.html index 32e849f..7fabf4e 100644 --- a/app/views/wallet.scala.html +++ b/app/views/wallet.scala.html @@ -1,318 +1,143 @@ @import java.util.Date -@(wallet: List[Wallet], transaction: List[Transaction]) - - - - - - - - - Encry Explorer - - - - - - - - - - - - - - - -
- - - -
- -
- -
-
-
-
-

Contract Hash / @for(wal <- wallet){ +@(wallet: List[Wallet], transaction: List[DBTransaction]) +@content_main { +
+ + + +
+ +
+ +
+
+
+
+

Contract Hash / @for(wal <- wallet){ + @wal.hash - - } -

-
-
- - - - - - - - - @for(wal <- wallet) { - - + } + + +
+
TokenBalance
+
+ + + + + + + + @for(wal <- wallet) { + + - + - - } + + + } - -
TokenBalance
@wal.tokenId - + @wal.amount -
-
-
+ +
-
-
-
-
-

Last 50 blocks

-
-
- - - - - - - - - - - @for(tx <- transaction) { - - - + + } + +
Transaction idTransaction timestampTransaction feeis CoinBase
- - @tx.id - + + +
+
+
+
+

Last 50 blocks

+
+
+ + + + + + + + + + + @for(tx <- transaction) { + + + - + - + - - } - -
Transaction idTransaction timestampTransaction feeis CoinBase
+ + @tx.id + @{ new Date(tx.timestamp).format("yyyy-MM-dd HH:mm:ss") } - + @tx.fee - + @{ if(tx.isCoinbase) "Yes" else None } -
-
-
+
- -
- - - - - - - - - \ No newline at end of file + + +
+
+} \ No newline at end of file diff --git a/build.sbt b/build.sbt index 83d93b8..819c98a 100644 --- a/build.sbt +++ b/build.sbt @@ -16,6 +16,7 @@ resolvers ++= Seq("Sonatype Releases" at "https://oss.sonatype.org/content/repos "Typesafe maven releases" at "http://repo.typesafe.com/typesafe/maven-releases/", "Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/") +val akkaVersion = "2.5.13" val akkaHttpVersion = "10.1.3" val circeVersion = "0.9.3" val doobieVersion = "0.5.2" @@ -50,7 +51,7 @@ libraryDependencies ++= (Seq( "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test, "com.google.guava" % "guava" % "21.+", "com.iheart" %% "ficus" % "1.4.2", - "org.encry" %% "encry-common" % "0.8.6", + "org.encry" %% "encry-common" % "0.9.0", //"0.8.6" "org.bouncycastle" % "bcprov-jdk15on" % "1.58", "org.whispersystems" % "curve25519-java" % "+", "org.rudogma" %% "supertagged" % "1.+", @@ -58,6 +59,9 @@ libraryDependencies ++= (Seq( "org.encry" %% "prism" % "0.2.3", "com.adrianhurt" %% "play-bootstrap" % "1.4-P26-B4-SNAPSHOT", "de.heikoseeberger" %% "akka-http-circe" % "1.20.1", + "com.typesafe.akka" %% "akka-actor" % akkaVersion, + "com.typesafe.akka" %% "akka-stream" % akkaVersion, + "com.typesafe.akka" %% "akka-remote" % akkaVersion ) ++ databaseDependencies ++ apiDependencies ++ loggingDependencies) .map(_ exclude("ch.qos.logback", "*") exclude("ch.qos.logback", "*")) diff --git a/conf/application.conf b/conf/application.conf index e856c89..2434379 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -4,13 +4,18 @@ encry { username = "" password = "" } + + trans { + unconfirmedTransactionExpiredInterval = 15m + } } play.modules { enabled += modules.MainModule + enabled += modules.ActorsBindModule } play.filters { - enabled += loggingSystem.LoggingFilter + enabled += logging.LoggingFilter hosts { allowed = ["."] } @@ -19,3 +24,19 @@ play.assets { path = "/public" urlPrefix = "/assets" } + +akka { + loglevel = "INFO" + actor { + provider = "akka.remote.RemoteActorRefProvider" + } + remote { + enabled-transports = ["akka.remote.netty.tcp"] + netty.tcp { + hostname = "127.0.0.1" + port = 5150 + } + log-sent-messages = on + log-received-messages = on + } +} diff --git a/conf/logback.xml b/conf/logback.xml index 7f272f4..efa89ac 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -1,7 +1,7 @@ - ${application.home:-.}/logs/Application.log + ${application.home:-.}/logs/application.log [%d{HH:mm:ss}] [%level] - %message%n%xException diff --git a/conf/routes b/conf/routes index f3061df..6586526 100644 --- a/conf/routes +++ b/conf/routes @@ -6,4 +6,5 @@ GET /wallet/:contractHash/boxes/:from/:to controllers.WalletContro GET /search/:search controllers.SearchController.search(search:String) GET /wallet/:contractHash controllers.WalletController.getWalletByAddress(contractHash: String) GET /node controllers.NodeController.nodes() +GET /transactions controllers.TransactionsController.getUnconfirmedTransactions(page: Int) GET /:to controllers.HomeController.listHeadersByHeightRangeApi(to: Int) \ No newline at end of file