diff --git a/benchmarks/src/test/scala/benches/BloomFilterBenchmark.scala b/benchmarks/src/test/scala/benches/BloomFilterBenchmark.scala index 63ee346499..b459e40d01 100644 --- a/benchmarks/src/test/scala/benches/BloomFilterBenchmark.scala +++ b/benchmarks/src/test/scala/benches/BloomFilterBenchmark.scala @@ -1,7 +1,6 @@ //package benches // //import java.util.concurrent.TimeUnit -//import benches.Utils._ //import akka.actor.ActorSystem //import benches.BloomFilterBenchmark.BloomFilterBenchmarkState //import encry.modifiers.mempool.Transaction diff --git a/benchmarks/src/test/scala/benches/HistoryBenches.scala b/benchmarks/src/test/scala/benches/HistoryBenches.scala index 1107a902ba..a77bcf1566 100644 --- a/benchmarks/src/test/scala/benches/HistoryBenches.scala +++ b/benchmarks/src/test/scala/benches/HistoryBenches.scala @@ -1,25 +1,29 @@ package benches -import java.io.File import java.util.concurrent.TimeUnit import benches.HistoryBenches.HistoryBenchState -import benches.Utils._ +import encry.utils.TestEntityGenerator.coinbaseTransaction +import encry.utils.HistoryGenerator import encry.view.history.History import encryBenchmark.BenchSettings -import org.encryfoundation.common.modifiers.history.Block +import org.encryfoundation.common.crypto.equihash.EquihashSolution +import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction +import org.encryfoundation.common.utils.TaggedTypes.{Difficulty, ModifierId} import org.openjdk.jmh.annotations._ import org.openjdk.jmh.infra.Blackhole import org.openjdk.jmh.profile.GCProfiler import org.openjdk.jmh.runner.{Runner, RunnerException} import org.openjdk.jmh.runner.options.{OptionsBuilder, TimeValue, VerboseMode} +import scala.util.Random class HistoryBenches { @Benchmark def appendBlocksToHistoryBench(benchStateHistory: HistoryBenchState, bh: Blackhole): Unit = { bh.consume { - val history: History = generateHistory(benchStateHistory.settings, getRandomTempDir) + val history: History = HistoryGenerator.dummyHistory(benchStateHistory.settings) benchStateHistory.blocks.foldLeft(history) { case (historyL, block) => historyL.append(block.header) historyL.append(block.payload) @@ -32,7 +36,7 @@ class HistoryBenches { @Benchmark def readHistoryFileBench(benchStateHistory: HistoryBenchState, bh: Blackhole): Unit = { bh.consume { - val history: History = generateHistory(benchStateHistory.settings, benchStateHistory.tmpDir) + val history: History = HistoryGenerator.dummyHistory(benchStateHistory.settings) history.closeStorage() } } @@ -61,15 +65,14 @@ object HistoryBenches extends BenchSettings { @State(Scope.Benchmark) class HistoryBenchState extends encry.settings.Settings { - val tmpDir: File = getRandomTempDir - val initialHistory: History = generateHistory(settings, tmpDir) + val initialHistory: History = HistoryGenerator.dummyHistory(settings) val resultedHistory: (History, Option[Block], Vector[Block]) = (0 until benchSettings.historyBenchSettings.blocksNumber) .foldLeft(initialHistory, Option.empty[Block], Vector.empty[Block]) { case ((prevHistory, prevBlock, vector), _) => val block: Block = - generateNextBlockValidForHistory(prevHistory, 0, prevBlock, Seq(coinbaseTransaction(0))) + generateNextBlockValidForHistory(prevHistory, 0, prevBlock, Seq(coinbaseTransaction(0)), settings.constants.InitialDifficulty) prevHistory.append(block.header) prevHistory.append(block.payload) (prevHistory.reportModifierIsValid(block), Some(block), vector :+ block) @@ -78,4 +81,27 @@ object HistoryBenches extends BenchSettings { val blocks: Vector[Block] = resultedHistory._3 } + + def generateNextBlockValidForHistory(history: History, + difficultyDiff: BigInt = 0, + prevBlock: Option[Block], + txs: Seq[Transaction], + initialDifficulty: Difficulty): Block = { + val previousHeaderId: ModifierId = prevBlock.map(_.id).getOrElse(Header.GenesisParentId) + val requiredDifficulty: Difficulty = prevBlock.map(b => + history.requiredDifficultyAfter(b.header).getOrElse(Difficulty @@ BigInt(0))) + .getOrElse(initialDifficulty) + val header = Header( + 1.toByte, + previousHeaderId, + Payload.rootHash(txs.map(_.id)), + Math.abs(Random.nextLong()), + history.getBestHeaderHeight + 1, + Random.nextLong(), + Difficulty @@ (requiredDifficulty + difficultyDiff), + EquihashSolution(Seq(1, 3)) + ) + Block(header, Payload(header.id, txs)) + } + } \ No newline at end of file diff --git a/benchmarks/src/test/scala/benches/SerializedAssetTransactionBenchmark.scala b/benchmarks/src/test/scala/benches/SerializedAssetTransactionBenchmark.scala index df3e1047db..71b59be8c0 100644 --- a/benchmarks/src/test/scala/benches/SerializedAssetTransactionBenchmark.scala +++ b/benchmarks/src/test/scala/benches/SerializedAssetTransactionBenchmark.scala @@ -3,8 +3,8 @@ package benches import java.util.concurrent.TimeUnit import benches.SerializedAssetTransactionBenchmark.SerializedAssetBenchState -import benches.Utils._ -import encryBenchmark.{BenchSettings, Settings} +import encry.utils.TestEntityGenerator +import encryBenchmark.BenchSettings import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionSerializer} import org.encryfoundation.common.modifiers.state.box.AssetBox import org.openjdk.jmh.annotations._ @@ -53,9 +53,9 @@ object SerializedAssetTransactionBenchmark extends BenchSettings { @Setup def createStateForBenchmark(): Unit = { - initialBoxes = generateInitialBoxes(benchSettings.serializedAssetBenchSettings.totalBoxesNumber) + initialBoxes = TestEntityGenerator.generateInitialBoxes(benchSettings.serializedAssetBenchSettings.totalBoxesNumber) initialTransactions = - generateAssetTransactions( + TestEntityGenerator.generateAssetTransactions( initialBoxes, benchSettings.serializedAssetBenchSettings.numberOfInputs, benchSettings.serializedAssetBenchSettings.numberOfOutputs diff --git a/benchmarks/src/test/scala/benches/SerializedDataTxBenchmark.scala b/benchmarks/src/test/scala/benches/SerializedDataTxBenchmark.scala index 8ab3ef0621..af1523138a 100644 --- a/benchmarks/src/test/scala/benches/SerializedDataTxBenchmark.scala +++ b/benchmarks/src/test/scala/benches/SerializedDataTxBenchmark.scala @@ -3,7 +3,7 @@ package benches import java.util.concurrent.TimeUnit import benches.SerializedDataTxBenchmark.SerializedDataBenchState -import benches.Utils._ +import encry.utils.TestEntityGenerator import encryBenchmark.{BenchSettings, Settings} import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionSerializer} import org.encryfoundation.common.modifiers.state.box.AssetBox @@ -53,8 +53,8 @@ object SerializedDataTxBenchmark extends BenchSettings { @Setup def createStateForBenchmark(): Unit = { - initialBoxes = generateInitialBoxes(benchSettings.serializedDataBenchSettings.totalBoxesNumber) - initialTransactions = generateDataTransactions( + initialBoxes = TestEntityGenerator.generateInitialBoxes(benchSettings.serializedDataBenchSettings.totalBoxesNumber) + initialTransactions = TestEntityGenerator.generateDataTransactions( initialBoxes, benchSettings.serializedDataBenchSettings.numberOfInputs, benchSettings.serializedDataBenchSettings.numberOfOutputs, diff --git a/benchmarks/src/test/scala/benches/SerializedMonetaryTxBenchmark.scala b/benchmarks/src/test/scala/benches/SerializedMonetaryTxBenchmark.scala index 404c5a9f8c..3369be073d 100644 --- a/benchmarks/src/test/scala/benches/SerializedMonetaryTxBenchmark.scala +++ b/benchmarks/src/test/scala/benches/SerializedMonetaryTxBenchmark.scala @@ -3,8 +3,8 @@ package benches import java.util.concurrent.TimeUnit import benches.SerializedMonetaryTxBenchmark.SerializedMonetaryBenchState -import benches.Utils._ -import encryBenchmark.{BenchSettings, Settings} +import encry.utils.TestEntityGenerator +import encryBenchmark.BenchSettings import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionSerializer} import org.encryfoundation.common.modifiers.state.box.AssetBox import org.openjdk.jmh.annotations._ @@ -53,9 +53,9 @@ object SerializedMonetaryTxBenchmark extends BenchSettings { @Setup def createStateForBenchmark(): Unit = { - initialBoxes = generateInitialBoxes(benchSettings.serializedMonetaryBenchSettings.totalBoxesNumber) + initialBoxes = TestEntityGenerator.generateInitialBoxes(benchSettings.serializedMonetaryBenchSettings.totalBoxesNumber) initialTransactions = - generatePaymentTransactions( + TestEntityGenerator.generatePaymentTransactions( initialBoxes, benchSettings.serializedMonetaryBenchSettings.numberOfInputs, benchSettings.serializedMonetaryBenchSettings.numberOfOutputs diff --git a/benchmarks/src/test/scala/benches/StateBenches.scala b/benchmarks/src/test/scala/benches/StateBenches.scala index 9aa45760b2..b94947dc88 100644 --- a/benchmarks/src/test/scala/benches/StateBenches.scala +++ b/benchmarks/src/test/scala/benches/StateBenches.scala @@ -1,31 +1,40 @@ package benches import java.io.File +import java.security.PublicKey import java.util.concurrent.TimeUnit import benches.StateBenches.StateBenchState import org.openjdk.jmh.annotations._ -import benches.Utils._ -import encry.EncryApp -import encry.settings.EncryAppSettings +import encry.modifiers.mempool.TransactionFactory.paymentTransactionWithMultipleOutputs +import encry.settings.Settings import encry.storage.VersionalStorage import encry.storage.VersionalStorage.IODB +import encry.utils.ChainGenerator._ +import encry.utils.FileHelper.getRandomTempDir +import encry.utils.{Keys, TestEntityGenerator} +import encry.utils.Utils.randomAddress import encry.view.state.{BoxHolder, UtxoState} -import encryBenchmark.{BenchSettings, Settings} -import org.encryfoundation.common.modifiers.history.Block +import encryBenchmark.BenchSettings +import org.encryfoundation.common.crypto.equihash.EquihashSolution +import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.AssetBox +import org.encryfoundation.common.utils.TaggedTypes.Difficulty import org.openjdk.jmh.infra.Blackhole import org.openjdk.jmh.profile.GCProfiler import org.openjdk.jmh.runner.{Runner, RunnerException} import org.openjdk.jmh.runner.options.{OptionsBuilder, TimeValue, VerboseMode} +import scala.util.Random + class StateBenches { @Benchmark def applyBlocksToTheState(stateBench: StateBenchState, bh: Blackhole): Unit = { bh.consume { val innerState: UtxoState = - utxoFromBoxHolder(stateBench.boxesHolder, getRandomTempDir, None, stateBench.settings, VersionalStorage.LevelDB) + utxoFromBoxHolder(stateBench.boxesHolder, getRandomTempDir, stateBench.settings, VersionalStorage.LevelDB) stateBench.chain.foldLeft(innerState) { case (state, block) => state.applyModifier(block).right.get } @@ -37,7 +46,7 @@ class StateBenches { def readStateFileBench(stateBench: StateBenchState, bh: Blackhole): Unit = { bh.consume { val localState: UtxoState = - utxoFromBoxHolder(stateBench.boxesHolder, stateBench.tmpDir, None, stateBench.settings, IODB) + utxoFromBoxHolder(stateBench.boxesHolder, stateBench.tmpDir, stateBench.settings, IODB) localState.close() } } @@ -64,16 +73,17 @@ object StateBenches extends BenchSettings { } @State(Scope.Benchmark) - class StateBenchState extends encry.settings.Settings { + class StateBenchState extends Settings with Keys { val tmpDir: File = getRandomTempDir val initialBoxes: IndexedSeq[AssetBox] = (0 until benchSettings.stateBenchSettings.totalBoxesNumber).map(nonce => - genHardcodedBox(privKey.publicImage.address.address, nonce) + TestEntityGenerator.genHardcodedBox(publicKey.address.address, nonce) ) val boxesHolder: BoxHolder = BoxHolder(initialBoxes) - var state: UtxoState = utxoFromBoxHolder(boxesHolder, tmpDir, None, settings, VersionalStorage.LevelDB) - val genesisBlock: Block = generateGenesisBlockValidForState(state) + var state: UtxoState = utxoFromBoxHolder(boxesHolder, tmpDir, settings, VersionalStorage.LevelDB) + val genesisBlock: Block = genGenesisBlock(publicKey, settings.constants.InitialEmissionAmount, + settings.constants.InitialDifficulty, settings.constants.GenesisHeight) state = state.applyModifier(genesisBlock).right.get @@ -104,5 +114,39 @@ object StateBenches extends BenchSettings { val chain: Vector[Block] = genesisBlock +: stateGenerationResults._1 state = stateGenerationResults._3 state.close() + + def generateNextBlockValidForState(prevBlock: Block, + state: UtxoState, + box: Seq[AssetBox], + transactionsNumberInEachBlock: Int, + numberOfInputsInOneTransaction: Int, + numberOfOutputsInOneTransaction: Int): Block = { + + val transactions: Seq[Transaction] = (0 until transactionsNumberInEachBlock).foldLeft(box, Seq.empty[Transaction]) { + case ((boxes, transactionsL), _) => + val tx: Transaction = paymentTransactionWithMultipleOutputs( + privKey, + fee = 111, + timestamp = 11L, + useBoxes = boxes.take(numberOfInputsInOneTransaction).toIndexedSeq, + recipient = randomAddress, + amount = 10000, + numOfOutputs = numberOfOutputsInOneTransaction + ) + (boxes.drop(numberOfInputsInOneTransaction), transactionsL :+ tx) + }._2 ++ Seq(TestEntityGenerator.coinbaseTransaction(prevBlock.header.height + 1)) + val header = Header( + 1.toByte, + prevBlock.id, + Payload.rootHash(transactions.map(_.id)), + System.currentTimeMillis(), + prevBlock.header.height + 1, + Random.nextLong(), + Difficulty @@ BigInt(1), + EquihashSolution(Seq(1, 3)) + ) + Block(header, Payload(header.id, transactions)) + } } + } \ No newline at end of file diff --git a/benchmarks/src/test/scala/benches/StateRollbackBench.scala b/benchmarks/src/test/scala/benches/StateRollbackBench.scala index 812fc160f9..d1f11f7d4e 100644 --- a/benchmarks/src/test/scala/benches/StateRollbackBench.scala +++ b/benchmarks/src/test/scala/benches/StateRollbackBench.scala @@ -4,19 +4,27 @@ import java.io.File import java.util.concurrent.TimeUnit import benches.StateRollbackBench.StateRollbackState -import benches.Utils._ +import encry.modifiers.mempool.TransactionFactory.paymentTransactionWithMultipleOutputs +import encry.settings.Settings import encry.storage.VersionalStorage import encry.utils.CoreTaggedTypes.VersionTag import encry.view.state.{BoxHolder, UtxoState} -import encryBenchmark.{BenchSettings, Settings} -import org.encryfoundation.common.modifiers.history.Block +import encryBenchmark.BenchSettings +import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} import org.encryfoundation.common.modifiers.state.box.AssetBox -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, Difficulty} +import org.encryfoundation.common.utils.TaggedTypes.Difficulty import org.openjdk.jmh.annotations.{Benchmark, Mode, Scope, State} import org.openjdk.jmh.infra.Blackhole import org.openjdk.jmh.profile.GCProfiler import org.openjdk.jmh.runner.{Runner, RunnerException} import org.openjdk.jmh.runner.options.{OptionsBuilder, TimeValue, VerboseMode} +import encry.utils.FileHelper.getRandomTempDir +import encry.utils.ChainGenerator.{genGenesisBlock, utxoFromBoxHolder} +import encry.utils.{Keys, TestEntityGenerator} +import org.encryfoundation.common.crypto.equihash.EquihashSolution +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction + +import scala.util.Random class StateRollbackBench { @@ -24,7 +32,7 @@ class StateRollbackBench { def applyBlocksToTheState(stateBench: StateRollbackState, bh: Blackhole): Unit = { bh.consume { val innerState: UtxoState = - utxoFromBoxHolder(stateBench.boxesHolder, getRandomTempDir, None, stateBench.settings, VersionalStorage.IODB) + utxoFromBoxHolder(stateBench.boxesHolder, getRandomTempDir, stateBench.settings, VersionalStorage.IODB) val newState = stateBench.chain.foldLeft(innerState -> List.empty[VersionTag]) { case ((state, rootHashes), block) => val newState = state.applyModifier(block).right.get newState -> (rootHashes :+ newState.version) @@ -57,16 +65,17 @@ object StateRollbackBench extends BenchSettings { } @State(Scope.Benchmark) - class StateRollbackState extends encry.settings.Settings { + class StateRollbackState extends Settings with Keys { val tmpDir: File = getRandomTempDir val initialBoxes: IndexedSeq[AssetBox] = (0 until benchSettings.stateBenchSettings.totalBoxesNumber).map(nonce => - genHardcodedBox(privKey.publicImage.address.address, nonce) + TestEntityGenerator.genHardcodedBox(privKey.publicImage.address.address, nonce) ) val boxesHolder: BoxHolder = BoxHolder(initialBoxes) - var state: UtxoState = utxoFromBoxHolder(boxesHolder, tmpDir, None, settings, VersionalStorage.LevelDB) - val genesisBlock: Block = generateGenesisBlockValidForState(state) + var state: UtxoState = utxoFromBoxHolder(boxesHolder, tmpDir, settings, VersionalStorage.LevelDB) + val genesisBlock: Block = genGenesisBlock(privKey.publicImage, settings.constants.InitialEmissionAmount, + settings.constants.InitialDifficulty, settings.constants.GenesisHeight) state = state.applyModifier(genesisBlock).right.get @@ -96,5 +105,38 @@ object StateRollbackBench extends BenchSettings { val forkBlocks: List[Block] = genesisBlock +: stateGenerationResults._1.map(_._2) state = stateGenerationResults._3 state.close() + + def generateNextBlockForStateWithSpendingAllPreviousBoxes(prevBlock: Block, + state: UtxoState, + box: Seq[AssetBox], + splitCoef: Int = 2, + addDiff: Difficulty = Difficulty @@ BigInt(0)): Block = { + + val transactions: Seq[Transaction] = box.indices.foldLeft(box, Seq.empty[Transaction]) { + case ((boxes, transactionsL), _) => + val tx: Transaction = paymentTransactionWithMultipleOutputs( + privKey, + fee = 1, + timestamp = 11L, + useBoxes = IndexedSeq(boxes.head), + recipient = privKey.publicImage.address.address, + amount = boxes.head.amount - 1, + numOfOutputs = splitCoef + ) + (boxes.tail, transactionsL :+ tx) + }._2.filter(tx => state.validate(tx).isRight) ++ Seq(TestEntityGenerator.coinbaseTransaction(prevBlock.header.height + 1)) + val header = Header( + 1.toByte, + prevBlock.id, + Payload.rootHash(transactions.map(_.id)), + System.currentTimeMillis(), + prevBlock.header.height + 1, + Random.nextLong(), + Difficulty @@ (BigInt(1) + addDiff), + EquihashSolution(Seq(1, 3)) + ) + Block(header, Payload(header.id, transactions)) + } } + } \ No newline at end of file diff --git a/benchmarks/src/test/scala/benches/Utils.scala b/benchmarks/src/test/scala/benches/Utils.scala deleted file mode 100644 index 0d8e54d3be..0000000000 --- a/benchmarks/src/test/scala/benches/Utils.scala +++ /dev/null @@ -1,417 +0,0 @@ -package benches - -import java.io.File - -import akka.actor.ActorRef -import com.typesafe.scalalogging.StrictLogging -import encry.modifiers.mempool.TransactionFactory -import encry.settings.{Settings, EncryAppSettings} -import encry.storage.VersionalStorage -import encry.storage.VersionalStorage.{StorageKey, StorageType, StorageValue, StorageVersion} -import encry.storage.iodb.versionalIODB.IODBWrapper -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} -import encry.storage.levelDb.versionalLevelDB._ -import encry.utils.{FileHelper, Mnemonic, NetworkTimeProvider} -import encry.view.history.History -import encry.view.history.storage.HistoryStorage -import encry.view.state.{BoxHolder, UtxoState} -import io.iohk.iodb.LSMStore -import org.encryfoundation.common.crypto.equihash.EquihashSolution -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519, Signature25519} -import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} -import org.encryfoundation.common.modifiers.mempool.directive.{AssetIssuingDirective, DataDirective, Directive, TransferDirective} -import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address -import org.encryfoundation.common.modifiers.mempool.transaction._ -import org.encryfoundation.common.modifiers.state.box.Box.Amount -import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryProposition, MonetaryBox} -import org.encryfoundation.common.utils.TaggedTypes._ -import org.encryfoundation.prismlang.core.wrapped.BoxedValue -import org.iq80.leveldb.Options -import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} -import scorex.utils.Random - -import scala.collection.immutable -import scala.util.{Random => R} - -object Utils extends Settings with StrictLogging { - - val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" - val privKey: PrivateKey25519 = createPrivKey(Some(mnemonicKey)) - - val defaultKeySize: Int = 32 - val defaultValueSize: Int = 256 - - def generateRandomKey(keySize: Int = defaultKeySize): VersionalLevelDbKey = - VersionalLevelDbKey @@ Random.randomBytes(keySize) - - def generateRandomValue(valueSize: Int = defaultValueSize): VersionalLevelDbValue = - VersionalLevelDbValue @@ Random.randomBytes(valueSize) - - def genRandomInsertValue(keySize: Int = defaultKeySize, - valueSize: Int = defaultValueSize): (VersionalLevelDbKey, VersionalLevelDbValue) = - (generateRandomKey(keySize), generateRandomValue(valueSize)) - - def generateRandomLevelDbElemsWithoutDeletions(qty: Int, qtyOfElemsToInsert: Int): List[LevelDbDiff] = - (0 until qty).foldLeft(List.empty[LevelDbDiff]) { - case (acc, i) => - LevelDbDiff( - LevelDBVersion @@ Random.randomBytes(), - List((0 until qtyOfElemsToInsert).map(_ => genRandomInsertValue()): _*) - ) :: acc - } - - def generateGenesisBlockValidForState(state: UtxoState): Block = { - val txs = Seq(coinbaseTransaction(0)) - val header = genHeader.copy( - parentId = Header.GenesisParentId, - height = settings.constants.GenesisHeight - ) - Block(header, Payload(header.id, txs)) - } - - def generateGenesisBlockValidForHistory: Block = { - val header = genHeader.copy(parentId = Header.GenesisParentId, height = settings.constants.GenesisHeight) - Block(header, Payload(header.id, Seq(coinbaseTransaction))) - } - - def generateNextBlockValidForState(prevBlock: Block, - state: UtxoState, - box: Seq[AssetBox], - transactionsNumberInEachBlock: Int, - numberOfInputsInOneTransaction: Int, - numberOfOutputsInOneTransaction: Int): Block = { - - val transactions: Seq[Transaction] = (0 until transactionsNumberInEachBlock).foldLeft(box, Seq.empty[Transaction]) { - case ((boxes, transactionsL), _) => - val tx: Transaction = defaultPaymentTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useBoxes = boxes.take(numberOfInputsInOneTransaction).toIndexedSeq, - recipient = randomAddress, - amount = 10000, - numOfOutputs = numberOfOutputsInOneTransaction - ) - (boxes.drop(numberOfInputsInOneTransaction), transactionsL :+ tx) - }._2 ++ Seq(coinbaseTransaction(prevBlock.header.height + 1)) - logger.info(s"Number of generated transactions: ${transactions.size}.") - val header = Header( - 1.toByte, - prevBlock.id, - Payload.rootHash(transactions.map(_.id)), - System.currentTimeMillis(), - prevBlock.header.height + 1, - R.nextLong(), - Difficulty @@ BigInt(1), - EquihashSolution(Seq(1, 3)) - ) - Block(header, Payload(header.id, transactions)) - } - - def generateNextBlockForStateWithSpendingAllPreviousBoxes(prevBlock: Block, - state: UtxoState, - box: Seq[AssetBox], - splitCoef: Int = 2, - addDiff: Difficulty = Difficulty @@ BigInt(0)): Block = { - - val transactions: Seq[Transaction] = box.indices.foldLeft(box, Seq.empty[Transaction]) { - case ((boxes, transactionsL), _) => - val tx: Transaction = defaultPaymentTransactionScratch( - privKey, - fee = 1, - timestamp = 11L, - useBoxes = IndexedSeq(boxes.head), - recipient = privKey.publicImage.address.address, - amount = boxes.head.amount - 1, - numOfOutputs = splitCoef - ) - (boxes.tail, transactionsL :+ tx) - }._2.filter(tx => state.validate(tx).isRight) ++ Seq(coinbaseTransaction(prevBlock.header.height + 1)) - logger.info(s"Number of generated transactions: ${transactions.size}.") - val header = Header( - 1.toByte, - prevBlock.id, - Payload.rootHash(transactions.map(_.id)), - System.currentTimeMillis(), - prevBlock.header.height + 1, - R.nextLong(), - Difficulty @@ (BigInt(1) + addDiff), - EquihashSolution(Seq(1, 3)) - ) - Block(header, Payload(header.id, transactions)) - } - - def generateNextBlockValidForHistory(history: History, - difficultyDiff: BigInt = 0, - prevBlock: Option[Block], - txs: Seq[Transaction]): Block = { - val previousHeaderId: ModifierId = prevBlock.map(_.id).getOrElse(Header.GenesisParentId) - val requiredDifficulty: Difficulty = prevBlock.map(b => - history.requiredDifficultyAfter(b.header).getOrElse(Difficulty @@ BigInt(0))) - .getOrElse(settings.constants.InitialDifficulty) - val header = genHeader.copy( - parentId = previousHeaderId, - height = history.getBestHeaderHeight + 1, - difficulty = Difficulty @@ (requiredDifficulty + difficultyDiff), - transactionsRoot = Payload.rootHash(txs.map(_.id)) - ) - Block(header, Payload(header.id, txs)) - } - - def genValidPaymentTxs(qty: Int): Seq[Transaction] = { - val now = System.currentTimeMillis() - (0 until qty).map { _ => - val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(genAssetBox(privKey.publicImage.address.address)) - defaultPaymentTransactionScratch(privKey, 4300, - now + scala.util.Random.nextInt(5000), useBoxes, randomAddress, 1000000) - } - } - - def genAssetBox(address: Address, amount: Amount = 100000L, tokenIdOpt: Option[ADKey] = None): AssetBox = - AssetBox(EncryProposition.addressLocked(address), R.nextLong(), amount, tokenIdOpt) - - def utxoFromBoxHolder(bh: BoxHolder, - dir: File, - nodeViewHolderRef: Option[ActorRef], - settings: EncryAppSettings, - storageType: StorageType): UtxoState = { - val storage = settings.storage.state match { - case VersionalStorage.IODB => - logger.info("Init state with iodb storage") - IODBWrapper(new LSMStore(dir, keepVersions = settings.constants.DefaultKeepVersions)) - case VersionalStorage.LevelDB => - logger.info("Init state with levelDB storage") - val levelDBInit = LevelDbFactory.factory.open(dir, new Options) - VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, settings.levelDB, keySize = 32)) - } - - storage.insert( - StorageVersion @@ Array.fill(32)(0: Byte), - bh.boxes.values.map(bx => (StorageKey !@@ bx.id, StorageValue @@ bx.bytes)).toList - ) - - new UtxoState(storage, settings.constants) - } - - def getRandomTempDir: File = { - val dir = java.nio.file.Files.createTempDirectory("encry_test_" + R.alphanumeric.take(15).mkString).toFile - dir.deleteOnExit() - dir - } - - def genHeader: Header = { - val random = new scala.util.Random - Header( - 1.toByte, - ModifierId @@ Random.randomBytes(), - Digest32 @@ Random.randomBytes(), - Math.abs(random.nextLong()), - Math.abs(random.nextInt(10000)), - random.nextLong(), - settings.constants.InitialDifficulty, - EquihashSolution(Seq(1, 3)) - ) - } - - def genHardcodedBox(address: Address, nonce: Long): AssetBox = - AssetBox(EncryProposition.addressLocked(address), nonce, 10000000L, None) - - def randomAddress: Address = Pay2PubKeyAddress(PublicKey @@ Random.randomBytes()).address - - def coinbaseTransaction(height: Int): Transaction = TransactionFactory.coinbaseTransactionScratch( - privKey.publicImage, - System.currentTimeMillis(), - supply = 10000000L, - amount = 1L, - height = Height @@ height - ) - - lazy val coinbaseTransaction: Transaction = { - TransactionFactory.coinbaseTransactionScratch( - privKey.publicImage, - System.currentTimeMillis(), - 10L, - 0, - Height @@ 100 - ) - } - - def createPrivKey(seed: Option[String]): PrivateKey25519 = { - val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( - Blake2b256.hash( - seed.map { - Mnemonic.seedFromMnemonic(_) - } - .getOrElse { - val phrase: String = Mnemonic.entropyToMnemonicCode(scorex.utils.Random.randomBytes(16)) - Mnemonic.seedFromMnemonic(phrase) - }) - ) - PrivateKey25519(privateKey, publicKey) - } - - def generateInitialBoxes(qty: Int): immutable.IndexedSeq[AssetBox] = - (0 until qty).map(_ => genAssetBox(privKey.publicImage.address.address)) - - def generatePaymentTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = defaultPaymentTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useBoxes = boxesLocal.take(numberOfInputs), - recipient = randomAddress, - amount = 10000, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), transactions :+ tx) - }._2 - - def generateDataTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int, - bytesQty: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = dataTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useOutputs = boxesLocal.take(numberOfInputs), - data = Random.randomBytes(bytesQty), - amount = 200L, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), tx +: transactions) - }._2 - - def generateAssetTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = assetIssuingTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useOutputs = boxesLocal.take(numberOfInputs), - amount = 200L, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), tx +: transactions) - }._2 - - def defaultPaymentTransactionScratch(privKey: PrivateKey25519, - fee: Amount, - timestamp: Long, - useBoxes: IndexedSeq[MonetaryBox], - recipient: Address, - amount: Amount, - tokenIdOpt: Option[ADKey] = None, - numOfOutputs: Int = 5): Transaction = { - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useBoxes - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useBoxes.map(_.amount).sum - (amount + fee) - - val directives: IndexedSeq[TransferDirective] = - if (change > 0) TransferDirective(recipient, amount, tokenIdOpt) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, tokenIdOpt)) - else IndexedSeq(TransferDirective(recipient, amount, tokenIdOpt)) - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, directives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def dataTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: IndexedSeq[MonetaryBox], - amount: Long, - data: Array[Byte], - numOfOutputs: Int = 5): Transaction = { - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useOutputs - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - - val directives: IndexedSeq[DataDirective] = - (0 until numOfOutputs).foldLeft(IndexedSeq.empty[DataDirective]) { case (directivesAll, _) => - directivesAll :+ DataDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, data) - } - - val newDirectives: IndexedSeq[Directive] = - if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives - else directives - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def assetIssuingTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: IndexedSeq[MonetaryBox], - amount: Long, - numOfOutputs: Int = 5): Transaction = { - val directives: IndexedSeq[AssetIssuingDirective] = - (0 until numOfOutputs).foldLeft(IndexedSeq.empty[AssetIssuingDirective]) { case (directivesAll, _) => - directivesAll :+ AssetIssuingDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, amount) - } - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useOutputs - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - - val newDirectives: IndexedSeq[Directive] = - if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives - else directives - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def generateHistory(settings: EncryAppSettings, file: File): History = { - - val indexStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) - val objectsStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) - val levelDBInit = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) - val vldbInit = VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, settings.levelDB)) - val storage: HistoryStorage = new HistoryStorage(vldbInit) - - val ntp: NetworkTimeProvider = new NetworkTimeProvider(settings.ntp) - - new History { - override val historyStorage: HistoryStorage = storage - override val timeProvider: NetworkTimeProvider = ntp - } - } - -} \ No newline at end of file diff --git a/benchmarks/src/test/scala/benches/VersionalLevelDBBanches.scala b/benchmarks/src/test/scala/benches/VersionalLevelDBBanches.scala index e8f8bebdec..1db110baaf 100644 --- a/benchmarks/src/test/scala/benches/VersionalLevelDBBanches.scala +++ b/benchmarks/src/test/scala/benches/VersionalLevelDBBanches.scala @@ -1,9 +1,11 @@ package benches import java.util.concurrent.TimeUnit + import benches.VersionalLevelDBBanches.VersionalLevelDBState import encry.settings.LevelDBSettings -import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, VersionalLevelDBCompanion} +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} +import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, LevelDbFactory, VersionalLevelDBCompanion} import encry.utils.FileHelper import org.iq80.leveldb.Options import org.openjdk.jmh.annotations.{Benchmark, Mode, Scope, State} @@ -11,6 +13,7 @@ import org.openjdk.jmh.infra.Blackhole import org.openjdk.jmh.profile.GCProfiler import org.openjdk.jmh.runner.options.{OptionsBuilder, TimeValue, VerboseMode} import org.openjdk.jmh.runner.{Runner, RunnerException} +import scorex.utils.Random class VersionalLevelDBBanches { @@ -53,9 +56,31 @@ object VersionalLevelDBBanches { @State(Scope.Benchmark) class VersionalLevelDBState { - //val elems1k = Utils.generateRandomLevelDbElemsWithoutDeletions(1000, 100) - //val elems5k = Utils.generateRandomLevelDbElemsWithoutDeletions(5000, 100) - val elems10k = Utils.generateRandomLevelDbElemsWithoutDeletions(10000, 100) - //val elems30k = Utils.generateRandomLevelDbElemsWithoutDeletions(30000, 100) + //val elems1k = generateRandomLevelDbElemsWithoutDeletions(1000, 100) + //val elems5k = generateRandomLevelDbElemsWithoutDeletions(5000, 100) + val elems10k = generateRandomLevelDbElemsWithoutDeletions(10000, 100) + //val elems30k = generateRandomLevelDbElemsWithoutDeletions(30000, 100) } + + val defaultKeySize: Int = 32 + val defaultValueSize: Int = 256 + + def generateRandomKey(keySize: Int = defaultKeySize): VersionalLevelDbKey = + VersionalLevelDbKey @@ Random.randomBytes(keySize) + + def generateRandomValue(valueSize: Int = defaultValueSize): VersionalLevelDbValue = + VersionalLevelDbValue @@ Random.randomBytes(valueSize) + + def genRandomInsertValue(keySize: Int = defaultKeySize, + valueSize: Int = defaultValueSize): (VersionalLevelDbKey, VersionalLevelDbValue) = + (generateRandomKey(keySize), generateRandomValue(valueSize)) + + def generateRandomLevelDbElemsWithoutDeletions(qty: Int, qtyOfElemsToInsert: Int): List[LevelDbDiff] = + (0 until qty).foldLeft(List.empty[LevelDbDiff]) { + case (acc, i) => + LevelDbDiff( + LevelDBVersion @@ Random.randomBytes(), + List((0 until qtyOfElemsToInsert).map(_ => genRandomInsertValue()): _*) + ) :: acc + } } \ No newline at end of file diff --git a/it/src/main/scala/encry/it/docker/Node.scala b/it/src/main/scala/encry/it/docker/Node.scala index 02d8402a43..4a484d8768 100644 --- a/it/src/main/scala/encry/it/docker/Node.scala +++ b/it/src/main/scala/encry/it/docker/Node.scala @@ -5,7 +5,7 @@ import java.net.{InetAddress, InetSocketAddress, URL} import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging import encry.it.api.HttpApi -import encry.it.util.KeyHelper.createPrivKey +import encry.utils.Utils.createPrivKey import encry.settings.Settings import org.asynchttpclient._ import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} diff --git a/it/src/main/scala/encry/it/util/KeyHelper.scala b/it/src/main/scala/encry/it/util/KeyHelper.scala deleted file mode 100644 index 82c8480520..0000000000 --- a/it/src/main/scala/encry/it/util/KeyHelper.scala +++ /dev/null @@ -1,21 +0,0 @@ -package encry.it.util - -import encry.utils.Mnemonic -import org.encryfoundation.common.crypto.PrivateKey25519 -import scorex.crypto.hash.Blake2b256 -import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} - -object KeyHelper { - - def createPrivKey(seed: Option[String]): PrivateKey25519 = { - val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( - Blake2b256.hash( - seed.map { Mnemonic.seedFromMnemonic(_) } - .getOrElse { - val phrase: String = Mnemonic.entropyToMnemonicCode(scorex.utils.Random.randomBytes(16)) - Mnemonic.seedFromMnemonic(phrase) - }) - ) - PrivateKey25519(privateKey, publicKey) - } -} \ No newline at end of file diff --git a/it/src/test/scala/TransactionGenerator/TransactionsUtil.scala b/it/src/test/scala/TransactionGenerator/TransactionsUtil.scala deleted file mode 100644 index 7bb546a1fd..0000000000 --- a/it/src/test/scala/TransactionGenerator/TransactionsUtil.scala +++ /dev/null @@ -1,180 +0,0 @@ -package TransactionGenerator - -import com.google.common.primitives.{Bytes, Longs} -import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519, Signature25519} -import org.encryfoundation.common.modifiers.mempool.directive._ -import org.encryfoundation.common.modifiers.mempool.transaction.{Input, Proof, PubKeyLockedContract, Transaction} -import org.encryfoundation.common.modifiers.state.box.{AssetBox, MonetaryBox, TokenIssuingBox} -import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.prismlang.compiler.CompiledContract -import org.encryfoundation.prismlang.core.wrapped.BoxedValue -import scorex.crypto.hash.{Blake2b256, Digest32} - -object CreateTransaction extends StrictLogging { - - def defaultPaymentTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - recipient: String, - encryCoinAmount: Long, - tokensAmount: Map[TokenId, Long] = Map()): Transaction = { - - val filteredBoxes: Map[Option[TokenId], (List[MonetaryBox], Long)] = - useOutputs.map(_._1).foldLeft(Map[Option[TokenId], (List[MonetaryBox], Long)]()) { - case (resultCollection, box) => - box match { - case tib: TokenIssuingBox => - resultCollection.updated( - Some(tib.tokenId), - (List(tib), tokensAmount.filter(elem => tib.tokenId.sameElements(elem._1)).head._2) - ) - case ab: AssetBox => - val currentBoxesByTokenId: Option[(List[MonetaryBox], Long)] = resultCollection.get(ab.tokenIdOpt) - currentBoxesByTokenId match { - case Some(collection) => resultCollection.updated(ab.tokenIdOpt, (collection._1 :+ ab, collection._2)) - case None => - val neededAmount: Long = ab.tokenIdOpt match { - case Some(tokenId) => tokensAmount.filter(elem => tokenId.sameElements(elem._1)).head._2 - case None => encryCoinAmount - } - resultCollection.updated(ab.tokenIdOpt, (List(ab), neededAmount)) - } - } - } - - val directivesForTransferAndChangeForAllCoins: IndexedSeq[TransferDirective] = - filteredBoxes.foldLeft(IndexedSeq[TransferDirective]()) { - case (collectionForTransfer, (tId, element)) => - val directiveForChange: IndexedSeq[TransferDirective] = - createDirectiveForChange( - privKey, - tId.map(_ => 0L).getOrElse(fee), - element._2, - element._1.map(_.amount).sum, - tId - ) - val collectionWithDirectiveForTransfer: IndexedSeq[TransferDirective] = - collectionForTransfer :+ TransferDirective(recipient, element._2, tId.map(id => ADKey @@ id)) - collectionWithDirectiveForTransfer ++ directiveForChange - } - - prepareTransaction( - privKey, - fee, - timestamp, - useOutputs, - directivesForTransferAndChangeForAllCoins - ) - } - - def scriptedAssetTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - numberOfTokensForIssue: Long, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val directiveForChange: IndexedSeq[TransferDirective] = - createDirectiveForChange(privKey, fee, amount = 0, useOutputs.map(_._1.amount).sum) - val directiveForScriptedAssetIssue: IndexedSeq[Directive] = - directiveForChange :+ ScriptedAssetDirective(contract.hash, numberOfTokensForIssue, tokenIdOpt) - prepareTransaction(privKey, fee, timestamp, useOutputs, directiveForScriptedAssetIssue) - } - - def assetIssuingTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - numberOfTokensForIssue: Long): Transaction = { - val directiveForChange: IndexedSeq[TransferDirective] = - createDirectiveForChange(privKey, fee, amount = 0, useOutputs.map(_._1.amount).sum) - val directiveForTokenIssue: IndexedSeq[Directive] = - directiveForChange :+ AssetIssuingDirective(contract.hash, numberOfTokensForIssue) - prepareTransaction(privKey, fee, timestamp, useOutputs, directiveForTokenIssue) - } - - def dataTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - data: Array[Byte]): Transaction = { - val directiveForChange: IndexedSeq[TransferDirective] = - createDirectiveForChange(privKey, fee, amount = 0, useOutputs.map(_._1.amount).sum) - val directiveForDataTransaction: IndexedSeq[Directive] = - directiveForChange :+ DataDirective(contract.hash, data) - prepareTransaction(privKey, fee, timestamp, useOutputs, directiveForDataTransaction) - } - - private def createDirectiveForChange(privKey: PrivateKey25519, - fee: Long, - amount: Long, - totalAmount: Long, - tokenId: Option[TokenId] = None): IndexedSeq[TransferDirective] = { - val change: Long = totalAmount - (amount + fee) - if (change < 0) { - logger.warn(s"Transaction impossible: required amount is bigger than available. Change is: $change.") - throw new RuntimeException(s"Transaction impossible: required amount is bigger than available $change") - } - if (change > 0) - IndexedSeq(TransferDirective(privKey.publicImage.address.address, change, tokenId.map(element => ADKey @@ element))) - else IndexedSeq() - } - - private def prepareTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - directivesForTransfer: IndexedSeq[Directive]): Transaction = { - - val pubKey: PublicKey25519 = privKey.publicImage - val uInputs: IndexedSeq[Input] = useOutputs.toIndexedSeq.map { case (box, contractOpt) => - Input.unsigned( - box.id, - contractOpt match { - case Some((ct, _)) => Left(ct) - case None => Right(PubKeyLockedContract(pubKey.pubKeyBytes)) - } - ) - } - - val uTransaction: UnsignedEncryTransaction = UnsignedEncryTransaction(fee, timestamp, uInputs, directivesForTransfer) - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - val proofs: IndexedSeq[Seq[Proof]] = useOutputs.flatMap(_._2.map(_._2)).toIndexedSeq - - uTransaction.toSigned(proofs, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } -} - -case class UnsignedEncryTransaction(fee: Long, - timestamp: Long, - inputs: IndexedSeq[Input], - directives: IndexedSeq[Directive]) { - - val messageToSign: Array[Byte] = UnsignedEncryTransaction.bytesToSign(fee, timestamp, inputs, directives) - - def toSigned(proofs: IndexedSeq[Seq[Proof]], defaultProofOpt: Option[Proof]): Transaction = { - val signedInputs: IndexedSeq[Input] = inputs.zipWithIndex.map { case (input, idx) => - if (proofs.nonEmpty && proofs.lengthCompare(idx + 1) <= 0) input.copy(proofs = proofs(idx).toList) else input - } - Transaction(fee, timestamp, signedInputs, directives, defaultProofOpt) - } -} - -object UnsignedEncryTransaction { - - def bytesToSign(fee: Long, - timestamp: Long, - inputs: IndexedSeq[Input], - directives: IndexedSeq[Directive]): Digest32 = - Blake2b256.hash(Bytes.concat( - inputs.flatMap(_.bytesWithoutProof).toArray, - directives.flatMap(_.bytes).toArray, - Longs.toByteArray(timestamp), - Longs.toByteArray(fee) - )) -} \ No newline at end of file diff --git a/it/src/test/scala/encry/it/transactions/AssetTokenTransactionTest.scala b/it/src/test/scala/encry/it/transactions/AssetTokenTransactionTest.scala index 0544b6a181..ca9286b395 100644 --- a/it/src/test/scala/encry/it/transactions/AssetTokenTransactionTest.scala +++ b/it/src/test/scala/encry/it/transactions/AssetTokenTransactionTest.scala @@ -1,34 +1,36 @@ package encry.it.transactions -import TransactionGenerator.CreateTransaction import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging import encry.consensus.EncrySupplyController import encry.it.configs.Configs import encry.it.docker.NodesFromDocker -import encry.it.util.KeyHelper._ +import encry.modifiers.mempool.TransactionFactory import encry.settings.Settings -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} +import encry.utils.Keys +import org.encryfoundation.common.crypto.PublicKey25519 import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address -import org.encryfoundation.common.modifiers.mempool.transaction.{PubKeyLockedContract, Transaction} +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, TokenIssuingBox} import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.Height +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, Height} import org.scalatest.concurrent.ScalaFutures import org.scalatest.{AsyncFunSuite, Matchers} import scorex.crypto.signatures.Curve25519 -import scorex.utils.Random +import scorex.utils.{Random => ScorexRandom} -import scala.concurrent.{Await, Future} import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} +import scala.util.Random class AssetTokenTransactionTest extends AsyncFunSuite with Matchers with ScalaFutures with StrictLogging with NodesFromDocker + with Keys with Settings { override protected def nodeConfigs: Seq[Config] = Seq(Configs.mining(true) @@ -41,25 +43,22 @@ class AssetTokenTransactionTest extends AsyncFunSuite val firstHeightToWait: Int = 5 val secondHeightToWait: Int = 8 val thirdHeightToWait: Int = 11 - val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" - val privKey: PrivateKey25519 = createPrivKey(Some(mnemonicKey)) val waitTime: FiniteDuration = 30.minutes - val amount: Int = scala.util.Random.nextInt(2000) - val fee: Int = scala.util.Random.nextInt(500) - val createdTokensAmount: Int = scala.util.Random.nextInt(2000) - val tokenAmount: Int = scala.util.Random.nextInt(500) - val recipientAddress: Address = PublicKey25519(Curve25519.createKeyPair(Random.randomBytes())._2).address.address + val amount: Int = Random.nextInt(2000) + val fee: Int = Random.nextInt(500) + val createdTokensAmount: Int = Random.nextInt(2000) + val tokenAmount: Int = Random.nextInt(500) + val recipientAddress: Address = PublicKey25519(Curve25519.createKeyPair(ScorexRandom.randomBytes())._2).address.address Await.result(dockerNodes().head.waitForHeadersHeight(firstHeightToWait), waitTime) val getBoxes: Seq[EncryBaseBox] = Await.result(dockerNodes().head.outputs, waitTime) val oneBox: AssetBox = getBoxes.collect { case ab: AssetBox => ab }.head - val transaction: Transaction = CreateTransaction.assetIssuingTransactionScratch( + val transaction: Transaction = TransactionFactory.assetIssuingTransactionScratch( privKey, fee, - timestamp = System.currentTimeMillis(), - useOutputs = IndexedSeq(oneBox).map(_ -> None), - contract = PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract, + System.currentTimeMillis(), + IndexedSeq(oneBox), createdTokensAmount ) @@ -104,14 +103,14 @@ class AssetTokenTransactionTest extends AsyncFunSuite case tb: TokenIssuingBox if tb.amount >= tokenAmount => tb }.head - val transactionWithAssetToken: Transaction = CreateTransaction.defaultPaymentTransaction( + val transactionWithAssetToken: Transaction = TransactionFactory.defaultPaymentTransactionScratch( privKey, fee, System.currentTimeMillis(), - IndexedSeq(assetBoxForFee, tokenIssuingBox).map(_ -> None), + IndexedSeq(assetBoxForFee, tokenIssuingBox), recipientAddress, amount, - Map(tokenId -> tokenAmount) + Some(ADKey @@ tokenId) ) Await.result(dockerNodes().head.sendTransaction(transactionWithAssetToken), waitTime) diff --git a/it/src/test/scala/encry/it/transactions/DataTransactionTest.scala b/it/src/test/scala/encry/it/transactions/DataTransactionTest.scala index 7388629245..41632dd693 100644 --- a/it/src/test/scala/encry/it/transactions/DataTransactionTest.scala +++ b/it/src/test/scala/encry/it/transactions/DataTransactionTest.scala @@ -1,24 +1,25 @@ package encry.it.transactions -import TransactionGenerator.CreateTransaction import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging import encry.it.configs.Configs import encry.it.docker.NodesFromDocker -import encry.it.util.KeyHelper._ -import org.encryfoundation.common.crypto.PrivateKey25519 +import encry.modifiers.mempool.TransactionFactory +import encry.utils.Keys import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.modifiers.mempool.transaction.{PubKeyLockedContract, Transaction} import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox} import org.scalatest.concurrent.ScalaFutures import org.scalatest.{AsyncFunSuite, Matchers} import scorex.utils.Random + import scala.concurrent.{Await, Future} import scala.concurrent.duration._ class DataTransactionTest extends AsyncFunSuite with Matchers with ScalaFutures + with Keys with StrictLogging with NodesFromDocker { @@ -30,8 +31,6 @@ class DataTransactionTest extends AsyncFunSuite val firstHeightToWait: Int = 5 val secondHeightToWait: Int = 8 - val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" - val privKey: PrivateKey25519 = createPrivKey(Some(mnemonicKey)) val waitTime: FiniteDuration = 30.minutes val fee: Long = scala.util.Random.nextInt(500) @@ -39,12 +38,12 @@ class DataTransactionTest extends AsyncFunSuite val boxes: Seq[EncryBaseBox] = Await.result(dockerNodes().head.outputs, waitTime) val oneBox: AssetBox = boxes.collect { case ab: AssetBox => ab }.head - val transaction: Transaction = CreateTransaction.dataTransactionScratch( + val transaction: Transaction = TransactionFactory.dataTransactionScratch( privKey, fee, System.currentTimeMillis(), - IndexedSeq(oneBox).map(_ -> None), - PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract, + IndexedSeq(oneBox), + 0, Random.randomBytes(32) ) diff --git a/it/src/test/scala/encry/it/transactions/ProcessingTransferTransactionWithEncryCoinsTest.scala b/it/src/test/scala/encry/it/transactions/ProcessingTransferTransactionWithEncryCoinsTest.scala index d8ee6981e4..ae5ee25053 100644 --- a/it/src/test/scala/encry/it/transactions/ProcessingTransferTransactionWithEncryCoinsTest.scala +++ b/it/src/test/scala/encry/it/transactions/ProcessingTransferTransactionWithEncryCoinsTest.scala @@ -1,14 +1,14 @@ package encry.it.transactions -import TransactionGenerator.CreateTransaction import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging import encry.consensus.EncrySupplyController import encry.it.configs.Configs import encry.it.docker.NodesFromDocker -import encry.it.util.KeyHelper._ +import encry.modifiers.mempool.TransactionFactory +import encry.utils.Keys import encry.settings.Settings -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} +import org.encryfoundation.common.crypto.PublicKey25519 import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address import org.encryfoundation.common.modifiers.mempool.transaction.Transaction @@ -28,6 +28,7 @@ class ProcessingTransferTransactionWithEncryCoinsTest extends AsyncFunSuite with ScalaFutures with StrictLogging with NodesFromDocker + with Keys with Settings { override protected def nodeConfigs: Seq[Config] = Seq(Configs.mining(true) @@ -40,8 +41,6 @@ class ProcessingTransferTransactionWithEncryCoinsTest extends AsyncFunSuite val fee: Long = scala.util.Random.nextInt(500) val firstHeightToWait: Int = 5 val secondHeightToWait: Int = 8 - val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" - val privKey: PrivateKey25519 = createPrivKey(Some(mnemonicKey)) val recipientAddress: Address = PublicKey25519(Curve25519.createKeyPair(Random.randomBytes())._2).address.address val waitTime: FiniteDuration = 30.minutes @@ -54,11 +53,11 @@ class ProcessingTransferTransactionWithEncryCoinsTest extends AsyncFunSuite val boxes: Seq[EncryBaseBox] = Await.result(dockerNodes().head.outputs, waitTime) val oneBox: AssetBox = boxes.collect { case ab: AssetBox => ab }.head - val transaction: Transaction = CreateTransaction.defaultPaymentTransaction( + val transaction: Transaction = TransactionFactory.defaultPaymentTransactionScratch( privKey, fee, System.currentTimeMillis(), - IndexedSeq(oneBox).map(_ -> None), + IndexedSeq(oneBox), recipientAddress, amount ) diff --git a/src/main/scala/encry/cli/commands/Transfer.scala b/src/main/scala/encry/cli/commands/Transfer.scala index 7b8a6663fb..9cd2a28b53 100644 --- a/src/main/scala/encry/cli/commands/Transfer.scala +++ b/src/main/scala/encry/cli/commands/Transfer.scala @@ -37,11 +37,11 @@ object Transfer extends Command { .map(_.asInstanceOf[AssetBox]).foldLeft(Seq[AssetBox]()) { case (seq, box) => if (seq.map(_.amount).sum < (amount + fee)) seq :+ box else seq }.toIndexedSeq - TransactionFactory.defaultPaymentTransaction( + TransactionFactory.defaultPaymentTransactionScratch( secret, fee, System.currentTimeMillis(), - boxes.map(_ -> None), + boxes, recipient, amount) }.toOption diff --git a/src/main/scala/encry/modifiers/mempool/TransactionFactory.scala b/src/main/scala/encry/modifiers/mempool/TransactionFactory.scala index 07e81d778a..970c3f2d6b 100644 --- a/src/main/scala/encry/modifiers/mempool/TransactionFactory.scala +++ b/src/main/scala/encry/modifiers/mempool/TransactionFactory.scala @@ -1,8 +1,9 @@ package encry.modifiers.mempool +import com.google.common.primitives.{Bytes, Longs} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519, Signature25519} -import org.encryfoundation.common.modifiers.mempool.directive.{Directive, TransferDirective} +import org.encryfoundation.common.modifiers.mempool.directive.{AssetIssuingDirective, DataDirective, Directive, ScriptedAssetDirective, TransferDirective} import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address import org.encryfoundation.common.modifiers.mempool.transaction._ import org.encryfoundation.common.modifiers.state.box.Box.Amount @@ -10,11 +11,23 @@ import org.encryfoundation.common.modifiers.state.box.MonetaryBox import org.encryfoundation.common.utils.TaggedTypes.{ADKey, Height} import org.encryfoundation.prismlang.compiler.CompiledContract import org.encryfoundation.prismlang.core.wrapped.BoxedValue +import scorex.crypto.hash.{Blake2b256, Digest32} import scala.util.Random object TransactionFactory extends StrictLogging { + def coinbaseTransactionScratch(pubKey: PublicKey25519, + timestamp: Long, + supply: Amount, + amount: Amount, + height: Height): Transaction = { + val directives: IndexedSeq[Directive with Product] = + IndexedSeq(TransferDirective(pubKey.address.address, amount + supply)) + + Transaction(0, timestamp, IndexedSeq.empty, directives, None) + } + def defaultPaymentTransactionScratch(privKey: PrivateKey25519, fee: Amount, timestamp: Long, @@ -39,67 +52,84 @@ object TransactionFactory extends StrictLogging { uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) } - def coinbaseTransactionScratch(pubKey: PublicKey25519, - timestamp: Long, - supply: Amount, - amount: Amount, - height: Height): Transaction = { - val directives: IndexedSeq[Directive with Product] = - IndexedSeq(TransferDirective(pubKey.address.address, amount + supply)) + def paymentTransactionWithMultipleOutputs(privKey: PrivateKey25519, fee: Amount, timestamp: Long, useBoxes: IndexedSeq[MonetaryBox], + recipient: Address, amount: Amount, tokenIdOpt: Option[ADKey] = None, + numOfOutputs: Int): Transaction = { - Transaction(0, timestamp, IndexedSeq.empty, directives, None) + val pubKey: PublicKey25519 = privKey.publicImage + val uInputs: IndexedSeq[Input] = useBoxes + .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) + .toIndexedSeq + + val change: Amount = useBoxes.map(_.amount).sum - (amount + fee) + val directives: IndexedSeq[TransferDirective] = + if (change > 0) TransferDirective(recipient, amount, tokenIdOpt) +: (0 until numOfOutputs).map(_ => + TransferDirective(pubKey.address.address, change / numOfOutputs, tokenIdOpt)) + else IndexedSeq(TransferDirective(recipient, amount, tokenIdOpt)) + + val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, directives) + val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) + uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) } - def prepareTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - directivesSeq: IndexedSeq[Directive], - amount: Long, - tokenIdOpt: Option[ADKey] = None): Transaction = { + def dataTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: IndexedSeq[MonetaryBox], + amount: Long, + data: Array[Byte], + numOfOutputs: Int = 5): Transaction = { val pubKey: PublicKey25519 = privKey.publicImage - val uInputs: IndexedSeq[Input] = useOutputs.toIndexedSeq.map { case (box, contractOpt) => - Input.unsigned( - box.id, - contractOpt match { - case Some((ct, _)) => Left(ct) - case None => Right(PubKeyLockedContract(pubKey.pubKeyBytes)) - } - ) - } - - val change: Long = amount + val uInputs: IndexedSeq[Input] = useOutputs + .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) + .toIndexedSeq - if (change < 0) { - logger.warn(s"Transaction impossible: required amount is bigger than available. Change is: $change.") - throw new RuntimeException("Transaction impossible: required amount is bigger than available.") - } + val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - val directives: IndexedSeq[Directive] = - if (change > 0) directivesSeq ++: IndexedSeq(TransferDirective(pubKey.address.address, change, tokenIdOpt)) - else directivesSeq + val directives: IndexedSeq[DataDirective] = + (0 until numOfOutputs).foldLeft(IndexedSeq.empty[DataDirective]) { case (directivesAll, _) => + directivesAll :+ DataDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, data) + } - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, directives) - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - val proofs: IndexedSeq[Seq[Proof]] = useOutputs.flatMap(_._2.map(_._2)).toIndexedSeq + val newDirectives: IndexedSeq[Directive] = + if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => + TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives + else directives - uTransaction.toSigned(proofs, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) + val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) + val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) + uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) } - def defaultPaymentTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - recipient: String, - amount: Long, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val howMuchCanTransfer: Long = useOutputs.map(_._1.amount).sum - fee - val howMuchWillTransfer: Long = howMuchCanTransfer - Math.abs(Random.nextLong % howMuchCanTransfer) - val change: Long = howMuchCanTransfer - howMuchWillTransfer - val directives: IndexedSeq[TransferDirective] = - IndexedSeq(TransferDirective(recipient, howMuchWillTransfer, tokenIdOpt)) - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, change, tokenIdOpt) + def assetIssuingTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: IndexedSeq[MonetaryBox], + amount: Long, + numOfOutputs: Int = 5): Transaction = { + val directives: IndexedSeq[AssetIssuingDirective] = + (0 until numOfOutputs).foldLeft(IndexedSeq.empty[AssetIssuingDirective]) { case (directivesAll, _) => + directivesAll :+ AssetIssuingDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, amount) + } + + val pubKey: PublicKey25519 = privKey.publicImage + + val uInputs: IndexedSeq[Input] = useOutputs + .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) + .toIndexedSeq + + val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) + + val newDirectives: IndexedSeq[Directive] = + if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => + TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives + else directives + + val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) + val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) + uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) } + } \ No newline at end of file diff --git a/src/main/scala/encry/network/PeerConnectionHandler.scala b/src/main/scala/encry/network/PeerConnectionHandler.scala index d4fe617311..31beac438b 100755 --- a/src/main/scala/encry/network/PeerConnectionHandler.scala +++ b/src/main/scala/encry/network/PeerConnectionHandler.scala @@ -14,11 +14,12 @@ import encry.network.PeerConnectionHandler.{AwaitingHandshake, CommunicationStat import encry.network.PeerConnectionHandler.ReceivableMessages._ import encry.network.PeersKeeper.{ConnectionStopped, HandshakedDone} import encry.settings.NetworkSettings +import encry.utils.Utils.protocolToBytes import org.encryfoundation.common.network.BasicMessagesRepo.{GeneralizedNetworkMessage, Handshake, NetworkMessage} import org.encryfoundation.common.utils.Algos import scala.annotation.tailrec -import scala.collection.immutable.{HashMap, SortedMap} +import scala.collection.immutable. SortedMap import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} @@ -281,7 +282,6 @@ class PeerConnectionHandler(connection: ActorRef, multiPacket(List[ByteString](), data) } - private def protocolToBytes(protocol: String): Array[Byte] = protocol.split("\\.").map(elem => elem.toByte) } object PeerConnectionHandler { diff --git a/src/test/scala/encry/utils/FileHelper.scala b/src/main/scala/encry/utils/FileHelper.scala similarity index 100% rename from src/test/scala/encry/utils/FileHelper.scala rename to src/main/scala/encry/utils/FileHelper.scala diff --git a/src/main/scala/encry/utils/Utils.scala b/src/main/scala/encry/utils/Utils.scala index 3e9c03c4a1..0fab0190dc 100644 --- a/src/main/scala/encry/utils/Utils.scala +++ b/src/main/scala/encry/utils/Utils.scala @@ -1,9 +1,15 @@ package encry.utils import com.google.common.primitives.Longs +import org.encryfoundation.common.crypto.PrivateKey25519 +import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address +import org.encryfoundation.common.modifiers.mempool.transaction.Pay2PubKeyAddress import org.encryfoundation.common.network.BasicMessagesRepo.BasicMsgDataTypes.InvData import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} +import scorex.crypto.hash.Blake2b256 +import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} +import scorex.utils.{Random => ScorexRandom} object Utils { @@ -19,4 +25,21 @@ object Utils { idsToString(ids.map(id => (modifierType, id))) def idsToString(invData: InvData): String = idsToString(invData._1, invData._2) + + def randomAddress: Address = Pay2PubKeyAddress(PublicKey @@ ScorexRandom.randomBytes()).address + + def protocolToBytes(protocol: String): Array[Byte] = protocol.split("\\.").map(elem => elem.toByte) + + def createPrivKey(seed: Option[String]): PrivateKey25519 = { + val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( + Blake2b256.hash( + seed.map { Mnemonic.seedFromMnemonic(_) } + .getOrElse { + val phrase: String = Mnemonic.entropyToMnemonicCode(ScorexRandom.randomBytes(16)) + Mnemonic.seedFromMnemonic(phrase) + }) + ) + PrivateKey25519(privateKey, publicKey) + } + } \ No newline at end of file diff --git a/src/main/scala/encry/view/state/UtxoState.scala b/src/main/scala/encry/view/state/UtxoState.scala index 0f70f1ef6f..53ff239327 100644 --- a/src/main/scala/encry/view/state/UtxoState.scala +++ b/src/main/scala/encry/view/state/UtxoState.scala @@ -53,7 +53,10 @@ final case class UtxoState(storage: VersionalStorage, constants: Constants) val result = mod match { case header: Header => logger.info(s"\n\nStarting to applyModifier as a header: ${Algos.encode(mod.id)} to state at height ${header.height}") - UtxoState(storage, constants).asRight[List[ModifierApplyError]] + val newState: UtxoState = UtxoState(storage, constants) + newState.height = height + newState.lastBlockTimestamp = header.timestamp + newState.asRight[List[ModifierApplyError]] case block: Block => logger.info(s"\n\nStarting to applyModifier as a Block: ${Algos.encode(mod.id)} to state at height ${block.header.height}") val lastTxId = block.payload.txs.last.id @@ -80,10 +83,10 @@ final case class UtxoState(storage: VersionalStorage, constants: Constants) combinedStateChange.inputsToDb.toList ) logger.info(s"Time of insert: ${(System.currentTimeMillis() - insertTimestart)/1000L} s") - UtxoState( - storage, - constants - ).asRight[List[ModifierApplyError]] + val newState: UtxoState = UtxoState(storage, constants) + newState.height = Height @@ block.header.height + newState.lastBlockTimestamp = block.header.timestamp + newState.asRight[List[ModifierApplyError]] } ) } diff --git a/src/test/scala/encry/api/http/routes/TransactionsApiRouteSpec.scala b/src/test/scala/encry/api/http/routes/TransactionsApiRouteSpec.scala index b26f5a8f47..8a591f7540 100644 --- a/src/test/scala/encry/api/http/routes/TransactionsApiRouteSpec.scala +++ b/src/test/scala/encry/api/http/routes/TransactionsApiRouteSpec.scala @@ -1,12 +1,12 @@ package encry.api.http.routes -import encry.modifiers.InstanceFactory import io.circe.Decoder.Result import io.circe.Json import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.scalatest.{Matchers, PropSpec} +import encry.utils.TestEntityGenerator.paymentTransactionValid -class TransactionsApiRouteSpec extends PropSpec with Matchers with InstanceFactory { +class TransactionsApiRouteSpec extends PropSpec with Matchers { private val tx = paymentTransactionValid diff --git a/src/test/scala/encry/consensus/emission/EncrySupplyControllerTest.scala b/src/test/scala/encry/consensus/emission/EncrySupplyControllerTest.scala index 655da749b5..fd71731e87 100644 --- a/src/test/scala/encry/consensus/emission/EncrySupplyControllerTest.scala +++ b/src/test/scala/encry/consensus/emission/EncrySupplyControllerTest.scala @@ -2,13 +2,12 @@ package encry.consensus.emission import encry.consensus.EncrySupplyController import encry.settings.Settings -import encry.utils.EncryGenerator import org.encryfoundation.common.utils.TaggedTypes.Height import org.scalatest.{Matchers, PropSpec} import scala.concurrent.duration._ -class EncrySupplyControllerTest extends PropSpec with Matchers with EncryGenerator with Settings { +class EncrySupplyControllerTest extends PropSpec with Matchers with Settings { val epochLen = 10 diff --git a/src/test/scala/encry/modifiers/InstanceFactory.scala b/src/test/scala/encry/modifiers/InstanceFactory.scala deleted file mode 100755 index 2444c6f56b..0000000000 --- a/src/test/scala/encry/modifiers/InstanceFactory.scala +++ /dev/null @@ -1,231 +0,0 @@ -package encry.modifiers - -import encry.modifiers.mempool._ -import encry.modifiers.state.Keys -import encry.settings.{EncryAppSettings, NodeSettings} -import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, VLDBWrapper, VersionalLevelDBCompanion} -import encry.utils.{EncryGenerator, FileHelper, NetworkTimeProvider, TestHelper} -import encry.view.history.History -import encry.view.history.storage.HistoryStorage -import io.iohk.iodb.LSMStore -import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} -import org.encryfoundation.common.modifiers.mempool.transaction.{Input, Transaction} -import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryProposition} -import org.encryfoundation.common.modifiers.state.box.Box.Amount -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{Height, _} - -import org.encryfoundation.prismlang.compiler.CompiledContract -import org.encryfoundation.prismlang.core.Ast.Expr -import org.encryfoundation.prismlang.core.{Ast, Types} -import org.iq80.leveldb.Options -import scorex.crypto.hash.Digest32 -import scorex.utils.Random - -import scala.util.{Random => Scarand} - -trait InstanceFactory extends Keys with EncryGenerator { - - private val genHelper = TestHelper - - lazy val fakeTransaction: Transaction = { - val fee = genHelper.Props.txFee - val useBoxes = IndexedSeq(genHelper.genAssetBox(publicKey.address.address), - genHelper.genAssetBox(publicKey.address.address)) - - TransactionFactory.defaultPaymentTransactionScratch(secret, fee, timestamp, useBoxes, - publicKey.address.address, 12345678L) - } - - lazy val paymentTransactionValid: Transaction = { - val fee: Amount = genHelper.Props.txFee - val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(genHelper.genAssetBox(publicKey.address.address), - genHelper.genAssetBox(publicKey.address.address)) - - TransactionFactory.defaultPaymentTransactionScratch(secret, fee, timestamp, useBoxes, - publicKey.address.address, genHelper.Props.txAmount) - } - - def generateGenesisBlock(genesisHeight: Height): Block = { - val txs: Seq[Transaction] = Seq(coinbaseTransaction) - val txsRoot: Digest32 = Payload.rootHash(txs.map(_.id)) - val header = genHeader.copy( - parentId = Header.GenesisParentId, - height = genesisHeight, - transactionsRoot = txsRoot - ) - Block(header, Payload(header.id, Seq(coinbaseTransaction))) - } - - def paymentTransactionDynamic: Transaction = { - val fee = genHelper.Props.txFee - val useBoxes = (0 to 5).map(_ => { - AssetBox( - EncryProposition.pubKeyLocked(secret.publicImage.pubKeyBytes), - Scarand.nextLong(), - 999L - ) - }) - - TransactionFactory.defaultPaymentTransactionScratch(secret, fee, Scarand.nextLong(), useBoxes, - publicKey.address.address, genHelper.Props.txAmount) - } - - lazy val paymentTransactionInvalid: Transaction = { - val useBoxes = IndexedSeq(genHelper.genAssetBox(publicKey.address.address)) - - TransactionFactory.defaultPaymentTransactionScratch(secret, -100, timestamp, useBoxes, - randomAddress, genHelper.Props.txAmount) - } - - lazy val coinbaseTransaction: Transaction = { - TransactionFactory.coinbaseTransactionScratch(secret.publicImage, timestamp, 10L, 0, Height @@ 100) - } - - def coinbaseTransactionWithDiffSupply(supply: Long = 10L): Transaction = { - TransactionFactory.coinbaseTransactionScratch(secret.publicImage, timestamp, supply, 0, Height @@ 100) - } - - lazy val AssetBoxI: AssetBox = - AssetBox( - EncryProposition.pubKeyLocked(secret.publicImage.pubKeyBytes), - 999L, - 100000L - ) - - lazy val OpenAssetBoxI: AssetBox = - AssetBox( - EncryProposition.open, - 999L, - 100000L - ) - - lazy val Contract: CompiledContract = CompiledContract( - List("state" -> Types.EncryState), - Expr.If( - Expr.Compare( - Expr.Attribute( - Expr.Name( - Ast.Ident("state"), - Types.EncryState - ), - Ast.Ident("height"), - Types.PInt - ), - List(Ast.CompOp.GtE), - List(Expr.IntConst(1000L)) - ), - Expr.True, - Expr.False, - Types.PBoolean - ) - ) - - lazy val UnsignedInput: Input = Input(ADKey @@ Random.randomBytes(), Left(Contract), List.empty) - - def generateFakeChain(blocksQty: Int, genesisHeight: Height): Seq[Block] = { - val srand = new Scarand() - (0 until blocksQty).foldLeft(Seq.empty[Block], Seq.empty[AssetBox]) { - case ((fakeBlockchain, utxo), blockHeight) => - val block = if (fakeBlockchain.isEmpty) generateGenesisBlock(genesisHeight) else { - val addr = randomAddress - val txs = - utxo.map(box => genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(box), addr)) ++ - Seq(coinbaseTransactionWithDiffSupply(Math.abs(srand.nextInt(1000)))) - val txsRoot = Algos.merkleTreeRoot(txs.map(tx => LeafData @@ tx.id.untag(ModifierId))) - val header = genHeaderAtHeight(blockHeight, txsRoot) - val payload = Payload(header.id, txs) - Block(header, payload) - } - val newUtxo = block.payload.txs.flatMap(_.newBoxes) - (fakeBlockchain :+ block) -> newUtxo.collect{case ab: AssetBox => ab} - } - }._1 - - def generateNextBlock(history: History, - difficultyDiff: BigInt = 0, - prevId: Option[ModifierId] = None, - txsQty: Int = 100, - additionalDifficulty: BigInt = 0): Block = { - val previousHeaderId: ModifierId = - prevId.getOrElse(history.getBestHeader.map(_.id).getOrElse(Header.GenesisParentId)) - val requiredDifficulty: Difficulty = history.getBestHeader.map(parent => - history.requiredDifficultyAfter(parent).getOrElse(Difficulty @@ BigInt(0))) - .getOrElse(history.settings.constants.InitialDifficulty) - val txs = (if (txsQty != 0) genValidPaymentTxs(Scarand.nextInt(txsQty)) else Seq.empty) ++ - Seq(coinbaseTransaction) - val header = genHeader.copy( - parentId = previousHeaderId, - height = history.getBestHeaderHeight + 1, - difficulty = Difficulty @@ (requiredDifficulty + difficultyDiff + additionalDifficulty), - transactionsRoot = Payload.rootHash(txs.map(_.id)) - ) - - Block(header, Payload(header.id, txs)) - } - - def genForkOn(qty: Int, - addDifficulty: BigInt = 0, - from: Int, - to: Int, - settings: EncryAppSettings): (List[Block], List[Block]) = { - val history: History = generateDummyHistory(settings) - val forkInterval = (from until to).toList - (0 until qty).foldLeft((List(history), List.empty[Block] -> List.empty[Block])) { - case ((histories, blocks), blockHeight) => - if (forkInterval.contains(blockHeight)) { - if (histories.length == 1) { - val secondHistory = generateDummyHistory(settings) - blocks._1.foldLeft(secondHistory) { - case (prevHistory, blockToApply) => - prevHistory.append(blockToApply.header) - prevHistory.append(blockToApply.payload) - prevHistory.reportModifierIsValid(blockToApply) - prevHistory - } - val nextBlockInFirstChain = generateNextBlock(histories.head) - val nextBlockInSecondChain = generateNextBlock(secondHistory, additionalDifficulty = addDifficulty) - histories.head.append(nextBlockInFirstChain.header) - histories.head.append(nextBlockInFirstChain.payload) - val a = histories.head.reportModifierIsValid(nextBlockInFirstChain) - secondHistory.append(nextBlockInSecondChain.header) - secondHistory.append(nextBlockInSecondChain.payload) - val b = secondHistory.reportModifierIsValid(nextBlockInSecondChain) - (List(a, b), (blocks._1 :+ nextBlockInFirstChain) -> List(nextBlockInSecondChain)) - } else { - val nextBlockInFirstChain = generateNextBlock(histories.head) - val nextBlockInSecondChain = generateNextBlock(histories.last, additionalDifficulty = addDifficulty) - histories.head.append(nextBlockInFirstChain.header) - histories.head.append(nextBlockInFirstChain.payload) - val a = histories.head.reportModifierIsValid(nextBlockInFirstChain) - histories.last.append(nextBlockInSecondChain.header) - histories.last.append(nextBlockInSecondChain.payload) - val b = histories.last.reportModifierIsValid(nextBlockInSecondChain) - (List(a, b), (blocks._1 :+ nextBlockInFirstChain) -> (blocks._2 :+ nextBlockInSecondChain)) - } - } else { - val block: Block = generateNextBlock(histories.head) - histories.head.append(block.header) - histories.head.append(block.payload) - val a = histories.head.reportModifierIsValid(block) - (List(a), (blocks._1 :+ block) -> blocks._2) - } - }._2 - } - - def generateDummyHistory(settings: EncryAppSettings): History = { - - val indexStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) - val objectsStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) - val levelDBInit = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) - val vldbInit = VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, settings.levelDB)) - val storage: HistoryStorage = new HistoryStorage(vldbInit) - - val ntp: NetworkTimeProvider = new NetworkTimeProvider(settings.ntp) - - new History { - override val historyStorage: HistoryStorage = storage - override val timeProvider: NetworkTimeProvider = ntp - } - } -} \ No newline at end of file diff --git a/src/test/scala/encry/modifiers/history/BlockSerializerTest.scala b/src/test/scala/encry/modifiers/history/BlockSerializerTest.scala index 5c799b1a3b..02a5d1e3e5 100755 --- a/src/test/scala/encry/modifiers/history/BlockSerializerTest.scala +++ b/src/test/scala/encry/modifiers/history/BlockSerializerTest.scala @@ -2,7 +2,7 @@ package encry.modifiers.history import encry.modifiers.mempool.TransactionFactory import encry.settings.Settings -import encry.utils.{EncryGenerator, TestHelper} +import encry.utils.TestHelper import org.encryfoundation.common.crypto.equihash.EquihashSolution import org.encryfoundation.common.modifiers.history._ import org.encryfoundation.common.utils.Algos @@ -10,8 +10,9 @@ import org.encryfoundation.common.utils.TaggedTypes.ModifierId import org.scalatest.FunSuite import scorex.crypto.hash.Digest32 import scorex.utils.Random +import encry.utils.Utils.randomAddress -class BlockSerializerTest extends FunSuite with EncryGenerator with Settings { +class BlockSerializerTest extends FunSuite with Settings { test("testToBytes $ testFromBytes") { diff --git a/src/test/scala/encry/modifiers/history/HeaderSerializerTest.scala b/src/test/scala/encry/modifiers/history/HeaderSerializerTest.scala index 3baea5e231..691687af81 100755 --- a/src/test/scala/encry/modifiers/history/HeaderSerializerTest.scala +++ b/src/test/scala/encry/modifiers/history/HeaderSerializerTest.scala @@ -1,10 +1,10 @@ package encry.modifiers.history -import encry.utils.EncryGenerator import org.encryfoundation.common.modifiers.history.HeaderSerializer import org.scalatest.{Matchers, PropSpec} +import encry.utils.TestEntityGenerator.genHeader -class HeaderSerializerTest extends PropSpec with Matchers with EncryGenerator{ +class HeaderSerializerTest extends PropSpec with Matchers { property("testToBytes & testParseBytes") { diff --git a/src/test/scala/encry/modifiers/history/HeaderSpec.scala b/src/test/scala/encry/modifiers/history/HeaderSpec.scala index 569c3a95da..60c4c22e43 100644 --- a/src/test/scala/encry/modifiers/history/HeaderSpec.scala +++ b/src/test/scala/encry/modifiers/history/HeaderSpec.scala @@ -1,12 +1,12 @@ package encry.modifiers.history -import encry.utils.EncryGenerator import org.scalatest.{Matchers, PropSpec} import scorex.crypto.hash.Blake2b256 +import encry.utils.TestEntityGenerator.genHeader -class HeaderSpec extends PropSpec with Matchers with EncryGenerator{ +class HeaderSpec extends PropSpec with Matchers { - property("Different headers should have different hash"){ + property("Different headers should have different hash") { val header = genHeader diff --git a/src/test/scala/encry/modifiers/history/HistoryApplicatorTest.scala b/src/test/scala/encry/modifiers/history/HistoryApplicatorTest.scala new file mode 100644 index 0000000000..ca4cb35e06 --- /dev/null +++ b/src/test/scala/encry/modifiers/history/HistoryApplicatorTest.scala @@ -0,0 +1,220 @@ +package encry.modifiers.history + +import java.io.File + +import akka.actor.ActorSystem +import akka.testkit.{ImplicitSender, TestActorRef, TestKit, TestProbe} +import encry.network.DeliveryManager.FullBlockChainIsSynced +import encry.settings.{EncryAppSettings, Settings} +import encry.storage.VersionalStorage +import encry.utils.ChainGenerator._ +import encry.utils.{FileHelper, Keys} +import encry.view.actors.HistoryApplicator +import encry.view.actors.NodeViewHolder.ReceivableMessages.{LocallyGeneratedBlock, ModifierFromRemote} +import encry.view.history.History +import encry.view.wallet.EncryWallet +import org.encryfoundation.common.modifiers.PersistentModifier +import org.encryfoundation.common.modifiers.history.Block +import org.encryfoundation.common.utils.Algos +import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.HistoryGenerator.dummyHistory + +import scala.collection.Seq +import scala.concurrent.duration._ + +class HistoryApplicatorTest extends TestKit(ActorSystem()) + with WordSpecLike + with ImplicitSender + with BeforeAndAfterAll + with Matchers + with OneInstancePerTest + with Keys + with Settings { + + override def afterAll: Unit = shutdown(system) + + val testSettings: EncryAppSettings = settings.copy(storage = settings.storage.copy(state = VersionalStorage.LevelDB)) + + def blockToModifiers(block: Block): Seq[PersistentModifier] = Seq(block.header, block.payload) + + def rollbackTest(transQty: Int) { + val history: History = dummyHistory(testSettings, withoutPow = true) + //generate invalid blocks begining from 6 blocks + val (initialState, state, chain) = genChain(privKey, dir, testSettings, 10, transQty, Some(6)) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + chain + .flatMap(blockToModifiers) + .foreach(historyApplicator ! ModifierFromRemote(_)) + + expectMsg(timeout, FullBlockChainIsSynced()) + + Thread.sleep(1000 + transQty/2) + + awaitCond(history.getBestBlockHeight == 4, timeout) + + checkBest(history, chain(4)) + } + + def checkBest(history: History, expectedBlock: Block) { + history.getBestHeaderHeight shouldBe expectedBlock.header.height + history.getBestHeader.map(h => Algos.encode(h.id)) shouldBe Some(Algos.encode(expectedBlock.header.id)) + history.getBestBlock.map(b => Algos.encode(b.id)) shouldBe Some(Algos.encode(expectedBlock.id)) + history.getBestBlockHeightDB shouldBe expectedBlock.header.height + } + + val dir: File = FileHelper.getRandomTempDir + val wallet: EncryWallet = EncryWallet.readOrGenerate(testSettings.copy(directory = dir.getAbsolutePath)) + + val nodeViewHolder = TestProbe() + val influx = TestProbe() + + val timeout: FiniteDuration = 30 seconds + + "HistoryApplicator" should { + + "apply locall blocks and check chain sync" in { + + val blockQty = 10 + val history: History = dummyHistory(testSettings, withoutPow = true) + val (initialState, state, chain) = genChain(privKey, dir, testSettings, blockQty) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + chain.foreach(historyApplicator ! LocallyGeneratedBlock(_)) + + expectMsg(timeout, FullBlockChainIsSynced()) + awaitCond(history.getBestBlockHeight == blockQty - 1, timeout) + + checkBest(history, chain(blockQty - 1)) + } + + "apply remote blocks and check chain sync" in { + + val blockQty = 10 + val history: History = dummyHistory(testSettings, withoutPow = true) + val (initialState, state, chain) = genChain(privKey, dir, testSettings, blockQty) + + val modifiers: Seq[PersistentModifier] = chain.flatMap(blockToModifiers) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + modifiers.foreach(historyApplicator ! ModifierFromRemote(_)) + + expectMsg(timeout, FullBlockChainIsSynced()) + awaitCond(history.getBestBlockHeight == blockQty - 1, timeout) + + checkBest(history, chain(blockQty - 1)) + } + + "apply remote blocks and check queue for rollback height" in { + + val overQty = 30 + + val history: History = dummyHistory(testSettings, withoutPow = true) + val (initialState, state, chain) = genChain(privKey, dir, testSettings, testSettings.levelDB.maxVersions + overQty, 10) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + chain + .flatMap(blockToModifiers) + .foreach(historyApplicator ! ModifierFromRemote(_)) + + historyApplicator.underlyingActor.modifiersQueue.size should be <= testSettings.levelDB.maxVersions + + expectMsg(timeout, FullBlockChainIsSynced()) + awaitCond(history.getBestBlockHeight == testSettings.levelDB.maxVersions + overQty - 1) + + checkBest(history, chain(testSettings.levelDB.maxVersions + overQty - 1)) + } + + "should sync and check history rollback on invalid block " in { + rollbackTest(100) + } + + "should sync and check history rollback on invalid block for fat blocks" in { + rollbackTest(5000) + } + + "sync when apply headers and payloads separately" in { + val blockQty = 10 + val history: History = dummyHistory(testSettings, withoutPow = true) + val (initialState, state, chain) = genChain(privKey, dir, testSettings, blockQty) + + val headers: Seq[PersistentModifier] = chain.map(_.header) + val payloads: Seq[PersistentModifier] = chain.map(_.payload) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + headers.foreach(historyApplicator ! ModifierFromRemote(_)) + + awaitCond(history.getBestHeaderHeight == blockQty - 1, timeout) + history.getBestHeaderHeight shouldBe blockQty - 1 + history.getBestHeader.map(h => Algos.encode(h.id)) shouldBe Some(Algos.encode(chain(blockQty - 1).header.id)) + + payloads.foreach(historyApplicator ! ModifierFromRemote(_)) + + expectMsg(timeout, FullBlockChainIsSynced()) + awaitCond(history.getBestBlockHeight == blockQty - 1, timeout) + checkBest(history, chain(blockQty - 1)) + } + + "sync and rollback headers for invalid payload" in { + + val blockQty = 10 + val history: History = dummyHistory(testSettings, withoutPow = true) + //generate invalid blocks begining from 6 blocks + val (initialState, state, chain) = genChain(privKey, dir, testSettings, blockQty, 100, Some(6)) + + val headers: Seq[PersistentModifier] = chain.map(_.header) + val payloads: Seq[PersistentModifier] = chain.map(_.payload) + + val historyApplicator: TestActorRef[HistoryApplicator] = + TestActorRef[HistoryApplicator]( + HistoryApplicator.props(history, testSettings, initialState, wallet, nodeViewHolder.ref, Some(influx.ref)) + ) + + system.eventStream.subscribe(self, classOf[FullBlockChainIsSynced]) + + headers.foreach(historyApplicator ! ModifierFromRemote(_)) + + awaitCond(history.getBestHeaderHeight == blockQty - 1, timeout, 100 millis, + s"history.getBestHeaderHeight ${history.getBestHeaderHeight} expected ${blockQty - 1}") + history.getBestHeaderHeight shouldBe blockQty - 1 + history.getBestHeader.map(h => Algos.encode(h.id)) shouldBe Some(Algos.encode(chain(blockQty - 1).header.id)) + + payloads.foreach(historyApplicator ! ModifierFromRemote(_)) + + expectMsg(timeout, FullBlockChainIsSynced()) + awaitCond(history.getBestBlockHeight == 4, timeout, 100 millis, + s"history.getBestBlockHeight ${history.getBestBlockHeight} expected 4") + + checkBest(history, chain(4)) + } + + } +} diff --git a/src/test/scala/encry/modifiers/history/ModifiersProtoTest.scala b/src/test/scala/encry/modifiers/history/ModifiersProtoTest.scala index ef8b9812e8..34c1345d84 100644 --- a/src/test/scala/encry/modifiers/history/ModifiersProtoTest.scala +++ b/src/test/scala/encry/modifiers/history/ModifiersProtoTest.scala @@ -5,25 +5,65 @@ import BoxesProto.BoxProtoMessage import HeaderProto.HeaderProtoMessage import PayloadProto.PayloadProtoMessage import TransactionProto.TransactionProtoMessage -import encry.modifiers.InstanceFactory +import encry.utils.TestEntityGenerator._ +import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519, Signature25519} import org.encryfoundation.common.modifiers.history._ import org.encryfoundation.common.modifiers.mempool.directive._ -import org.encryfoundation.common.modifiers.mempool.transaction.{Pay2PubKeyAddress, PubKeyLockedContract, Transaction, TransactionProtoSerializer} +import org.encryfoundation.common.modifiers.mempool.transaction._ +import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box._ import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.prismlang.core.wrapped.BoxedValue import org.scalatest.{Matchers, PropSpec} import scorex.crypto.signatures.PublicKey import scorex.utils.Random import scala.util.Try -class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { +class ModifiersProtoTest extends PropSpec with Matchers { //todo add tests for merkel root, sing + def universalTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: IndexedSeq[MonetaryBox], + amount: Long, + numOfOutputs: Int = 5): Transaction = { + val directives: IndexedSeq[Directive] = IndexedSeq( + AssetIssuingDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, amount), + DataDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, Random.randomBytes()), + ScriptedAssetDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, 10L, + Option(ADKey @@ Random.randomBytes())), + ScriptedAssetDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, 10L, + Option.empty[ADKey]), + TransferDirective(publicKey.address.address, 10L, Option(ADKey @@ Random.randomBytes())), + TransferDirective(publicKey.address.address, 10L, Option.empty[ADKey]) + ) + + val pubKey: PublicKey25519 = publicKey + + val uInputs: IndexedSeq[Input] = useOutputs + .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) + .toIndexedSeq + + val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) + + val newDirectives: IndexedSeq[Directive] = + if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => + TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives + else directives + + val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) + + val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) + + uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) + } + property("AssetIssuingDirective should be serialized correctly") { val assetIssuingDirective: AssetIssuingDirective = - AssetIssuingDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, 1000L) + AssetIssuingDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, 1000L) val assetIssuingDirectiveToProto: TransactionProtoMessage.DirectiveProtoMessage = assetIssuingDirective.toDirectiveProto val assetIssuingDirectiveFromProto: Option[AssetIssuingDirective] = AssetIssuingDirectiveProtoSerializer.fromProto(assetIssuingDirectiveToProto) @@ -34,7 +74,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { property("DataDirective should be serialized correctly") { val dataDirective: DataDirective = - DataDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, Random.randomBytes()) + DataDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, Random.randomBytes()) val dataDirectiveToProto: TransactionProtoMessage.DirectiveProtoMessage = dataDirective.toDirectiveProto val dataDirectiveFromProto = DataDirectiveProtoSerializer.fromProto(dataDirectiveToProto) dataDirectiveFromProto.isDefined shouldBe true @@ -44,7 +84,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { property("ScriptedAssetDirective should be serialized correctly") { val scriptedAssetDirectiveWithTokenId: ScriptedAssetDirective = - ScriptedAssetDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, 10L, + ScriptedAssetDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, 10L, Option(ADKey @@ Random.randomBytes())) val scriptedAssetDirectiveWithTokenIdToProto: TransactionProtoMessage.DirectiveProtoMessage = scriptedAssetDirectiveWithTokenId.toDirectiveProto @@ -56,7 +96,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { scriptedAssetDirectiveWithTokenId.tokenIdOpt.get.sameElements(scriptedAssetDirectiveWithTokenIdFromProto.get.tokenIdOpt.get) val scriptedAssetDirectiveWithoutTokenId: ScriptedAssetDirective = - ScriptedAssetDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, 10L, + ScriptedAssetDirective(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash, 10L, Option.empty[ADKey]) val scriptedAssetDirectiveWithoutTokenIdToProto: TransactionProtoMessage.DirectiveProtoMessage = scriptedAssetDirectiveWithoutTokenId.toDirectiveProto @@ -70,7 +110,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { property("TransferDirective should be serialized correctly") { val transferDirectiveWithTokenId: TransferDirective = - TransferDirective(privKey.publicImage.address.address, 10L, Option(ADKey @@ Random.randomBytes())) + TransferDirective(publicKey.address.address, 10L, Option(ADKey @@ Random.randomBytes())) val transferDirectiveDirectiveWithTokenIdToProto: TransactionProtoMessage.DirectiveProtoMessage = transferDirectiveWithTokenId.toDirectiveProto val transferDirectiveWithTokenIdFromProto: Option[TransferDirective] = @@ -81,7 +121,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { transferDirectiveWithTokenId.tokenIdOpt.get.sameElements(transferDirectiveWithTokenIdFromProto.get.tokenIdOpt.get) val transferDirectiveWithoutTokenId: TransferDirective = - TransferDirective(privKey.publicImage.address.address, 10L, Option.empty[ADKey]) + TransferDirective(publicKey.address.address, 10L, Option.empty[ADKey]) val transferDirectiveWithoutTokenIdToProto: TransactionProtoMessage.DirectiveProtoMessage = transferDirectiveWithoutTokenId.toDirectiveProto val transferDirectiveWithoutTokenIdFromProto: Option[TransferDirective] = @@ -93,7 +133,7 @@ class ModifiersProtoTest extends PropSpec with Matchers with InstanceFactory { } property("Transaction should be serialized correctly") { - val boxes: Seq[AssetBox] = (0 to 10).map(_ => genAssetBox(privKey.publicImage.address.address)) + val boxes: Seq[AssetBox] = (0 to 10).map(_ => genAssetBox(publicKey.address.address)) val transaction: Transaction = universalTransactionScratch(privKey, 10, 10, boxes.toIndexedSeq, 10, 1) val transactionToProto: TransactionProtoMessage = transaction.toTransactionProto diff --git a/src/test/scala/encry/modifiers/mempool/TransactionSerializerTest.scala b/src/test/scala/encry/modifiers/mempool/TransactionSerializerTest.scala index 0c7b303ba1..27659f6a1f 100755 --- a/src/test/scala/encry/modifiers/mempool/TransactionSerializerTest.scala +++ b/src/test/scala/encry/modifiers/mempool/TransactionSerializerTest.scala @@ -1,10 +1,10 @@ package encry.modifiers.mempool -import encry.modifiers.InstanceFactory import org.encryfoundation.common.modifiers.mempool.transaction.TransactionSerializer import org.scalatest.FunSuite +import encry.utils.TestEntityGenerator.{paymentTransactionDynamic, coinbaseTransaction} -class TransactionSerializerTest extends FunSuite with InstanceFactory { +class TransactionSerializerTest extends FunSuite { test("toBytes & parseBytes (Transfer)") { @@ -21,7 +21,7 @@ class TransactionSerializerTest extends FunSuite with InstanceFactory { test("toBytes & parseBytes (Coinbase)") { - val tx = coinbaseTransaction + val tx = coinbaseTransaction(0) val txSerialized = tx.bytes diff --git a/src/test/scala/encry/modifiers/mempool/TransactionSpec.scala b/src/test/scala/encry/modifiers/mempool/TransactionSpec.scala index a962d24daa..9573e3c6a7 100755 --- a/src/test/scala/encry/modifiers/mempool/TransactionSpec.scala +++ b/src/test/scala/encry/modifiers/mempool/TransactionSpec.scala @@ -1,17 +1,16 @@ package encry.modifiers.mempool -import encry.modifiers.InstanceFactory +import encry.utils.Utils.randomAddress +import encry.utils.TestEntityGenerator._ import encry.utils.TestHelper import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.AssetBox import org.scalatest.{Matchers, PropSpec} -class TransactionSpec extends PropSpec with Matchers with InstanceFactory { +class TransactionSpec extends PropSpec with Matchers { private val txValid = paymentTransactionValid - private val txInvalid = paymentTransactionInvalid - property("semanticValidity of valid tx") { txValid.semanticValidity.isSuccess shouldBe true @@ -22,7 +21,7 @@ class TransactionSpec extends PropSpec with Matchers with InstanceFactory { val tx: Transaction = { val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(TestHelper.genAssetBox(publicKey.address.address)) - TransactionFactory.defaultPaymentTransactionScratch(secret, 100, timestamp, useBoxes ++ useBoxes ++ useBoxes, + TransactionFactory.defaultPaymentTransactionScratch(privKey, 100, System.currentTimeMillis(), useBoxes ++ useBoxes ++ useBoxes, randomAddress, TestHelper.Props.txAmount) } @@ -34,7 +33,7 @@ class TransactionSpec extends PropSpec with Matchers with InstanceFactory { val tx: Transaction = { val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(TestHelper.genAssetBox(publicKey.address.address)) - TransactionFactory.defaultPaymentTransactionScratch(secret, -100, timestamp, useBoxes, + TransactionFactory.defaultPaymentTransactionScratch(privKey, -100, System.currentTimeMillis(), useBoxes, randomAddress, TestHelper.Props.txAmount) } @@ -46,7 +45,7 @@ class TransactionSpec extends PropSpec with Matchers with InstanceFactory { val tx: Transaction = { val useBoxes: IndexedSeq[AssetBox] = IndexedSeq.empty - TransactionFactory.defaultPaymentTransactionScratch(secret, -100, timestamp, useBoxes, + TransactionFactory.defaultPaymentTransactionScratch(privKey, -100, System.currentTimeMillis(), useBoxes, randomAddress, TestHelper.Props.txAmount) } @@ -59,7 +58,7 @@ class TransactionSpec extends PropSpec with Matchers with InstanceFactory { val useBoxes: IndexedSeq[AssetBox] = (0 to Short.MaxValue + 10) .foldLeft(IndexedSeq.empty[AssetBox]) { case (acc, _) => acc :+ TestHelper.genAssetBox(publicKey.address.address) } - TransactionFactory.defaultPaymentTransactionScratch(secret, 100, timestamp, useBoxes, + TransactionFactory.defaultPaymentTransactionScratch(privKey, 100, System.currentTimeMillis(), useBoxes, randomAddress, TestHelper.Props.txAmount) } diff --git a/src/test/scala/encry/modifiers/state/AssetBoxSerializerTest.scala b/src/test/scala/encry/modifiers/state/AssetBoxSerializerTest.scala index 094f52c111..3346afae96 100644 --- a/src/test/scala/encry/modifiers/state/AssetBoxSerializerTest.scala +++ b/src/test/scala/encry/modifiers/state/AssetBoxSerializerTest.scala @@ -1,14 +1,17 @@ package encry.modifiers.state -import encry.modifiers.InstanceFactory -import org.encryfoundation.common.modifiers.state.box.AssetBoxSerializer +import encry.utils.Keys +import org.encryfoundation.common.modifiers.state.box.{AssetBox, AssetBoxSerializer, EncryProposition} import org.scalatest.FunSuite -class AssetBoxSerializerTest extends FunSuite with InstanceFactory { +class AssetBoxSerializerTest extends FunSuite with Keys { + + lazy val assetBoxI: AssetBox = AssetBox(EncryProposition.pubKeyLocked(publicKey.pubKeyBytes), 999L, 100000L) + lazy val openAssetBoxI: AssetBox = AssetBox(EncryProposition.open, 999L, 100000L) test("toBytes & parseBytes") { - val bx = AssetBoxI + val bx = assetBoxI val bxSerialized = bx.bytes @@ -21,7 +24,7 @@ class AssetBoxSerializerTest extends FunSuite with InstanceFactory { test("toBytes & parseBytes (OpenProposition)") { - val bx = OpenAssetBoxI + val bx = openAssetBoxI val bxSerialized = bx.bytes diff --git a/src/test/scala/encry/modifiers/state/Keys.scala b/src/test/scala/encry/modifiers/state/Keys.scala deleted file mode 100644 index a61be652e9..0000000000 --- a/src/test/scala/encry/modifiers/state/Keys.scala +++ /dev/null @@ -1,12 +0,0 @@ -package encry.modifiers.state - -import encry.utils.TestHelper -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} - -trait Keys { - - val secrets: Seq[PrivateKey25519] = TestHelper.genKeys(TestHelper.Props.keysQty) - - val secret: PrivateKey25519 = secrets.head - val publicKey: PublicKey25519 = secret.publicImage -} diff --git a/src/test/scala/encry/network/BlackListTests.scala b/src/test/scala/encry/network/BlackListTests.scala index b90463a9ca..3b303e319c 100644 --- a/src/test/scala/encry/network/BlackListTests.scala +++ b/src/test/scala/encry/network/BlackListTests.scala @@ -4,7 +4,6 @@ import java.net.{InetAddress, InetSocketAddress} import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} -import encry.modifiers.InstanceFactory import encry.network.BlackList.BanReason._ import encry.network.PeerConnectionHandler.{ConnectedPeer, Outgoing} import encry.network.PeerConnectionHandler.ReceivableMessages.CloseConnection @@ -12,12 +11,12 @@ import encry.network.PeersKeeper.BanPeer import encry.settings.TestNetSettings import org.encryfoundation.common.network.BasicMessagesRepo.Handshake import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.Utils.protocolToBytes import scala.concurrent.duration._ class BlackListTests extends WordSpecLike with Matchers with BeforeAndAfterAll - with InstanceFactory with OneInstancePerTest with TestNetSettings { @@ -26,11 +25,11 @@ class BlackListTests extends WordSpecLike override def afterAll(): Unit = system.terminate() val knowPeersSettings = testNetSettings.copy( - network = settings.network.copy( + network = testNetSettings.network.copy( knownPeers = Seq(new InetSocketAddress("172.16.11.11", 9001)), connectOnlyWithKnownPeers = Some(true) ), - blackList = settings.blackList.copy( + blackList = testNetSettings.blackList.copy( banTime = 2 seconds, cleanupTime = 3 seconds )) diff --git a/src/test/scala/encry/network/ConnectWithNewPeerTests.scala b/src/test/scala/encry/network/ConnectWithNewPeerTests.scala index d64de7aa11..42546527b7 100644 --- a/src/test/scala/encry/network/ConnectWithNewPeerTests.scala +++ b/src/test/scala/encry/network/ConnectWithNewPeerTests.scala @@ -4,7 +4,6 @@ import java.net.InetSocketAddress import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} -import encry.modifiers.InstanceFactory import encry.network.BlackList.BanReason._ import encry.network.NetworkController.ReceivableMessages.DataFromPeer import encry.network.PeerConnectionHandler.{ConnectedPeer, Incoming, Outgoing} @@ -12,12 +11,12 @@ import encry.network.PeersKeeper._ import encry.settings.TestNetSettings import org.encryfoundation.common.network.BasicMessagesRepo.{Handshake, PeersNetworkMessage} import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.Utils.protocolToBytes import scala.concurrent.duration._ class ConnectWithNewPeerTests extends WordSpecLike with Matchers with BeforeAndAfterAll - with InstanceFactory with OneInstancePerTest with TestNetSettings { diff --git a/src/test/scala/encry/network/ConnectedPeersCollectionsTests.scala b/src/test/scala/encry/network/ConnectedPeersCollectionsTests.scala index 76abcb9bf6..be0a029d69 100644 --- a/src/test/scala/encry/network/ConnectedPeersCollectionsTests.scala +++ b/src/test/scala/encry/network/ConnectedPeersCollectionsTests.scala @@ -5,17 +5,16 @@ import java.net.InetSocketAddress import akka.actor.ActorSystem import akka.testkit.TestProbe import encry.consensus.HistoryConsensus.{Fork, Older, Unknown, Younger} -import encry.modifiers.InstanceFactory import encry.network.ConnectedPeersCollection.PeerInfo import encry.network.PeerConnectionHandler.{ConnectedPeer, Outgoing} import encry.network.PrioritiesCalculator.PeersPriorityStatus.PeersPriorityStatus._ import encry.settings.TestNetSettings import org.encryfoundation.common.network.BasicMessagesRepo.Handshake import org.scalatest.{Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.Utils.protocolToBytes class ConnectedPeersCollectionsTests extends WordSpecLike with Matchers - with InstanceFactory with OneInstancePerTest with TestNetSettings { diff --git a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerPriorityTests.scala b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerPriorityTests.scala index 410373e2b3..c2c6e3265f 100644 --- a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerPriorityTests.scala +++ b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerPriorityTests.scala @@ -2,12 +2,11 @@ package encry.network.DeliveryManagerTests import java.net.InetSocketAddress -import encry.network.DeliveryManagerTests.DMUtils.{createPeer, generateBlocks, initialiseDeliveryManager} +import encry.network.DeliveryManagerTests.DeliveryManagerUtils.{createPeer, generateBlocks, initialiseDeliveryManager} import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestKit} import encry.consensus.HistoryConsensus import encry.consensus.HistoryConsensus.{Equal, Older, Younger} -import encry.modifiers.InstanceFactory import encry.network.DeliveryManager import encry.network.NetworkController.ReceivableMessages.DataFromPeer import encry.network.NodeViewSynchronizer.ReceivableMessages.RequestFromLocal @@ -20,11 +19,11 @@ import org.encryfoundation.common.modifiers.history.{Block, Header, HeaderProtoS import org.encryfoundation.common.network.BasicMessagesRepo.ModifiersNetworkMessage import org.encryfoundation.common.utils.TaggedTypes.ModifierId import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.HistoryGenerator.dummyHistory class DeliveryManagerPriorityTests extends WordSpecLike with BeforeAndAfterAll with Matchers - with InstanceFactory with OneInstancePerTest with TestNetSettings { @@ -45,7 +44,7 @@ class DeliveryManagerPriorityTests extends WordSpecLike val (_: InetSocketAddress, cp7: ConnectedPeer) = createPeer(9007, "172.16.13.16", testNetSettings) val (_: InetSocketAddress, cp8: ConnectedPeer) = createPeer(9008, "172.16.13.17", testNetSettings) val (_: InetSocketAddress, cp9: ConnectedPeer) = createPeer(9009, "172.16.13.18", testNetSettings) - val blocks: List[Block] = generateBlocks(10, generateDummyHistory(testNetSettings))._2 + val blocks: List[Block] = generateBlocks(10, dummyHistory(testNetSettings))._2 val headersIds: List[ModifierId] = blocks.map(_.header.id) (deliveryManager, cp1, cp2, cp3, cp4, cp5, cp6,cp7, cp8, cp9, blocks, headersIds) } diff --git a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerReRequestModifiesSpec.scala b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerReRequestModifiesSpec.scala index 97acbe4976..a3f4906de2 100644 --- a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerReRequestModifiesSpec.scala +++ b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerReRequestModifiesSpec.scala @@ -5,9 +5,8 @@ import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} import encry.consensus.HistoryConsensus import encry.consensus.HistoryConsensus.Older -import encry.modifiers.InstanceFactory import encry.network.DeliveryManager -import encry.network.DeliveryManagerTests.DMUtils._ +import encry.network.DeliveryManagerTests.DeliveryManagerUtils._ import encry.network.NetworkController.ReceivableMessages.DataFromPeer import encry.network.NodeViewSynchronizer.ReceivableMessages._ import encry.network.PeerConnectionHandler.{ConnectedPeer, Incoming} @@ -23,11 +22,13 @@ import org.encryfoundation.common.utils.TaggedTypes.ModifierId import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} import scala.concurrent.duration._ import scala.collection.mutable.WrappedArray +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.TestEntityGenerator._ +import encry.utils.Utils.protocolToBytes class DeliveryManagerReRequestModifiesSpec extends WordSpecLike with BeforeAndAfterAll with Matchers - with InstanceFactory with OneInstancePerTest with TestNetSettings { @@ -42,7 +43,7 @@ class DeliveryManagerReRequestModifiesSpec extends WordSpecLike val (_: InetSocketAddress, cp1: ConnectedPeer) = createPeer(9001, "172.16.13.10", testNetSettings) val (_: InetSocketAddress, cp2: ConnectedPeer) = createPeer(9002, "172.16.13.11", testNetSettings) val (_: InetSocketAddress, cp3: ConnectedPeer) = createPeer(9003, "172.16.13.12", testNetSettings) - val blocks: List[Block] = generateBlocks(10, generateDummyHistory(testNetSettings))._2 + val blocks: List[Block] = generateBlocks(10, dummyHistory(testNetSettings))._2 val headersIds: List[ModifierId] = blocks.map(_.header.id) val headersAsKey = headersIds.map(toKey) (deliveryManager, cp1, cp2, cp3, blocks, headersIds, headersAsKey, history) diff --git a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerRequestModifiesSpec.scala b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerRequestModifiesSpec.scala index 7bdcf9eafb..0a05a6af37 100644 --- a/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerRequestModifiesSpec.scala +++ b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerRequestModifiesSpec.scala @@ -5,7 +5,6 @@ import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestKit, TestProbe} import encry.consensus.HistoryConsensus import encry.consensus.HistoryConsensus.{Fork, Older, Younger} -import encry.modifiers.InstanceFactory import encry.network.DeliveryManager import encry.network.NetworkController.ReceivableMessages.DataFromPeer import encry.network.NodeViewSynchronizer.ReceivableMessages.RequestFromLocal @@ -13,7 +12,7 @@ import encry.network.PeerConnectionHandler.{ConnectedPeer, Incoming} import encry.settings.TestNetSettings import encry.view.actors.NodeViewHolder.DownloadRequest import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} -import encry.network.DeliveryManagerTests.DMUtils._ +import encry.network.DeliveryManagerTests.DeliveryManagerUtils._ import encry.network.PeersKeeper.UpdatedPeersCollection import encry.network.PrioritiesCalculator.PeersPriorityStatus.PeersPriorityStatus import encry.network.PrioritiesCalculator.PeersPriorityStatus.PeersPriorityStatus._ @@ -23,10 +22,13 @@ import org.encryfoundation.common.network.BasicMessagesRepo.{Handshake, Modifier import org.encryfoundation.common.network.SyncInfo import org.encryfoundation.common.utils.TaggedTypes.ModifierId import scala.collection.mutable.WrappedArray +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.Utils.protocolToBytes +import encry.utils.TestEntityGenerator._ -class DeliveryManagerRequestModifiesSpec extends WordSpecLike with BeforeAndAfterAll +class DeliveryManagerRequestModifiesSpec extends WordSpecLike + with BeforeAndAfterAll with Matchers - with InstanceFactory with OneInstancePerTest with TestNetSettings { @@ -40,7 +42,7 @@ class DeliveryManagerRequestModifiesSpec extends WordSpecLike with BeforeAndAfte val (_: InetSocketAddress, cp1: ConnectedPeer) = createPeer(9001, "172.16.13.10", testNetSettings) val (_: InetSocketAddress, cp2: ConnectedPeer) = createPeer(9002, "172.16.13.11", testNetSettings) val (_: InetSocketAddress, cp3: ConnectedPeer) = createPeer(9003, "172.16.13.12", testNetSettings) - val blocks: List[Block] = generateBlocks(10, generateDummyHistory(testNetSettings))._2 + val blocks: List[Block] = generateBlocks(10, dummyHistory(testNetSettings))._2 val headersIds: List[ModifierId] = blocks.map(_.header.id) val headersAsKey = headersIds.map(toKey) (deliveryManager, cp1, cp2, cp3, blocks, headersIds, headersAsKey) diff --git a/src/test/scala/encry/network/DeliveryManagerTests/DMUtils.scala b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerUtils.scala similarity index 92% rename from src/test/scala/encry/network/DeliveryManagerTests/DMUtils.scala rename to src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerUtils.scala index 288b3b276b..990955a376 100644 --- a/src/test/scala/encry/network/DeliveryManagerTests/DMUtils.scala +++ b/src/test/scala/encry/network/DeliveryManagerTests/DeliveryManagerUtils.scala @@ -4,7 +4,6 @@ import java.net.InetSocketAddress import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} import encry.local.miner.Miner.{DisableMining, StartMining} -import encry.modifiers.InstanceFactory import encry.network.DeliveryManager import encry.network.DeliveryManager.FullBlockChainIsSynced import encry.network.NodeViewSynchronizer.ReceivableMessages.UpdatedHistory @@ -14,16 +13,19 @@ import encry.view.history.History import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.network.BasicMessagesRepo.Handshake import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.TestEntityGenerator._ import scala.collection.mutable import scala.collection.mutable.WrappedArray +import encry.utils.Utils.protocolToBytes -object DMUtils extends InstanceFactory { +object DeliveryManagerUtils { def initialiseDeliveryManager(isBlockChainSynced: Boolean, isMining: Boolean, settings: EncryAppSettings) (implicit actorSystem: ActorSystem): (TestActorRef[DeliveryManager], History) = { - val history: History = generateDummyHistory(settings) + val history: History = dummyHistory(settings) val deliveryManager: TestActorRef[DeliveryManager] = TestActorRef[DeliveryManager](DeliveryManager .props(None, TestProbe().ref, TestProbe().ref, TestProbe().ref, TestProbe().ref, TestProbe().ref, settings)) diff --git a/src/test/scala/encry/network/DownloadedModifiersValidatorTests.scala b/src/test/scala/encry/network/DownloadedModifiersValidatorTests.scala index a3e9f5677b..ca8e05a57c 100644 --- a/src/test/scala/encry/network/DownloadedModifiersValidatorTests.scala +++ b/src/test/scala/encry/network/DownloadedModifiersValidatorTests.scala @@ -4,7 +4,6 @@ import java.net.InetSocketAddress import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} -import encry.modifiers.InstanceFactory import encry.network.BlackList.BanReason._ import encry.network.DownloadedModifiersValidator.{InvalidModifier, ModifiersForValidating} import encry.network.NodeViewSynchronizer.ReceivableMessages.UpdatedHistory @@ -20,11 +19,13 @@ import org.encryfoundation.common.utils.TaggedTypes.ModifierId import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} import scorex.crypto.hash.Digest32 import scorex.utils.Random +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.Utils.protocolToBytes +import encry.utils.TestEntityGenerator._ class DownloadedModifiersValidatorTests extends WordSpecLike with Matchers with BeforeAndAfterAll - with InstanceFactory with OneInstancePerTest with TestNetSettings { @@ -43,7 +44,7 @@ class DownloadedModifiersValidatorTests extends WordSpecLike val downloadedModifiersValidator = TestActorRef[DownloadedModifiersValidator](DownloadedModifiersValidator.props( testNetSettings.constants.ModifierIdSize, nodeViewHolder.ref, peersKeeper.ref, nodeViewSync.ref, mempool.ref, None) ) - val history: History = generateDummyHistory(testNetSettings) + val history: History = dummyHistory(testNetSettings) val address: InetSocketAddress = new InetSocketAddress("0.0.0.0", 9000) val peerHandler: TestProbe = TestProbe() @@ -111,7 +112,7 @@ class DownloadedModifiersValidatorTests extends WordSpecLike val downloadedModifiersValidator = TestActorRef[DownloadedModifiersValidator](DownloadedModifiersValidator.props( testNetSettings.constants.ModifierIdSize, nodeViewHolder.ref, peersKeeper.ref, nodeViewSync.ref, mempool.ref, None) ) - val history: History = generateDummyHistory(testNetSettings) + val history: History = dummyHistory(testNetSettings) val historyWith10Blocks = (0 until 10).foldLeft(history, Seq.empty[Block]) { case ((prevHistory, blocks), _) => @@ -121,7 +122,7 @@ class DownloadedModifiersValidatorTests extends WordSpecLike (prevHistory.reportModifierIsValid(block), blocks :+ block) } - val payload = Payload(ModifierId @@ scorex.utils.Random.randomBytes(), Seq(coinbaseTransaction)) + val payload = Payload(ModifierId @@ scorex.utils.Random.randomBytes(), Seq(coinbaseTransaction(0))) nodeViewSync.send(downloadedModifiersValidator, UpdatedHistory(historyWith10Blocks._1)) diff --git a/src/test/scala/encry/network/NetworkMessagesProtoTest/BasicNetworkMessagesProtoTest.scala b/src/test/scala/encry/network/NetworkMessagesProtoTest/BasicNetworkMessagesProtoTest.scala index 7746922350..49a6d02ee6 100644 --- a/src/test/scala/encry/network/NetworkMessagesProtoTest/BasicNetworkMessagesProtoTest.scala +++ b/src/test/scala/encry/network/NetworkMessagesProtoTest/BasicNetworkMessagesProtoTest.scala @@ -4,21 +4,24 @@ import java.net.InetSocketAddress import NetworkMessagesProto.GeneralizedNetworkProtoMessage import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage -import encry.EncryApp -import encry.modifiers.InstanceFactory -import encry.settings.{EncryAppSettings, Settings} +import encry.settings. Settings import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.network.BasicMessagesRepo._ import org.encryfoundation.common.network.{BasicMessagesRepo, SyncInfo} import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} import org.scalatest.{Matchers, PropSpec} +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.Utils.protocolToBytes +import encry.utils.TestEntityGenerator._ import scala.util.Try -class BasicNetworkMessagesProtoTest extends PropSpec with Matchers with InstanceFactory with Settings { +class BasicNetworkMessagesProtoTest extends PropSpec + with Matchers + with Settings { - val testedBlocks: Vector[Block] = (0 until 10).foldLeft(generateDummyHistory(settings), Vector.empty[Block]) { + val testedBlocks: Vector[Block] = (0 until 10).foldLeft(dummyHistory(settings), Vector.empty[Block]) { case ((prevHistory, blocks), _) => val block: Block = generateNextBlock(prevHistory) prevHistory.append(block.header) diff --git a/src/test/scala/encry/utils/ChainGenerator.scala b/src/test/scala/encry/utils/ChainGenerator.scala new file mode 100644 index 0000000000..fd0b901d74 --- /dev/null +++ b/src/test/scala/encry/utils/ChainGenerator.scala @@ -0,0 +1,138 @@ +package encry.utils + +import java.io.File + +import akka.actor.ActorRef +import encry.modifiers.mempool.TransactionFactory +import encry.settings.EncryAppSettings +import encry.storage.VersionalStorage +import encry.storage.VersionalStorage.{StorageKey, StorageType, StorageValue, StorageVersion} +import encry.storage.iodb.versionalIODB.IODBWrapper +import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, VLDBWrapper, VersionalLevelDBCompanion} +import encry.view.state.{BoxHolder, UtxoState} +import io.iohk.iodb.LSMStore +import org.encryfoundation.common.crypto.equihash.EquihashSolution +import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} +import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} +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 +import org.encryfoundation.common.utils.TaggedTypes.{Difficulty, Height} +import org.iq80.leveldb.Options +import scorex.crypto.hash.Digest32 +import encry.utils.Utils.randomAddress + +import scala.collection.Seq +import scala.util.Random + +object ChainGenerator { + + def genChain(privKey: PrivateKey25519, dir: File, settings: EncryAppSettings, blockQty: Int, transPerBlock: Int = 100, + genInvalidBlockFrom: Option[Int] = None): (UtxoState, UtxoState, List[Block]) = { + + assert(blockQty >= 2, "chain at least 2 blocks") + + val genesisBlock: Block = genGenesisBlock(privKey.publicImage, settings.constants.InitialEmissionAmount, + settings.constants.InitialDifficulty, Height @@ 0) + + val state: UtxoState = utxoFromBoxHolder(BoxHolder(Seq.empty), dir, settings, VersionalStorage.LevelDB) + val genesisState = state.applyModifier(genesisBlock).right.get + + val (forkBlock, forkBoxes) = + genForkBlock(privKey, settings.constants.InitialEmissionAmount, transPerBlock, genesisBlock, + genesisBlock.payload.txs.head.newBoxes.map(_.asInstanceOf[AssetBox]).head, randomAddress) + val forkState = genesisState.applyModifier(forkBlock).right.get + + val (chain, _, newState, _) = + (2 until blockQty).foldLeft(List(genesisBlock, forkBlock), forkBlock, forkState, forkBoxes) { + case ((blocks, blockL, stateL, boxes), height) => + Thread.sleep(1)//timestamp 1569926861646 less than parent's 1569926861646 + val invalid = genInvalidBlockFrom.exists(height + 1 >= _) + val (newBlock, newBoxes) = genNextBlockForState(privKey, blockL, settings.constants.InitialEmissionAmount, stateL, boxes, + randomAddress, invalid) + val newState = if (invalid) stateL else stateL.applyModifier(newBlock).right.get + (blocks :+ newBlock, newBlock, newState, newBoxes) + } + (state, newState, chain) + } + + def utxoFromBoxHolder(bh: BoxHolder, dir: File, settings: EncryAppSettings, + storageType: StorageType): UtxoState = { + val storage = settings.storage.state match { + case VersionalStorage.IODB => + IODBWrapper(new LSMStore(dir, keepVersions = settings.constants.DefaultKeepVersions)) + case VersionalStorage.LevelDB => + val levelDBInit = LevelDbFactory.factory.open(dir, new Options) + VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, settings.levelDB, keySize = 32)) + } + + storage.insert( + StorageVersion @@ Array.fill(32)(0: Byte), + bh.boxes.values.map(bx => (StorageKey !@@ bx.id, StorageValue @@ bx.bytes)).toList + ) + + new UtxoState(storage, settings.constants) + } + + def genGenesisBlock(pubKey: PublicKey25519, initialEmissionAmount: Int, initialDifficulty: Difficulty, genesisHeight: Height): Block = { + val timestamp = System.currentTimeMillis() + val coinbaseTrans = TransactionFactory.coinbaseTransactionScratch( + pubKey, + timestamp, + initialEmissionAmount, + amount = 0L, + height = genesisHeight + ) + + val txs: Seq[Transaction] = Seq(coinbaseTrans) + val txsRoot: Digest32 = Payload.rootHash(txs.map(_.id)) + + val header = Header( + 1.toByte, + Header.GenesisParentId, + txsRoot, + timestamp, + genesisHeight, + Random.nextLong(), + initialDifficulty, + EquihashSolution(Seq(1, 3)) + ) + Block(header, Payload(header.id, txs)) + } + + def genForkBlock(privKey: PrivateKey25519, initialEmissionAmount: Int, boxQty: Int, prevBlock: Block, box: AssetBox, recipient: Address, + addDiff: Difficulty = Difficulty @@ BigInt(0)): (Block, Seq[AssetBox]) = { + val timestamp = System.currentTimeMillis() + + val transactions: Seq[Transaction] = Seq( + TransactionFactory.paymentTransactionWithMultipleOutputs(privKey, 1L, timestamp, IndexedSeq(box), recipient, 1L, None, boxQty), + TransactionFactory.coinbaseTransactionScratch(privKey.publicImage, System.currentTimeMillis(), initialEmissionAmount, + 1L, Height @@ (prevBlock.header.height + 1)) + ) + + val header = Header(1.toByte, prevBlock.id, Payload.rootHash(transactions.map(_.id)), timestamp, + prevBlock.header.height + 1, Random.nextLong(), Difficulty @@ (BigInt(1) + addDiff), EquihashSolution(Seq(1, 3))) + + (Block(header, Payload(header.id, transactions)), transactions.head.newBoxes.tail.map(_.asInstanceOf[AssetBox]).toSeq) + } + + def genNextBlockForState(privKey: PrivateKey25519, prevBlock: Block, initialEmissionAmount: Int, state: UtxoState, boxes: Seq[AssetBox], + recipient: Address, invalid: Boolean = false, + addDiff: Difficulty = Difficulty @@ BigInt(0)): (Block, Seq[AssetBox]) = { + val timestamp = System.currentTimeMillis() + + val amount = if (invalid) boxes.map(_.amount).sum + 1L else 1L + + val transactions: Seq[Transaction] = + boxes.map(b => TransactionFactory.defaultPaymentTransactionScratch(privKey, 1L, timestamp, IndexedSeq(b), recipient, amount)) :+ + TransactionFactory.coinbaseTransactionScratch(privKey.publicImage, System.currentTimeMillis(), initialEmissionAmount, + 1L, Height @@ (prevBlock.header.height + 1)) + + val header = Header(1.toByte, prevBlock.id, Payload.rootHash(transactions.map(_.id)), timestamp, + prevBlock.header.height + 1, Random.nextLong(), Difficulty @@ (BigInt(1) + addDiff), EquihashSolution(Seq(1, 3))) + + val newBoxes = transactions.filter(_.newBoxes.size > 1).map(_.newBoxes.toSeq(1).asInstanceOf[AssetBox]) + (Block(header, Payload(header.id, transactions)), newBoxes) + } + +} \ No newline at end of file diff --git a/src/test/scala/encry/utils/EncryGenerator.scala b/src/test/scala/encry/utils/EncryGenerator.scala deleted file mode 100644 index d102e45619..0000000000 --- a/src/test/scala/encry/utils/EncryGenerator.scala +++ /dev/null @@ -1,340 +0,0 @@ -package encry.utils - -import encry.modifiers.mempool.TransactionFactory -import encry.settings.Settings -import encry.utils.TestHelper.Props -import org.encryfoundation.common.crypto.equihash.EquihashSolution -import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519, Signature25519} -import org.encryfoundation.common.modifiers.history.Header -import org.encryfoundation.common.modifiers.mempool.directive._ -import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address -import org.encryfoundation.common.modifiers.mempool.transaction._ -import org.encryfoundation.common.modifiers.state.box.Box.Amount -import org.encryfoundation.common.modifiers.state.box._ -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} -import org.encryfoundation.prismlang.core.wrapped.BoxedValue -import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} -import scorex.utils.Random - -import scala.util.{Random => ScRand} - -trait EncryGenerator extends Settings { - - val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" - val privKey: PrivateKey25519 = createPrivKey(Some(mnemonicKey)) - - - def createPrivKey(seed: Option[String]): PrivateKey25519 = { - val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( - Blake2b256.hash( - seed.map { - Mnemonic.seedFromMnemonic(_) - } - .getOrElse { - val phrase: String = Mnemonic.entropyToMnemonicCode(scorex.utils.Random.randomBytes(16)) - Mnemonic.seedFromMnemonic(phrase) - }) - ) - PrivateKey25519(privateKey, publicKey) - } - - def protocolToBytes(protocol: String): Array[Byte] = protocol.split("\\.").map(elem => elem.toByte) - - def timestamp: Long = System.currentTimeMillis() - - def randomAddress: Address = Pay2PubKeyAddress(PublicKey @@ Random.randomBytes()).address - - def genAssetBox(address: Address, amount: Amount = 100000L, tokenIdOpt: Option[ADKey] = None): AssetBox = - AssetBox(EncryProposition.addressLocked(address), ScRand.nextLong(), amount, tokenIdOpt) - - def generateDataBox(address: Address, nonce: Long, data: Array[Byte]): DataBox = - DataBox(EncryProposition.addressLocked(address), nonce, data) - - def generateTokenIssuingBox(address: Address, amount: Amount = 100000L, tokenIdOpt: ADKey): TokenIssuingBox = - TokenIssuingBox(EncryProposition.addressLocked(address), ScRand.nextLong(), amount, tokenIdOpt) - - def genTxOutputs(boxes: Traversable[EncryBaseBox]): IndexedSeq[ADKey] = - boxes.foldLeft(IndexedSeq[ADKey]()) { case (s, box) => - s :+ box.id - } - - def genValidAssetBoxes(secret: PrivateKey25519, amount: Amount, qty: Int): Seq[AssetBox] = - (0 to qty).foldLeft(IndexedSeq[AssetBox]()) { case (bxs, _) => - bxs :+ AssetBox(EncryProposition.pubKeyLocked(secret.publicKeyBytes), ScRand.nextLong(), amount) - } - - def genPrivKeys(qty: Int): Seq[PrivateKey25519] = (0 until qty).map { _ => - val keys: (PrivateKey, PublicKey) = Curve25519.createKeyPair(Random.randomBytes()) - PrivateKey25519(keys._1, keys._2) - } - - def genValidPaymentTxs(qty: Int): Seq[Transaction] = { - val keys: Seq[PrivateKey25519] = genPrivKeys(qty) - val now = System.currentTimeMillis() - - keys.map { k => - val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(genAssetBox(k.publicImage.address.address)) - TransactionFactory.defaultPaymentTransactionScratch(k, Props.txFee, - now + scala.util.Random.nextInt(5000), useBoxes, randomAddress, Props.boxValue) - } - } - - def genValidPaymentTxsToAddr(qty: Int, address: Address): Seq[Transaction] = { - val keys: Seq[PrivateKey25519] = genPrivKeys(qty) - keys.map { k => - val useBoxes = IndexedSeq(genAssetBox(address)) - TransactionFactory.defaultPaymentTransactionScratch(k, Props.txFee, - scala.util.Random.nextLong(), useBoxes, address, Props.boxValue) - } - } - - def genValidPaymentTxToAddrWithSpentBoxes(boxes: IndexedSeq[AssetBox], address: Address): Transaction = { - val key: PrivateKey25519 = genPrivKeys(1).head - TransactionFactory.defaultPaymentTransactionScratch(key, Props.txFee, - scala.util.Random.nextLong(), boxes, address, Props.boxValue) - } - - def genValidPaymentTxsToAddrWithDiffTokens(qty: Int, address: Address): Seq[Transaction] = { - val keys: Seq[PrivateKey25519] = genPrivKeys(qty) - val tokens: Seq[ADKey] = (0 until qty).foldLeft(Seq[ADKey]()) { - case (seq, _) => seq :+ (ADKey @@ Random.randomBytes()) - } - val pksZipTokens: Seq[(PrivateKey25519, ADKey)] = keys.zip(tokens) - val timestamp: Amount = System.currentTimeMillis() - pksZipTokens.map { k => - val useBoxes = IndexedSeq(genAssetBox(address, tokenIdOpt = Some(k._2))) - TransactionFactory.defaultPaymentTransactionScratch(k._1, Props.txFee, - timestamp, useBoxes, address, Props.boxValue, tokenIdOpt = Some(k._2)) - } - } - - def genSelfSpendingTxs(qty: Int): Seq[Transaction] = { - val keys: Seq[PrivateKey25519] = genPrivKeys(qty) - val timestamp: Amount = System.currentTimeMillis() - keys.foldLeft(Seq[Transaction]()) { (seq, key) => - val useBoxes: IndexedSeq[MonetaryBox] = if (seq.isEmpty) IndexedSeq(genAssetBox(key.publicImage.address.address)) - else seq.last.newBoxes.map(_.asInstanceOf[MonetaryBox]).toIndexedSeq - seq :+ TransactionFactory.defaultPaymentTransactionScratch(key, Props.txFee, - timestamp, useBoxes, randomAddress, Props.boxValue) - } - } - - def genInvalidPaymentTxs(qty: Int): Seq[Transaction] = { - val timestamp: Amount = System.currentTimeMillis() - genPrivKeys(qty).map { key => - val useBoxes: IndexedSeq[AssetBox] = - IndexedSeq(genAssetBox(PublicKey25519(PublicKey @@ Random.randomBytes(32)).address.address)) - TransactionFactory.defaultPaymentTransactionScratch(key, -100, timestamp, useBoxes, randomAddress, Props.boxValue) - } - } - - def genHeader: Header = { - val random = new scala.util.Random - Header( - 1.toByte, - ModifierId @@ Random.randomBytes(), - Digest32 @@ Random.randomBytes(), - Math.abs(random.nextLong()), - Math.abs(random.nextInt(10000)), - random.nextLong(), - settings.constants.InitialDifficulty, - EquihashSolution(Seq(1, 3)) - ) - } - - def genHeaderAtHeight(height: Int = 0, transactionsRoot: Digest32 = Digest32 @@ Random.randomBytes()): Header = { - val random = new scala.util.Random - Header( - 1.toByte, - ModifierId @@ Random.randomBytes(), - transactionsRoot, - Math.abs(random.nextLong()), - height, - random.nextLong(), - settings.constants.InitialDifficulty, - EquihashSolution(Seq(1, 3)) - ) - } - - def generatePaymentTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = defaultPaymentTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useBoxes = boxesLocal.take(numberOfInputs), - recipient = randomAddress, - amount = 10000, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), transactions :+ tx) - }._2 - - def generateDataTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int, - bytesQty: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = dataTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useOutputs = boxesLocal.take(numberOfInputs), - data = Random.randomBytes(bytesQty), - amount = 200L, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), tx +: transactions) - }._2 - - def generateAssetTransactions(boxes: IndexedSeq[AssetBox], - numberOfInputs: Int, - numberOfOutputs: Int): Vector[Transaction] = - (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { - case ((boxesLocal, transactions), _) => - val tx: Transaction = assetIssuingTransactionScratch( - privKey, - fee = 111, - timestamp = 11L, - useOutputs = boxesLocal.take(numberOfInputs), - amount = 200L, - numOfOutputs = numberOfOutputs - ) - (boxesLocal.drop(numberOfInputs), tx +: transactions) - }._2 - - def defaultPaymentTransactionScratch(privKey: PrivateKey25519, - fee: Amount, - timestamp: Long, - useBoxes: IndexedSeq[MonetaryBox], - recipient: Address, - amount: Amount, - tokenIdOpt: Option[ADKey] = None, - numOfOutputs: Int = 5): Transaction = { - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useBoxes - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useBoxes.map(_.amount).sum - (amount + fee) - - val directives: IndexedSeq[TransferDirective] = - if (change > 0) TransferDirective(recipient, amount, tokenIdOpt) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, tokenIdOpt)) - else IndexedSeq(TransferDirective(recipient, amount, tokenIdOpt)) - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, directives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def dataTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: IndexedSeq[MonetaryBox], - amount: Long, - data: Array[Byte], - numOfOutputs: Int = 5): Transaction = { - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useOutputs - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - - val directives: IndexedSeq[DataDirective] = - (0 until numOfOutputs).foldLeft(IndexedSeq.empty[DataDirective]) { case (directivesAll, _) => - directivesAll :+ DataDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, data) - } - - val newDirectives: IndexedSeq[Directive] = - if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives - else directives - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def assetIssuingTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: IndexedSeq[MonetaryBox], - amount: Long, - numOfOutputs: Int = 5): Transaction = { - val directives: IndexedSeq[AssetIssuingDirective] = - (0 until numOfOutputs).foldLeft(IndexedSeq.empty[AssetIssuingDirective]) { case (directivesAll, _) => - directivesAll :+ AssetIssuingDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, amount) - } - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useOutputs - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - - val newDirectives: IndexedSeq[Directive] = - if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives - else directives - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } - - def universalTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: IndexedSeq[MonetaryBox], - amount: Long, - numOfOutputs: Int = 5): Transaction = { - val directives: IndexedSeq[Directive] = IndexedSeq( - AssetIssuingDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, amount), - DataDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, Random.randomBytes()), - ScriptedAssetDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, 10L, - Option(ADKey @@ Random.randomBytes())), - ScriptedAssetDirective(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash, 10L, - Option.empty[ADKey]), - TransferDirective(privKey.publicImage.address.address, 10L, Option(ADKey @@ Random.randomBytes())), - TransferDirective(privKey.publicImage.address.address, 10L, Option.empty[ADKey]) - ) - - val pubKey: PublicKey25519 = privKey.publicImage - - val uInputs: IndexedSeq[Input] = useOutputs - .map(bx => Input.unsigned(bx.id, Right(PubKeyLockedContract(pubKey.pubKeyBytes)))) - .toIndexedSeq - - val change: Amount = useOutputs.map(_.amount).sum - (amount + fee) - - val newDirectives: IndexedSeq[Directive] = - if (change > 0) TransferDirective(pubKey.address.address, amount, None) +: (0 until numOfOutputs).map(_ => - TransferDirective(pubKey.address.address, change / numOfOutputs, None)) ++: directives - else directives - - val uTransaction: UnsignedTransaction = UnsignedTransaction(fee, timestamp, uInputs, newDirectives) - - val signature: Signature25519 = privKey.sign(uTransaction.messageToSign) - - uTransaction.toSigned(IndexedSeq.empty, Some(Proof(BoxedValue.Signature25519Value(signature.bytes.toList)))) - } -} diff --git a/src/test/scala/encry/utils/HistoryGenerator.scala b/src/test/scala/encry/utils/HistoryGenerator.scala new file mode 100644 index 0000000000..3e79ceb7d0 --- /dev/null +++ b/src/test/scala/encry/utils/HistoryGenerator.scala @@ -0,0 +1,47 @@ +package encry.utils + +import encry.consensus.EquihashPowScheme +import encry.crypto.equihash.EquihashValidationErrors +import encry.settings.EncryAppSettings +import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, VLDBWrapper, VersionalLevelDBCompanion} +import encry.view.history.History +import encry.view.history.storage.HistoryStorage +import io.iohk.iodb.LSMStore +import org.encryfoundation.common.modifiers.history.Header +import org.encryfoundation.common.utils.TaggedTypes.Height +import org.iq80.leveldb.Options + +object HistoryGenerator { + + def dummyHistory(historySettings: EncryAppSettings, withoutPow: Boolean = false): History = { + + val indexStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) + val objectsStore: LSMStore = new LSMStore(FileHelper.getRandomTempDir, keepVersions = 0) + val levelDBInit = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) + val vldbInit = VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, historySettings.levelDB)) + val storage: HistoryStorage = new HistoryStorage(vldbInit) + + val ntp: NetworkTimeProvider = new NetworkTimeProvider(historySettings.ntp) + + class EquihashPowSchemeWithoutValidateSolution(n: Char, k: Char, version: Byte, preGenesisHeight: Height, maxTarget: BigInt) + extends EquihashPowScheme(n: Char, k: Char, version: Byte, preGenesisHeight: Height, maxTarget: BigInt) { + override def verify(header: Header): Either[EquihashValidationErrors, Boolean] = Right(true) + } + + if (withoutPow) { + new History { + override val historyStorage: HistoryStorage = storage + override val timeProvider: NetworkTimeProvider = ntp + override val powScheme: EquihashPowScheme = + new EquihashPowSchemeWithoutValidateSolution(historySettings.constants.n, historySettings.constants.k, + historySettings.constants.Version, historySettings.constants.PreGenesisHeight, historySettings.constants.MaxTarget) + } + } else + new History { + override val historyStorage: HistoryStorage = storage + override val timeProvider: NetworkTimeProvider = ntp + } + + } + +} diff --git a/src/test/scala/encry/utils/Keys.scala b/src/test/scala/encry/utils/Keys.scala new file mode 100644 index 0000000000..2d62223af9 --- /dev/null +++ b/src/test/scala/encry/utils/Keys.scala @@ -0,0 +1,9 @@ +package encry.utils + +import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} + +trait Keys { + val mnemonicKey: String = "index another island accuse valid aerobic little absurd bunker keep insect scissors" + val privKey: PrivateKey25519 = Utils.createPrivKey(Some(mnemonicKey)) + val publicKey: PublicKey25519 = privKey.publicImage +} diff --git a/src/test/scala/encry/utils/TestEntityGenerator.scala b/src/test/scala/encry/utils/TestEntityGenerator.scala new file mode 100644 index 0000000000..92c056d96e --- /dev/null +++ b/src/test/scala/encry/utils/TestEntityGenerator.scala @@ -0,0 +1,301 @@ +package encry.utils + + +import encry.modifiers.mempool.TransactionFactory +import encry.modifiers.mempool.TransactionFactory._ +import encry.settings.{EncryAppSettings, Settings} +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.TestHelper.Props +import encry.view.history.History +import org.encryfoundation.common.crypto.equihash.EquihashSolution +import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} +import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} +import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address +import org.encryfoundation.common.modifiers.mempool.transaction._ +import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box._ +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, Difficulty, Height, ModifierId} +import scorex.crypto.hash.Digest32 +import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} +import encry.utils.Utils.randomAddress + +import scala.util.Random +import scorex.utils.{Random => ScorexRandom} + +object TestEntityGenerator extends Keys with Settings { + + def timestamp(): Long = System.currentTimeMillis() + + def genHardcodedBox(address: Address, nonce: Long): AssetBox = + AssetBox(EncryProposition.addressLocked(address), nonce, 10000000L, None) + + def generateInitialBoxes(qty: Int): IndexedSeq[AssetBox] = + (0 until qty).map(_ => TestEntityGenerator.genAssetBox(privKey.publicImage.address.address)) + + def genAssetBox(address: Address, amount: Amount = 100000L, tokenIdOpt: Option[ADKey] = None): AssetBox = + AssetBox(EncryProposition.addressLocked(address), Random.nextLong(), amount, tokenIdOpt) + + def generateDataBox(address: Address, nonce: Long, data: Array[Byte]): DataBox = + DataBox(EncryProposition.addressLocked(address), nonce, data) + + def generateTokenIssuingBox(address: Address, amount: Amount = 100000L, tokenIdOpt: ADKey): TokenIssuingBox = + TokenIssuingBox(EncryProposition.addressLocked(address), Random.nextLong(), amount, tokenIdOpt) + + def genTxOutputs(boxes: Traversable[EncryBaseBox]): IndexedSeq[ADKey] = + boxes.foldLeft(IndexedSeq[ADKey]()) { case (s, box) => + s :+ box.id + } + + def genValidAssetBoxes(secret: PrivateKey25519, amount: Amount, qty: Int): Seq[AssetBox] = + (0 to qty).foldLeft(IndexedSeq[AssetBox]()) { case (bxs, _) => + bxs :+ AssetBox(EncryProposition.pubKeyLocked(secret.publicKeyBytes), Random.nextLong(), amount) + } + + def genPrivKeys(qty: Int): Seq[PrivateKey25519] = (0 until qty).map { _ => + val keys: (PrivateKey, PublicKey) = Curve25519.createKeyPair(ScorexRandom.randomBytes()) + PrivateKey25519(keys._1, keys._2) + } + + def genValidPaymentTxs(qty: Int): Seq[Transaction] = { + val keys: Seq[PrivateKey25519] = genPrivKeys(qty) + + keys.map { k => + val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(genAssetBox(k.publicImage.address.address)) + defaultPaymentTransactionScratch(k, Props.txFee, + timestamp() + Random.nextInt(5000), useBoxes, randomAddress, Props.boxValue) + } + } + + def genInvalidPaymentTxs(qty: Int): Seq[Transaction] = { + genPrivKeys(qty).map { key => + val useBoxes: IndexedSeq[AssetBox] = + IndexedSeq(genAssetBox(PublicKey25519(PublicKey @@ ScorexRandom.randomBytes(32)).address.address)) + defaultPaymentTransactionScratch(key, -100, timestamp(), useBoxes, randomAddress, Props.boxValue) + } + } + + def genValidPaymentTxsToAddr(qty: Int, address: Address): Seq[Transaction] = { + val keys: Seq[PrivateKey25519] = genPrivKeys(qty) + keys.map { k => + val useBoxes = IndexedSeq(genAssetBox(address)) + defaultPaymentTransactionScratch(k, Props.txFee, + Random.nextLong(), useBoxes, address, Props.boxValue) + } + } + + def genValidPaymentTxToAddrWithSpentBoxes(boxes: IndexedSeq[AssetBox], address: Address): Transaction = { + val key: PrivateKey25519 = genPrivKeys(1).head + defaultPaymentTransactionScratch(key, Props.txFee, + Random.nextLong(), boxes, address, Props.boxValue) + } + + def genValidPaymentTxsToAddrWithDiffTokens(qty: Int, address: Address): Seq[Transaction] = { + val keys: Seq[PrivateKey25519] = genPrivKeys(qty) + val tokens: Seq[ADKey] = (0 until qty).foldLeft(Seq[ADKey]()) { + case (seq, _) => seq :+ (ADKey @@ ScorexRandom.randomBytes()) + } + val pksZipTokens: Seq[(PrivateKey25519, ADKey)] = keys.zip(tokens) + pksZipTokens.map { k => + val useBoxes = IndexedSeq(genAssetBox(address, tokenIdOpt = Some(k._2))) + defaultPaymentTransactionScratch(k._1, Props.txFee, + timestamp(), useBoxes, address, Props.boxValue, tokenIdOpt = Some(k._2)) + } + } + + def genSelfSpendingTxs(qty: Int): Seq[Transaction] = { + val keys: Seq[PrivateKey25519] = genPrivKeys(qty) + keys.foldLeft(Seq[Transaction]()) { (seq, key) => + val useBoxes: IndexedSeq[MonetaryBox] = if (seq.isEmpty) IndexedSeq(genAssetBox(key.publicImage.address.address)) + else seq.last.newBoxes.map(_.asInstanceOf[MonetaryBox]).toIndexedSeq + seq :+ defaultPaymentTransactionScratch(key, Props.txFee, + timestamp(), useBoxes, randomAddress, Props.boxValue) + } + } + + def genHeader: Header = { + Header( + 1.toByte, + ModifierId @@ ScorexRandom.randomBytes(), + Digest32 @@ ScorexRandom.randomBytes(), + Math.abs(Random.nextLong()), + Math.abs(Random.nextInt(10000)), + Random.nextLong(), + settings.constants.InitialDifficulty, + EquihashSolution(Seq(1, 3)) + ) + } + + def genHeaderAtHeight(height: Int = 0, transactionsRoot: Digest32 = Digest32 @@ ScorexRandom.randomBytes()): Header = { + val random = new scala.util.Random + Header( + 1.toByte, + ModifierId @@ ScorexRandom.randomBytes(), + transactionsRoot, + Math.abs(Random.nextLong()), + height, + Random.nextLong(), + settings.constants.InitialDifficulty, + EquihashSolution(Seq(1, 3)) + ) + } + + def generatePaymentTransactions(boxes: IndexedSeq[AssetBox], + numberOfInputs: Int, + numberOfOutputs: Int): Vector[Transaction] = + (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { + case ((boxesLocal, transactions), _) => + val tx: Transaction = paymentTransactionWithMultipleOutputs( + privKey, + fee = 111, + timestamp = 11L, + useBoxes = boxesLocal.take(numberOfInputs), + recipient = randomAddress, + amount = 10000, + numOfOutputs = numberOfOutputs + ) + (boxesLocal.drop(numberOfInputs), transactions :+ tx) + }._2 + + def generateDataTransactions(boxes: IndexedSeq[AssetBox], + numberOfInputs: Int, + numberOfOutputs: Int, + bytesQty: Int): Vector[Transaction] = + (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { + case ((boxesLocal, transactions), _) => + val tx: Transaction = dataTransactionScratch( + privKey, + fee = 111, + timestamp = 11L, + useOutputs = boxesLocal.take(numberOfInputs), + data = ScorexRandom.randomBytes(bytesQty), + amount = 200L, + numOfOutputs = numberOfOutputs + ) + (boxesLocal.drop(numberOfInputs), tx +: transactions) + }._2 + + def generateAssetTransactions(boxes: IndexedSeq[AssetBox], + numberOfInputs: Int, + numberOfOutputs: Int): Vector[Transaction] = + (0 until boxes.size / numberOfInputs).foldLeft(boxes, Vector.empty[Transaction]) { + case ((boxesLocal, transactions), _) => + val tx: Transaction = assetIssuingTransactionScratch( + privKey, + fee = 111, + timestamp = 11L, + useOutputs = boxesLocal.take(numberOfInputs), + amount = 200L, + numOfOutputs = numberOfOutputs + ) + (boxesLocal.drop(numberOfInputs), tx +: transactions) + }._2 + + private val genHelper = TestHelper + + lazy val paymentTransactionValid: Transaction = { + val fee: Amount = genHelper.Props.txFee + val useBoxes: IndexedSeq[AssetBox] = IndexedSeq(genHelper.genAssetBox(publicKey.address.address), + genHelper.genAssetBox(publicKey.address.address)) + + TransactionFactory.defaultPaymentTransactionScratch(privKey, fee, timestamp(), useBoxes, + publicKey.address.address, genHelper.Props.txAmount) + } + + def paymentTransactionDynamic: Transaction = { + val fee = genHelper.Props.txFee + val useBoxes = (0 to 5).map(_ => { + AssetBox( + EncryProposition.pubKeyLocked(publicKey.pubKeyBytes), + Random.nextLong(), + 999L + ) + }) + + TransactionFactory.defaultPaymentTransactionScratch(privKey, fee, Random.nextLong(), useBoxes, + publicKey.address.address, genHelper.Props.txAmount) + } + + // def coinbaseTransaction(height: Int): Transaction = TransactionFactory.coinbaseTransactionScratch( + // publicKey, + // System.currentTimeMillis(), + // supply = 10000000L, + // amount = 1L, + // height = Height @@ height + // ) + + def coinbaseTransaction(height: Int): Transaction = { + TransactionFactory.coinbaseTransactionScratch(publicKey, timestamp(), 10000000L, 1L, Height @@ height) + } + + def generateNextBlock(history: History, + difficultyDiff: BigInt = 0, + prevId: Option[ModifierId] = None, + txsQty: Int = 100, + additionalDifficulty: BigInt = 0): Block = { + val previousHeaderId: ModifierId = + prevId.getOrElse(history.getBestHeader.map(_.id).getOrElse(Header.GenesisParentId)) + val requiredDifficulty: Difficulty = history.getBestHeader.map(parent => + history.requiredDifficultyAfter(parent).getOrElse(Difficulty @@ BigInt(0))) + .getOrElse(history.settings.constants.InitialDifficulty) + val txs = (if (txsQty != 0) genValidPaymentTxs(Random.nextInt(txsQty)) else Seq.empty) ++ + Seq(coinbaseTransaction(0)) + val header = genHeader.copy( + parentId = previousHeaderId, + height = history.getBestHeaderHeight + 1, + difficulty = Difficulty @@ (requiredDifficulty + difficultyDiff + additionalDifficulty), + transactionsRoot = Payload.rootHash(txs.map(_.id)) + ) + + Block(header, Payload(header.id, txs)) + } + + def generateForkOn(qty: Int, + addDifficulty: BigInt = 0, + from: Int, + to: Int, + settings: EncryAppSettings): (List[Block], List[Block]) = { + val history: History = dummyHistory(settings) + val forkInterval = (from until to).toList + (0 until qty).foldLeft((List(history), List.empty[Block] -> List.empty[Block])) { + case ((histories, blocks), blockHeight) => + if (forkInterval.contains(blockHeight)) { + if (histories.length == 1) { + val secondHistory = dummyHistory(settings) + blocks._1.foldLeft(secondHistory) { + case (prevHistory, blockToApply) => + prevHistory.append(blockToApply.header) + prevHistory.append(blockToApply.payload) + prevHistory.reportModifierIsValid(blockToApply) + prevHistory + } + val nextBlockInFirstChain = generateNextBlock(histories.head) + val nextBlockInSecondChain = generateNextBlock(secondHistory, additionalDifficulty = addDifficulty) + histories.head.append(nextBlockInFirstChain.header) + histories.head.append(nextBlockInFirstChain.payload) + val a = histories.head.reportModifierIsValid(nextBlockInFirstChain) + secondHistory.append(nextBlockInSecondChain.header) + secondHistory.append(nextBlockInSecondChain.payload) + val b = secondHistory.reportModifierIsValid(nextBlockInSecondChain) + (List(a, b), (blocks._1 :+ nextBlockInFirstChain) -> List(nextBlockInSecondChain)) + } else { + val nextBlockInFirstChain = generateNextBlock(histories.head) + val nextBlockInSecondChain = generateNextBlock(histories.last, additionalDifficulty = addDifficulty) + histories.head.append(nextBlockInFirstChain.header) + histories.head.append(nextBlockInFirstChain.payload) + val a = histories.head.reportModifierIsValid(nextBlockInFirstChain) + histories.last.append(nextBlockInSecondChain.header) + histories.last.append(nextBlockInSecondChain.payload) + val b = histories.last.reportModifierIsValid(nextBlockInSecondChain) + (List(a, b), (blocks._1 :+ nextBlockInFirstChain) -> (blocks._2 :+ nextBlockInSecondChain)) + } + } else { + val block: Block = generateNextBlock(histories.head) + histories.head.append(block.header) + histories.head.append(block.payload) + val a = histories.head.reportModifierIsValid(block) + (List(a), (blocks._1 :+ block) -> blocks._2) + } + }._2 + } + +} diff --git a/src/test/scala/encry/utils/TestHelper.scala b/src/test/scala/encry/utils/TestHelper.scala index a2d53b6e46..013edb20b6 100644 --- a/src/test/scala/encry/utils/TestHelper.scala +++ b/src/test/scala/encry/utils/TestHelper.scala @@ -30,20 +30,7 @@ object TestHelper { } } - def genAssetBoxes: IndexedSeq[AssetBox] = { - val rnd: Random = new scala.util.Random(genesisSeed) - genKeys(Props.keysQty).foldLeft(IndexedSeq[AssetBox]()) { case (bxs, pk) => - bxs :+ AssetBox( - EncryProposition.pubKeyLocked(pk.publicKeyBytes), - rnd.nextLong(), Props.boxValue) - } - } - def genAssetBox(address: EncryAddress.Address, amount: Amount = 9L): AssetBox = AssetBox(EncryProposition.addressLocked(address), amount, Props.boxValue) - def genTxOutputs(boxes: Traversable[EncryBaseBox]): IndexedSeq[ADKey] = - boxes.foldLeft(IndexedSeq[ADKey]()) { case(s, box) => - s :+ box.id - } } diff --git a/src/test/scala/encry/view/history/EncryHistoryTest.scala b/src/test/scala/encry/view/history/EncryHistoryTest.scala index 45b528b971..77a629ff20 100644 --- a/src/test/scala/encry/view/history/EncryHistoryTest.scala +++ b/src/test/scala/encry/view/history/EncryHistoryTest.scala @@ -1,10 +1,8 @@ package encry.view.history -import encry.modifiers.InstanceFactory -import encry.utils.EncryGenerator import org.scalatest.{Matchers, PropSpec} -class EncryHistoryTest extends PropSpec with Matchers with InstanceFactory with EncryGenerator { +class EncryHistoryTest extends PropSpec with Matchers { property("PreGenesis height test check.") { diff --git a/src/test/scala/encry/view/history/HistoryComparisionResultTest.scala b/src/test/scala/encry/view/history/HistoryComparisionResultTest.scala index 9c448ee320..146f9ebe34 100644 --- a/src/test/scala/encry/view/history/HistoryComparisionResultTest.scala +++ b/src/test/scala/encry/view/history/HistoryComparisionResultTest.scala @@ -1,23 +1,24 @@ package encry.view.history import encry.consensus.HistoryConsensus._ -import encry.modifiers.InstanceFactory -import encry.network.DeliveryManagerTests.DMUtils.generateBlocks -import encry.settings.{EncryAppSettings, TestNetSettings} +import encry.network.DeliveryManagerTests.DeliveryManagerUtils.generateBlocks +import encry.settings. TestNetSettings +import encry.utils.TestEntityGenerator import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.network.SyncInfo import org.scalatest.{Matchers, OneInstancePerTest, WordSpecLike} +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.TestEntityGenerator._ class HistoryComparisionResultTest extends WordSpecLike with Matchers - with InstanceFactory with OneInstancePerTest with TestNetSettings { "History Reader" should { "mark history as Equal where our best header is the same as other history best header" in { - val history: History = generateDummyHistory(testNetSettings) - val blocks: List[Block] = generateBlocks(100, generateDummyHistory(testNetSettings))._2 + val history: History = dummyHistory(testNetSettings) + val blocks: List[Block] = generateBlocks(100, dummyHistory(testNetSettings))._2 val syncInfo: SyncInfo = SyncInfo(blocks.map(_.header.id)) val updatedHistory: History = blocks.foldLeft(history) { case (hst, block) => @@ -31,8 +32,8 @@ class HistoryComparisionResultTest extends WordSpecLike } "mark history as Older where our best header is in other history best chain, but not at the last position" in { - val history: History = generateDummyHistory(testNetSettings) - val blocks: List[Block] = generateBlocks(100, generateDummyHistory(testNetSettings))._2 + val history: History = dummyHistory(testNetSettings) + val blocks: List[Block] = generateBlocks(100, dummyHistory(testNetSettings))._2 val syncInfo: SyncInfo = SyncInfo(blocks.map(_.header.id)) val updatedHistory: History = blocks.take(50).foldLeft(history) { case (hst, block) => @@ -46,8 +47,8 @@ class HistoryComparisionResultTest extends WordSpecLike } "mark history as Younger when comparing history is empty" in { - val history: History = generateDummyHistory(testNetSettings) - val blocks: List[Block] = generateBlocks(100, generateDummyHistory(testNetSettings))._2 + val history: History = dummyHistory(testNetSettings) + val blocks: List[Block] = generateBlocks(100, dummyHistory(testNetSettings))._2 val syncInfo: SyncInfo = SyncInfo(Seq.empty) val updatedHistory: History = blocks.foldLeft(history) { case (hst, block) => @@ -62,8 +63,8 @@ class HistoryComparisionResultTest extends WordSpecLike "mark history as Younger when our history contains all other history but other history " + "doesn't contain our last 70 headers" in { - val history: History = generateDummyHistory(testNetSettings) - val blocks: List[Block] = generateBlocks(100, generateDummyHistory(testNetSettings))._2 + val history: History = dummyHistory(testNetSettings) + val blocks: List[Block] = generateBlocks(100, dummyHistory(testNetSettings))._2 val syncInfo: SyncInfo = SyncInfo(blocks.take(30).map(_.header.id)) val updatedHistory: History = blocks.foldLeft(history) { case (hst, block) => @@ -77,9 +78,9 @@ class HistoryComparisionResultTest extends WordSpecLike } "mark history as Fork when we have same point in histories" in { - val history: History = generateDummyHistory(testNetSettings) + val history: History = dummyHistory(testNetSettings) - val fork = genForkOn(100, 1000, 25, 30, testNetSettings) + val fork = generateForkOn(100, 1000, 25, 30, testNetSettings) val syncInfo: SyncInfo = SyncInfo( fork._1.take(25).map(_.header.id) ++: fork._2.map(_.header.id) @@ -96,7 +97,7 @@ class HistoryComparisionResultTest extends WordSpecLike } "mark history as Equal where both nodes do not keep any blocks" in { - val history: History = generateDummyHistory(testNetSettings) + val history: History = dummyHistory(testNetSettings) val syncInfo: SyncInfo = SyncInfo(Seq.empty) val comparisonResult = history.compare(syncInfo) @@ -104,9 +105,9 @@ class HistoryComparisionResultTest extends WordSpecLike } "mark history as Older " in { - val history: History = generateDummyHistory(testNetSettings) + val history: History = dummyHistory(testNetSettings) val syncInfo: SyncInfo = SyncInfo( - generateBlocks(30, generateDummyHistory(testNetSettings))._2.map(_.header.id)) + generateBlocks(30, dummyHistory(testNetSettings))._2.map(_.header.id)) val comparisonResult = history.compare(syncInfo) assert(comparisonResult == Older) diff --git a/src/test/scala/encry/view/history/ModifiersValidationTest.scala b/src/test/scala/encry/view/history/ModifiersValidationTest.scala index e879864ea8..6225f1a383 100644 --- a/src/test/scala/encry/view/history/ModifiersValidationTest.scala +++ b/src/test/scala/encry/view/history/ModifiersValidationTest.scala @@ -1,29 +1,32 @@ package encry.view.history -import encry.modifiers.InstanceFactory -import encry.network.DeliveryManagerTests.DMUtils.generateBlocks -import encry.settings.{EncryAppSettings, TestNetSettings} +import encry.network.DeliveryManagerTests.DeliveryManagerUtils.generateBlocks +import encry.settings.TestNetSettings +import encry.utils.ChainGenerator.genGenesisBlock +import encry.utils.HistoryGenerator.dummyHistory +import encry.utils.Keys import org.encryfoundation.common.modifiers.history.Block import org.scalatest.{Matchers, OneInstancePerTest, WordSpecLike} class ModifiersValidationTest extends WordSpecLike with Matchers - with InstanceFactory with OneInstancePerTest + with Keys with TestNetSettings { "Modifiers validator" should { "validate genesis block" in { - val newHistory: History = generateDummyHistory(testNetSettings) - val genesisBlock: Block = generateGenesisBlock(testNetSettings.constants.GenesisHeight) + val newHistory: History = dummyHistory(testNetSettings) + val genesisBlock: Block = genGenesisBlock(privKey.publicImage, testNetSettings.constants.InitialEmissionAmount, + testNetSettings.constants.InitialDifficulty, testNetSettings.constants.GenesisHeight) newHistory.testApplicable(genesisBlock.header).isRight shouldBe true newHistory.append(genesisBlock.header) val updatedHistory: History = newHistory.reportModifierIsValid(genesisBlock.header) updatedHistory.testApplicable(genesisBlock.payload).isRight shouldBe true } "reject incorrect modifiers" in { - val blocks: List[Block] = generateBlocks(2, generateDummyHistory(testNetSettings))._2 - val newHistory: History = generateDummyHistory(testNetSettings) + val blocks: List[Block] = generateBlocks(2, dummyHistory(testNetSettings))._2 + val newHistory: History = dummyHistory(testNetSettings) blocks.take(1).foldLeft(newHistory) { case (history, block) => history.testApplicable(block.header).isRight shouldBe true history.append(block.header) diff --git a/src/test/scala/encry/view/mempool/MemoryPoolTests.scala b/src/test/scala/encry/view/mempool/MemoryPoolTests.scala index fae9457a7b..a8fd032ea0 100644 --- a/src/test/scala/encry/view/mempool/MemoryPoolTests.scala +++ b/src/test/scala/encry/view/mempool/MemoryPoolTests.scala @@ -3,9 +3,9 @@ package encry.view.mempool import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} import com.typesafe.scalalogging.StrictLogging -import encry.modifiers.InstanceFactory -import encry.settings.{EncryAppSettings, TestNetSettings} +import encry.settings.TestNetSettings import encry.utils.NetworkTimeProvider +import encry.utils.TestEntityGenerator.genValidPaymentTxs import encry.view.mempool.MemoryPool.{NewTransaction, TransactionsForMiner} import org.scalatest.{BeforeAndAfterAll, Matchers, OneInstancePerTest, WordSpecLike} @@ -13,7 +13,6 @@ import scala.concurrent.duration._ class MemoryPoolTests extends WordSpecLike with Matchers - with InstanceFactory with BeforeAndAfterAll with OneInstancePerTest with TestNetSettings diff --git a/src/test/scala/encry/view/state/EncryStateSpec.scala b/src/test/scala/encry/view/state/EncryStateSpec.scala index 3cae5063cb..6102b51692 100644 --- a/src/test/scala/encry/view/state/EncryStateSpec.scala +++ b/src/test/scala/encry/view/state/EncryStateSpec.scala @@ -1,10 +1,9 @@ package encry.view.state -import encry.utils.EncryGenerator import org.encryfoundation.common.modifiers.state.box.AssetBox import org.scalatest.{Matchers, PropSpec} -class EncryStateSpec extends PropSpec with Matchers with EncryGenerator { +class EncryStateSpec extends PropSpec with Matchers { property("EncryState.genesisBoxes() output equality") { diff --git a/src/test/scala/encry/view/state/UtxoStateSpec.scala b/src/test/scala/encry/view/state/UtxoStateSpec.scala index 91ef368a25..3211d871c4 100755 --- a/src/test/scala/encry/view/state/UtxoStateSpec.scala +++ b/src/test/scala/encry/view/state/UtxoStateSpec.scala @@ -9,7 +9,7 @@ import encry.storage.VersionalStorage import encry.storage.VersionalStorage.{StorageKey, StorageValue, StorageVersion} import encry.storage.iodb.versionalIODB.IODBWrapper import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, VLDBWrapper, VersionalLevelDBCompanion} -import encry.utils.{EncryGenerator, FileHelper, TestHelper} +import encry.utils.{TestEntityGenerator, FileHelper, TestHelper} import io.iohk.iodb.LSMStore import org.encryfoundation.common.modifiers.history.{Block, Payload} import org.encryfoundation.common.utils.constants.TestNetConstants @@ -18,7 +18,7 @@ import org.scalatest.{Matchers, PropSpec} import scala.concurrent.ExecutionContextExecutor -class UtxoStateSpec extends PropSpec with Matchers with EncryGenerator { +class UtxoStateSpec extends PropSpec with Matchers { // def utxoFromBoxHolder(bh: BoxHolder, // dir: File, @@ -45,27 +45,6 @@ class UtxoStateSpec extends PropSpec with Matchers with EncryGenerator { // ) // } - def utxoFromBoxHolder(bh: BoxHolder, - dir: File, - nodeViewHolderRef: Option[ActorRef], - settings: EncryAppSettings): UtxoState = { - val storage = settings.storage.state match { - case VersionalStorage.IODB => - IODBWrapper(new LSMStore(dir, keepVersions = settings.constants.DefaultKeepVersions)) - case VersionalStorage.LevelDB => - val levelDBInit = LevelDbFactory.factory.open(dir, new Options) - VLDBWrapper(VersionalLevelDBCompanion(levelDBInit, settings.levelDB, keySize = 32)) - } - - storage.insert( - StorageVersion @@ Array.fill(32)(0: Byte), - bh.boxes.values.map(bx => (StorageKey !@@ bx.id, StorageValue @@ bx.bytes)).toList - ) - - new UtxoState(storage, settings.constants) - } - - // property("Proofs for transaction") { // // val (privKey: PrivateKey, pubKey: PublicKey) = Curve25519.createKeyPair(Random.randomBytes()) diff --git a/src/test/scala/encry/view/wallet/WalletSpec.scala b/src/test/scala/encry/view/wallet/WalletSpec.scala index c039060746..69d63906c3 100644 --- a/src/test/scala/encry/view/wallet/WalletSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletSpec.scala @@ -1,18 +1,21 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging -import encry.EncryApp -import encry.modifiers.InstanceFactory -import encry.settings.{EncryAppSettings, LevelDBSettings, Settings} +import encry.settings.{LevelDBSettings, Settings} import encry.utils.TestHelper.Props -import encry.utils.{EncryGenerator, FileHelper} +import encry.utils.FileHelper import org.encryfoundation.common.modifiers.history.{Block, Header, Payload} import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.{AssetBox, MonetaryBox} import org.encryfoundation.common.utils.TaggedTypes.ModifierId import org.scalatest.{Matchers, PropSpec} +import encry.utils.TestEntityGenerator._ +import encry.utils.Utils.randomAddress -class WalletSpec extends PropSpec with Matchers with InstanceFactory with EncryGenerator with StrictLogging with Settings { +class WalletSpec extends PropSpec + with Matchers + with Settings + with StrictLogging { val dummyLevelDBSettings = LevelDBSettings(5)