Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app/actors/Receiver.scala
Original file line number Diff line number Diff line change
@@ -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))
}
60 changes: 60 additions & 0 deletions app/actors/TransStorage.scala
Original file line number Diff line number Diff line change
@@ -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])
}
5 changes: 3 additions & 2 deletions app/controllers/BlockController.scala
Original file line number Diff line number Diff line change
@@ -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}
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/HomeController.scala
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/NodeController.scala
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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))
}
}
}
36 changes: 4 additions & 32 deletions app/controllers/SearchController.scala
Original file line number Diff line number Diff line change
@@ -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}

Expand All @@ -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
Expand Down
37 changes: 16 additions & 21 deletions app/controllers/TransactionsController.scala
Original file line number Diff line number Diff line change
@@ -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))
}
}

}
11 changes: 5 additions & 6 deletions app/controllers/WalletController.scala
Original file line number Diff line number Diff line change
@@ -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)

}
}
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package loggingSystem
package logging

import akka.stream.Materializer
import javax.inject.Inject
Expand Down
2 changes: 1 addition & 1 deletion app/models/Block.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package models

case class Block(header: Header, payload: List[Transaction])
case class Block(header: Header, payload: List[DBTransaction])
3 changes: 1 addition & 2 deletions app/models/Contract.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
package models

case class Contract (hash: String,
contract: String)
case class Contract(hash: String)
18 changes: 18 additions & 0 deletions app/models/DBInput.scala
Original file line number Diff line number Diff line change
@@ -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)
}
61 changes: 61 additions & 0 deletions app/models/DBOutput.scala
Original file line number Diff line number Diff line change
@@ -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(""))
}
}
}
Loading