Skip to content

Commit

Permalink
Merge pull request #96 from gagarin55/utxpool
Browse files Browse the repository at this point in the history
UnconfirmedTransactionDatabaseImpl now has customizable SizeLimit
  • Loading branch information
catena2w committed Jun 3, 2016
2 parents 876bb5d + 7f75642 commit 47f8766
Show file tree
Hide file tree
Showing 10 changed files with 29 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ trait TransactionModule[TransactionBlockData] extends BlockProcessingModule[Tran

val blockStorage: BlockStorage

val utxStorage: UnconfirmedTransactionsStorage

def isValid(block: Block): Boolean

def transactions(block: Block): Seq[Transaction]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package scorex.transaction.state.database
package scorex.transaction

import scorex.transaction.Transaction

trait UnconfirmedTransactionsStorage {
val SizeLimit: Int

trait UnconfirmedTransactionsDatabase {
def putIfNew(tx: Transaction): Boolean

def all(): Seq[Transaction]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import play.api.libs.json.{JsArray, Json}
import scorex.app.Application
import scorex.crypto.encode.Base58
import scorex.transaction.LagonakiState
import scorex.transaction.state.database.UnconfirmedTransactionsDatabaseImpl
import scorex.transaction.state.database.blockchain.StoredBlockchain

import scala.util.{Success, Try}
Expand Down Expand Up @@ -88,7 +87,7 @@ case class TransactionsApiRoute(override val application: Application)(implicit
def unconfirmed: Route = {
path("unconfirmed") {
getJsonRoute {
JsArray(UnconfirmedTransactionsDatabaseImpl.all().map(_.json))
JsArray(application.transactionModule.utxStorage.all().map(_.json))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scorex.network
import scorex.app.Application
import scorex.network.NetworkController.DataFromPeer
import scorex.network.TransactionalMessagesRepo.TransactionMessageSpec
import scorex.transaction.state.database.UnconfirmedTransactionsDatabaseImpl
import scorex.transaction.{LagonakiTransaction, Transaction}
import scorex.utils.ScorexLogging

Expand All @@ -22,7 +21,7 @@ class UnconfirmedPoolSynchronizer(application: Application) extends ViewSynchron
case DataFromPeer(msgId, tx: Transaction, remote) if msgId == TransactionMessageSpec.messageCode =>
log.debug(s"Got tx: $tx")
(tx, transactionModule.blockStorage.state.isValid(tx)) match {
case (ltx: LagonakiTransaction, true) => UnconfirmedTransactionsDatabaseImpl.putIfNew(ltx)
case (ltx: LagonakiTransaction, true) => transactionModule.utxStorage.putIfNew(ltx)
case (atx, false) => log.error(s"Transaction $atx is not valid")
case m => log.error(s"Got unexpected transaction: $m")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class SimpleTransactionModule(implicit val settings: TransactionSettings with Se

private val instance = this

override val utxStorage: UnconfirmedTransactionsStorage = new UnconfirmedTransactionsDatabaseImpl

override val blockStorage = new BlockStorage {

val db = settings.dataDirOpt match {
Expand Down Expand Up @@ -105,13 +107,13 @@ class SimpleTransactionModule(implicit val settings: TransactionSettings with Se

override def packUnconfirmed(): StoredInBlock = {
clearIncorrectTransaction()
blockStorage.state.validate(UnconfirmedTransactionsDatabaseImpl.all().sortBy(-_.fee).take(MaxTransactionsPerBlock))
blockStorage.state.validate(utxStorage.all().sortBy(-_.fee).take(MaxTransactionsPerBlock))
}

//todo: check: clear unconfirmed txs on receiving a block
override def clearFromUnconfirmed(data: StoredInBlock): Unit = {
data.foreach(tx => UnconfirmedTransactionsDatabaseImpl.getBySignature(tx.signature) match {
case Some(unconfirmedTx) => UnconfirmedTransactionsDatabaseImpl.remove(unconfirmedTx)
data.foreach(tx => utxStorage.getBySignature(tx.signature) match {
case Some(unconfirmedTx) => utxStorage.remove(unconfirmedTx)
case None =>
})

Expand All @@ -121,17 +123,18 @@ class SimpleTransactionModule(implicit val settings: TransactionSettings with Se
//Romove too old or invalid transactions from UnconfirmedTransactionsPool
def clearIncorrectTransaction(): Unit = {
val lastBlockTs = blockStorage.history.lastBlock.timestampField.value
val txs = UnconfirmedTransactionsDatabaseImpl.all()

val txs = utxStorage.all()
val notTooOld = txs.filter { tx =>
if ((lastBlockTs - tx.timestamp).seconds > MaxTimeForUnconfirmed) UnconfirmedTransactionsDatabaseImpl.remove(tx)
if ((lastBlockTs - tx.timestamp).seconds > MaxTimeForUnconfirmed) utxStorage.remove(tx)
(lastBlockTs - tx.timestamp).seconds <= MaxTimeForUnconfirmed
}

notTooOld.diff(blockStorage.state.validate(txs)).foreach(tx => UnconfirmedTransactionsDatabaseImpl.remove(tx))
notTooOld.diff(blockStorage.state.validate(txs)).foreach(tx => utxStorage.remove(tx))
}

override def onNewOffchainTransaction(transaction: Transaction): Unit =
if (UnconfirmedTransactionsDatabaseImpl.putIfNew(transaction)) {
if (utxStorage.putIfNew(transaction)) {
val spec = TransactionalMessagesRepo.TransactionMessageSpec
val ntwMsg = Message(spec, Right(transaction), None)
networkController ! NetworkController.SendToNetwork(ntwMsg, Broadcast)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package scorex.transaction.state.database

import com.google.common.primitives.Longs
import scorex.transaction.Transaction
import scorex.transaction.{Transaction, UnconfirmedTransactionsStorage}
import scorex.utils.ScorexLogging

import scala.collection.concurrent.TrieMap


object UnconfirmedTransactionsDatabaseImpl extends UnconfirmedTransactionsDatabase with ScorexLogging {

//TODO move to config
val SizeLimit = 1000
class UnconfirmedTransactionsDatabaseImpl(val SizeLimit: Int = 1000) extends UnconfirmedTransactionsStorage with ScorexLogging {

val transactions = TrieMap[Long, Transaction]()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import scorex.transaction.state.database.state._
class StoredStateUnitTests extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks with Matchers
with PrivateMethodTester with OptionValues with TransactionGen {

val folder = "/tmp/scorex/test"
val folder = "/tmp/scorex/test/"
new File(folder).mkdirs()
val stateFile = folder + "state.dat"
new File(stateFile).delete()

val db = new MVStore.Builder().fileName(stateFile).compress().open()
val state = new StoredState(db)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scorex.lagonaki.integration
import org.scalatest._
import scorex.account.Account
import scorex.lagonaki.{TestingCommons, TransactionTestingCommons}
import scorex.transaction.state.database.UnconfirmedTransactionsDatabaseImpl
import scorex.transaction.state.database.state.AccState
import scorex.transaction.{BalanceSheet, FeesStateChange}
import scorex.utils.ScorexLogging
Expand Down Expand Up @@ -54,7 +53,7 @@ class StoredStateSpecification extends FunSuite with Matchers with BeforeAndAfte
}

test("validate plenty of transactions") {
val trans = (1 to UnconfirmedTransactionsDatabaseImpl.SizeLimit).map { i =>
val trans = (1 to transactionModule.utxStorage.SizeLimit).map { i =>
val account = accounts(Random.nextInt(accounts.size))
val senderBalance = state.asInstanceOf[BalanceSheet].balance(account.address)
senderBalance should be > 0L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import scorex.block.Block
import scorex.consensus.mining.BlockGeneratorController._
import scorex.lagonaki.{TestingCommons, TransactionTestingCommons}
import scorex.transaction.BalanceSheet
import scorex.transaction.state.database.UnconfirmedTransactionsDatabaseImpl
import scorex.utils.{ScorexLogging, untilTimeout}

import scala.concurrent.Await
Expand Down Expand Up @@ -38,8 +37,8 @@ with TransactionTestingCommons {
def maxHeight(): Int = peers.map(_.blockStorage.history.height()).max

def cleanTransactionPool(): Unit = untilTimeout(1.second) {
UnconfirmedTransactionsDatabaseImpl.all().foreach(tx => UnconfirmedTransactionsDatabaseImpl.remove(tx))
UnconfirmedTransactionsDatabaseImpl.all().size shouldBe 0
transactionModule.utxStorage.all().foreach(tx => transactionModule.utxStorage.remove(tx))
transactionModule.utxStorage.all().size shouldBe 0
}

test("generate 10 blocks and synchronize") {
Expand All @@ -58,7 +57,7 @@ with TransactionTestingCommons {
test("Generate block with plenty of transactions") {
val block = untilTimeout(1.minute) {
stopGeneration()
val toGen = UnconfirmedTransactionsDatabaseImpl.SizeLimit - transactionModule.packUnconfirmed().size
val toGen = transactionModule.utxStorage.SizeLimit - transactionModule.packUnconfirmed().size
(0 until toGen) foreach (i => genValidTransaction())
val blocksFuture = application.consensusModule.generateNextBlocks(Seq(accounts.head))(transactionModule)
val blocks: Seq[Block] = Await.result(blocksFuture, 10.seconds)
Expand Down Expand Up @@ -90,10 +89,10 @@ with TransactionTestingCommons {
stopGeneration()
cleanTransactionPool()

incl.foreach(tx => UnconfirmedTransactionsDatabaseImpl.putIfNew(tx))
UnconfirmedTransactionsDatabaseImpl.all().size shouldBe incl.size
incl.foreach(tx => transactionModule.utxStorage.putIfNew(tx))
transactionModule.utxStorage.all().size shouldBe incl.size
val tx = genValidTransaction(randomAmnt = false)
UnconfirmedTransactionsDatabaseImpl.all().size shouldBe incl.size + 1
transactionModule.utxStorage.all().size shouldBe incl.size + 1

applications.foreach(_.blockGenerator ! StartGeneration)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scorex.lagonaki.integration.api
import org.scalatest.{FunSuite, Matchers}
import play.api.libs.json.JsValue
import scorex.lagonaki.TransactionTestingCommons
import scorex.transaction.state.database.UnconfirmedTransactionsDatabaseImpl

class TransactionsAPISpecification extends FunSuite with Matchers with TransactionTestingCommons {

Expand All @@ -16,7 +15,7 @@ class TransactionsAPISpecification extends FunSuite with Matchers with Transacti

test("/transactions/unconfirmed API route") {
(1 to 20) foreach (i => genValidTransaction())
val unconfirmed = UnconfirmedTransactionsDatabaseImpl.all()
val unconfirmed = transactionModule.utxStorage.all()
unconfirmed.size should be > 0
val tr = GET.request("/transactions/unconfirmed")
(tr \\ "signature").toList.size shouldBe unconfirmed.size
Expand Down

0 comments on commit 47f8766

Please sign in to comment.