From 6d5d4f820bcfa5e78dc23217d196ebe6f1cf8359 Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 14 Oct 2019 18:15:27 +0500 Subject: [PATCH 01/21] fix sql --- src/main/resources/data_model.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/data_model.sql b/src/main/resources/data_model.sql index e7369c3..09ba962 100644 --- a/src/main/resources/data_model.sql +++ b/src/main/resources/data_model.sql @@ -58,7 +58,7 @@ CREATE INDEX tx_id_inputs_index ON inputs (txId); CREATE TABLE contracts( hash VARCHAR(64) PRIMARY KEY, contract TEXT -) +); CREATE TABLE accounts( -- idx SERIAL, From b017518f6b93abd03ca59706f9011fd5a273774f Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 14 Oct 2019 18:21:52 +0500 Subject: [PATCH 02/21] copy from txgenerator --- src/main/protobuf/NetworkMessagesProto.proto | 55 +++ src/main/protobuf/SyntaxMessageProto.proto | 6 + src/main/protobuf/TransactionProto.proto | 48 ++ src/main/resources/application.conf | 4 +- src/main/scala/encry/ExplorerApp.scala | 2 +- src/main/scala/encry/net/GeneratorApp.scala | 26 + .../encry/net/actors/BlockchainListener.scala | 43 ++ .../scala/encry/net/actors/BoxesHolder.scala | 157 ++++++ .../scala/encry/net/actors/Generator.scala | 173 +++++++ .../scala/encry/net/actors/InfluxActor.scala | 45 ++ .../encry/net/modifiers/Transaction.scala | 95 ++++ .../net/modifiers/TransactionsFactory.scala | 161 ++++++ .../encry/net/modifiers/box/AssetBox.scala | 84 ++++ .../scala/encry/net/modifiers/box/Box.scala | 55 +++ .../encry/net/modifiers/box/DataBox.scala | 66 +++ .../net/modifiers/box/EncryBaseBox.scala | 38 ++ .../encry/net/modifiers/box/EncryBox.scala | 14 + .../modifiers/box/EncryBoxStateChanges.scala | 23 + .../net/modifiers/box/EncryProposition.scala | 46 ++ .../encry/net/modifiers/box/MonetaryBox.scala | 3 + .../net/modifiers/box/TokenIssuingBox.scala | 56 +++ .../directives/AssetIssuingDirective.scala | 85 ++++ .../modifiers/directives/DataDirective.scala | 89 ++++ .../net/modifiers/directives/Directive.scala | 45 ++ .../directives/DirectiveSerializer.scala | 42 ++ .../directives/ScriptedAssetDirective.scala | 100 ++++ .../directives/TransferDirective.scala | 112 +++++ .../encry/net/network/BasicMessagesRepo.scala | 458 ++++++++++++++++++ .../net/network/NetworkMessagesHandler.scala | 48 ++ .../encry/net/network/NetworkServer.scala | 110 +++++ .../scala/encry/net/network/PeerHandler.scala | 232 +++++++++ .../encry/net/transaction/Contracts.scala | 31 ++ .../encry/net/utils/CoreTaggedTypes.scala | 14 + src/main/scala/encry/net/utils/Mnemonic.scala | 40 ++ .../encry/net/utils/NetworkService.scala | 78 +++ .../scala/encry/net/utils/NetworkTime.scala | 82 ++++ src/main/scala/encry/net/utils/Settings.scala | 53 ++ 37 files changed, 2816 insertions(+), 3 deletions(-) create mode 100644 src/main/protobuf/NetworkMessagesProto.proto create mode 100644 src/main/protobuf/SyntaxMessageProto.proto create mode 100644 src/main/protobuf/TransactionProto.proto create mode 100644 src/main/scala/encry/net/GeneratorApp.scala create mode 100644 src/main/scala/encry/net/actors/BlockchainListener.scala create mode 100644 src/main/scala/encry/net/actors/BoxesHolder.scala create mode 100644 src/main/scala/encry/net/actors/Generator.scala create mode 100644 src/main/scala/encry/net/actors/InfluxActor.scala create mode 100644 src/main/scala/encry/net/modifiers/Transaction.scala create mode 100644 src/main/scala/encry/net/modifiers/TransactionsFactory.scala create mode 100644 src/main/scala/encry/net/modifiers/box/AssetBox.scala create mode 100644 src/main/scala/encry/net/modifiers/box/Box.scala create mode 100644 src/main/scala/encry/net/modifiers/box/DataBox.scala create mode 100644 src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala create mode 100644 src/main/scala/encry/net/modifiers/box/EncryBox.scala create mode 100644 src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala create mode 100644 src/main/scala/encry/net/modifiers/box/EncryProposition.scala create mode 100644 src/main/scala/encry/net/modifiers/box/MonetaryBox.scala create mode 100644 src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/DataDirective.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/Directive.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala create mode 100644 src/main/scala/encry/net/modifiers/directives/TransferDirective.scala create mode 100644 src/main/scala/encry/net/network/BasicMessagesRepo.scala create mode 100644 src/main/scala/encry/net/network/NetworkMessagesHandler.scala create mode 100644 src/main/scala/encry/net/network/NetworkServer.scala create mode 100644 src/main/scala/encry/net/network/PeerHandler.scala create mode 100644 src/main/scala/encry/net/transaction/Contracts.scala create mode 100644 src/main/scala/encry/net/utils/CoreTaggedTypes.scala create mode 100644 src/main/scala/encry/net/utils/Mnemonic.scala create mode 100644 src/main/scala/encry/net/utils/NetworkService.scala create mode 100644 src/main/scala/encry/net/utils/NetworkTime.scala create mode 100644 src/main/scala/encry/net/utils/Settings.scala diff --git a/src/main/protobuf/NetworkMessagesProto.proto b/src/main/protobuf/NetworkMessagesProto.proto new file mode 100644 index 0000000..76ebfbc --- /dev/null +++ b/src/main/protobuf/NetworkMessagesProto.proto @@ -0,0 +1,55 @@ +syntax = "proto3"; +import "SyntaxMessageProto.proto"; + +message GeneralizedNetworkProtoMessage { + + message SyncInfoProtoMessage { + repeated bytes lastHeaderIds = 1; + } + + message InvProtoMessage { + bytes modifierTypeId = 1; + repeated bytes modifiers = 2; + } + + message RequestModifiersProtoMessage { + bytes modifierTypeId = 1; + repeated bytes modifiers = 2; + } + + message ModifiersProtoMessage { + + message MapFieldEntry { + bytes key = 1; + bytes value = 2; + } + + bytes modifierTypeId = 1; + repeated MapFieldEntry map = 2; + } + + message GetPeersProtoMessage { } + + message PeersProtoMessage { + repeated InetSocketAddressProtoMessage peers = 1; + } + + message HandshakeProtoMessage { + bytes protocolVersion = 1; + string nodeName = 2; + InetSocketAddressProtoMessage declaredAddress = 3; + uint64 time = 4; + } + + bytes magic = 1; + bytes checksum = 2; + oneof innerMessage { + SyncInfoProtoMessage syncInfoProtoMessage = 3; + InvProtoMessage invProtoMessage = 4; + RequestModifiersProtoMessage requestModifiersProtoMessage = 5; + ModifiersProtoMessage modifiersProtoMessage = 6; + GetPeersProtoMessage getPeersProtoMessage = 7; + PeersProtoMessage peersProtoMessage = 8; + HandshakeProtoMessage handshakeProtoMessage = 9; + } +} \ No newline at end of file diff --git a/src/main/protobuf/SyntaxMessageProto.proto b/src/main/protobuf/SyntaxMessageProto.proto new file mode 100644 index 0000000..891c2fb --- /dev/null +++ b/src/main/protobuf/SyntaxMessageProto.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +message InetSocketAddressProtoMessage { + string host = 1; + uint32 port = 2; +} \ No newline at end of file diff --git a/src/main/protobuf/TransactionProto.proto b/src/main/protobuf/TransactionProto.proto new file mode 100644 index 0000000..083a94b --- /dev/null +++ b/src/main/protobuf/TransactionProto.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; +import "scalapb/scalapb.proto"; + +message TransactionProtoMessage { + + message DirectiveProtoMessage { + + message ADKeyProto { + bytes tokenIdOpt = 1; + } + + message TransferDirectiveProtoMessage { + string address = 1; + uint64 amount = 2; + ADKeyProto tokenIdOpt = 3; + } + + message AssetIssuingDirectiveProtoMessage { + bytes contractHash = 1; + uint64 amount = 2; + } + + message ScriptedAssetDirectiveProtoMessage { + + bytes contractHash = 1; + uint64 amount = 2; + ADKeyProto tokenIdOpt = 3; + } + + message DataDirectiveProtoMessage { + bytes contractHash = 1; + bytes data = 2; + } + + oneof directiveProto { + TransferDirectiveProtoMessage transferDirectiveProto = 1; + AssetIssuingDirectiveProtoMessage assetIssuingDirectiveProto = 2; + ScriptedAssetDirectiveProtoMessage scriptedAssetDirectiveProto = 3; + DataDirectiveProtoMessage dataDirectiveProto = 4; + } + } + + uint64 fee = 1; + uint64 timestamp = 2; + repeated bytes inputs = 3 [(scalapb.field).collection_type = "scala.collection.immutable.IndexedSeq"]; + repeated DirectiveProtoMessage directives = 4 [(scalapb.field).collection_type = "scala.collection.immutable.IndexedSeq"]; + bytes proof = 5; +} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 1a29e67..2809165 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -2,8 +2,8 @@ explorer { parseSettings { nodes = [""] recoverBatchSize = 15 - infinitePing = true // if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts - askNode = false // if set false, explorer won't ping the node if it stopped working + infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts + askNode = false # if set false, explorer won't ping the node if it stopped working } blackListSettings { banTime = 60m diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index e93ce17..fda1b56 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -12,7 +12,7 @@ import doobie.implicits._ import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} -object ExplorerApp extends App { +object ExplorerApp extends App { implicit val system: ActorSystem = ActorSystem() implicit val materializer: ActorMaterializer = ActorMaterializer() diff --git a/src/main/scala/encry/net/GeneratorApp.scala b/src/main/scala/encry/net/GeneratorApp.scala new file mode 100644 index 0000000..bb635a5 --- /dev/null +++ b/src/main/scala/encry/net/GeneratorApp.scala @@ -0,0 +1,26 @@ +package encry.net + +import akka.actor.{ActorRef, ActorSystem} +import akka.stream.ActorMaterializer +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.actors.InfluxActor +import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} +import com.typesafe.config.ConfigFactory +import net.ceedubs.ficus.Ficus._ +import net.ceedubs.ficus.readers.ArbitraryTypeReader._ +import org.encryfoundation.generator.network.NetworkServer +import scala.concurrent.ExecutionContextExecutor + +object GeneratorApp extends App with StrictLogging { + + implicit lazy val system: ActorSystem = ActorSystem() + implicit lazy val materializer: ActorMaterializer = ActorMaterializer() + implicit lazy val ec: ExecutionContextExecutor = system.dispatcher + val settings: Settings = ConfigFactory.load("local.conf") + .withFallback(ConfigFactory.load()).as[Settings] + val influx: Option[ActorRef] = + settings.influxDB.map(_ => system.actorOf(InfluxActor.props(settings), "influxDB")) + + val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntp) + val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings, timeProvider, influx)) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/BlockchainListener.scala b/src/main/scala/encry/net/actors/BlockchainListener.scala new file mode 100644 index 0000000..73ce9e4 --- /dev/null +++ b/src/main/scala/encry/net/actors/BlockchainListener.scala @@ -0,0 +1,43 @@ +package encry.net.actors + +import akka.actor.Actor +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain, TimeToCheck} +import org.encryfoundation.generator.utils.{NetworkService, Settings} + +import scala.concurrent.{ExecutionContextExecutor, Future} +import scala.concurrent.duration._ + +class BlockchainListener(settings: Settings) extends Actor with StrictLogging { + + implicit val ec: ExecutionContextExecutor = context.dispatcher + + override def receive: Receive = operating(Vector.empty) + + override def preStart(): Unit = + context.system.scheduler.schedule(settings.multisig.checkTxMinedPeriod.seconds, settings.multisig.checkTxMinedPeriod.seconds) { + self ! TimeToCheck + } + + def operating(txsToCheck: Vector[String]): Receive = { + case CheckTxMined(id) => context.become(operating(txsToCheck :+ id)) + case TimeToCheck if txsToCheck.nonEmpty => + NetworkService.checkTxsInBlockchain(settings.network, txsToCheck, settings.multisig.numberOfBlocksToCheck) + .foreach { txs => + if (txs.nonEmpty) { + logger.info(s"Multisig txs ${txs.mkString(", ")} are in blockchain now") + context.parent ! MultisigTxsInBlockchain(txs.toSet) + self ! MultisigTxsInBlockchain(txs.toSet) + } + } + case TimeToCheck => + case MultisigTxsInBlockchain(txs) => context.become(operating(txsToCheck.diff(txs.toSeq))) + } + +} + +object BlockchainListener { + case class CheckTxMined(txId: String) + case class MultisigTxsInBlockchain(ids: Set[String]) + case object TimeToCheck +} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/BoxesHolder.scala b/src/main/scala/encry/net/actors/BoxesHolder.scala new file mode 100644 index 0000000..90f9a08 --- /dev/null +++ b/src/main/scala/encry/net/actors/BoxesHolder.scala @@ -0,0 +1,157 @@ +package encry.net.actors + +import akka.actor.{Actor, ActorRef, Cancellable, Props} +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.actors.BoxesHolder._ +import org.encryfoundation.generator.actors.InfluxActor._ +import org.encryfoundation.generator.modifiers.box.AssetBox +import org.encryfoundation.generator.utils.{NetworkService, Node, Settings} +import com.google.common.base.Charsets +import com.google.common.hash.{BloomFilter, Funnels} +import org.encryfoundation.common.utils.Algos + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration._ + +class BoxesHolder(settings: Settings, + influx: Option[ActorRef], + peer: Node) extends Actor with StrictLogging { + + context.system.scheduler.schedule( + 5.seconds, settings.boxesHolderSettings.askingAPIFrequency, self, RequestForNewBoxesFromApi + ) + + var bloomFilter: BloomFilter[String] = initBloomFilter + + context.system.scheduler.schedule( + settings.boxesHolderSettings.bloomFilterCleanupInterval, + settings.boxesHolderSettings.bloomFilterCleanupInterval) { + bloomFilter = initBloomFilter + } + + override def receive: Receive = boxesHolderBehavior() + + def boxesHolderBehavior(pool: List[Batch] = List()): Receive = { + case BoxesFromApi(boxes) => + logger.info(s"BoxesHolder got message `BoxesFromApi`. Number of received boxes is: ${boxes.size}.") + val batchesPool: List[Batch] = batchesForTransactions(boxes) + val newBatches: List[Batch] = pool ++: batchesPool + influx.foreach(_ ! PoolState(newBatches.size)) + logger.info(s"Number of batches is: ${newBatches.size}") + context.become(boxesHolderBehavior(newBatches)) + + case AskBoxesFromGenerator => + logger.info(s"BoxesHolder got message `AskBoxesFromGenerator`. Current pool is: ${pool.size}") + val batchesAfterMT: List[Batch] = + if (settings.transactions.numberOfMonetaryTxs > 0) { + val batchesForTxs: List[Batch] = pool.take(settings.transactions.numberOfMonetaryTxs) + batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 2)) + pool.drop(settings.transactions.numberOfMonetaryTxs) + } else pool + influx.foreach(_ ! PoolState(batchesAfterMT.size)) + + val batchesAfterDT: List[Batch] = + if (settings.transactions.numberOfDataTxs > 0) { + val batchesForTxs: List[Batch] = batchesAfterMT.take(settings.transactions.numberOfDataTxs) + batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 1)) + batchesAfterMT.drop(settings.transactions.numberOfDataTxs) + } else batchesAfterMT + influx.foreach(_ ! PoolState(batchesAfterDT.size)) + + logger.info(s"Number of batches before diff: ${pool.size}.") + logger.info(s"Number of batches after diff: ${batchesAfterDT.size}.") + + val batchesAfterMultisigTx: List[Batch] = + if (settings.transactions.numberOfMultisigTxs > 0) { + val batchesForTxs: List[Batch] = batchesAfterDT.take(settings.transactions.numberOfMultisigTxs) + batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 3)) + batchesAfterDT.drop(settings.transactions.numberOfMultisigTxs) + } else batchesAfterDT + influx.foreach(_ ! PoolState(batchesAfterMultisigTx.size)) + + logger.info(s"Number of batches before diff: ${pool.size}.") + logger.info(s"Number of batches after diff: ${batchesAfterMultisigTx.size}.") + + influx.foreach(_ ! SentBatches(batchesAfterMultisigTx.size)) + context.become(boxesHolderBehavior(batchesAfterMultisigTx)) + + case AskBoxesForMultisigSigning(txs) => + logger.info(s"BoxesHolder got message `AskBoxesForMultisigSigning`. Current pool is: ${pool.size}, and number of txs is ${txs.size}") + txs.foreach(tx => sender() ! BoxesForGenerator(List.empty, 4, Some(tx))) + logger.info(s"Number of batches after diff: ${pool.size}.") + + case RequestForNewBoxesFromApi => + if (pool.size < settings.boxesHolderSettings.poolSize) { + logger.info(s"Current pool size is: ${pool.size}. Asking new boxes from api!") + getBoxes(0, settings.boxesHolderSettings.rangeForAskingBoxes) + } + else logger.info(s"Current pool is: ${pool.size}. We won't ask new boxes from api!") + } + + def batchesForTransactions(list: List[AssetBox]): List[Batch] = { + val batchesList: (List[Batch], Batch, Long) = list.foldLeft(List[Batch](), Batch(List()), 0L) { + case ((listBatches, batch, amount), box) => + val newBatch: List[AssetBox] = box :: batch.boxes + val newAmount: Long = amount + box.amount + if (newAmount > settings.transactions.feeAmount) (Batch(newBatch) :: listBatches, Batch(List()), 0) + else (listBatches, Batch(newBatch), newAmount) + } + batchesList._1 + } + + def cleanReceivedBoxesFromUsed(usedB: Map[String, Cancellable], + newB: List[AssetBox]): (List[AssetBox], Map[String, Cancellable]) = { + val newBMap: Map[String, AssetBox] = Map(newB.map(k => Algos.encode(k.id) -> k): _*) + logger.debug(s"cleanReceivedBoxesFromUsed: New boxes map size is: ${newBMap.size}") + val (usedBoxes: Map[String, Cancellable], newBoxes: Map[String, AssetBox]) = + usedB.foldLeft(Map[String, Cancellable](), newBMap) { + case ((newUsedCollection, newBoxesCollection), (id, timer)) => newBoxesCollection.get(id) match { + case Some(_) => (newUsedCollection.updated(id, timer), newBoxesCollection - id) + case None => + timer.cancel() + (newUsedCollection, newBoxesCollection) + } + } + logger.debug(s"CleanNewBoxesFromUsed: Used - ${usedBoxes.size}. New - ${newBoxes.size}") + (newBoxes.values.toList, usedBoxes) + } + + def getBoxes(from: Int, to: Int): Future[Unit] = + NetworkService.requestUtxos(peer, from, to).map { request => + logger.debug(s"Boxes from API: ${request.size}") + if (request.nonEmpty && to < settings.boxesHolderSettings.maxPoolSize) { + val newFrom: Int = from + settings.boxesHolderSettings.rangeForAskingBoxes + val newTo: Int = to + settings.boxesHolderSettings.rangeForAskingBoxes + getBoxes(newFrom, newTo) + logger.debug(s"Asking new boxes in range: $newFrom -> $newTo.") + } + request.collect { case mb: AssetBox if mb.tokenIdOpt.isEmpty && !bloomFilter.mightContain(Algos.encode(mb.id)) => + bloomFilter.put(Algos.encode(mb.id)) + mb + } + }.map(boxes => self ! BoxesFromApi(boxes)) + + def initBloomFilter: BloomFilter[String] = BloomFilter.create( + Funnels.stringFunnel(Charsets.UTF_8), + settings.boxesHolderSettings.bloomFilterCapacity, + settings.boxesHolderSettings.bloomFilterFailureProbability + ) +} + +object BoxesHolder { + def props(settings: Settings, influx: Option[ActorRef], peer: Node): Props = + Props(new BoxesHolder(settings, influx, peer)) + + case object RequestForNewBoxesFromApi + + case object AskBoxesFromGenerator + + case class BoxesFromApi(list: List[AssetBox]) + + case class BoxesForGenerator(list: List[AssetBox], txType: Int, forTx: Option[String] = None) + + case class Batch(boxes: List[AssetBox]) + + case class AskBoxesForMultisigSigning(txs: Set[String]) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/Generator.scala b/src/main/scala/encry/net/actors/Generator.scala new file mode 100644 index 0000000..93cdf3f --- /dev/null +++ b/src/main/scala/encry/net/actors/Generator.scala @@ -0,0 +1,173 @@ +package encry.net.actors + +import akka.actor.{Actor, ActorRef, Props} +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.common.crypto.PrivateKey25519 +import org.encryfoundation.common.modifiers.mempool.transaction.{Proof, PubKeyLockedContract} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.generator.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain} +import org.encryfoundation.generator.actors.BoxesHolder._ +import org.encryfoundation.generator.actors.Generator.TransactionForCommit +import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, MonetaryBox} +import org.encryfoundation.generator.modifiers.{Transaction, TransactionsFactory} +import org.encryfoundation.generator.transaction.Contracts +import org.encryfoundation.generator.utils.{Mnemonic, Node, Settings} +import org.encryfoundation.prismlang.compiler.CompiledContract +import org.encryfoundation.prismlang.core.wrapped.BoxedValue.MultiSignatureValue +import scorex.crypto.hash.Blake2b256 +import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} +import scorex.utils +import scorex.utils.Random.{randomBytes => rBytes} + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration._ +import scala.util.Random + +class Generator(settings: Settings, + privKey: PrivateKey25519, + nodeForLocalPrivKey: Node, + influx: Option[ActorRef], + networkServer: ActorRef) extends Actor with StrictLogging { + + val boxesHolder: ActorRef = context.system.actorOf( + BoxesHolder.props(settings, influx, nodeForLocalPrivKey), s"boxesHolder${nodeForLocalPrivKey.explorerHost}") + context.system.scheduler.schedule(10.seconds, settings.generator.transactionsSendingFrequency.seconds) { + boxesHolder ! AskBoxesFromGenerator + logger.info(s"Generator asked boxesHolder for new boxes.") + } + + val multisigKeys: Seq[PrivateKey25519] = if (settings.multisig.mnemonicKeys.size >= 3) + settings.multisig.mnemonicKeys + .take(3) + .map(Some(_)) + .map(Mnemonic.createPrivKey) + else + (1 to 3) + .map(_ => Curve25519.createKeyPair(rBytes())) + .map(pair => PrivateKey25519(pair._1, pair._2)) + + var multisigBoxes: Map[String, Seq[Box]] = Map.empty + val blockchainListener: ActorRef = + context.actorOf(Props(classOf[BlockchainListener], settings), "blockchainListener") + + override def receive: Receive = { + case BoxesForGenerator(boxes, txType, None) if boxes.nonEmpty => + generateAndSendTransaction(boxes, txType) + case BoxesForGenerator(boxes, txType, Some(forTx)) if txType == 4 && multisigBoxes.get(forTx).exists(_.nonEmpty) => + generateAndSendTransaction(boxes, txType, Some(forTx)) + case MultisigTxsInBlockchain(txs) => boxesHolder ! AskBoxesForMultisigSigning(txs) + case _ => + } + + def generateAndSendTransaction(boxes: List[AssetBox], txsType: Int, forTx: Option[String] = None): Future[Unit] = Future { + val transaction: Transaction = txsType match { + case 1 => TransactionsFactory.dataTransactionScratch( + privKey, + settings.transactions.feeAmount, + System.currentTimeMillis(), + boxes.map(_ -> None), + PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract, + settings.transactions.requiredAmount, + utils.Random.randomBytes(settings.transactions.dataTxSize), + settings.transactions.numberOfCreatedDirectives + ) + case 2 => TransactionsFactory.defaultPaymentTransaction( + privKey, + settings.transactions.feeAmount, + System.currentTimeMillis(), + boxes.map(_ -> None), + privKey.publicImage.address.address, + settings.transactions.requiredAmount, + settings.transactions.numberOfCreatedDirectives + ) + case 3 => + val contract = Contracts.multiSigContractScratch(multisigKeys.map(_.publicKeyBytes)).get + + TransactionsFactory.scriptedAssetTransactionScratch( + privKey, + settings.transactions.feeAmount, + System.currentTimeMillis(), + boxes.map(_ -> None), + contract, + settings.transactions.requiredAmount, + settings.transactions.numberOfCreatedDirectives, + None + ) + case 4 if forTx.isDefined => + val compiledContract: CompiledContract = Contracts.multiSigContractScratch(multisigKeys.map(_.publicKeyBytes)).get + val ts: Long = System.currentTimeMillis() + + val txWithoutProofs: Transaction = TransactionsFactory.defaultPaymentTransactionWithoutRandom( + privKey, + settings.transactions.feeAmount, + ts, + multisigBoxes(forTx.get).collect { + case b: MonetaryBox => b + }.map(_ -> Some(compiledContract -> Seq())) match { + case init :+ last => init :+ last._1 -> None + }, + privKey.publicImage.address.address, + settings.transactions.requiredAmount - settings.transactions.feeAmount, + settings.transactions.numberOfCreatedDirectives + ) + + val signatures: List[List[Byte]] = Random.shuffle(multisigKeys) + .take(2) + .map(_.sign(txWithoutProofs.messageToSign)) + .map(_.signature) + .map(_.toList) + .toList + + val proofs: Seq[Proof] = Seq(Proof(MultiSignatureValue(signatures), Some("signature"))) + TransactionsFactory.defaultPaymentTransactionWithoutRandom( + privKey, + settings.transactions.feeAmount, + ts, + multisigBoxes(forTx.get).collect { + case b: MonetaryBox => b + }.map(_ -> Some(compiledContract -> proofs)) match { + case init :+ last => init :+ last._1 -> None + }, + privKey.publicImage.address.address, + settings.transactions.requiredAmount - settings.transactions.feeAmount, + settings.transactions.numberOfCreatedDirectives + ) + } + if (txsType == 3) { + blockchainListener ! CheckTxMined(Algos.encode(transaction.id)) + multisigBoxes = multisigBoxes.updated(Algos.encode(transaction.id), transaction.newBoxes) + } + if (txsType == 4) { + blockchainListener ! CheckTxMined(Algos.encode(transaction.id)) + multisigBoxes = multisigBoxes - Algos.encode(transaction.id) + } + + logger.info(s"Commit tx ${Algos.encode(transaction.id)} with type: ${txsType match { + case 1 => "DataTx" + case 2 => "MonetaryTx" + case 3 => "Multisig deploy" + case 4 => "Multisig signing" + }}") + networkServer ! TransactionForCommit(transaction) + } + + def createKeyPair: PrivateKey25519 = { + val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( + Blake2b256.hash(scorex.utils.Random.randomBytes(16)) + ) + PrivateKey25519(privateKey, publicKey) + } +} + +object Generator { + + case class TransactionForCommit(tx: Transaction) + + def props(settings: Settings, + privKey: PrivateKey25519, + nodeForLocalPrivKey: Node, + influx: Option[ActorRef], + networkServer: ActorRef): Props = + Props(new Generator(settings, privKey, nodeForLocalPrivKey, influx, networkServer)) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/InfluxActor.scala b/src/main/scala/encry/net/actors/InfluxActor.scala new file mode 100644 index 0000000..e48cc23 --- /dev/null +++ b/src/main/scala/encry/net/actors/InfluxActor.scala @@ -0,0 +1,45 @@ +package encry.net.actors + +import akka.actor.{Actor, Props} +import com.typesafe.scalalogging.StrictLogging +import org.influxdb.{InfluxDB, InfluxDBFactory} +import java.net._ +import org.encryfoundation.generator.actors.InfluxActor._ +import org.encryfoundation.generator.utils.Settings + +class InfluxActor(settings: Settings) extends Actor with StrictLogging { + + val nodeName: String = InetAddress.getLocalHost.getHostAddress + val udpPort: Int = settings.influxDB.map(_.udpPort).getOrElse(0) + val influxDB: InfluxDB = InfluxDBFactory.connect( + settings.influxDB.map(_.url).getOrElse(""), + settings.influxDB.map(_.login).getOrElse(""), + settings.influxDB.map(_.password).getOrElse("") + ) + influxDB.setRetentionPolicy("autogen") + + override def preStart(): Unit = { + logger.info("Influx actor started") + influxDB.write(udpPort, s"""txGenStartTime value="$nodeName"""") + } + + + override def receive: Receive = { + case PoolState(newO) => + influxDB.write(udpPort, s"txsStatFromGenerator,nodeName=$nodeName value=$newO") + + case SentBatches(num) => + influxDB.write(udpPort, s"numberOfSendedBatches,nodeName=$nodeName value=$num") + + case GetAllTimeSeconds(time) => + influxDB.write(udpPort, s"getAllTime,nodeName=$nodeName value=$time") + } +} + +object InfluxActor { + def props(settings: Settings): Props = Props(new InfluxActor(settings)) + + case class PoolState(newO: Int) + case class SentBatches(num: Int) + case class GetAllTimeSeconds(time: Long) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/Transaction.scala b/src/main/scala/encry/net/modifiers/Transaction.scala new file mode 100644 index 0000000..cd54fe2 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/Transaction.scala @@ -0,0 +1,95 @@ +package encry.net.modifiers + +import TransactionProto.TransactionProtoMessage +import com.google.protobuf.ByteString +import org.encryfoundation.generator.modifiers.box.Box +import org.encryfoundation.generator.modifiers.directives.{Directive, DirectiveProtoSerializer} +import scorex.crypto.hash.{Blake2b256, Digest32} +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.modifiers.mempool.transaction.{Input, InputSerializer, Proof, ProofSerializer} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.prismlang.core.Types +import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} + +import scala.util.Try + +case class Transaction(fee: Long, + timestamp: Long, + inputs: IndexedSeq[Input], + directives: IndexedSeq[Directive], + defaultProofOpt: Option[Proof]) { + + val messageToSign: Array[Byte] = UnsignedEncryTransaction.bytesToSign(fee, timestamp, inputs, directives) + lazy val id: Array[Byte] = Blake2b256.hash(messageToSign) + lazy val newBoxes: IndexedSeq[Box] = + directives.zipWithIndex.flatMap { case (d, idx) => d.boxes(Digest32 !@@ id, idx) } + + val tpe: Types.Product = Types.EncryTransaction + + def asVal: PValue = PValue(PObject(Map( + "inputs" -> PValue(inputs.map(_.boxId.toList), Types.PCollection(Types.PCollection.ofByte)), + "outputs" -> PValue(newBoxes.map(_.asPrism), Types.PCollection(Types.EncryBox)), + "messageToSign" -> PValue(messageToSign, Types.PCollection.ofByte) + ), tpe), tpe) +} + +object Transaction { + + val modifierTypeId: Byte = 2.toByte + + implicit val jsonEncoder: Encoder[Transaction] = (tx: Transaction) => Map( + "id" -> Algos.encode(tx.id).asJson, + "fee" -> tx.fee.asJson, + "timestamp" -> tx.timestamp.asJson, + "inputs" -> tx.inputs.map(_.asJson).asJson, + "directives" -> tx.directives.map(_.asJson).asJson, + "defaultProofOpt" -> tx.defaultProofOpt.map(_.asJson).asJson + ).asJson + + implicit val jsonDecoder: Decoder[Transaction] = (c: HCursor) => { + for { + fee <- c.downField("fee").as[Long] + timestamp <- c.downField("timestamp").as[Long] + inputs <- c.downField("inputs").as[IndexedSeq[Input]] + directives <- c.downField("directives").as[IndexedSeq[Directive]] + defaultProofOpt <- c.downField("defaultProofOpt").as[Option[Proof]] + } yield Transaction( + fee, + timestamp, + inputs, + directives, + defaultProofOpt + ) + } +} + +trait ProtoTransactionSerializer[T] { + + def toProto(message: T): TransactionProtoMessage + + def fromProto(message: TransactionProtoMessage): Try[T] +} + +object TransactionProtoSerializer extends ProtoTransactionSerializer[Transaction] { + + override def toProto(message: Transaction): TransactionProtoMessage = { + val initialTx: TransactionProtoMessage = TransactionProtoMessage() + .withFee(message.fee) + .withTimestamp(message.timestamp) + .withInputs(message.inputs.map(input => ByteString.copyFrom(input.bytes)).to[scala.collection.immutable.IndexedSeq]) + .withDirectives(message.directives.map(_.toDirectiveProto).to[scala.collection.immutable.IndexedSeq]) + message.defaultProofOpt match { + case Some(value) => initialTx.withProof(ByteString.copyFrom(value.bytes)) + case None => initialTx + } + } + + override def fromProto(message: TransactionProtoMessage): Try[Transaction] = Try(Transaction( + message.fee, + message.timestamp, + message.inputs.map(element => InputSerializer.parseBytes(element.toByteArray).get), + message.directives.map(directive => DirectiveProtoSerializer.fromProto(directive).get), + ProofSerializer.parseBytes(message.proof.toByteArray).toOption + )) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/TransactionsFactory.scala b/src/main/scala/encry/net/modifiers/TransactionsFactory.scala new file mode 100644 index 0000000..64dfd5d --- /dev/null +++ b/src/main/scala/encry/net/modifiers/TransactionsFactory.scala @@ -0,0 +1,161 @@ +package encry.net.modifiers + +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.transaction.{Input, Proof, PubKeyLockedContract} +import org.encryfoundation.prismlang.compiler.CompiledContract +import org.encryfoundation.prismlang.core.wrapped.BoxedValue +import scorex.crypto.hash.{Blake2b256, Digest32} +import org.encryfoundation.generator.modifiers.directives._ +import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.generator.modifiers.box.MonetaryBox + +import scala.util.Random + +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.isDefinedAt(idx)) 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) + )) +} + +object TransactionsFactory extends StrictLogging { + + def defaultPaymentTransaction(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + recipient: String, + amount: Long, + numberOfCreatedDirectives: Int = 1, + 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 + logger.debug(s"howMuchCanTransfer - $howMuchCanTransfer. howMuchWillTransfer - $howMuchWillTransfer. " + + s"Change - $change") + val directives: IndexedSeq[TransferDirective] = + IndexedSeq(TransferDirective(recipient, howMuchWillTransfer, tokenIdOpt)) + prepareTransaction(privKey, fee, timestamp, useOutputs, directives, change, tokenIdOpt) + } + + def defaultPaymentTransactionWithoutRandom(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + recipient: String, + amount: Long, + numberOfCreatedDirectives: Int = 1, + tokenIdOpt: Option[ADKey] = None): Transaction = { + val howMuchCanTransfer: Long = useOutputs.map(_._1.amount).sum - fee + val change: Long = howMuchCanTransfer - amount + val directives: IndexedSeq[TransferDirective] = + IndexedSeq(TransferDirective(recipient, amount, tokenIdOpt)) + prepareTransaction(privKey, fee, timestamp, useOutputs, directives, change, tokenIdOpt) + } + + def scriptedAssetTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + contract: CompiledContract, + amount: Long, + numberOfCreatedDirectives: Int = 1, + tokenIdOpt: Option[ADKey] = None): Transaction = { + val directives: IndexedSeq[ScriptedAssetDirective] = + (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[ScriptedAssetDirective]) { case (directivesAll, _) => + directivesAll :+ ScriptedAssetDirective(contract.hash, amount, tokenIdOpt) + } + prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) + } + + def assetIssuingTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + contract: CompiledContract, + amount: Long, + numberOfCreatedDirectives: Int = 1, + tokenIdOpt: Option[ADKey] = None): Transaction = { + val directives: IndexedSeq[AssetIssuingDirective] = + (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[AssetIssuingDirective]) { case (directivesAll, _) => + directivesAll :+ AssetIssuingDirective(contract.hash, amount) + } + prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) + } + + def dataTransactionScratch(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + contract: CompiledContract, + amount: Long, + data: Array[Byte], + numberOfCreatedDirectives: Int = 1, + tokenIdOpt: Option[ADKey] = None): Transaction = { + val directives: IndexedSeq[DataDirective] = + (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[DataDirective]) { case (directivesAll, _) => + directivesAll :+ DataDirective(contract.hash, data) + } + prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) + } + + private def prepareTransaction(privKey: PrivateKey25519, + fee: Long, + timestamp: Long, + useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], + directivesSeq: IndexedSeq[Directive], + change: Long, + tokenIdOpt: Option[ADKey] = None): 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)) + } + ) + } + + 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 directives: IndexedSeq[Directive] = + if (change > 0) directivesSeq ++: IndexedSeq(TransferDirective(pubKey.address.address, change, tokenIdOpt)) + else directivesSeq + + val uTransaction: UnsignedEncryTransaction = UnsignedEncryTransaction(fee, timestamp, uInputs, directives) + 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)))) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/AssetBox.scala b/src/main/scala/encry/net/modifiers/box/AssetBox.scala new file mode 100644 index 0000000..7b29469 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/AssetBox.scala @@ -0,0 +1,84 @@ +package encry.net.modifiers.box + +import com.google.common.primitives.{Bytes, Longs, Shorts} +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.generator.modifiers.box.TokenIssuingBox.TokenId + +import scala.util.Try + +/** Represents monetary asset of some type locked with some `proposition`. + * `tokenIdOpt = None` if the asset is of intrinsic type. */ +case class AssetBox(override val proposition: EncryProposition, + override val nonce: Long, + override val amount: Long, + tokenIdOpt: Option[TokenId] = None) extends EncryBox[EncryProposition] with MonetaryBox { + + override val typeId: Byte = AssetBox.TypeId + + lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty +} + +object AssetBox { + + val IntrinsicTokenId: String = Algos.encode(Algos.hash("intrinsic_token")) + + def apply(proposition: EncryProposition, + nonce: Long, + amount: Long, + tokenIdOpt: Option[TokenId] = None): AssetBox = new AssetBox( + proposition, + nonce, + amount, + tokenIdOpt match { + case Some(id) if Algos.encode(id) == IntrinsicTokenId => Option.empty[TokenId] + case ex => ex + } + ) + + val TypeId: Byte = 1.toByte + + implicit val jsonEncoder: Encoder[AssetBox] = (bx: AssetBox) => Map( + "type" -> TypeId.asJson, + "id" -> Algos.encode(bx.id).asJson, + "proposition" -> bx.proposition.asJson, + "nonce" -> bx.nonce.asJson, + "value" -> bx.amount.asJson, + "tokenId" -> bx.tokenIdOpt.map(id => Algos.encode(id)).asJson + ).asJson + + implicit val jsonDecoder: Decoder[AssetBox] = (c: HCursor) => for { + nonce <- c.downField("nonce").as[Long] + proposition <- c.downField("proposition").as[EncryProposition] + value <- c.downField("value").as[Long] + tokenIdOpt <- c.downField("tokenId") + .as[Option[TokenId]](Decoder.decodeOption(Decoder.decodeString.emapTry(Algos.decode))) + } yield AssetBox.apply(proposition, nonce, value, tokenIdOpt) +} + +object AssetBoxSerializer extends Serializer[AssetBox] { + + override def toBytes(obj: AssetBox): Array[Byte] = { + val propBytes = EncryPropositionSerializer.toBytes(obj.proposition) + Bytes.concat( + Shorts.toByteArray(propBytes.length.toShort), + propBytes, + Longs.toByteArray(obj.nonce), + Longs.toByteArray(obj.amount), + obj.tokenIdOpt.getOrElse(Array.empty) + ) + } + + override def parseBytes(bytes: Array[Byte]): Try[AssetBox] = Try { + val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) + val iBytes: Array[Byte] = bytes.drop(2) + val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get + val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) + val amount: Long = Longs.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 8)) + val tokenIdOpt: Option[TokenId] = + if ((iBytes.length - (propositionLen + 8 + 8)) == 32) Some(iBytes.takeRight(32)) else None + AssetBox(proposition, nonce, amount, tokenIdOpt) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/Box.scala b/src/main/scala/encry/net/modifiers/box/Box.scala new file mode 100644 index 0000000..7179fd8 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/Box.scala @@ -0,0 +1,55 @@ +package encry.net.modifiers.box + +import com.google.common.primitives.Longs +import io.circe.{Decoder, DecodingFailure, Encoder} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.prismlang.core.Types +import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} + +trait Box { + + val proposition: EncryProposition + + val typeId: Byte + + val nonce: Long + + lazy val id: ADKey = ADKey @@ Algos.hash(Longs.toByteArray(nonce)).updated(0, typeId) + + def isAmountCarrying: Boolean = this.isInstanceOf[MonetaryBox] + + val tpe: Types.Product = Types.EncryBox + + def asVal: PValue = PValue(asPrism, tpe) + + lazy val baseFields: Map[String, PValue] = Map( + "contractHash" -> PValue(proposition.contractHash, Types.PCollection.ofByte), + "typeId" -> PValue(typeId.toLong, Types.PInt), + "id" -> PValue(id, Types.PCollection.ofByte) + ) + + def asPrism: PObject = PObject(baseFields, tpe) +} + +object Box { + + implicit val jsonEncoder: Encoder[Box] = { + case ab: AssetBox => AssetBox.jsonEncoder(ab) + case db: DataBox => DataBox.jsonEncoder(db) + case aib: TokenIssuingBox => TokenIssuingBox.jsonEncoder(aib) + } + + implicit val jsonDecoder: Decoder[Box] = { + Decoder.instance { c => + c.downField("type").as[Byte] match { + case Right(s) => s match { + case AssetBox.TypeId => AssetBox.jsonDecoder(c) + case DataBox.TypeId => DataBox.jsonDecoder(c) + case _ => Left(DecodingFailure("Incorrect directive typeID", c.history)) + } + case Left(_) => Left(DecodingFailure("None typeId", c.history)) + } + } + } +} diff --git a/src/main/scala/encry/net/modifiers/box/DataBox.scala b/src/main/scala/encry/net/modifiers/box/DataBox.scala new file mode 100644 index 0000000..4ec49f8 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/DataBox.scala @@ -0,0 +1,66 @@ +package encry.net.modifiers.box + +import com.google.common.primitives.{Bytes, Longs, Shorts} +import io.circe.{Decoder, Encoder, HCursor} +import io.circe.syntax._ +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.Algos + +import scala.util.Try + +/** Stores arbitrary data in EncryTL binary format. */ +case class DataBox(override val proposition: EncryProposition, + override val nonce: Long, + data: Array[Byte]) extends EncryBox[EncryProposition] { + + override val typeId: Byte = DataBox.TypeId +} + +object DataBox { + + val TypeId: Byte = 4.toByte + + implicit val jsonEncoder: Encoder[DataBox] = (bx: DataBox) => Map( + "type" -> TypeId.asJson, + "id" -> Algos.encode(bx.id).asJson, + "proposition" -> bx.proposition.asJson, + "nonce" -> bx.nonce.asJson, + "data" -> Algos.encode(bx.data).asJson, + ).asJson + + implicit val jsonDecoder: Decoder[DataBox] = (c: HCursor) => { + for { + proposition <- c.downField("proposition").as[EncryProposition] + nonce <- c.downField("nonce").as[Long] + data <- c.downField("data").as[String] + } yield DataBox( + proposition, + nonce, + Algos.decode(data).getOrElse(Array.emptyByteArray) + ) + } +} + +object DataBoxSerializer extends Serializer[DataBox] { + + override def toBytes(obj: DataBox): Array[Byte] = { + val propBytes: Array[Byte] = EncryPropositionSerializer.toBytes(obj.proposition) + Bytes.concat( + Shorts.toByteArray(propBytes.length.toShort), + propBytes, + Longs.toByteArray(obj.nonce), + Shorts.toByteArray(obj.data.length.toShort), + obj.data + ) + } + + override def parseBytes(bytes: Array[Byte]): Try[DataBox] = Try { + val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) + val iBytes: Array[Byte] = bytes.drop(2) + val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get + val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) + val dataLen: Short = Shorts.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 2)) + val data: Array[Byte] = iBytes.takeRight(dataLen) + DataBox(proposition, nonce, data) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala b/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala new file mode 100644 index 0000000..7a2a377 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala @@ -0,0 +1,38 @@ +package encry.net.modifiers.box + +import com.google.common.primitives.Longs +import io.circe.Encoder +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} +import org.encryfoundation.prismlang.core.{PConvertible, Types} + +trait EncryBaseBox extends Box with PConvertible { + + val typeId: Byte + + val nonce: Long + + override lazy val id: ADKey = ADKey @@ Algos.hash(Longs.toByteArray(nonce)).updated(0, typeId) + + override val tpe: Types.Product = Types.EncryBox + + override def asVal: PValue = PValue(asPrism, tpe) + + override lazy val baseFields: Map[String, PValue] = Map( + "contractHash" -> PValue(proposition.contractHash, Types.PCollection.ofByte), + "typeId" -> PValue(typeId.toLong, Types.PInt), + "id" -> PValue(id, Types.PCollection.ofByte) + ) + + override def asPrism: PObject = PObject(baseFields, tpe) +} + +object EncryBaseBox { + + implicit val jsonEncoder: Encoder[EncryBaseBox] = { + case ab: AssetBox => AssetBox.jsonEncoder(ab) + case db: DataBox => DataBox.jsonEncoder(db) + case aib: TokenIssuingBox => TokenIssuingBox.jsonEncoder(aib) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBox.scala b/src/main/scala/encry/net/modifiers/box/EncryBox.scala new file mode 100644 index 0000000..14e6048 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/EncryBox.scala @@ -0,0 +1,14 @@ +package encry.net.modifiers.box + +trait EncryBox[P <: EncryProposition] extends EncryBaseBox { + + override val proposition: P + +} + +object EncryBox { + + type BxTypeId = Byte + + val BoxIdSize = 32 +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala b/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala new file mode 100644 index 0000000..09087a3 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala @@ -0,0 +1,23 @@ +package encry.net.modifiers.box + +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.ADKey + +abstract class EncryBoxStateChangeOperation + +case class Removal(boxId: ADKey) extends EncryBoxStateChangeOperation { + + override def toString: String = s"Removal(id: ${Algos.encode(boxId)})" +} + +case class Insertion(box: Box) extends EncryBoxStateChangeOperation { + + override def toString: String = s"Insertion(id: ${Algos.encode(box.id)})" +} + +case class EncryBoxStateChanges(operations: Seq[EncryBoxStateChangeOperation]){ + + def toAppend: Seq[EncryBoxStateChangeOperation] = operations.filter(_.isInstanceOf[Insertion]) + + def toRemove: Seq[EncryBoxStateChangeOperation] = operations.filter(_.isInstanceOf[Removal]) +} diff --git a/src/main/scala/encry/net/modifiers/box/EncryProposition.scala b/src/main/scala/encry/net/modifiers/box/EncryProposition.scala new file mode 100644 index 0000000..80fd8b6 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/EncryProposition.scala @@ -0,0 +1,46 @@ +package encry.net.modifiers.box + +import io.circe.syntax._ +import io.circe.{Decoder, Encoder} +import org.encryfoundation.common.modifiers.mempool.transaction._ +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import scorex.crypto.signatures.PublicKey + +import scala.util.{Failure, Success, Try} + +case class EncryProposition(contractHash: ContractHash) extends Proposition { + + override type M = EncryProposition + + override def serializer: Serializer[M] = EncryPropositionSerializer +} + +object EncryProposition { + + implicit val jsonEncoder: Encoder[EncryProposition] = (p: EncryProposition) => Map( + "contractHash" -> Algos.encode(p.contractHash).asJson + ).asJson + + implicit val jsonDecoder: Decoder[EncryProposition] = + Decoder.decodeString + .emapTry(Algos.decode) + .map(EncryProposition.apply) + .prepare(_.downField("contractHash")) + + def open: EncryProposition = EncryProposition(OpenContract.contract.hash) + def heightLocked(height: Int): EncryProposition = EncryProposition(HeightLockedContract(height).contract.hash) + def pubKeyLocked(pubKey: PublicKey): EncryProposition = EncryProposition(PubKeyLockedContract(pubKey).contract.hash) + def addressLocked(address: String): EncryProposition = EncryAddress.resolveAddress(address).map { + case p2pk: Pay2PubKeyAddress => EncryProposition(PubKeyLockedContract(p2pk.pubKey).contract.hash) + case p2sh: Pay2ContractHashAddress => EncryProposition(p2sh.contractHash) + }.getOrElse(throw EncryAddress.InvalidAddressException) +} + +object EncryPropositionSerializer extends Serializer[EncryProposition] { + def toBytes(obj: EncryProposition): Array[Byte] = obj.contractHash + def parseBytes(bytes: Array[Byte]): Try[EncryProposition] = + if (bytes.lengthCompare(32) == 0) Success(EncryProposition(bytes)) + else Failure(new Exception("Invalid contract hash length")) +} diff --git a/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala b/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala new file mode 100644 index 0000000..481d06b --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala @@ -0,0 +1,3 @@ +package encry.net.modifiers.box + +trait MonetaryBox extends Box { val amount: Long } diff --git a/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala b/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala new file mode 100644 index 0000000..6319169 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala @@ -0,0 +1,56 @@ +package encry.net.modifiers.box + +import com.google.common.primitives.{Bytes, Longs, Shorts} +import io.circe.Encoder +import io.circe.syntax._ +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.generator.modifiers.box.TokenIssuingBox.TokenId + +import scala.util.Try + +case class TokenIssuingBox(override val proposition: EncryProposition, + override val nonce: Long, + override val amount: Long, + tokenId: TokenId) extends EncryBox[EncryProposition] with MonetaryBox { + + override val typeId: Byte = AssetBox.TypeId +} + +object TokenIssuingBox { + + type TokenId = Array[Byte] + + val TypeId: Byte = 3.toByte + + implicit val jsonEncoder: Encoder[TokenIssuingBox] = (bx: TokenIssuingBox) => Map( + "type" -> TypeId.asJson, + "id" -> Algos.encode(bx.id).asJson, + "proposition" -> bx.proposition.asJson, + "nonce" -> bx.nonce.asJson + ).asJson +} + +object AssetIssuingBoxSerializer extends Serializer[TokenIssuingBox] { + + override def toBytes(obj: TokenIssuingBox): Array[Byte] = { + val propBytes: Array[Byte] = EncryPropositionSerializer.toBytes(obj.proposition) + Bytes.concat( + Shorts.toByteArray(propBytes.length.toShort), + propBytes, + Longs.toByteArray(obj.nonce), + Longs.toByteArray(obj.amount), + obj.tokenId + ) + } + + override def parseBytes(bytes: Array[Byte]): Try[TokenIssuingBox] = Try { + val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) + val iBytes: Array[Byte] = bytes.drop(2) + val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get + val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) + val amount: Long = Longs.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 8)) + val creationId: TokenId = bytes.takeRight(32) + TokenIssuingBox(proposition, nonce, amount, creationId) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala b/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala new file mode 100644 index 0000000..0e4f546 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala @@ -0,0 +1,85 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.AssetIssuingDirectiveProtoMessage +import com.google.common.primitives.{Bytes, Ints, Longs} +import com.google.protobuf.ByteString +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.{Algos, Utils} +import org.encryfoundation.common.utils.constants.TestNetConstants +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import scorex.crypto.hash.Digest32 +import org.encryfoundation.generator.modifiers.box.{Box, EncryProposition, TokenIssuingBox} + +import scala.util.Try + +case class AssetIssuingDirective(contractHash: ContractHash, amount: Long) extends Directive { + + override type M = AssetIssuingDirective + override val typeId: Byte = AssetIssuingDirective.TypeId + override lazy val isValid: Boolean = amount > 0 + + override def boxes(digest: Digest32, idx: Int): Seq[Box] = + Seq(TokenIssuingBox( + EncryProposition(contractHash), + Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), + amount, + Algos.hash(Ints.toByteArray(idx) ++ digest) + )) + + override def serializer: Serializer[M] = AssetIssuingDirectiveSerializer + + override def toDirectiveProto: DirectiveProtoMessage = AssetIssuingDirectiveProtoSerializer.toProto(this) + +} + +object AssetIssuingDirective { + + val TypeId: Byte = 2.toByte + + implicit val jsonEncoder: Encoder[AssetIssuingDirective] = (d: AssetIssuingDirective) => Map( + "typeId" -> d.typeId.asJson, + "contractHash" -> Algos.encode(d.contractHash).asJson, + "amount" -> d.amount.asJson + ).asJson + + implicit val jsonDecoder: Decoder[AssetIssuingDirective] = (c: HCursor) => { + for { + contractHash <- c.downField("contractHash").as[ContractHash](Decoder.decodeString.emapTry(Algos.decode)) + amount <- c.downField("amount").as[Long] + } yield AssetIssuingDirective(contractHash, amount) + } +} + +object AssetIssuingDirectiveProtoSerializer extends ProtoDirectiveSerializer[AssetIssuingDirective] { + + override def toProto(message: AssetIssuingDirective): DirectiveProtoMessage = + DirectiveProtoMessage().withAssetIssuingDirectiveProto(AssetIssuingDirectiveProtoMessage() + .withAmount(message.amount) + .withContractHash(ByteString.copyFrom(message.contractHash)) + ) + + override def fromProto(message: DirectiveProtoMessage): Option[AssetIssuingDirective] = + message.directiveProto.assetIssuingDirectiveProto match { + case Some(value) => Some(AssetIssuingDirective(value.contractHash.toByteArray, value.amount)) + case None => Option.empty[AssetIssuingDirective] + } +} + + +object AssetIssuingDirectiveSerializer extends Serializer[AssetIssuingDirective] { + + override def toBytes(obj: AssetIssuingDirective): Array[Byte] = + Bytes.concat( + obj.contractHash, + Longs.toByteArray(obj.amount) + ) + + override def parseBytes(bytes: Array[Byte]): Try[AssetIssuingDirective] = Try { + val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) + val amount: Long = Longs.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 8)) + AssetIssuingDirective(contractHash, amount) + } +} diff --git a/src/main/scala/encry/net/modifiers/directives/DataDirective.scala b/src/main/scala/encry/net/modifiers/directives/DataDirective.scala new file mode 100644 index 0000000..75fc000 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/DataDirective.scala @@ -0,0 +1,89 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.DataDirectiveProtoMessage +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import com.google.common.primitives.{Bytes, Ints} +import com.google.protobuf.ByteString +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.{Algos, Utils} +import org.encryfoundation.common.utils.constants.TestNetConstants +import scorex.crypto.hash.Digest32 +import org.encryfoundation.generator.modifiers.box.{DataBox, EncryProposition} +import org.encryfoundation.generator.modifiers.directives.Directive.DTypeId + +import scala.util.Try + +case class DataDirective(contractHash: ContractHash, data: Array[Byte]) extends Directive { + + override type M = DataDirective + + override val typeId: DTypeId = DataDirective.TypeId + + override def boxes(digest: Digest32, idx: Int): Seq[DataBox] = + Seq(DataBox(EncryProposition(contractHash), + Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), data)) + + val MaxDataLength: Int = 1000 + + override lazy val isValid: Boolean = data.length <= MaxDataLength + + override def serializer: Serializer[M] = DataDirectiveSerializer + + override def toDirectiveProto: DirectiveProtoMessage = DataDirectiveProtoSerializer.toProto(this) + +} + +object DataDirective { + + val TypeId: DTypeId = 5.toByte + + implicit val jsonEncoder: Encoder[DataDirective] = (d: DataDirective) => Map( + "typeId" -> d.typeId.asJson, + "contractHash" -> Algos.encode(d.contractHash).asJson, + "data" -> Algos.encode(d.data).asJson + ).asJson + + implicit val jsonDecoder: Decoder[DataDirective] = (c: HCursor) => { + for { + contractHash <- c.downField("contractHash").as[String] + dataEnc <- c.downField("data").as[String] + } yield Algos.decode(contractHash) + .flatMap(ch => Algos.decode(dataEnc).map(data => DataDirective(ch, data))) + .getOrElse(throw new Exception("Decoding failed")) + } +} + + +object DataDirectiveProtoSerializer extends ProtoDirectiveSerializer[DataDirective] { + + override def toProto(message: DataDirective): DirectiveProtoMessage = DirectiveProtoMessage() + .withDataDirectiveProto(DataDirectiveProtoMessage() + .withContractHash(ByteString.copyFrom(message.contractHash)) + .withData(ByteString.copyFrom(message.data))) + + override def fromProto(message: DirectiveProtoMessage): Option[DataDirective] = + message.directiveProto.dataDirectiveProto match { + case Some(value) => Some(DataDirective(value.contractHash.toByteArray, value.data.toByteArray)) + case None => Option.empty[DataDirective] + } +} + +object DataDirectiveSerializer extends Serializer[DataDirective] { + + override def toBytes(obj: DataDirective): Array[Byte] = + Bytes.concat( + obj.contractHash, + Ints.toByteArray(obj.data.length), + obj.data + ) + + override def parseBytes(bytes: Array[Byte]): Try[DataDirective] = Try { + val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) + val dataLen: Int = Ints.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 4)) + val data: Array[DTypeId] = bytes.slice(TestNetConstants.DigestLength + 4, TestNetConstants.DigestLength + 4 + dataLen) + DataDirective(contractHash, data) + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/directives/Directive.scala b/src/main/scala/encry/net/modifiers/directives/Directive.scala new file mode 100644 index 0000000..aab6b00 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/Directive.scala @@ -0,0 +1,45 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import io.circe._ +import org.encryfoundation.common.serialization.BytesSerializable +import scorex.crypto.hash.Digest32 +import org.encryfoundation.generator.modifiers.box.Box + +trait Directive extends BytesSerializable { + + val typeId: Byte + val isValid: Boolean + + def boxes(digest: Digest32, idx: Int): Seq[Box] + + def toDirectiveProto: DirectiveProtoMessage +} + +object Directive { + + type DTypeId = Byte + + implicit val jsonEncoder: Encoder[Directive] = { + case td: TransferDirective => TransferDirective.jsonEncoder(td) + case aid: AssetIssuingDirective => AssetIssuingDirective.jsonEncoder(aid) + case sad: ScriptedAssetDirective => ScriptedAssetDirective.jsonEncoder(sad) + case dad: DataDirective => DataDirective.jsonEncoder(dad) + case _ => throw new Exception("Incorrect directive type") + } + + implicit val jsonDecoder: Decoder[Directive] = { + Decoder.instance { c => + c.downField("typeId").as[DTypeId] match { + case Right(s) => s match { + case TransferDirective.TypeId => TransferDirective.jsonDecoder(c) + case AssetIssuingDirective.TypeId => AssetIssuingDirective.jsonDecoder(c) + case ScriptedAssetDirective.TypeId => ScriptedAssetDirective.jsonDecoder(c) + case DataDirective.TypeId => DataDirective.jsonDecoder(c) + case _ => Left(DecodingFailure("Incorrect directive typeID", c.history)) + } + case Left(_) => Left(DecodingFailure("None typeId", c.history)) + } + } + } +} diff --git a/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala b/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala new file mode 100644 index 0000000..852ff40 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala @@ -0,0 +1,42 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.DirectiveProto + +import scala.util.{Failure, Try} +import org.encryfoundation.common.serialization.Serializer + + +trait ProtoDirectiveSerializer[T] { + + def toProto(message: T): DirectiveProtoMessage + + def fromProto(message: DirectiveProtoMessage): Option[T] +} + +object DirectiveProtoSerializer { + def fromProto(message: DirectiveProtoMessage): Option[Directive] = message.directiveProto match { + case DirectiveProto.AssetIssuingDirectiveProto(_) => AssetIssuingDirectiveProtoSerializer.fromProto(message) + case DirectiveProto.DataDirectiveProto(_) => DataDirectiveProtoSerializer.fromProto(message) + case DirectiveProto.TransferDirectiveProto(_) => TransferDirectiveProtoSerializer.fromProto(message) + case DirectiveProto.ScriptedAssetDirectiveProto(_) => ScriptedAssetDirectiveProtoSerializer.fromProto(message) + case DirectiveProto.Empty => None + } +} + +object DirectiveSerializer extends Serializer[Directive] { + + override def toBytes(obj: Directive): Array[Byte] = obj match { + case td: TransferDirective => TransferDirective.TypeId +: TransferDirectiveSerializer.toBytes(td) + case aid: AssetIssuingDirective => AssetIssuingDirective.TypeId +: AssetIssuingDirectiveSerializer.toBytes(aid) + case sad: ScriptedAssetDirective => ScriptedAssetDirective.TypeId +: ScriptedAssetDirectiveSerializer.toBytes(sad) + case m => throw new Exception(s"Serialization of unknown directive type: $m") + } + + override def parseBytes(bytes: Array[Byte]): Try[Directive] = Try(bytes.head).flatMap { + case TransferDirective.`TypeId` => TransferDirectiveSerializer.parseBytes(bytes.tail) + case AssetIssuingDirective.`TypeId` => AssetIssuingDirectiveSerializer.parseBytes(bytes.tail) + case ScriptedAssetDirective.`TypeId` => ScriptedAssetDirectiveSerializer.parseBytes(bytes.tail) + case t => Failure(new Exception(s"Got unknown typeId: $t")) + } +} diff --git a/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala b/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala new file mode 100644 index 0000000..de074f9 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala @@ -0,0 +1,100 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.{ADKeyProto, ScriptedAssetDirectiveProtoMessage} + +import scala.util.Try +import com.google.common.primitives.{Bytes, Ints, Longs} +import com.google.protobuf.ByteString +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.{Algos, Utils} +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.common.utils.constants.TestNetConstants +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import scorex.crypto.hash.Digest32 +import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, EncryProposition} + +case class ScriptedAssetDirective(contractHash: ContractHash, + amount: Long, + tokenIdOpt: Option[ADKey] = None) extends Directive { + + override type M = ScriptedAssetDirective + + override val typeId: Byte = ScriptedAssetDirective.TypeId + + override def boxes(digest: Digest32, idx: Int): Seq[Box] = + Seq(AssetBox(EncryProposition(contractHash), + Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount)) + + override lazy val isValid: Boolean = amount > 0 + + override def serializer: Serializer[M] = ScriptedAssetDirectiveSerializer + + lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty + + override def toDirectiveProto: DirectiveProtoMessage = ScriptedAssetDirectiveProtoSerializer.toProto(this) + +} + +object ScriptedAssetDirective { + + val TypeId: Byte = 3.toByte + + implicit val jsonEncoder: Encoder[ScriptedAssetDirective] = (d: ScriptedAssetDirective) => Map( + "typeId" -> d.typeId.asJson, + "contractHash" -> Algos.encode(d.contractHash).asJson, + "amount" -> d.amount.asJson, + "tokenId" -> d.tokenIdOpt.map(id => Algos.encode(id)).asJson + ).asJson + + implicit val jsonDecoder: Decoder[ScriptedAssetDirective] = (c: HCursor) => for { + contractHash <- c.downField("contractHash").as[ContractHash](Decoder.decodeString.emapTry(Algos.decode)) + amount <- c.downField("amount").as[Long] + tokenIdOpt <- c.downField("tokenId").as[Option[ADKey]](Decoder.decodeOption(Decoder.decodeString.emapTry(Algos.decode).map(ADKey @@ _))) + } yield ScriptedAssetDirective(contractHash, amount, tokenIdOpt) +} + +object ScriptedAssetDirectiveProtoSerializer extends ProtoDirectiveSerializer[ScriptedAssetDirective] { + + override def toProto(message: ScriptedAssetDirective): DirectiveProtoMessage ={ + val initialDirective: ScriptedAssetDirectiveProtoMessage = ScriptedAssetDirectiveProtoMessage() + .withContractHash(ByteString.copyFrom(message.contractHash)) + .withAmount(message.amount) + val saDirective: ScriptedAssetDirectiveProtoMessage = message.tokenIdOpt match { + case Some(value) => initialDirective.withTokenIdOpt( ADKeyProto().withTokenIdOpt(ByteString.copyFrom(value))) + case None => initialDirective + } + DirectiveProtoMessage().withScriptedAssetDirectiveProto(saDirective) + } + + override def fromProto(message: DirectiveProtoMessage): Option[ScriptedAssetDirective] = + message.directiveProto.scriptedAssetDirectiveProto match { + case Some(value) => Some(ScriptedAssetDirective( + value.contractHash.toByteArray, + value.amount, + value.tokenIdOpt.map(x => ADKey @@ x.tokenIdOpt.toByteArray)) + ) + case None => Option.empty[ScriptedAssetDirective] + } +} + +object ScriptedAssetDirectiveSerializer extends Serializer[ScriptedAssetDirective] { + + override def toBytes(obj: ScriptedAssetDirective): Array[Byte] = + Bytes.concat( + obj.contractHash, + Longs.toByteArray(obj.amount), + obj.tokenIdOpt.getOrElse(Array.empty) + ) + + override def parseBytes(bytes: Array[Byte]): Try[ScriptedAssetDirective] = Try { + val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) + val amount: Long = Longs.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 8)) + val tokenIdOpt: Option[ADKey] = if ((bytes.length - (TestNetConstants.DigestLength + 8)) == TestNetConstants.ModifierIdSize) { + Some(ADKey @@ bytes.takeRight(TestNetConstants.ModifierIdSize)) + } else None + ScriptedAssetDirective(contractHash, amount, tokenIdOpt) + } +} diff --git a/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala b/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala new file mode 100644 index 0000000..c5ecbd8 --- /dev/null +++ b/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala @@ -0,0 +1,112 @@ +package encry.net.modifiers.directives + +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.{ADKeyProto, TransferDirectiveProtoMessage} +import com.google.common.primitives.{Bytes, Ints, Longs} +import com.google.protobuf.ByteString +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, HCursor} +import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress +import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address +import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.TaggedTypes.ADKey +import org.encryfoundation.common.utils.{Algos, Utils} +import org.encryfoundation.common.utils.constants.{Constants, TestNetConstants} +import scorex.crypto.hash.Digest32 +import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, EncryProposition} + +import scala.util.Try + +case class TransferDirective(address: Address, + amount: Long, + tokenIdOpt: Option[ADKey] = None) extends Directive { + + override type M = TransferDirective + + override val typeId: Byte = TransferDirective.TypeId + + override def boxes(digest: Digest32, idx: Int): Seq[Box] = + Seq(AssetBox(EncryProposition.addressLocked(address), + Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount, tokenIdOpt)) + + override lazy val isValid: Boolean = amount > 0 && EncryAddress.resolveAddress(address).isSuccess + + override def serializer: Serializer[M] = TransferDirectiveSerializer + + lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty + + override def toDirectiveProto: DirectiveProtoMessage = TransferDirectiveProtoSerializer.toProto(this) + +} + +object TransferDirective { + + val TypeId: Byte = 1.toByte + + implicit val jsonEncoder: Encoder[TransferDirective] = (d: TransferDirective) => Map( + "typeId" -> d.typeId.asJson, + "address" -> d.address.toString.asJson, + "amount" -> d.amount.asJson, + "tokenId" -> d.tokenIdOpt.map(id => Algos.encode(id)).getOrElse("null").asJson + ).asJson + + implicit val jsonDecoder: Decoder[TransferDirective] = (c: HCursor) => { + for { + address <- c.downField("address").as[String] + amount <- c.downField("amount").as[Long] + tokenIdOpt <- c.downField("tokenId").as[Option[String]] + } yield { + TransferDirective( + address, + amount, + tokenIdOpt.flatMap(id => Algos.decode(id).map(ADKey @@ _).toOption) + ) + } + } +} + +object TransferDirectiveProtoSerializer extends ProtoDirectiveSerializer[TransferDirective] { + + override def toProto(message: TransferDirective): DirectiveProtoMessage = { + val initialDirective: TransferDirectiveProtoMessage = TransferDirectiveProtoMessage() + .withAddress(message.address) + .withAmount(message.amount) + val transferDirective: TransferDirectiveProtoMessage = message.tokenIdOpt match { + case Some(value) => initialDirective.withTokenIdOpt(ADKeyProto().withTokenIdOpt(ByteString.copyFrom(value))) + case None => initialDirective + } + DirectiveProtoMessage().withTransferDirectiveProto(transferDirective) + } + + override def fromProto(message: DirectiveProtoMessage): Option[TransferDirective] = + message.directiveProto.transferDirectiveProto match { + case Some(value) => Some(TransferDirective( + value.address, + value.amount, + value.tokenIdOpt.map(x => ADKey @@ x.tokenIdOpt.toByteArray)) + ) + case None => Option.empty[TransferDirective] + } +} + +object TransferDirectiveSerializer extends Serializer[TransferDirective] { + + override def toBytes(obj: TransferDirective): Array[Byte] = { + val address: Array[Byte] = obj.address.getBytes(Algos.charset) + address.length.toByte +: Bytes.concat( + address, + Longs.toByteArray(obj.amount), + obj.tokenIdOpt.getOrElse(Array.empty) + ) + } + + override def parseBytes(bytes: Array[Byte]): Try[TransferDirective] = Try { + val addressLen: Int = bytes.head.toInt + val address: Address = new String(bytes.slice(1, 1 + addressLen), Algos.charset) + val amount: Long = Longs.fromByteArray(bytes.slice(1 + addressLen, 1 + addressLen + 8)) + val tokenIdOpt: Option[ADKey] = if ((bytes.length - (1 + addressLen + 8)) == TestNetConstants.ModifierIdSize) { + Some(ADKey @@ bytes.takeRight(TestNetConstants.ModifierIdSize)) + } else None + TransferDirective(address, amount, tokenIdOpt) + } +} diff --git a/src/main/scala/encry/net/network/BasicMessagesRepo.scala b/src/main/scala/encry/net/network/BasicMessagesRepo.scala new file mode 100644 index 0000000..7866ddd --- /dev/null +++ b/src/main/scala/encry/net/network/BasicMessagesRepo.scala @@ -0,0 +1,458 @@ +package encry.net.network + +import java.net.InetSocketAddress +import NetworkMessagesProto.GeneralizedNetworkProtoMessage +import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage +import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage._ +import NetworkMessagesProto.GeneralizedNetworkProtoMessage.ModifiersProtoMessage.MapFieldEntry +import NetworkMessagesProto.GeneralizedNetworkProtoMessage.{GetPeersProtoMessage => GetPeersProto, HandshakeProtoMessage => hPM, InvProtoMessage => InvPM, ModifiersProtoMessage => ModifiersPM, PeersProtoMessage => PeersPM, RequestModifiersProtoMessage => rModsPM, SyncInfoProtoMessage => sIPM} +import SyntaxMessageProto.InetSocketAddressProtoMessage +import akka.actor.ActorRef +import com.google.protobuf.{ByteString => GoogleByteString} +import akka.util.{ByteString => AkkaByteString} +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.network.BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} +import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import org.encryfoundation.generator.utils.Settings +import scorex.crypto.hash.Blake2b256 +import scala.util.Try + +object BasicMessagesRepo extends StrictLogging { + + object BasicMsgDataTypes { + type InvData = (ModifierTypeId, Seq[ModifierId]) + type ModifiersData = (ModifierTypeId, Map[ModifierId, Array[Byte]]) + } + + sealed trait NetworkMessage { + + val messageName: String + + val NetworkMessageTypeID: Byte + + def checkSumBytes(innerMessage: InnerMessage): Array[Byte] + + def toInnerMessage: InnerMessage + + def isValid(setting: Settings): Boolean + } + + sealed trait ProtoNetworkMessagesSerializer[T] { + + def toProto(message: T): InnerMessage + + def fromProto(message: InnerMessage): Option[T] + } + + object MessageOptions { + + val MAGIC: GoogleByteString = GoogleByteString.copyFrom(Array[Byte](0x12: Byte, 0x34: Byte, 0x56: Byte, 0x78: Byte)) + + val ChecksumLength: Int = 4 + + def calculateCheckSum(bytes: Array[Byte]): GoogleByteString = + GoogleByteString.copyFrom(Blake2b256.hash(bytes).take(ChecksumLength)) + } + + /** + * @param message - message, received from network + * @param source - sender of received message + * + * This case class transfers network message from PeerConnectionHandler actor to the NetworkController. + * Main duty is to transfer message from network with sender of it message to the NetworkController as an end point. + */ + + case class MessageFromNetwork(message: NetworkMessage, source: Option[ConnectedPeer]) + + /** + * This object contains functions, connected with protobuf serialization to the generalized network message. + * + * toProto function first computes checkSum as a hash from NetworkMessageProtoSerialized bytes. Next, + * assembles GeneralizedMessage, which contains from first dour calculated checkSum bytes, MAGIC constant, network message. + * + * fromProto function tries to serialize raw bytes to GeneralizedMessage and compare + * magic bytes. Next, tries to collect networkMessage. + */ + + object GeneralizedNetworkMessage { + + def toProto(message: NetworkMessage): GeneralizedNetworkProtoMessage = { + val innerMessage: InnerMessage = message.toInnerMessage + val calculatedCheckSum: GoogleByteString = MessageOptions.calculateCheckSum(message.checkSumBytes(innerMessage)) + GeneralizedNetworkProtoMessage() + .withMagic(MessageOptions.MAGIC) + .withChecksum(calculatedCheckSum) + .withInnerMessage(innerMessage) + } + + def fromProto(message: AkkaByteString): Try[NetworkMessage] = Try { + val netMessage: GeneralizedNetworkProtoMessage = + GeneralizedNetworkProtoMessage.parseFrom(message.toArray) + require(netMessage.magic.toByteArray.sameElements(MessageOptions.MAGIC.toByteArray), + s"Wrong MAGIC! Got ${netMessage.magic.toByteArray.mkString(",")}") + netMessage.innerMessage match { + case InnerMessage.SyncInfoProtoMessage(_) => + checkMessageValidity(SyncInfoNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.InvProtoMessage(_) => + checkMessageValidity(InvNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.RequestModifiersProtoMessage(_) => + checkMessageValidity(RequestModifiersSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.ModifiersProtoMessage(_) => + checkMessageValidity(ModifiersNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.GetPeersProtoMessage(_) => + checkMessageValidity(GetPeersNetworkMessage.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.PeersProtoMessage(_) => + checkMessageValidity(PeersNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.HandshakeProtoMessage(_) => + checkMessageValidity(HandshakeSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) + case InnerMessage.Empty => throw new RuntimeException("Empty inner message!") + case _ => throw new RuntimeException("Can't find serializer for received message!") + } + }.flatten + + /** + * @param serializer - function, which takes as a parameter other function, which provides serialisation to NetworkMessage. + * As a result it gives serialized network message contained in option. + * @param innerMessage - type of protobuf generalized nested message. + * @param requiredBytes - checkSum, stored in received message. + * @return - serialized network message contained in option. + * + * This function provides validation check for inner message parsing and compares checkSum bytes. + */ + + def checkMessageValidity(serializer: InnerMessage => Option[NetworkMessage], + innerMessage: InnerMessage, + requiredBytes: GoogleByteString): Try[NetworkMessage] = Try { + val serializedMessage: Option[NetworkMessage] = serializer(innerMessage) + require(serializedMessage.isDefined, "Nested message is invalid!") + val networkMessage: NetworkMessage = serializedMessage.get + val checkSumBytes: Array[Byte] = networkMessage.checkSumBytes(innerMessage) + val calculatedCheckSumBytes = MessageOptions.calculateCheckSum(checkSumBytes) + require(calculatedCheckSumBytes.toByteArray.sameElements(requiredBytes.toByteArray), + "Checksum of received message is invalid!") + networkMessage + } + } + + /** + * @param esi - EncrySyncInfo case class which contains sequence of modifiers ids/ + * + * This message is a nested type of generalized network message. It's sent with the aim to show other peer, + * which last N modifiers this peer has. + * Response for this message is an InvMessage which contains all modifiers older than local. + */ + + case class SyncInfoNetworkMessage(esi: SyncInfo) extends NetworkMessage { + + override val messageName: String = "Sync" + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.syncInfoProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override def toInnerMessage: InnerMessage = SyncInfoNetworkMessageSerializer.toProto(this) + + override val NetworkMessageTypeID: Byte = SyncInfoNetworkMessage.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = + if (esi.lastHeaderIds.size <= setting.network.syncPacketLength) true else false + } + + object SyncInfoNetworkMessage { + + val NetworkMessageTypeID: Byte = 65: Byte + } + + object SyncInfoNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[SyncInfoNetworkMessage] { + + override def toProto(message: SyncInfoNetworkMessage): InnerMessage = + SyncInfoProtoMessage(sIPM().withLastHeaderIds(message.esi.lastHeaderIds.map(GoogleByteString.copyFrom))) + + override def fromProto(message: InnerMessage): Option[SyncInfoNetworkMessage] = message.syncInfoProtoMessage match { + case Some(value) => + Some(SyncInfoNetworkMessage(SyncInfo(value.lastHeaderIds.map(modId => ModifierId @@ modId.toByteArray)))) + case None => Option.empty[SyncInfoNetworkMessage] + } + } + + /** + * @param data - modifiersIds sequence. + * + * This message sends as a respons for SyncInfoMessage or to show other peers locally generated modifier. + */ + + case class InvNetworkMessage(data: InvData) extends NetworkMessage { + + override val messageName: String = "Inv" + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.invProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override def toInnerMessage: InnerMessage = InvNetworkMessageSerializer.toProto(this) + + override val NetworkMessageTypeID: Byte = InvNetworkMessage.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = + if (data._2.size <= setting.network.syncPacketLength) true else false + } + + object InvNetworkMessage { + + val NetworkMessageTypeID: Byte = 55: Byte + } + + object InvNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[InvNetworkMessage] { + + def toProto(message: InvNetworkMessage): InnerMessage = InvProtoMessage(InvPM() + .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) + .withModifiers(message.data._2.map(elem => GoogleByteString.copyFrom(elem))) + ) + + def fromProto(message: InnerMessage): Option[InvNetworkMessage] = message.invProtoMessage match { + case Some(value) => value.modifiers match { + case mods: Seq[_] if mods.nonEmpty => Some(InvNetworkMessage( + ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> value.modifiers.map(x => ModifierId @@ x.toByteArray))) + case _ => Option.empty[InvNetworkMessage] + } + case None => Option.empty[InvNetworkMessage] + } + } + + /** + * @param data - modifiersIds sequence. + * + * This message sends to the peer to request missing in local history modifiers. + */ + + case class RequestModifiersNetworkMessage(data: InvData) extends NetworkMessage { + + override val messageName: String = "RequestModifier" + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.requestModifiersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override def toInnerMessage: InnerMessage = RequestModifiersSerializer.toProto(this) + + override val NetworkMessageTypeID: Byte = RequestModifiersNetworkMessage.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = + if (data._2.size <= setting.network.syncPacketLength) true else false + } + + object RequestModifiersNetworkMessage { + + val NetworkMessageTypeID: Byte = 22: Byte + } + + object RequestModifiersSerializer extends ProtoNetworkMessagesSerializer[RequestModifiersNetworkMessage] { + + override def toProto(message: RequestModifiersNetworkMessage): InnerMessage = + RequestModifiersProtoMessage( + rModsPM() + .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) + .withModifiers(message.data._2.map(elem => GoogleByteString.copyFrom(elem))) + ) + + override def fromProto(message: InnerMessage): Option[RequestModifiersNetworkMessage] = + message.requestModifiersProtoMessage match { + case Some(value) => value.modifiers match { + case mods: Seq[_] if mods.nonEmpty => Some(RequestModifiersNetworkMessage( + ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> value.modifiers.map(x => ModifierId @@ x.toByteArray))) + case _ => Option.empty[RequestModifiersNetworkMessage] + } + case None => Option.empty[RequestModifiersNetworkMessage] + } + } + + /** + * @param data - map with modifierId as a key and serialized to protobuf modifiers as a value. + * + * This message sends as a RESPONSE ONLY to RequestModifiers message. + */ + + case class ModifiersNetworkMessage(data: ModifiersData) extends NetworkMessage { + + override val messageName: String = "Modifier" + + override def toInnerMessage: InnerMessage = ModifiersNetworkMessageSerializer.toProto(this) + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.modifiersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override val NetworkMessageTypeID: Byte = ModifiersNetworkMessage.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = + if (data._2.size <= setting.network.syncPacketLength) true else false + } + + object ModifiersNetworkMessage { + + val NetworkMessageTypeID: Byte = 33: Byte + } + + object ModifiersNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[ModifiersNetworkMessage] { + + override def toProto(message: ModifiersNetworkMessage): InnerMessage = ModifiersProtoMessage(ModifiersPM() + .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) + .withMap(message.data._2.map(element => + MapFieldEntry().withKey(GoogleByteString.copyFrom(element._1)).withValue(GoogleByteString.copyFrom(element._2))).toSeq)) + + override def fromProto(message: InnerMessage): Option[ModifiersNetworkMessage] = message.modifiersProtoMessage match { + case Some(value) => Some(ModifiersNetworkMessage(ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> + value.map.map(element => ModifierId @@ element.key.toByteArray -> element.value.toByteArray).toMap)) + case None => Option.empty[ModifiersNetworkMessage] + } + } + + /** + * This network message sends to a random peer as a request for receiver's known peers. + */ + + case object GetPeersNetworkMessage extends NetworkMessage { + + override val messageName: String = "GetPeers message" + + override def toInnerMessage: InnerMessage = toProto + + def toProto: InnerMessage = GetPeersProtoMessage(GetPeersProto()) + + def fromProto(message: InnerMessage): Option[GetPeersNetworkMessage.type] = message.getPeersProtoMessage match { + case Some(_) => Some(GetPeersNetworkMessage) + case None => Option.empty[GetPeersNetworkMessage.type] + } + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.getPeersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override val NetworkMessageTypeID: Byte = 1: Byte + + override def isValid(setting: Settings): Boolean = true + } + + /** + * @param peers - sequence of known by this peer other peers. + * + * This network message sends directly to the sender of 'GetPeers' message. + */ + + case class PeersNetworkMessage(peers: Seq[InetSocketAddress]) extends NetworkMessage { + + override val messageName: String = "Peers message" + + override def toInnerMessage: InnerMessage = PeersNetworkMessageSerializer.toProto(this) + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.peersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override val NetworkMessageTypeID: Byte = PeersNetworkMessage.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = true + } + + object PeersNetworkMessage { + + val NetworkMessageTypeID: Byte = 2: Byte + } + + object PeersNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[PeersNetworkMessage] { + + override def toProto(message: PeersNetworkMessage): InnerMessage = PeersProtoMessage( + PeersPM().withPeers( + message.peers.map(element => InetSocketAddressProtoMessage().withHost(element.getHostName).withPort(element.getPort)) + )) + + override def fromProto(message: InnerMessage): Option[PeersNetworkMessage] = message.peersProtoMessage match { + case Some(value) => value.peers match { + case peers: Seq[_] if peers.nonEmpty => + Some(PeersNetworkMessage(value.peers.map(element => new InetSocketAddress(element.host, element.port)))) + case _ => Option.empty[PeersNetworkMessage] + } + case None => Option.empty[PeersNetworkMessage] + } + } + + /** + * @param protocolVersion - peer network communication protocol version + * @param nodeName - peer name + * @param declaredAddress - peer address + * @param time - handshake creation time + * + * This network message are using for set up network connection between two peers. + * First peer sends this message to the second one. Second peer processes this message + * and send back response with it's own handshake. After both + * peers received handshakes from each other, network connection raises. + */ + + case class Handshake(protocolVersion: Array[Byte], + nodeName: String, + declaredAddress: Option[InetSocketAddress], + time: Long) extends NetworkMessage { + + require(protocolVersion.length > 0, "Empty protocol version!") + + override val messageName: String = "Handshake" + + override def toInnerMessage: InnerMessage = HandshakeSerializer.toProto(this) + + override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = + innerMessage.handshakeProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) + + override val NetworkMessageTypeID: Byte = Handshake.NetworkMessageTypeID + + override def isValid(setting: Settings): Boolean = true + } + + object Handshake { + + val NetworkMessageTypeID: Byte = 75: Byte + } + + object HandshakeSerializer extends ProtoNetworkMessagesSerializer[Handshake] { + + override def toProto(message: Handshake): InnerMessage = { + val initialHandshakeProto: hPM = hPM() + .withProtocolVersion(GoogleByteString.copyFrom(message.protocolVersion)) + .withNodeName(message.nodeName) + .withTime(message.time) + val updatedHandshakeProto: hPM = message.declaredAddress match { + case Some(value) => initialHandshakeProto.withDeclaredAddress(InetSocketAddressProtoMessage() + .withHost(value.getHostName) + .withPort(value.getPort) + ) + case None => initialHandshakeProto + } + HandshakeProtoMessage(updatedHandshakeProto) + } + + override def fromProto(message: InnerMessage): Option[Handshake] = message.handshakeProtoMessage match { + case Some(value) => value.nodeName match { + case name: String if name.nonEmpty => Some( + Handshake( + value.protocolVersion.toByteArray, + value.nodeName, + value.declaredAddress.map(element => new InetSocketAddress(element.host, element.port)), + value.time + )) + case _ => Option.empty[Handshake] + } + case None => Option.empty[Handshake] + } + } + + sealed trait ConnectionType + case object Incoming extends ConnectionType + case object Outgoing extends ConnectionType + + case class ConnectedPeer(socketAddress: InetSocketAddress, + handlerRef: ActorRef, + direction: ConnectionType, + handshake: Handshake) { + + override def toString: String = s"ConnectedPeer($socketAddress)" + } + + case class SyncInfo(lastHeaderIds: Seq[ModifierId]) { + + def startingPoints = lastHeaderIds.map(id => ModifierTypeId @@ (101: Byte) -> id) + + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala new file mode 100644 index 0000000..b0be275 --- /dev/null +++ b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala @@ -0,0 +1,48 @@ +package encry.net.network + +import akka.actor.{Actor, Props} +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.generator.actors.Generator.TransactionForCommit +import org.encryfoundation.generator.network.BasicMessagesRepo._ +import org.encryfoundation.generator.network.NetworkMessagesHandler.BroadcastInvForTx +import org.encryfoundation.generator.modifiers.{Transaction, TransactionProtoSerializer} +import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import org.encryfoundation.generator.utils.{CoreTaggedTypes, Settings} +import supertagged.@@ + +class NetworkMessagesHandler(settings: Settings) extends Actor with StrictLogging { + + var localGeneratedTransactions: Seq[Transaction] = Seq.empty + + override def receive: Receive = { + case TransactionForCommit(transaction) => + localGeneratedTransactions :+= transaction + context.parent ! BroadcastInvForTx(transaction) + + case MessageFromNetwork(message, _) => message match { + case RequestModifiersNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => + logger.debug(s"Got request modifiers on NMH") + val tmpInv: Seq[String] = invData._2.map(Algos.encode) + val transactions: Seq[Transaction] = localGeneratedTransactions.filter(tx => tmpInv.contains(Algos.encode(tx.id))) + val forSend: Map[Array[Byte] @@ CoreTaggedTypes.ModifierId.Tag, Array[Byte]] = transactions.map { tx => + ModifierId @@ tx.id -> TransactionProtoSerializer.toProto(tx).toByteArray + }.toMap + sender() ! ModifiersNetworkMessage(ModifierTypeId @@ Transaction.modifierTypeId -> forSend) + val tmpTxs = transactions.map(tx => Algos.encode(tx.id)) + localGeneratedTransactions = localGeneratedTransactions.filter(tx => + !tmpTxs.contains(Algos.encode(tx.id)) + ) + logger.debug(s"Sent modifiers to node.") + case _ => + } + case _ => + } +} + +object NetworkMessagesHandler { + + case class BroadcastInvForTx(tx: Transaction) + + def props(settings: Settings) = Props(new NetworkMessagesHandler(settings)) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/network/NetworkServer.scala b/src/main/scala/encry/net/network/NetworkServer.scala new file mode 100644 index 0000000..2f4a590 --- /dev/null +++ b/src/main/scala/encry/net/network/NetworkServer.scala @@ -0,0 +1,110 @@ +package encry.net.network + +import java.net.InetSocketAddress + +import akka.actor.{Actor, ActorRef, ActorSystem, Props} +import akka.io.Tcp.SO.KeepAlive +import akka.io.Tcp._ +import akka.io.{IO, Tcp} +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.actors.Generator +import org.encryfoundation.generator.actors.Generator.TransactionForCommit +import org.encryfoundation.generator.network.BasicMessagesRepo.{InvNetworkMessage, Outgoing} +import org.encryfoundation.generator.network.NetworkMessagesHandler.BroadcastInvForTx +import org.encryfoundation.generator.network.NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} +import org.encryfoundation.generator.network.PeerHandler._ +import org.encryfoundation.generator.modifiers.Transaction +import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import org.encryfoundation.generator.utils.Mnemonic.createPrivKey +import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} + +import scala.concurrent.duration._ +import scala.concurrent.ExecutionContextExecutor + +class NetworkServer(settings: Settings, + timeProvider: NetworkTimeProvider, + influx: Option[ActorRef]) extends Actor with StrictLogging { + + implicit val system: ActorSystem = context.system + implicit val ec: ExecutionContextExecutor = context.dispatcher + + var isConnected = false + + val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(settings)) + + var tmpConnectionHandler: Option[ActorRef] = None + + val selfPeer: InetSocketAddress = + new InetSocketAddress(settings.network.bindAddressHost, settings.network.bindAddressPort) + + val connectingPeer: InetSocketAddress = + new InetSocketAddress(settings.network.peerForConnectionHost, settings.network.peerForConnectionPort) + + IO(Tcp) ! Bind(self, selfPeer) + + override def receive: Receive = { + case Bound(localAddress) => + logger.info(s"Local app was successfully bound to $localAddress!") + context.system.scheduler.schedule(5.seconds, 30.seconds, self, CheckConnection) + + case CommandFailed(add: Bind) => + logger.info(s"Failed to bind to ${add.localAddress}.") + context.stop(self) + + case Connected(remote, _) if !isConnected && remote.getAddress == connectingPeer.getAddress => + val handler: ActorRef = context.actorOf( + PeerHandler.props(remote, sender(), settings, timeProvider, Outgoing, messagesHandler) + ) + logger.info(s"Successfully connected to $remote. Creating handler: $handler.") + isConnected = true + tmpConnectionHandler = Some(handler) + sender ! Register(handler) + sender ! ResumeReading + + case Connected(remote, _) => logger.info(s"Remote: $remote try to connect but isConnected: $isConnected.") + + case CommandFailed(c: Connect) => + isConnected = false + tmpConnectionHandler = None + logger.info(s"Failed to connect to: ${c.remoteAddress}") + + case CheckConnection if !isConnected => + IO(Tcp) ! Connect(connectingPeer, options = KeepAlive(true) :: Nil, timeout = Some(5.seconds)) + logger.info(s"Trying to connect to $connectingPeer.") + + case CheckConnection => + logger.info(s"Triggered CheckConnection. Current connection is: $isConnected") + + case RemovePeerFromConnectionList(peer) => + isConnected = false + tmpConnectionHandler = None + logger.info(s"Disconnected from $peer.") + + case BroadcastInvForTx(tx) => + val inv: BasicMessagesRepo.NetworkMessage = + InvNetworkMessage(ModifierTypeId @@ Transaction.modifierTypeId -> Seq(ModifierId @@ tx.id)) + tmpConnectionHandler.foreach(_ ! inv) + logger.debug(s"Send inv message to remote.") + + case ConnectionSetupSuccessfully => + settings.peers.foreach { peer => + logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") + system.actorOf( + Generator.props(settings, createPrivKey(Some(peer.mnemonicKey)), peer, influx, self), peer.explorerHost) + } + + case msg@TransactionForCommit(_) => messagesHandler ! msg + + case msg => logger.info(s"Got strange message on NetworkServer: $msg.") + } +} + +object NetworkServer { + + case object CheckConnection + + case object ConnectionSetupSuccessfully + + def props(settings: Settings, timeProvider: NetworkTimeProvider, influx: Option[ActorRef]): Props = + Props(new NetworkServer(settings, timeProvider, influx)) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/network/PeerHandler.scala b/src/main/scala/encry/net/network/PeerHandler.scala new file mode 100644 index 0000000..59fb8c6 --- /dev/null +++ b/src/main/scala/encry/net/network/PeerHandler.scala @@ -0,0 +1,232 @@ +package encry.net.network + +import java.net.InetSocketAddress +import java.nio.ByteOrder +import akka.actor.{Actor, ActorRef, Cancellable, Props} +import akka.io.Tcp +import akka.io.Tcp._ +import akka.util.{ByteString, CompactByteString} +import com.google.common.primitives.Ints +import com.typesafe.scalalogging.StrictLogging +import org.encryfoundation.generator.network.PeerHandler._ +import org.encryfoundation.generator.network.BasicMessagesRepo._ +import org.encryfoundation.generator.network.NetworkServer.ConnectionSetupSuccessfully +import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} +import scala.annotation.tailrec +import scala.collection.immutable.HashMap +import scala.concurrent.ExecutionContextExecutor +import scala.util.{Failure, Success} + +class PeerHandler(remoteAddress: InetSocketAddress, + listener: ActorRef, + settings: Settings, + timeProvider: NetworkTimeProvider, + direction: ConnectionType, + receivedMessagesHandler: ActorRef) extends Actor with StrictLogging { + + context.watch(listener) + + implicit val ec: ExecutionContextExecutor = context.dispatcher + + override def preStart(): Unit = self ! StartIteration + + override def postStop(): Unit = { + logger.info(s"Peer handler $self to $remoteAddress is destroyed.") + context.parent ! RemovePeerFromConnectionList(remoteAddress) + listener ! Close + } + + var chunksBuffer: ByteString = CompactByteString.empty + var outMessagesBuffer: HashMap[Long, ByteString] = HashMap.empty + var outMessagesCounter: Long = 0 + + var isHandshakeSent: Boolean = false + var receivedHandshake: Option[Handshake] = None + + def awaitingConnectionBehaviour(timeout: Option[Cancellable]): Receive = { + + case StartIteration => timeProvider.time() map { time => + val handshake: Handshake = Handshake( + protocolToBytes(settings.network.appVersion), + settings.network.nodeName, + Some(new InetSocketAddress(settings.network.declaredAddressHost, settings.network.declaredAddressPort)), + time + ) + listener ! Write(ByteString(GeneralizedNetworkMessage.toProto(handshake).toByteArray)) + isHandshakeSent = true + logger.info(s"Sent initial handshake to $remoteAddress.") + if (receivedHandshake.isDefined && isHandshakeSent) { + logger.info(s"Got successfully bounded connection with $remoteAddress. Starting working behaviour.") + timeout.foreach(_.cancel()) + context.parent ! ConnectionSetupSuccessfully + context.become(workingCycleWriting(ConnectedPeer(remoteAddress, self, Outgoing, receivedHandshake.get))) + } else context.become(awaitingConnectionBehaviour( + Some(context.system.scheduler.scheduleOnce(settings.network.handshakeTimeout, self, HandshakeTimeout))) + ) + } + + case HandshakeTimeout => + logger.info(s"Handshake timeout has expired for $remoteAddress, going to drop the connection.") + self ! Close + + case HandshakeDone => + logger.info(s"Got successfully bounded connection with $remoteAddress. Starting working behaviour.") + listener ! ResumeReading + timeout.foreach(_.cancel()) + context.become(workingCycleWriting(ConnectedPeer(remoteAddress, self, Outgoing, receivedHandshake.get))) + + case Received(data) => GeneralizedNetworkMessage.fromProto(data) match { + case Success(value) => value match { + case handshake: Handshake => + logger.info(s"Got a Handshake from $remoteAddress.") + receivedHandshake = Some(handshake) + listener ! ResumeReading + if (isHandshakeSent && receivedHandshake.isDefined) { + logger.info(s"Got successfully bounded connection with $remoteAddress. Starting working behaviour.") + timeout.foreach(_.cancel()) + context.parent ! ConnectionSetupSuccessfully + context.become(workingCycleWriting(ConnectedPeer(remoteAddress, self, Outgoing, handshake))) + } else context.become(awaitingConnectionBehaviour(timeout)) + + case message => logger.info(s"Expecting handshake, but received ${message.messageName}.") + } + case Failure(exception) => + logger.info(s"Error during parsing a handshake: $exception.") + self ! Close + } + case _ => + } + + override def receive: Receive = awaitingConnectionBehaviour(None) + + def defaultLogic: Receive = { + case cc: ConnectionClosed => + logger.info(s"Connection closed to $remoteAddress cause ${cc.getErrorCause}.") + context.stop(self) + + case fail@CommandFailed(cmd: Command) => + logger.info(s"Failed to execute command : $cmd cause ${fail.cause}.") + listener ! ResumeReading + + case _ => + } + + def workingCycleWriting(cp: ConnectedPeer): Receive = workingCycleLocalInterfaceWritingMode(cp) + .orElse(workingCycleRemoteInterface(cp)) + .orElse(defaultLogic) + + def workingCycleLocalInterfaceWritingMode(cp: ConnectedPeer): Receive = { + case message: NetworkMessage => + def sendMessage(): Unit = { + outMessagesCounter += 1 + val messageToNetwork: Array[Byte] = GeneralizedNetworkMessage.toProto(message).toByteArray + val bytes: ByteString = ByteString(Ints.toByteArray(messageToNetwork.length) ++ messageToNetwork) + listener ! Write(bytes, Ack(outMessagesCounter)) + } + + sendMessage() + + case fail@CommandFailed(Write(msg, Ack(id))) => + logger.debug(s"Failed to write ${msg.length} bytes to $remoteAddress cause ${fail.cause}, switching to buffering mode") + listener ! ResumeReading + toBuffer(id, msg) + context.become(workingCycleBuffering(cp)) + case Ack(_) => // ignore ACKs in stable mode + case WritingResumed => // ignore in stable mode + } + + def workingCycleRemoteInterface(cp: ConnectedPeer): Receive = { + case Received(data) => + val packet: (List[ByteString], ByteString) = getPacket(chunksBuffer ++ data) + chunksBuffer = packet._2 + packet._1.find { packet => + GeneralizedNetworkMessage.fromProto(packet) match { + case Success(message) => + receivedMessagesHandler ! MessageFromNetwork(message, Some(cp)) + logger.debug("Received message " + message.messageName + " from " + remoteAddress) + false + case Failure(e) => + logger.info(s"Corrupted data from: " + remoteAddress + s"$e") + true + } + } + listener ! ResumeReading + } + + def workingCycleBuffering(cp: ConnectedPeer): Receive = workingCycleLocalInterfaceBufferingMode(cp) + .orElse(workingCycleRemoteInterface(cp)) + .orElse(defaultLogic) + + // operate in ACK mode until all buffered messages are transmitted + def workingCycleLocalInterfaceBufferingMode(cp: ConnectedPeer): Receive = { + case message: NetworkMessage => + outMessagesCounter += 1 + val messageToNetwork: Array[Byte] = GeneralizedNetworkMessage.toProto(message).toByteArray + val bytes: ByteString = ByteString(Ints.toByteArray(messageToNetwork.length) ++ messageToNetwork) + toBuffer(outMessagesCounter, bytes) + case fail@CommandFailed(Write(msg, Ack(id))) => + logger.debug(s"Failed to buffer ${msg.length} bytes to $remoteAddress cause ${fail.cause}") + listener ! ResumeWriting + toBuffer(id, msg) + case CommandFailed(ResumeWriting) => // ignore in ACK mode + case WritingResumed => writeFirst() + case Ack(id) => + outMessagesBuffer -= id + if (outMessagesBuffer.nonEmpty) writeFirst() + else { + logger.debug("Buffered messages processed, exiting buffering mode") + context.become(workingCycleWriting(cp)) + } + } + + def getPacket(data: ByteString): (List[ByteString], ByteString) = { + + val headerSize: Int = 4 + + @tailrec + def multiPacket(packets: List[ByteString], current: ByteString): (List[ByteString], ByteString) = + if (current.length < headerSize) (packets.reverse, current) + else { + val len: Int = current.iterator.getInt(ByteOrder.BIG_ENDIAN) + if (current.length < len + headerSize) (packets.reverse, current) + else { + val rem: ByteString = current drop headerSize + val (front: ByteString, back: ByteString) = rem.splitAt(len) + multiPacket(front :: packets, back) + } + } + + multiPacket(List[ByteString](), data) + } + + def writeFirst(): Unit = outMessagesBuffer.headOption.foreach { case (id, msg) => listener ! Write(msg, Ack(id)) } + + def writeAll(): Unit = outMessagesBuffer.foreach { case (id, msg) => listener ! Write(msg, Ack(id)) } + + def toBuffer(id: Long, message: ByteString): Unit = outMessagesBuffer += id -> message + + private def protocolToBytes(protocol: String): Array[Byte] = protocol.split("\\.").map(elem => elem.toByte) +} + +object PeerHandler { + + case object StartIteration + + sealed trait ConnectionMessages + + case object HandshakeTimeout extends ConnectionMessages + + case object HandshakeDone extends ConnectionMessages + + case class RemovePeerFromConnectionList(peer: InetSocketAddress) extends ConnectionMessages + + final case class Ack(offset: Long) extends Tcp.Event + + def props(remoteAddress: InetSocketAddress, + listener: ActorRef, + settings: Settings, + timeProvider: NetworkTimeProvider, + direction: ConnectionType, + messagesHandler: ActorRef): Props = + Props(new PeerHandler(remoteAddress, listener, settings, timeProvider, direction, messagesHandler)) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/transaction/Contracts.scala b/src/main/scala/encry/net/transaction/Contracts.scala new file mode 100644 index 0000000..f7fcbe5 --- /dev/null +++ b/src/main/scala/encry/net/transaction/Contracts.scala @@ -0,0 +1,31 @@ +package encry.net.transaction + +import org.encryfoundation.prismlang.compiler.{CompiledContract, PCompiler} +import scorex.crypto.encode.Base58 +import scorex.crypto.signatures.PublicKey + +import scala.util.{Failure, Try} + +object Contracts { + + def multiSigContractScratch(owners: Seq[PublicKey]): Try[CompiledContract] = + if (owners.nonEmpty) { + val contractCode: String = + s""" + |contract (signature: MultiSig, transaction: Transaction) = { + | def isValidSig(signature: MultiSig, message: Array[Byte], key: Array[Byte]): Bool = { + | anyOf(signature.map(lamb (x: Array[Byte]) = checkSig(x, message, key))) + | } + | + | let ownerPubKey = base58'${Base58.encode(owners.head)}' + | let garantPubKey = base58'${Base58.encode(owners(1))}' + | let receiverPubKey = base58'${Base58.encode(owners(2))}' + | let keys = Array(ownerPubKey, garantPubKey, receiverPubKey) + | let all: Array[Int] = keys.map(lamb(x: Array[Byte]) = if(isValidSig(signature, transaction.messageToSign, x)) 1 else 0) + | all.sum > 1 + |} + """.stripMargin + PCompiler.compile(contractCode) + } else Failure(new IllegalArgumentException) + +} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/CoreTaggedTypes.scala b/src/main/scala/encry/net/utils/CoreTaggedTypes.scala new file mode 100644 index 0000000..194133b --- /dev/null +++ b/src/main/scala/encry/net/utils/CoreTaggedTypes.scala @@ -0,0 +1,14 @@ +package encry.net.utils + +import supertagged.TaggedType + +object CoreTaggedTypes { + + object ModifierTypeId extends TaggedType[Byte] + + object ModifierId extends TaggedType[Array[Byte]] + + type ModifierTypeId = ModifierTypeId.Type + + type ModifierId = ModifierId.Type +} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/Mnemonic.scala b/src/main/scala/encry/net/utils/Mnemonic.scala new file mode 100644 index 0000000..c68d8e1 --- /dev/null +++ b/src/main/scala/encry/net/utils/Mnemonic.scala @@ -0,0 +1,40 @@ +package encry.net.utils + +import org.encryfoundation.common.crypto.PrivateKey25519 +import org.encryfoundation.common.utils.Algos +import scodec.bits.BitVector +import scorex.crypto.hash.Blake2b256 +import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} + +import scala.io.Source + +object Mnemonic { + + def createPrivKey(seed: Option[String]): PrivateKey25519 = { + val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( + Blake2b256.hash( + seed.map { + seedFromMnemonic(_) + } + .getOrElse { + val phrase: String = entropyToMnemonicCode(scorex.utils.Random.randomBytes(16)) + seedFromMnemonic(phrase) + }) + ) + PrivateKey25519(privateKey, publicKey) + } + + private def seedFromMnemonic(mnemonicCode: String, passPhrase: String = ""): Array[Byte] = + Algos.hash(mnemonicCode + "mnemonic=" + passPhrase) + + private def entropyToMnemonicCode(entropy: Array[Byte]): String = { + val words: Array[String] = + Source.fromInputStream(getClass.getResourceAsStream("/languages/english/words.txt")).getLines.toArray + val checkSum: BitVector = BitVector(Algos.hash(entropy)) + val entropyWithCheckSum: BitVector = BitVector(entropy) ++ checkSum.take(4) + + entropyWithCheckSum.grouped(11).map { i => + words(i.toInt(signed = false)) + }.mkString(" ") + } +} diff --git a/src/main/scala/encry/net/utils/NetworkService.scala b/src/main/scala/encry/net/utils/NetworkService.scala new file mode 100644 index 0000000..1be5b9e --- /dev/null +++ b/src/main/scala/encry/net/utils/NetworkService.scala @@ -0,0 +1,78 @@ +package encry.net.utils + +import akka.http.scaladsl.Http +import akka.http.scaladsl.model._ +import akka.http.scaladsl.model.headers.Host +import akka.util.ByteString +import com.typesafe.scalalogging.StrictLogging +import io.circe.{Decoder, HCursor} +import io.circe.parser.decode +import org.encryfoundation.common.modifiers.mempool.transaction.PubKeyLockedContract +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.generator.GeneratorApp._ +import org.encryfoundation.generator.modifiers.box.Box + +import scala.concurrent.Future +import scala.util.control.NonFatal + +object NetworkService extends StrictLogging { + + def requestUtxos(node: Node, from: Int, to: Int): Future[List[Box]] = { + val privKey = Mnemonic.createPrivKey(Option(node.mnemonicKey)) + val contractHash: String = Algos.encode(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash) + Http().singleRequest(HttpRequest( + method = HttpMethods.GET, + uri = s"/wallet/$contractHash/boxes/$from/$to" + ).withEffectiveUri(securedConnection = false, Host(node.explorerHost, node.explorerPort))) + .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) + .map(_.utf8String) + .map(decode[List[Box]]) + .flatMap(_.fold(Future.failed, Future.successful)) + } + + def checkTxsInBlockchain(node: NetworkSettings, txsToCheck: Vector[String], numberOfBlocks: Int): Future[List[String]] = + Http().singleRequest(HttpRequest( + method = HttpMethods.GET, + uri = s"/history/lastHeaders/$numberOfBlocks" + ).withEffectiveUri(securedConnection = false, Host(node.peerForConnectionHost, node.peerForConnectionApiPort))) + .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) + .map(_.utf8String) + .map(decode[List[HeaderId]]) + .flatMap(_.fold(Future.failed, Future.successful)) + .flatMap { headers => + Future.sequence(headers.map(checkTxsInBlock(node, txsToCheck, _))).map(_.flatten) + } + .flatMap { txs => + if (txs.isEmpty) Future.failed(new RuntimeException) else Future.successful(txs) + } + .recover { + case NonFatal(_) => List.empty + } + + private def checkTxsInBlock(node: NetworkSettings, txsToCheck: Vector[String], headerId: HeaderId): Future[List[String]] = + Http().singleRequest(HttpRequest( + method = HttpMethods.GET, + uri = s"/history/${headerId.id}/transactions" + ).withEffectiveUri(securedConnection = false, Host(node.peerForConnectionHost, node.peerForConnectionApiPort))) + .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) + .map(_.utf8String) + .map(decode[List[TransactionId]]) + .flatMap(_.fold(Future.failed, Future.successful)) + .map(_.map(_.id).intersect(txsToCheck)) + .map { txs => + if (txs.nonEmpty) logger.info(s"txs ${txs.mkString(",")} are in a block ${headerId.id}") + txs + }.recoverWith { + case NonFatal(th) => + th.printStackTrace() + Future.failed(th) + } + + case class HeaderId(id: String) + case class TransactionId(id: String) + + implicit val headerIdDecoder: Decoder[HeaderId] = (c: HCursor) => + for { id <- c.downField("id").as[String] } yield HeaderId(id) + implicit val transactionIdDecoder: Decoder[TransactionId] = (c: HCursor) => + for { id <- c.downField("id").as[String] } yield TransactionId(id) +} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/NetworkTime.scala b/src/main/scala/encry/net/utils/NetworkTime.scala new file mode 100644 index 0000000..7871110 --- /dev/null +++ b/src/main/scala/encry/net/utils/NetworkTime.scala @@ -0,0 +1,82 @@ +package encry.net.utils + +import java.net.InetAddress +import com.typesafe.scalalogging.StrictLogging +import org.apache.commons.net.ntp.{NTPUDPClient, TimeInfo} +import org.encryfoundation.generator.utils.NetworkTime.Time +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration._ +import scala.concurrent.Future +import scala.util.Left +import scala.util.control.NonFatal + +object NetworkTime { + def localWithOffset(offset: Long): Long = System.currentTimeMillis() + offset + + type Offset = Long + type Time = Long +} + +protected case class NetworkTime(offset: NetworkTime.Offset, lastUpdate: NetworkTime.Time) + +case class NetworkTimeProviderSettings(server: String, updateEvery: FiniteDuration, timeout: FiniteDuration) + +class NetworkTimeProvider(ntpSettings: NetworkTimeProviderSettings) extends StrictLogging { + + private var state: State = Right(NetworkTime(0L, 0L)) + private var delta: Time = 0L + + private type State = Either[(NetworkTime, Future[NetworkTime]), NetworkTime] + + private def updateOffSet(): Option[NetworkTime.Offset] = { + val client: NTPUDPClient = new NTPUDPClient() + client.setDefaultTimeout(ntpSettings.timeout.toMillis.toInt) + try { + client.open() + val info: TimeInfo = client.getTime(InetAddress.getByName(ntpSettings.server)) + info.computeDetails() + Option(info.getOffset) + } catch { + case t: Throwable => None + } finally { + client.close() + } + } + + private def timeAndState(currentState: State): Future[(NetworkTime.Time, State)] = + currentState match { + case Right(nt) => + val time: Long = NetworkTime.localWithOffset(nt.offset) + val state: Either[(NetworkTime, Future[NetworkTime]), NetworkTime] = + if (time > nt.lastUpdate + ntpSettings.updateEvery.toMillis) { + Left(nt -> Future(updateOffSet()).map { mbOffset => + logger.info("New offset adjusted: " + mbOffset) + val offset = mbOffset.getOrElse(nt.offset) + NetworkTime(offset, NetworkTime.localWithOffset(offset)) + }) + } else Right(nt) + Future.successful((time, state)) + case Left((nt, networkTimeFuture)) => + networkTimeFuture + .map(networkTime => NetworkTime.localWithOffset(networkTime.offset) -> Right(networkTime)) + .recover { + case NonFatal(th) => + logger.warn(s"Failed to evaluate networkTimeFuture $th") + NetworkTime.localWithOffset(nt.offset) -> Left(nt -> networkTimeFuture) + } + } + + def estimatedTime: Time = state match { + case Right(nt) if NetworkTime.localWithOffset(nt.offset) <= nt.lastUpdate + ntpSettings.updateEvery.toMillis => + NetworkTime.localWithOffset(nt.offset) + case _ => System.currentTimeMillis() + delta + } + + def time(): Future[NetworkTime.Time] = + timeAndState(state) + .map { case (timeFutureResult, stateFutureResult) => + state = stateFutureResult + delta = timeFutureResult - System.currentTimeMillis() + timeFutureResult + } +} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/Settings.scala b/src/main/scala/encry/net/utils/Settings.scala new file mode 100644 index 0000000..e74e924 --- /dev/null +++ b/src/main/scala/encry/net/utils/Settings.scala @@ -0,0 +1,53 @@ +package encry.net.utils + +import scala.concurrent.duration.FiniteDuration + +case class Settings(peers: List[Node], + influxDB: Option[InfluxDBSettings], + generator: GeneratorSettings, + boxesHolderSettings: BoxesHolderSettings, + transactions: TransactionsSettings, + network: NetworkSettings, + ntp: NetworkTimeProviderSettings, + multisig: MultisigSettings) + +case class Node(explorerHost: String, + explorerPort: Int, + mnemonicKey: String) + +case class InfluxDBSettings(url: String, + login: String, + password: String, + udpPort: Int) + +case class GeneratorSettings(transactionsSendingFrequency: Int) + +case class BoxesHolderSettings(askingAPIFrequency: FiniteDuration, + rangeForAskingBoxes: Int, + poolSize: Int, + maxPoolSize: Long, + bloomFilterCleanupInterval: FiniteDuration, + bloomFilterCapacity: Long, + bloomFilterFailureProbability: Double) + +case class TransactionsSettings(numberOfDataTxs: Int, + numberOfMonetaryTxs: Int, + numberOfMultisigTxs: Int, + requiredAmount: Int, + feeAmount: Int, + dataTxSize: Int, + numberOfCreatedDirectives: Int) + +case class MultisigSettings(checkTxMinedPeriod: Int, numberOfBlocksToCheck: Int, mnemonicKeys: List[String]) + +case class NetworkSettings(syncPacketLength: Int, + bindAddressHost: String, + bindAddressPort: Int, + nodeName: String, + appVersion: String, + handshakeTimeout: FiniteDuration, + peerForConnectionHost: String, + peerForConnectionPort: Int, + peerForConnectionApiPort: Int, + declaredAddressHost: String, + declaredAddressPort: Int) \ No newline at end of file From c176295d8ce1d7b63a39a9ad9ee074c3cef3ea95 Mon Sep 17 00:00:00 2001 From: capdev Date: Tue, 15 Oct 2019 12:37:50 +0500 Subject: [PATCH 03/21] off unused refactor settings --- build.sbt | 1 + src/main/resources/application.conf | 74 ++++++-- src/main/resources/logback.xml | 2 +- src/main/scala/encry/ExplorerApp.scala | 18 +- src/main/scala/encry/net/GeneratorApp.scala | 26 --- .../encry/net/actors/BlockchainListener.scala | 11 +- .../scala/encry/net/actors/BoxesHolder.scala | 157 ---------------- .../scala/encry/net/actors/Generator.scala | 173 ------------------ .../scala/encry/net/actors/InfluxActor.scala | 45 ----- .../encry/net/modifiers/Transaction.scala | 4 +- .../net/modifiers/TransactionsFactory.scala | 4 +- .../encry/net/modifiers/box/AssetBox.scala | 2 +- .../net/modifiers/box/TokenIssuingBox.scala | 2 +- .../directives/AssetIssuingDirective.scala | 2 +- .../modifiers/directives/DataDirective.scala | 4 +- .../net/modifiers/directives/Directive.scala | 2 +- .../directives/ScriptedAssetDirective.scala | 2 +- .../directives/TransferDirective.scala | 2 +- .../encry/net/network/BasicMessagesRepo.scala | 32 ++-- .../net/network/NetworkMessagesHandler.scala | 17 +- .../encry/net/network/NetworkServer.scala | 42 ++--- .../scala/encry/net/network/PeerHandler.scala | 27 ++- .../encry/net/utils/NetworkService.scala | 11 +- .../scala/encry/net/utils/NetworkTime.scala | 2 +- src/main/scala/encry/net/utils/Settings.scala | 53 ------ .../encry/settings/BlackListSettings.scala | 5 - src/main/scala/encry/settings/Constants.scala | 1 - .../encry/settings/DatabaseSettings.scala | 7 - .../encry/settings/ExplorerSettings.scala | 38 +++- .../scala/encry/settings/NodeSettings.scala | 3 - .../scala/encry/settings/ParseSettings.scala | 9 - 31 files changed, 185 insertions(+), 593 deletions(-) delete mode 100644 src/main/scala/encry/net/GeneratorApp.scala delete mode 100644 src/main/scala/encry/net/actors/BoxesHolder.scala delete mode 100644 src/main/scala/encry/net/actors/Generator.scala delete mode 100644 src/main/scala/encry/net/actors/InfluxActor.scala delete mode 100644 src/main/scala/encry/net/utils/Settings.scala delete mode 100644 src/main/scala/encry/settings/BlackListSettings.scala delete mode 100644 src/main/scala/encry/settings/DatabaseSettings.scala delete mode 100644 src/main/scala/encry/settings/NodeSettings.scala delete mode 100644 src/main/scala/encry/settings/ParseSettings.scala diff --git a/build.sbt b/build.sbt index 06c9ab8..ed266da 100644 --- a/build.sbt +++ b/build.sbt @@ -41,6 +41,7 @@ libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, "com.iheart" %% "ficus" % "1.4.2", + "commons-net" % "commons-net" % "3.3" ) ++ apiDependencies ++ loggingDependencies ++ databaseDependencies fork := true diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 2809165..9ba9788 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1,25 +1,59 @@ explorer { - parseSettings { - nodes = [""] - recoverBatchSize = 15 - infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts - askNode = false # if set false, explorer won't ping the node if it stopped working - } - blackListSettings { - banTime = 60m - cleanupTime = 5s - } - databaseSettings { - host = "" - user = "" - password = "" - maxPoolSize = 5 - connectionTimeout = 60000 - } - nodeSettings { - maxRollbackDepth = 100 - } + + parseSettings { + nodes = [""] + recoverBatchSize = 15 + infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts + askNode = false # if set false, explorer won't ping the node if it stopped working + } + + blackListSettings { + banTime = 60m + cleanupTime = 5s + } + + databaseSettings { + host = "" + user = "" + password = "" + maxPoolSize = 5 + connectionTimeout = 60000 + } + + nodeSettings { + explorerHost = "172.16.11.11" + explorerPort = 9001 + mnemonicKey = "" + maxRollbackDepth = 100 + } + + networkSettings { + syncPacketLength = 1000 + bindAddressHost = "0.0.0.0" + bindAddressPort = 8000 + nodeName = "explorer" + appVersion = 0.9.3 + handshakeTimeout = 1m + peerForConnectionHost = "172.16.11.11" + peerForConnectionPort = 9001 + peerForConnectionApiPort = 9051 + declaredAddressHost = "" + declaredAddressPort = 0 + } + + multisigSettings { + checkTxMinedPeriod = 30 + numberOfBlocksToCheck = 3 + mnemonicKeys = [] + } + + ntpSettings { + server = "pool.ntp.org" + updateEvery = 30m + timeout = 30s + } } + parser-dispatcher { type = Dispatcher executor = "thread-pool-executor" diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 2df2ebd..aea2ae9 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -7,7 +7,7 @@ 2 - INFO + DEBUG [%d{yyyy-MM-dd HH:mm:ss}] >> [%thread] >> [%-5level] >> %msg%n diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index fda1b56..1972a40 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -1,14 +1,15 @@ package encry -import akka.actor.{ActorSystem, Props} +import akka.actor.{ActorRef, ActorSystem, Props} import akka.stream.ActorMaterializer -import cats.effect.{Blocker, IO} +import cats.effect.{Blocker, ContextShift, IO} import cats.implicits._ import doobie.hikari.HikariTransactor import doobie.util.ExecutionContexts import encry.database.{DBActor, DBService} +import encry.net.network.NetworkServer +import encry.net.utils.NetworkTimeProvider import encry.settings.ExplorerSettings -import doobie.implicits._ import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} @@ -20,7 +21,7 @@ object ExplorerApp extends App { val settings = ExplorerSettings.read - implicit val cs = IO.contextShift(ExecutionContext.global) + implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) val pgTransactor = for { ce <- ExecutionContexts.fixedThreadPool[IO](settings.databaseSettings.maxPoolSize) @@ -42,9 +43,12 @@ object ExplorerApp extends App { ds.setConnectionTimeout(settings.databaseSettings.connectionTimeout) } } *> IO { - val dbService = DBService(xa) - val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") - system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") + val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) + val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider), "networkServer") + +// val dbService = DBService(xa) +// val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") +// system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") } *> IO.never }.unsafeRunSync() } diff --git a/src/main/scala/encry/net/GeneratorApp.scala b/src/main/scala/encry/net/GeneratorApp.scala deleted file mode 100644 index bb635a5..0000000 --- a/src/main/scala/encry/net/GeneratorApp.scala +++ /dev/null @@ -1,26 +0,0 @@ -package encry.net - -import akka.actor.{ActorRef, ActorSystem} -import akka.stream.ActorMaterializer -import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.actors.InfluxActor -import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} -import com.typesafe.config.ConfigFactory -import net.ceedubs.ficus.Ficus._ -import net.ceedubs.ficus.readers.ArbitraryTypeReader._ -import org.encryfoundation.generator.network.NetworkServer -import scala.concurrent.ExecutionContextExecutor - -object GeneratorApp extends App with StrictLogging { - - implicit lazy val system: ActorSystem = ActorSystem() - implicit lazy val materializer: ActorMaterializer = ActorMaterializer() - implicit lazy val ec: ExecutionContextExecutor = system.dispatcher - val settings: Settings = ConfigFactory.load("local.conf") - .withFallback(ConfigFactory.load()).as[Settings] - val influx: Option[ActorRef] = - settings.influxDB.map(_ => system.actorOf(InfluxActor.props(settings), "influxDB")) - - val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntp) - val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings, timeProvider, influx)) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/BlockchainListener.scala b/src/main/scala/encry/net/actors/BlockchainListener.scala index 73ce9e4..9767515 100644 --- a/src/main/scala/encry/net/actors/BlockchainListener.scala +++ b/src/main/scala/encry/net/actors/BlockchainListener.scala @@ -2,27 +2,28 @@ package encry.net.actors import akka.actor.Actor import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain, TimeToCheck} -import org.encryfoundation.generator.utils.{NetworkService, Settings} +import encry.net.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain, TimeToCheck} +import encry.net.utils.NetworkService +import encry.settings.ExplorerSettings import scala.concurrent.{ExecutionContextExecutor, Future} import scala.concurrent.duration._ -class BlockchainListener(settings: Settings) extends Actor with StrictLogging { +class BlockchainListener(settings: ExplorerSettings) extends Actor with StrictLogging { implicit val ec: ExecutionContextExecutor = context.dispatcher override def receive: Receive = operating(Vector.empty) override def preStart(): Unit = - context.system.scheduler.schedule(settings.multisig.checkTxMinedPeriod.seconds, settings.multisig.checkTxMinedPeriod.seconds) { + context.system.scheduler.schedule(settings.multisigSettings.checkTxMinedPeriod.seconds, settings.multisigSettings.checkTxMinedPeriod.seconds) { self ! TimeToCheck } def operating(txsToCheck: Vector[String]): Receive = { case CheckTxMined(id) => context.become(operating(txsToCheck :+ id)) case TimeToCheck if txsToCheck.nonEmpty => - NetworkService.checkTxsInBlockchain(settings.network, txsToCheck, settings.multisig.numberOfBlocksToCheck) + NetworkService.checkTxsInBlockchain(settings.networkSettings, txsToCheck, settings.multisigSettings.numberOfBlocksToCheck) .foreach { txs => if (txs.nonEmpty) { logger.info(s"Multisig txs ${txs.mkString(", ")} are in blockchain now") diff --git a/src/main/scala/encry/net/actors/BoxesHolder.scala b/src/main/scala/encry/net/actors/BoxesHolder.scala deleted file mode 100644 index 90f9a08..0000000 --- a/src/main/scala/encry/net/actors/BoxesHolder.scala +++ /dev/null @@ -1,157 +0,0 @@ -package encry.net.actors - -import akka.actor.{Actor, ActorRef, Cancellable, Props} -import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.actors.BoxesHolder._ -import org.encryfoundation.generator.actors.InfluxActor._ -import org.encryfoundation.generator.modifiers.box.AssetBox -import org.encryfoundation.generator.utils.{NetworkService, Node, Settings} -import com.google.common.base.Charsets -import com.google.common.hash.{BloomFilter, Funnels} -import org.encryfoundation.common.utils.Algos - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.duration._ - -class BoxesHolder(settings: Settings, - influx: Option[ActorRef], - peer: Node) extends Actor with StrictLogging { - - context.system.scheduler.schedule( - 5.seconds, settings.boxesHolderSettings.askingAPIFrequency, self, RequestForNewBoxesFromApi - ) - - var bloomFilter: BloomFilter[String] = initBloomFilter - - context.system.scheduler.schedule( - settings.boxesHolderSettings.bloomFilterCleanupInterval, - settings.boxesHolderSettings.bloomFilterCleanupInterval) { - bloomFilter = initBloomFilter - } - - override def receive: Receive = boxesHolderBehavior() - - def boxesHolderBehavior(pool: List[Batch] = List()): Receive = { - case BoxesFromApi(boxes) => - logger.info(s"BoxesHolder got message `BoxesFromApi`. Number of received boxes is: ${boxes.size}.") - val batchesPool: List[Batch] = batchesForTransactions(boxes) - val newBatches: List[Batch] = pool ++: batchesPool - influx.foreach(_ ! PoolState(newBatches.size)) - logger.info(s"Number of batches is: ${newBatches.size}") - context.become(boxesHolderBehavior(newBatches)) - - case AskBoxesFromGenerator => - logger.info(s"BoxesHolder got message `AskBoxesFromGenerator`. Current pool is: ${pool.size}") - val batchesAfterMT: List[Batch] = - if (settings.transactions.numberOfMonetaryTxs > 0) { - val batchesForTxs: List[Batch] = pool.take(settings.transactions.numberOfMonetaryTxs) - batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 2)) - pool.drop(settings.transactions.numberOfMonetaryTxs) - } else pool - influx.foreach(_ ! PoolState(batchesAfterMT.size)) - - val batchesAfterDT: List[Batch] = - if (settings.transactions.numberOfDataTxs > 0) { - val batchesForTxs: List[Batch] = batchesAfterMT.take(settings.transactions.numberOfDataTxs) - batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 1)) - batchesAfterMT.drop(settings.transactions.numberOfDataTxs) - } else batchesAfterMT - influx.foreach(_ ! PoolState(batchesAfterDT.size)) - - logger.info(s"Number of batches before diff: ${pool.size}.") - logger.info(s"Number of batches after diff: ${batchesAfterDT.size}.") - - val batchesAfterMultisigTx: List[Batch] = - if (settings.transactions.numberOfMultisigTxs > 0) { - val batchesForTxs: List[Batch] = batchesAfterDT.take(settings.transactions.numberOfMultisigTxs) - batchesForTxs.foreach(batch => sender() ! BoxesForGenerator(batch.boxes, 3)) - batchesAfterDT.drop(settings.transactions.numberOfMultisigTxs) - } else batchesAfterDT - influx.foreach(_ ! PoolState(batchesAfterMultisigTx.size)) - - logger.info(s"Number of batches before diff: ${pool.size}.") - logger.info(s"Number of batches after diff: ${batchesAfterMultisigTx.size}.") - - influx.foreach(_ ! SentBatches(batchesAfterMultisigTx.size)) - context.become(boxesHolderBehavior(batchesAfterMultisigTx)) - - case AskBoxesForMultisigSigning(txs) => - logger.info(s"BoxesHolder got message `AskBoxesForMultisigSigning`. Current pool is: ${pool.size}, and number of txs is ${txs.size}") - txs.foreach(tx => sender() ! BoxesForGenerator(List.empty, 4, Some(tx))) - logger.info(s"Number of batches after diff: ${pool.size}.") - - case RequestForNewBoxesFromApi => - if (pool.size < settings.boxesHolderSettings.poolSize) { - logger.info(s"Current pool size is: ${pool.size}. Asking new boxes from api!") - getBoxes(0, settings.boxesHolderSettings.rangeForAskingBoxes) - } - else logger.info(s"Current pool is: ${pool.size}. We won't ask new boxes from api!") - } - - def batchesForTransactions(list: List[AssetBox]): List[Batch] = { - val batchesList: (List[Batch], Batch, Long) = list.foldLeft(List[Batch](), Batch(List()), 0L) { - case ((listBatches, batch, amount), box) => - val newBatch: List[AssetBox] = box :: batch.boxes - val newAmount: Long = amount + box.amount - if (newAmount > settings.transactions.feeAmount) (Batch(newBatch) :: listBatches, Batch(List()), 0) - else (listBatches, Batch(newBatch), newAmount) - } - batchesList._1 - } - - def cleanReceivedBoxesFromUsed(usedB: Map[String, Cancellable], - newB: List[AssetBox]): (List[AssetBox], Map[String, Cancellable]) = { - val newBMap: Map[String, AssetBox] = Map(newB.map(k => Algos.encode(k.id) -> k): _*) - logger.debug(s"cleanReceivedBoxesFromUsed: New boxes map size is: ${newBMap.size}") - val (usedBoxes: Map[String, Cancellable], newBoxes: Map[String, AssetBox]) = - usedB.foldLeft(Map[String, Cancellable](), newBMap) { - case ((newUsedCollection, newBoxesCollection), (id, timer)) => newBoxesCollection.get(id) match { - case Some(_) => (newUsedCollection.updated(id, timer), newBoxesCollection - id) - case None => - timer.cancel() - (newUsedCollection, newBoxesCollection) - } - } - logger.debug(s"CleanNewBoxesFromUsed: Used - ${usedBoxes.size}. New - ${newBoxes.size}") - (newBoxes.values.toList, usedBoxes) - } - - def getBoxes(from: Int, to: Int): Future[Unit] = - NetworkService.requestUtxos(peer, from, to).map { request => - logger.debug(s"Boxes from API: ${request.size}") - if (request.nonEmpty && to < settings.boxesHolderSettings.maxPoolSize) { - val newFrom: Int = from + settings.boxesHolderSettings.rangeForAskingBoxes - val newTo: Int = to + settings.boxesHolderSettings.rangeForAskingBoxes - getBoxes(newFrom, newTo) - logger.debug(s"Asking new boxes in range: $newFrom -> $newTo.") - } - request.collect { case mb: AssetBox if mb.tokenIdOpt.isEmpty && !bloomFilter.mightContain(Algos.encode(mb.id)) => - bloomFilter.put(Algos.encode(mb.id)) - mb - } - }.map(boxes => self ! BoxesFromApi(boxes)) - - def initBloomFilter: BloomFilter[String] = BloomFilter.create( - Funnels.stringFunnel(Charsets.UTF_8), - settings.boxesHolderSettings.bloomFilterCapacity, - settings.boxesHolderSettings.bloomFilterFailureProbability - ) -} - -object BoxesHolder { - def props(settings: Settings, influx: Option[ActorRef], peer: Node): Props = - Props(new BoxesHolder(settings, influx, peer)) - - case object RequestForNewBoxesFromApi - - case object AskBoxesFromGenerator - - case class BoxesFromApi(list: List[AssetBox]) - - case class BoxesForGenerator(list: List[AssetBox], txType: Int, forTx: Option[String] = None) - - case class Batch(boxes: List[AssetBox]) - - case class AskBoxesForMultisigSigning(txs: Set[String]) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/Generator.scala b/src/main/scala/encry/net/actors/Generator.scala deleted file mode 100644 index 93cdf3f..0000000 --- a/src/main/scala/encry/net/actors/Generator.scala +++ /dev/null @@ -1,173 +0,0 @@ -package encry.net.actors - -import akka.actor.{Actor, ActorRef, Props} -import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.common.crypto.PrivateKey25519 -import org.encryfoundation.common.modifiers.mempool.transaction.{Proof, PubKeyLockedContract} -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.generator.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain} -import org.encryfoundation.generator.actors.BoxesHolder._ -import org.encryfoundation.generator.actors.Generator.TransactionForCommit -import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, MonetaryBox} -import org.encryfoundation.generator.modifiers.{Transaction, TransactionsFactory} -import org.encryfoundation.generator.transaction.Contracts -import org.encryfoundation.generator.utils.{Mnemonic, Node, Settings} -import org.encryfoundation.prismlang.compiler.CompiledContract -import org.encryfoundation.prismlang.core.wrapped.BoxedValue.MultiSignatureValue -import scorex.crypto.hash.Blake2b256 -import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} -import scorex.utils -import scorex.utils.Random.{randomBytes => rBytes} - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.util.Random - -class Generator(settings: Settings, - privKey: PrivateKey25519, - nodeForLocalPrivKey: Node, - influx: Option[ActorRef], - networkServer: ActorRef) extends Actor with StrictLogging { - - val boxesHolder: ActorRef = context.system.actorOf( - BoxesHolder.props(settings, influx, nodeForLocalPrivKey), s"boxesHolder${nodeForLocalPrivKey.explorerHost}") - context.system.scheduler.schedule(10.seconds, settings.generator.transactionsSendingFrequency.seconds) { - boxesHolder ! AskBoxesFromGenerator - logger.info(s"Generator asked boxesHolder for new boxes.") - } - - val multisigKeys: Seq[PrivateKey25519] = if (settings.multisig.mnemonicKeys.size >= 3) - settings.multisig.mnemonicKeys - .take(3) - .map(Some(_)) - .map(Mnemonic.createPrivKey) - else - (1 to 3) - .map(_ => Curve25519.createKeyPair(rBytes())) - .map(pair => PrivateKey25519(pair._1, pair._2)) - - var multisigBoxes: Map[String, Seq[Box]] = Map.empty - val blockchainListener: ActorRef = - context.actorOf(Props(classOf[BlockchainListener], settings), "blockchainListener") - - override def receive: Receive = { - case BoxesForGenerator(boxes, txType, None) if boxes.nonEmpty => - generateAndSendTransaction(boxes, txType) - case BoxesForGenerator(boxes, txType, Some(forTx)) if txType == 4 && multisigBoxes.get(forTx).exists(_.nonEmpty) => - generateAndSendTransaction(boxes, txType, Some(forTx)) - case MultisigTxsInBlockchain(txs) => boxesHolder ! AskBoxesForMultisigSigning(txs) - case _ => - } - - def generateAndSendTransaction(boxes: List[AssetBox], txsType: Int, forTx: Option[String] = None): Future[Unit] = Future { - val transaction: Transaction = txsType match { - case 1 => TransactionsFactory.dataTransactionScratch( - privKey, - settings.transactions.feeAmount, - System.currentTimeMillis(), - boxes.map(_ -> None), - PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract, - settings.transactions.requiredAmount, - utils.Random.randomBytes(settings.transactions.dataTxSize), - settings.transactions.numberOfCreatedDirectives - ) - case 2 => TransactionsFactory.defaultPaymentTransaction( - privKey, - settings.transactions.feeAmount, - System.currentTimeMillis(), - boxes.map(_ -> None), - privKey.publicImage.address.address, - settings.transactions.requiredAmount, - settings.transactions.numberOfCreatedDirectives - ) - case 3 => - val contract = Contracts.multiSigContractScratch(multisigKeys.map(_.publicKeyBytes)).get - - TransactionsFactory.scriptedAssetTransactionScratch( - privKey, - settings.transactions.feeAmount, - System.currentTimeMillis(), - boxes.map(_ -> None), - contract, - settings.transactions.requiredAmount, - settings.transactions.numberOfCreatedDirectives, - None - ) - case 4 if forTx.isDefined => - val compiledContract: CompiledContract = Contracts.multiSigContractScratch(multisigKeys.map(_.publicKeyBytes)).get - val ts: Long = System.currentTimeMillis() - - val txWithoutProofs: Transaction = TransactionsFactory.defaultPaymentTransactionWithoutRandom( - privKey, - settings.transactions.feeAmount, - ts, - multisigBoxes(forTx.get).collect { - case b: MonetaryBox => b - }.map(_ -> Some(compiledContract -> Seq())) match { - case init :+ last => init :+ last._1 -> None - }, - privKey.publicImage.address.address, - settings.transactions.requiredAmount - settings.transactions.feeAmount, - settings.transactions.numberOfCreatedDirectives - ) - - val signatures: List[List[Byte]] = Random.shuffle(multisigKeys) - .take(2) - .map(_.sign(txWithoutProofs.messageToSign)) - .map(_.signature) - .map(_.toList) - .toList - - val proofs: Seq[Proof] = Seq(Proof(MultiSignatureValue(signatures), Some("signature"))) - TransactionsFactory.defaultPaymentTransactionWithoutRandom( - privKey, - settings.transactions.feeAmount, - ts, - multisigBoxes(forTx.get).collect { - case b: MonetaryBox => b - }.map(_ -> Some(compiledContract -> proofs)) match { - case init :+ last => init :+ last._1 -> None - }, - privKey.publicImage.address.address, - settings.transactions.requiredAmount - settings.transactions.feeAmount, - settings.transactions.numberOfCreatedDirectives - ) - } - if (txsType == 3) { - blockchainListener ! CheckTxMined(Algos.encode(transaction.id)) - multisigBoxes = multisigBoxes.updated(Algos.encode(transaction.id), transaction.newBoxes) - } - if (txsType == 4) { - blockchainListener ! CheckTxMined(Algos.encode(transaction.id)) - multisigBoxes = multisigBoxes - Algos.encode(transaction.id) - } - - logger.info(s"Commit tx ${Algos.encode(transaction.id)} with type: ${txsType match { - case 1 => "DataTx" - case 2 => "MonetaryTx" - case 3 => "Multisig deploy" - case 4 => "Multisig signing" - }}") - networkServer ! TransactionForCommit(transaction) - } - - def createKeyPair: PrivateKey25519 = { - val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( - Blake2b256.hash(scorex.utils.Random.randomBytes(16)) - ) - PrivateKey25519(privateKey, publicKey) - } -} - -object Generator { - - case class TransactionForCommit(tx: Transaction) - - def props(settings: Settings, - privKey: PrivateKey25519, - nodeForLocalPrivKey: Node, - influx: Option[ActorRef], - networkServer: ActorRef): Props = - Props(new Generator(settings, privKey, nodeForLocalPrivKey, influx, networkServer)) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/actors/InfluxActor.scala b/src/main/scala/encry/net/actors/InfluxActor.scala deleted file mode 100644 index e48cc23..0000000 --- a/src/main/scala/encry/net/actors/InfluxActor.scala +++ /dev/null @@ -1,45 +0,0 @@ -package encry.net.actors - -import akka.actor.{Actor, Props} -import com.typesafe.scalalogging.StrictLogging -import org.influxdb.{InfluxDB, InfluxDBFactory} -import java.net._ -import org.encryfoundation.generator.actors.InfluxActor._ -import org.encryfoundation.generator.utils.Settings - -class InfluxActor(settings: Settings) extends Actor with StrictLogging { - - val nodeName: String = InetAddress.getLocalHost.getHostAddress - val udpPort: Int = settings.influxDB.map(_.udpPort).getOrElse(0) - val influxDB: InfluxDB = InfluxDBFactory.connect( - settings.influxDB.map(_.url).getOrElse(""), - settings.influxDB.map(_.login).getOrElse(""), - settings.influxDB.map(_.password).getOrElse("") - ) - influxDB.setRetentionPolicy("autogen") - - override def preStart(): Unit = { - logger.info("Influx actor started") - influxDB.write(udpPort, s"""txGenStartTime value="$nodeName"""") - } - - - override def receive: Receive = { - case PoolState(newO) => - influxDB.write(udpPort, s"txsStatFromGenerator,nodeName=$nodeName value=$newO") - - case SentBatches(num) => - influxDB.write(udpPort, s"numberOfSendedBatches,nodeName=$nodeName value=$num") - - case GetAllTimeSeconds(time) => - influxDB.write(udpPort, s"getAllTime,nodeName=$nodeName value=$time") - } -} - -object InfluxActor { - def props(settings: Settings): Props = Props(new InfluxActor(settings)) - - case class PoolState(newO: Int) - case class SentBatches(num: Int) - case class GetAllTimeSeconds(time: Long) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/Transaction.scala b/src/main/scala/encry/net/modifiers/Transaction.scala index cd54fe2..9359059 100644 --- a/src/main/scala/encry/net/modifiers/Transaction.scala +++ b/src/main/scala/encry/net/modifiers/Transaction.scala @@ -2,8 +2,8 @@ package encry.net.modifiers import TransactionProto.TransactionProtoMessage import com.google.protobuf.ByteString -import org.encryfoundation.generator.modifiers.box.Box -import org.encryfoundation.generator.modifiers.directives.{Directive, DirectiveProtoSerializer} +import encry.net.modifiers.box.Box +import encry.net.modifiers.directives.{Directive, DirectiveProtoSerializer} import scorex.crypto.hash.{Blake2b256, Digest32} import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} diff --git a/src/main/scala/encry/net/modifiers/TransactionsFactory.scala b/src/main/scala/encry/net/modifiers/TransactionsFactory.scala index 64dfd5d..c4f94c2 100644 --- a/src/main/scala/encry/net/modifiers/TransactionsFactory.scala +++ b/src/main/scala/encry/net/modifiers/TransactionsFactory.scala @@ -7,9 +7,9 @@ import org.encryfoundation.common.modifiers.mempool.transaction.{Input, Proof, P import org.encryfoundation.prismlang.compiler.CompiledContract import org.encryfoundation.prismlang.core.wrapped.BoxedValue import scorex.crypto.hash.{Blake2b256, Digest32} -import org.encryfoundation.generator.modifiers.directives._ +import encry.net.modifiers.directives._ import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.generator.modifiers.box.MonetaryBox +import encry.net.modifiers.box.MonetaryBox import scala.util.Random diff --git a/src/main/scala/encry/net/modifiers/box/AssetBox.scala b/src/main/scala/encry/net/modifiers/box/AssetBox.scala index 7b29469..48dfdb1 100644 --- a/src/main/scala/encry/net/modifiers/box/AssetBox.scala +++ b/src/main/scala/encry/net/modifiers/box/AssetBox.scala @@ -5,7 +5,7 @@ import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.encryfoundation.common.serialization.Serializer import org.encryfoundation.common.utils.Algos -import org.encryfoundation.generator.modifiers.box.TokenIssuingBox.TokenId +import encry.net.modifiers.box.TokenIssuingBox.TokenId import scala.util.Try diff --git a/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala b/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala index 6319169..4708210 100644 --- a/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala +++ b/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala @@ -5,7 +5,7 @@ import io.circe.Encoder import io.circe.syntax._ import org.encryfoundation.common.serialization.Serializer import org.encryfoundation.common.utils.Algos -import org.encryfoundation.generator.modifiers.box.TokenIssuingBox.TokenId +import encry.net.modifiers.box.TokenIssuingBox.TokenId import scala.util.Try diff --git a/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala b/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala index 0e4f546..087fc91 100644 --- a/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala +++ b/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala @@ -11,7 +11,7 @@ import org.encryfoundation.common.utils.{Algos, Utils} import org.encryfoundation.common.utils.constants.TestNetConstants import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import scorex.crypto.hash.Digest32 -import org.encryfoundation.generator.modifiers.box.{Box, EncryProposition, TokenIssuingBox} +import encry.net.modifiers.box.{Box, EncryProposition, TokenIssuingBox} import scala.util.Try diff --git a/src/main/scala/encry/net/modifiers/directives/DataDirective.scala b/src/main/scala/encry/net/modifiers/directives/DataDirective.scala index 75fc000..a7d467e 100644 --- a/src/main/scala/encry/net/modifiers/directives/DataDirective.scala +++ b/src/main/scala/encry/net/modifiers/directives/DataDirective.scala @@ -11,8 +11,8 @@ import org.encryfoundation.common.serialization.Serializer import org.encryfoundation.common.utils.{Algos, Utils} import org.encryfoundation.common.utils.constants.TestNetConstants import scorex.crypto.hash.Digest32 -import org.encryfoundation.generator.modifiers.box.{DataBox, EncryProposition} -import org.encryfoundation.generator.modifiers.directives.Directive.DTypeId +import encry.net.modifiers.box.{DataBox, EncryProposition} +import encry.net.modifiers.directives.Directive.DTypeId import scala.util.Try diff --git a/src/main/scala/encry/net/modifiers/directives/Directive.scala b/src/main/scala/encry/net/modifiers/directives/Directive.scala index aab6b00..13a3f48 100644 --- a/src/main/scala/encry/net/modifiers/directives/Directive.scala +++ b/src/main/scala/encry/net/modifiers/directives/Directive.scala @@ -4,7 +4,7 @@ import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage import io.circe._ import org.encryfoundation.common.serialization.BytesSerializable import scorex.crypto.hash.Digest32 -import org.encryfoundation.generator.modifiers.box.Box +import encry.net.modifiers.box.Box trait Directive extends BytesSerializable { diff --git a/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala b/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala index de074f9..5895c93 100644 --- a/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala +++ b/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala @@ -14,7 +14,7 @@ import org.encryfoundation.common.utils.TaggedTypes.ADKey import org.encryfoundation.common.utils.constants.TestNetConstants import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import scorex.crypto.hash.Digest32 -import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, EncryProposition} +import encry.net.modifiers.box.{AssetBox, Box, EncryProposition} case class ScriptedAssetDirective(contractHash: ContractHash, amount: Long, diff --git a/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala b/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala index c5ecbd8..dd76aa3 100644 --- a/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala +++ b/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala @@ -13,7 +13,7 @@ import org.encryfoundation.common.utils.TaggedTypes.ADKey import org.encryfoundation.common.utils.{Algos, Utils} import org.encryfoundation.common.utils.constants.{Constants, TestNetConstants} import scorex.crypto.hash.Digest32 -import org.encryfoundation.generator.modifiers.box.{AssetBox, Box, EncryProposition} +import encry.net.modifiers.box.{AssetBox, Box, EncryProposition} import scala.util.Try diff --git a/src/main/scala/encry/net/network/BasicMessagesRepo.scala b/src/main/scala/encry/net/network/BasicMessagesRepo.scala index 7866ddd..32c9593 100644 --- a/src/main/scala/encry/net/network/BasicMessagesRepo.scala +++ b/src/main/scala/encry/net/network/BasicMessagesRepo.scala @@ -1,6 +1,7 @@ package encry.net.network import java.net.InetSocketAddress + import NetworkMessagesProto.GeneralizedNetworkProtoMessage import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage._ @@ -11,10 +12,11 @@ import akka.actor.ActorRef import com.google.protobuf.{ByteString => GoogleByteString} import akka.util.{ByteString => AkkaByteString} import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.network.BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} -import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import org.encryfoundation.generator.utils.Settings +import encry.net.network.BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} +import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import encry.settings.ExplorerSettings import scorex.crypto.hash.Blake2b256 + import scala.util.Try object BasicMessagesRepo extends StrictLogging { @@ -34,7 +36,7 @@ object BasicMessagesRepo extends StrictLogging { def toInnerMessage: InnerMessage - def isValid(setting: Settings): Boolean + def isValid(syncPacketLength: Int): Boolean } sealed trait ProtoNetworkMessagesSerializer[T] { @@ -153,8 +155,8 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = SyncInfoNetworkMessage.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = - if (esi.lastHeaderIds.size <= setting.network.syncPacketLength) true else false + override def isValid(syncPacketLength: Int): Boolean = + if (esi.lastHeaderIds.size <= syncPacketLength) true else false } object SyncInfoNetworkMessage { @@ -191,8 +193,8 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = InvNetworkMessage.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = - if (data._2.size <= setting.network.syncPacketLength) true else false + override def isValid(syncPacketLength: Int): Boolean = + if (data._2.size <= syncPacketLength) true else false } object InvNetworkMessage { @@ -234,8 +236,8 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = RequestModifiersNetworkMessage.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = - if (data._2.size <= setting.network.syncPacketLength) true else false + override def isValid(syncPacketLength: Int): Boolean = + if (data._2.size <= syncPacketLength) true else false } object RequestModifiersNetworkMessage { @@ -280,8 +282,8 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = ModifiersNetworkMessage.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = - if (data._2.size <= setting.network.syncPacketLength) true else false + override def isValid(syncPacketLength: Int): Boolean = + if (data._2.size <= syncPacketLength) true else false } object ModifiersNetworkMessage { @@ -325,7 +327,7 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = 1: Byte - override def isValid(setting: Settings): Boolean = true + override def isValid(syncPacketLength: Int): Boolean = true } /** @@ -345,7 +347,7 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = PeersNetworkMessage.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = true + override def isValid(syncPacketLength: Int): Boolean = true } object PeersNetworkMessage { @@ -398,7 +400,7 @@ object BasicMessagesRepo extends StrictLogging { override val NetworkMessageTypeID: Byte = Handshake.NetworkMessageTypeID - override def isValid(setting: Settings): Boolean = true + override def isValid(syncPacketLength: Int): Boolean = true } object Handshake { diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala index b0be275..3dee94f 100644 --- a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala @@ -3,15 +3,14 @@ package encry.net.network import akka.actor.{Actor, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos -import org.encryfoundation.generator.actors.Generator.TransactionForCommit -import org.encryfoundation.generator.network.BasicMessagesRepo._ -import org.encryfoundation.generator.network.NetworkMessagesHandler.BroadcastInvForTx -import org.encryfoundation.generator.modifiers.{Transaction, TransactionProtoSerializer} -import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import org.encryfoundation.generator.utils.{CoreTaggedTypes, Settings} +import encry.net.network.BasicMessagesRepo._ +import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, TransactionForCommit} +import encry.net.modifiers.{Transaction, TransactionProtoSerializer} +import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import encry.net.utils.CoreTaggedTypes import supertagged.@@ -class NetworkMessagesHandler(settings: Settings) extends Actor with StrictLogging { +class NetworkMessagesHandler() extends Actor with StrictLogging { var localGeneratedTransactions: Seq[Transaction] = Seq.empty @@ -41,8 +40,8 @@ class NetworkMessagesHandler(settings: Settings) extends Actor with StrictLoggin } object NetworkMessagesHandler { - + case class TransactionForCommit(tx: Transaction) case class BroadcastInvForTx(tx: Transaction) - def props(settings: Settings) = Props(new NetworkMessagesHandler(settings)) + def props() = Props(new NetworkMessagesHandler()) } \ No newline at end of file diff --git a/src/main/scala/encry/net/network/NetworkServer.scala b/src/main/scala/encry/net/network/NetworkServer.scala index 2f4a590..1a8fd44 100644 --- a/src/main/scala/encry/net/network/NetworkServer.scala +++ b/src/main/scala/encry/net/network/NetworkServer.scala @@ -7,38 +7,36 @@ import akka.io.Tcp.SO.KeepAlive import akka.io.Tcp._ import akka.io.{IO, Tcp} import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.actors.Generator -import org.encryfoundation.generator.actors.Generator.TransactionForCommit -import org.encryfoundation.generator.network.BasicMessagesRepo.{InvNetworkMessage, Outgoing} -import org.encryfoundation.generator.network.NetworkMessagesHandler.BroadcastInvForTx -import org.encryfoundation.generator.network.NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} -import org.encryfoundation.generator.network.PeerHandler._ -import org.encryfoundation.generator.modifiers.Transaction -import org.encryfoundation.generator.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import org.encryfoundation.generator.utils.Mnemonic.createPrivKey -import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} +import encry.net.network.BasicMessagesRepo.{InvNetworkMessage, Outgoing} +import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, TransactionForCommit} +import encry.net.network.NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} +import encry.net.network.PeerHandler._ +import encry.net.modifiers.Transaction +import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} +import encry.net.utils.Mnemonic.createPrivKey +import encry.net.utils.NetworkTimeProvider +import encry.settings.NetworkSettings import scala.concurrent.duration._ import scala.concurrent.ExecutionContextExecutor -class NetworkServer(settings: Settings, - timeProvider: NetworkTimeProvider, - influx: Option[ActorRef]) extends Actor with StrictLogging { +class NetworkServer(settings: NetworkSettings, + timeProvider: NetworkTimeProvider) extends Actor with StrictLogging { implicit val system: ActorSystem = context.system implicit val ec: ExecutionContextExecutor = context.dispatcher var isConnected = false - val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(settings)) + val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props()) var tmpConnectionHandler: Option[ActorRef] = None val selfPeer: InetSocketAddress = - new InetSocketAddress(settings.network.bindAddressHost, settings.network.bindAddressPort) + new InetSocketAddress(settings.bindAddressHost, settings.bindAddressPort) val connectingPeer: InetSocketAddress = - new InetSocketAddress(settings.network.peerForConnectionHost, settings.network.peerForConnectionPort) + new InetSocketAddress(settings.peerForConnectionHost, settings.peerForConnectionPort) IO(Tcp) ! Bind(self, selfPeer) @@ -87,11 +85,7 @@ class NetworkServer(settings: Settings, logger.debug(s"Send inv message to remote.") case ConnectionSetupSuccessfully => - settings.peers.foreach { peer => - logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") - system.actorOf( - Generator.props(settings, createPrivKey(Some(peer.mnemonicKey)), peer, influx, self), peer.explorerHost) - } + //logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") case msg@TransactionForCommit(_) => messagesHandler ! msg @@ -100,11 +94,9 @@ class NetworkServer(settings: Settings, } object NetworkServer { - case object CheckConnection - case object ConnectionSetupSuccessfully - def props(settings: Settings, timeProvider: NetworkTimeProvider, influx: Option[ActorRef]): Props = - Props(new NetworkServer(settings, timeProvider, influx)) + def props(settings: NetworkSettings, timeProvider: NetworkTimeProvider): Props = + Props(new NetworkServer(settings, timeProvider)) } \ No newline at end of file diff --git a/src/main/scala/encry/net/network/PeerHandler.scala b/src/main/scala/encry/net/network/PeerHandler.scala index 59fb8c6..7959b18 100644 --- a/src/main/scala/encry/net/network/PeerHandler.scala +++ b/src/main/scala/encry/net/network/PeerHandler.scala @@ -2,16 +2,19 @@ package encry.net.network import java.net.InetSocketAddress import java.nio.ByteOrder + import akka.actor.{Actor, ActorRef, Cancellable, Props} import akka.io.Tcp import akka.io.Tcp._ import akka.util.{ByteString, CompactByteString} import com.google.common.primitives.Ints import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.generator.network.PeerHandler._ -import org.encryfoundation.generator.network.BasicMessagesRepo._ -import org.encryfoundation.generator.network.NetworkServer.ConnectionSetupSuccessfully -import org.encryfoundation.generator.utils.{NetworkTimeProvider, Settings} +import encry.net.network.PeerHandler._ +import encry.net.network.BasicMessagesRepo._ +import encry.net.network.NetworkServer.ConnectionSetupSuccessfully +import encry.net.utils.NetworkTimeProvider +import encry.settings.NetworkSettings + import scala.annotation.tailrec import scala.collection.immutable.HashMap import scala.concurrent.ExecutionContextExecutor @@ -19,7 +22,7 @@ import scala.util.{Failure, Success} class PeerHandler(remoteAddress: InetSocketAddress, listener: ActorRef, - settings: Settings, + settings: NetworkSettings, timeProvider: NetworkTimeProvider, direction: ConnectionType, receivedMessagesHandler: ActorRef) extends Actor with StrictLogging { @@ -47,9 +50,9 @@ class PeerHandler(remoteAddress: InetSocketAddress, case StartIteration => timeProvider.time() map { time => val handshake: Handshake = Handshake( - protocolToBytes(settings.network.appVersion), - settings.network.nodeName, - Some(new InetSocketAddress(settings.network.declaredAddressHost, settings.network.declaredAddressPort)), + protocolToBytes(settings.appVersion), + settings.nodeName, + Some(new InetSocketAddress(settings.declaredAddressHost, settings.declaredAddressPort)), time ) listener ! Write(ByteString(GeneralizedNetworkMessage.toProto(handshake).toByteArray)) @@ -61,7 +64,7 @@ class PeerHandler(remoteAddress: InetSocketAddress, context.parent ! ConnectionSetupSuccessfully context.become(workingCycleWriting(ConnectedPeer(remoteAddress, self, Outgoing, receivedHandshake.get))) } else context.become(awaitingConnectionBehaviour( - Some(context.system.scheduler.scheduleOnce(settings.network.handshakeTimeout, self, HandshakeTimeout))) + Some(context.system.scheduler.scheduleOnce(settings.handshakeTimeout, self, HandshakeTimeout))) ) } @@ -141,6 +144,10 @@ class PeerHandler(remoteAddress: InetSocketAddress, chunksBuffer = packet._2 packet._1.find { packet => GeneralizedNetworkMessage.fromProto(packet) match { + case Success(message) if message.messageName == "Inv" => + logger.debug("Received message " + message.messageName + " from " + remoteAddress) + logger.debug("Inv message :" + message) + false case Success(message) => receivedMessagesHandler ! MessageFromNetwork(message, Some(cp)) logger.debug("Received message " + message.messageName + " from " + remoteAddress) @@ -224,7 +231,7 @@ object PeerHandler { def props(remoteAddress: InetSocketAddress, listener: ActorRef, - settings: Settings, + settings: NetworkSettings, timeProvider: NetworkTimeProvider, direction: ConnectionType, messagesHandler: ActorRef): Props = diff --git a/src/main/scala/encry/net/utils/NetworkService.scala b/src/main/scala/encry/net/utils/NetworkService.scala index 1be5b9e..0e67211 100644 --- a/src/main/scala/encry/net/utils/NetworkService.scala +++ b/src/main/scala/encry/net/utils/NetworkService.scala @@ -9,21 +9,22 @@ import io.circe.{Decoder, HCursor} import io.circe.parser.decode import org.encryfoundation.common.modifiers.mempool.transaction.PubKeyLockedContract import org.encryfoundation.common.utils.Algos -import org.encryfoundation.generator.GeneratorApp._ -import org.encryfoundation.generator.modifiers.box.Box +import encry.net.modifiers.box.Box +import encry.settings.{NetworkSettings, NodeSettings} import scala.concurrent.Future import scala.util.control.NonFatal +import encry.ExplorerApp._ object NetworkService extends StrictLogging { - def requestUtxos(node: Node, from: Int, to: Int): Future[List[Box]] = { - val privKey = Mnemonic.createPrivKey(Option(node.mnemonicKey)) + def requestUtxos(settings: NodeSettings, from: Int, to: Int): Future[List[Box]] = { + val privKey = Mnemonic.createPrivKey(Option(settings.mnemonicKey)) val contractHash: String = Algos.encode(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash) Http().singleRequest(HttpRequest( method = HttpMethods.GET, uri = s"/wallet/$contractHash/boxes/$from/$to" - ).withEffectiveUri(securedConnection = false, Host(node.explorerHost, node.explorerPort))) + ).withEffectiveUri(securedConnection = false, Host(settings.explorerHost, settings.explorerPort))) .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) .map(_.utf8String) .map(decode[List[Box]]) diff --git a/src/main/scala/encry/net/utils/NetworkTime.scala b/src/main/scala/encry/net/utils/NetworkTime.scala index 7871110..bc9516a 100644 --- a/src/main/scala/encry/net/utils/NetworkTime.scala +++ b/src/main/scala/encry/net/utils/NetworkTime.scala @@ -3,7 +3,7 @@ package encry.net.utils import java.net.InetAddress import com.typesafe.scalalogging.StrictLogging import org.apache.commons.net.ntp.{NTPUDPClient, TimeInfo} -import org.encryfoundation.generator.utils.NetworkTime.Time +import encry.net.utils.NetworkTime.Time import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.concurrent.Future diff --git a/src/main/scala/encry/net/utils/Settings.scala b/src/main/scala/encry/net/utils/Settings.scala deleted file mode 100644 index e74e924..0000000 --- a/src/main/scala/encry/net/utils/Settings.scala +++ /dev/null @@ -1,53 +0,0 @@ -package encry.net.utils - -import scala.concurrent.duration.FiniteDuration - -case class Settings(peers: List[Node], - influxDB: Option[InfluxDBSettings], - generator: GeneratorSettings, - boxesHolderSettings: BoxesHolderSettings, - transactions: TransactionsSettings, - network: NetworkSettings, - ntp: NetworkTimeProviderSettings, - multisig: MultisigSettings) - -case class Node(explorerHost: String, - explorerPort: Int, - mnemonicKey: String) - -case class InfluxDBSettings(url: String, - login: String, - password: String, - udpPort: Int) - -case class GeneratorSettings(transactionsSendingFrequency: Int) - -case class BoxesHolderSettings(askingAPIFrequency: FiniteDuration, - rangeForAskingBoxes: Int, - poolSize: Int, - maxPoolSize: Long, - bloomFilterCleanupInterval: FiniteDuration, - bloomFilterCapacity: Long, - bloomFilterFailureProbability: Double) - -case class TransactionsSettings(numberOfDataTxs: Int, - numberOfMonetaryTxs: Int, - numberOfMultisigTxs: Int, - requiredAmount: Int, - feeAmount: Int, - dataTxSize: Int, - numberOfCreatedDirectives: Int) - -case class MultisigSettings(checkTxMinedPeriod: Int, numberOfBlocksToCheck: Int, mnemonicKeys: List[String]) - -case class NetworkSettings(syncPacketLength: Int, - bindAddressHost: String, - bindAddressPort: Int, - nodeName: String, - appVersion: String, - handshakeTimeout: FiniteDuration, - peerForConnectionHost: String, - peerForConnectionPort: Int, - peerForConnectionApiPort: Int, - declaredAddressHost: String, - declaredAddressPort: Int) \ No newline at end of file diff --git a/src/main/scala/encry/settings/BlackListSettings.scala b/src/main/scala/encry/settings/BlackListSettings.scala deleted file mode 100644 index a6e8bfb..0000000 --- a/src/main/scala/encry/settings/BlackListSettings.scala +++ /dev/null @@ -1,5 +0,0 @@ -package encry.settings - -import scala.concurrent.duration.FiniteDuration - -case class BlackListSettings(banTime: FiniteDuration, cleanupTime: FiniteDuration) \ No newline at end of file diff --git a/src/main/scala/encry/settings/Constants.scala b/src/main/scala/encry/settings/Constants.scala index c723456..a2f7b58 100644 --- a/src/main/scala/encry/settings/Constants.scala +++ b/src/main/scala/encry/settings/Constants.scala @@ -4,6 +4,5 @@ import org.encryfoundation.common.utils.Algos object Constants { val IntrinsicTokenId: Array[Byte] = Algos.hash("intrinsic_token") - val EttTokenId: String = Algos.encode(IntrinsicTokenId) } diff --git a/src/main/scala/encry/settings/DatabaseSettings.scala b/src/main/scala/encry/settings/DatabaseSettings.scala deleted file mode 100644 index 5297b8b..0000000 --- a/src/main/scala/encry/settings/DatabaseSettings.scala +++ /dev/null @@ -1,7 +0,0 @@ -package encry.settings - -case class DatabaseSettings(host: String, - user: String, - password: String, - maxPoolSize: Int, - connectionTimeout: Long) diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/ExplorerSettings.scala index c5cb1eb..898d4b9 100644 --- a/src/main/scala/encry/settings/ExplorerSettings.scala +++ b/src/main/scala/encry/settings/ExplorerSettings.scala @@ -1,15 +1,47 @@ package encry.settings import java.net.InetSocketAddress + import com.typesafe.config.{Config, ConfigFactory} +import encry.net.utils.NetworkTimeProviderSettings import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ import net.ceedubs.ficus.readers.ValueReader +import scala.concurrent.duration.FiniteDuration + case class ExplorerSettings(parseSettings: ParseSettings, blackListSettings: BlackListSettings, databaseSettings: DatabaseSettings, - nodeSettings: NodeSettings) + ntpSettings: NetworkTimeProviderSettings, + nodeSettings: NodeSettings, + networkSettings: NetworkSettings, + multisigSettings: MultisigSettings) + +case class NetworkSettings(syncPacketLength: Int, + bindAddressHost: String, + bindAddressPort: Int, + nodeName: String, + appVersion: String, + handshakeTimeout: FiniteDuration, + peerForConnectionHost: String, + peerForConnectionPort: Int, + peerForConnectionApiPort: Int, + declaredAddressHost: String, + declaredAddressPort: Int) + +case class NodeSettings(explorerHost: String, + explorerPort: Int, + mnemonicKey: String) + +case class MultisigSettings(checkTxMinedPeriod: Int, numberOfBlocksToCheck: Int, mnemonicKeys: List[String]) + +case class ParseSettings(nodes: List[InetSocketAddress], recoverBatchSize: Int, infinitePing: Boolean, askNode: Boolean, + numberOfAttempts: Option[Int] = None) + +case class DatabaseSettings(host: String, user: String, password: String, maxPoolSize: Int, connectionTimeout: Long) + +case class BlackListSettings(banTime: FiniteDuration, cleanupTime: FiniteDuration) object ExplorerSettings { @@ -18,8 +50,6 @@ object ExplorerSettings { new InetSocketAddress(split(0), split(1).toInt) } - val configPath: String = "explorer" - def read: ExplorerSettings = ConfigFactory.load("local.conf") - .withFallback(ConfigFactory.load()).as[ExplorerSettings](configPath) + .withFallback(ConfigFactory.load()).as[ExplorerSettings]("explorer") } diff --git a/src/main/scala/encry/settings/NodeSettings.scala b/src/main/scala/encry/settings/NodeSettings.scala deleted file mode 100644 index 84f28dd..0000000 --- a/src/main/scala/encry/settings/NodeSettings.scala +++ /dev/null @@ -1,3 +0,0 @@ -package encry.settings - -case class NodeSettings(maxRollbackDepth: Int) diff --git a/src/main/scala/encry/settings/ParseSettings.scala b/src/main/scala/encry/settings/ParseSettings.scala deleted file mode 100644 index d53e327..0000000 --- a/src/main/scala/encry/settings/ParseSettings.scala +++ /dev/null @@ -1,9 +0,0 @@ -package encry.settings - -import java.net.InetSocketAddress - -case class ParseSettings(nodes: List[InetSocketAddress], - recoverBatchSize: Int, - infinitePing: Boolean, - askNode: Boolean, - numberOfAttempts: Option[Int] = None) From 124a7f3e461351480c24fa264bef207216354011 Mon Sep 17 00:00:00 2001 From: capdev Date: Tue, 15 Oct 2019 18:09:18 +0500 Subject: [PATCH 04/21] request transaction by notify extract transactions from RequestModifiers --- src/main/scala/encry/ExplorerApp.scala | 1 - .../net/network/NetworkMessagesHandler.scala | 22 ++++++++++++++++++- .../scala/encry/net/network/PeerHandler.scala | 4 ---- .../encry/settings/ExplorerSettings.scala | 4 +--- .../scala/encry/utils/CoreTaggedTypes.scala | 18 --------------- 5 files changed, 22 insertions(+), 27 deletions(-) delete mode 100644 src/main/scala/encry/utils/CoreTaggedTypes.scala diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index 1972a40..87d6141 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -6,7 +6,6 @@ import cats.effect.{Blocker, ContextShift, IO} import cats.implicits._ import doobie.hikari.HikariTransactor import doobie.util.ExecutionContexts -import encry.database.{DBActor, DBService} import encry.net.network.NetworkServer import encry.net.utils.NetworkTimeProvider import encry.settings.ExplorerSettings diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala index 3dee94f..e4a426b 100644 --- a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala @@ -1,5 +1,6 @@ package encry.net.network +import TransactionProto.TransactionProtoMessage import akka.actor.{Actor, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos @@ -19,7 +20,26 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { localGeneratedTransactions :+= transaction context.parent ! BroadcastInvForTx(transaction) - case MessageFromNetwork(message, _) => message match { + case MessageFromNetwork(message, peerOpt) => message match { + + case InvNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => + logger.debug(s"Got transactionIds: ${invData._2.map(Algos.encode).mkString(",")}") + peerOpt.foreach { peer => + logger.debug(s"Request transactions: ${invData._2.map(Algos.encode).mkString(",")}") + peer.handlerRef ! RequestModifiersNetworkMessage((invData._1, invData._2)) + } + + case ModifiersNetworkMessage((modifierTypeId, modifiers)) => + logger.debug(s"Response modifiers: $modifierTypeId size ${modifiers.size}") + val transactions = modifiers.flatMap {case (modifierId, bytes) => + TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)).toOption + } + logger.debug(s"Extracted transactions: $transactions") + + case InvNetworkMessage((modifierTypeId, modifierIds)) => + logger.debug(s"Inv message : $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") + + case RequestModifiersNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => logger.debug(s"Got request modifiers on NMH") val tmpInv: Seq[String] = invData._2.map(Algos.encode) diff --git a/src/main/scala/encry/net/network/PeerHandler.scala b/src/main/scala/encry/net/network/PeerHandler.scala index 7959b18..e366ae9 100644 --- a/src/main/scala/encry/net/network/PeerHandler.scala +++ b/src/main/scala/encry/net/network/PeerHandler.scala @@ -144,10 +144,6 @@ class PeerHandler(remoteAddress: InetSocketAddress, chunksBuffer = packet._2 packet._1.find { packet => GeneralizedNetworkMessage.fromProto(packet) match { - case Success(message) if message.messageName == "Inv" => - logger.debug("Received message " + message.messageName + " from " + remoteAddress) - logger.debug("Inv message :" + message) - false case Success(message) => receivedMessagesHandler ! MessageFromNetwork(message, Some(cp)) logger.debug("Received message " + message.messageName + " from " + remoteAddress) diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/ExplorerSettings.scala index 898d4b9..b9d35f4 100644 --- a/src/main/scala/encry/settings/ExplorerSettings.scala +++ b/src/main/scala/encry/settings/ExplorerSettings.scala @@ -30,9 +30,7 @@ case class NetworkSettings(syncPacketLength: Int, declaredAddressHost: String, declaredAddressPort: Int) -case class NodeSettings(explorerHost: String, - explorerPort: Int, - mnemonicKey: String) +case class NodeSettings(explorerHost: String, explorerPort: Int, mnemonicKey: String) case class MultisigSettings(checkTxMinedPeriod: Int, numberOfBlocksToCheck: Int, mnemonicKeys: List[String]) diff --git a/src/main/scala/encry/utils/CoreTaggedTypes.scala b/src/main/scala/encry/utils/CoreTaggedTypes.scala deleted file mode 100644 index 79a7b57..0000000 --- a/src/main/scala/encry/utils/CoreTaggedTypes.scala +++ /dev/null @@ -1,18 +0,0 @@ -package encry.utils - -import supertagged.TaggedType - -object CoreTaggedTypes { - object ModifierTypeId extends TaggedType[Byte] - - object ModifierId extends TaggedType[Array[Byte]] - - object VersionTag extends TaggedType[Array[Byte]] - - type ModifierTypeId = ModifierTypeId.Type - - type ModifierId = ModifierId.Type - - type VersionTag = VersionTag.Type - -} From e3ea3c32d5dd7e8b249fc1f9dc2afded84f07fe4 Mon Sep 17 00:00:00 2001 From: capdev Date: Tue, 15 Oct 2019 18:12:54 +0500 Subject: [PATCH 05/21] todo --- src/main/scala/encry/net/network/NetworkMessagesHandler.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala index e4a426b..eeddaf0 100644 --- a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala @@ -11,6 +11,8 @@ import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} import encry.net.utils.CoreTaggedTypes import supertagged.@@ +//TODO: replace everywhere encry.net to org.encryfoundation.common + class NetworkMessagesHandler() extends Actor with StrictLogging { var localGeneratedTransactions: Seq[Transaction] = Seq.empty From 53ea1573779c80aaad5a2864ee745b81435a981e Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 16 Oct 2019 11:27:20 +0500 Subject: [PATCH 06/21] modifiers request modifiers receive and deserialize --- .../net/network/NetworkMessagesHandler.scala | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala index eeddaf0..6831717 100644 --- a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/net/network/NetworkMessagesHandler.scala @@ -1,14 +1,17 @@ package encry.net.network +import HeaderProto.HeaderProtoMessage +import PayloadProto.PayloadProtoMessage import TransactionProto.TransactionProtoMessage import akka.actor.{Actor, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos import encry.net.network.BasicMessagesRepo._ -import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, TransactionForCommit} +import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, ReceiveHeader, ReceivePayload, ReceiveTransaction, TransactionForCommit} import encry.net.modifiers.{Transaction, TransactionProtoSerializer} import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} import encry.net.utils.CoreTaggedTypes +import org.encryfoundation.common.modifiers.history.{Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} import supertagged.@@ //TODO: replace everywhere encry.net to org.encryfoundation.common @@ -24,23 +27,33 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { case MessageFromNetwork(message, peerOpt) => message match { - case InvNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => - logger.debug(s"Got transactionIds: ${invData._2.map(Algos.encode).mkString(",")}") + case InvNetworkMessage((modifierTypeId, modifierIds)) => + logger.debug(s"Got modifiers: $modifierTypeId (${modifierIds.map(Algos.encode).mkString(",")})") peerOpt.foreach { peer => - logger.debug(s"Request transactions: ${invData._2.map(Algos.encode).mkString(",")}") - peer.handlerRef ! RequestModifiersNetworkMessage((invData._1, invData._2)) + if (List(Transaction.modifierTypeId, Header.modifierTypeId, Payload.modifierTypeId).contains(modifierTypeId)) { + logger.debug(s"Request modifier: $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") + peer.handlerRef ! RequestModifiersNetworkMessage((modifierTypeId, modifierIds)) + } } - case ModifiersNetworkMessage((modifierTypeId, modifiers)) => - logger.debug(s"Response modifiers: $modifierTypeId size ${modifiers.size}") - val transactions = modifiers.flatMap {case (modifierId, bytes) => - TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)).toOption - } - logger.debug(s"Extracted transactions: $transactions") + case ModifiersNetworkMessage((modifierTypeId, modifierMap)) => + logger.debug(s"Response modifiers: $modifierTypeId size ${modifierMap.size}") - case InvNetworkMessage((modifierTypeId, modifierIds)) => - logger.debug(s"Inv message : $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") + modifierMap.foreach { case (modifierId, bytes) => + modifierTypeId match { + case Transaction.modifierTypeId => + val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) + tx.foreach(target ! ReceiveTransaction(_)) + case Header.modifierTypeId => + val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) + header.foreach(target ! ReceiveHeader(_)) + + case Payload.modifierTypeId => + val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) + payload.foreach(target ! ReceivePayload(_)) + } + } case RequestModifiersNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => logger.debug(s"Got request modifiers on NMH") @@ -62,7 +75,15 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { } object NetworkMessagesHandler { + + case class ReceiveTransaction(tx: Transaction) + + case class ReceiveHeader(header: Header) + + case class ReceivePayload(payload: Payload) + case class TransactionForCommit(tx: Transaction) + case class BroadcastInvForTx(tx: Transaction) def props() = Props(new NetworkMessagesHandler()) From 860654f45199c897501346ac6fef3f318a25f705 Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 16 Oct 2019 12:00:54 +0500 Subject: [PATCH 07/21] cleanup --- src/main/scala/encry/ExplorerApp.scala | 3 +- .../modifiers/ScriptedAssetDirective.scala | 1 - .../encry/net/actors/BlockchainListener.scala | 44 ----- .../encry/net/modifiers/Transaction.scala | 95 ----------- .../net/modifiers/TransactionsFactory.scala | 161 ------------------ .../encry/net/modifiers/box/AssetBox.scala | 84 --------- .../scala/encry/net/modifiers/box/Box.scala | 55 ------ .../encry/net/modifiers/box/DataBox.scala | 66 ------- .../net/modifiers/box/EncryBaseBox.scala | 38 ----- .../encry/net/modifiers/box/EncryBox.scala | 14 -- .../modifiers/box/EncryBoxStateChanges.scala | 23 --- .../net/modifiers/box/EncryProposition.scala | 46 ----- .../encry/net/modifiers/box/MonetaryBox.scala | 3 - .../net/modifiers/box/TokenIssuingBox.scala | 56 ------ .../directives/AssetIssuingDirective.scala | 85 --------- .../modifiers/directives/DataDirective.scala | 89 ---------- .../net/modifiers/directives/Directive.scala | 45 ----- .../directives/DirectiveSerializer.scala | 42 ----- .../directives/ScriptedAssetDirective.scala | 100 ----------- .../directives/TransferDirective.scala | 112 ------------ .../encry/net/transaction/Contracts.scala | 31 ---- .../encry/net/utils/CoreTaggedTypes.scala | 14 -- src/main/scala/encry/net/utils/Mnemonic.scala | 40 ----- .../encry/net/utils/NetworkService.scala | 79 --------- .../{net => }/network/BasicMessagesRepo.scala | 11 +- .../network/NetworkMessagesHandler.scala | 39 +---- .../{net => }/network/NetworkServer.scala | 24 +-- .../{net/utils => network}/NetworkTime.scala | 8 +- .../encry/{net => }/network/PeerHandler.scala | 13 +- .../encry/settings/ExplorerSettings.scala | 2 +- 30 files changed, 30 insertions(+), 1393 deletions(-) delete mode 100644 src/main/scala/encry/net/actors/BlockchainListener.scala delete mode 100644 src/main/scala/encry/net/modifiers/Transaction.scala delete mode 100644 src/main/scala/encry/net/modifiers/TransactionsFactory.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/AssetBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/Box.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/DataBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/EncryBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/EncryProposition.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/MonetaryBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/DataDirective.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/Directive.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala delete mode 100644 src/main/scala/encry/net/modifiers/directives/TransferDirective.scala delete mode 100644 src/main/scala/encry/net/transaction/Contracts.scala delete mode 100644 src/main/scala/encry/net/utils/CoreTaggedTypes.scala delete mode 100644 src/main/scala/encry/net/utils/Mnemonic.scala delete mode 100644 src/main/scala/encry/net/utils/NetworkService.scala rename src/main/scala/encry/{net => }/network/BasicMessagesRepo.scala (98%) rename src/main/scala/encry/{net => }/network/NetworkMessagesHandler.scala (54%) rename src/main/scala/encry/{net => }/network/NetworkServer.scala (77%) rename src/main/scala/encry/{net/utils => network}/NetworkTime.scala (97%) rename src/main/scala/encry/{net => }/network/PeerHandler.scala (96%) diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index 87d6141..d6e379f 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -6,8 +6,7 @@ import cats.effect.{Blocker, ContextShift, IO} import cats.implicits._ import doobie.hikari.HikariTransactor import doobie.util.ExecutionContexts -import encry.net.network.NetworkServer -import encry.net.utils.NetworkTimeProvider +import encry.network.{NetworkServer, NetworkTimeProvider} import encry.settings.ExplorerSettings import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} diff --git a/src/main/scala/encry/blockchain/modifiers/ScriptedAssetDirective.scala b/src/main/scala/encry/blockchain/modifiers/ScriptedAssetDirective.scala index be817ad..bf5cfc3 100644 --- a/src/main/scala/encry/blockchain/modifiers/ScriptedAssetDirective.scala +++ b/src/main/scala/encry/blockchain/modifiers/ScriptedAssetDirective.scala @@ -4,7 +4,6 @@ import com.google.common.primitives.Ints import io.circe.syntax._ import encry.blockchain.modifiers.Directive.DTypeId import encry.blockchain.modifiers.boxes.{AssetBox, EncryBaseBox, EncryProposition} -import encry.utils.CoreTaggedTypes.ModifierId import encry.utils.Utils import io.circe.{Decoder, Encoder, HCursor} import org.encryfoundation.common.utils.Algos diff --git a/src/main/scala/encry/net/actors/BlockchainListener.scala b/src/main/scala/encry/net/actors/BlockchainListener.scala deleted file mode 100644 index 9767515..0000000 --- a/src/main/scala/encry/net/actors/BlockchainListener.scala +++ /dev/null @@ -1,44 +0,0 @@ -package encry.net.actors - -import akka.actor.Actor -import com.typesafe.scalalogging.StrictLogging -import encry.net.actors.BlockchainListener.{CheckTxMined, MultisigTxsInBlockchain, TimeToCheck} -import encry.net.utils.NetworkService -import encry.settings.ExplorerSettings - -import scala.concurrent.{ExecutionContextExecutor, Future} -import scala.concurrent.duration._ - -class BlockchainListener(settings: ExplorerSettings) extends Actor with StrictLogging { - - implicit val ec: ExecutionContextExecutor = context.dispatcher - - override def receive: Receive = operating(Vector.empty) - - override def preStart(): Unit = - context.system.scheduler.schedule(settings.multisigSettings.checkTxMinedPeriod.seconds, settings.multisigSettings.checkTxMinedPeriod.seconds) { - self ! TimeToCheck - } - - def operating(txsToCheck: Vector[String]): Receive = { - case CheckTxMined(id) => context.become(operating(txsToCheck :+ id)) - case TimeToCheck if txsToCheck.nonEmpty => - NetworkService.checkTxsInBlockchain(settings.networkSettings, txsToCheck, settings.multisigSettings.numberOfBlocksToCheck) - .foreach { txs => - if (txs.nonEmpty) { - logger.info(s"Multisig txs ${txs.mkString(", ")} are in blockchain now") - context.parent ! MultisigTxsInBlockchain(txs.toSet) - self ! MultisigTxsInBlockchain(txs.toSet) - } - } - case TimeToCheck => - case MultisigTxsInBlockchain(txs) => context.become(operating(txsToCheck.diff(txs.toSeq))) - } - -} - -object BlockchainListener { - case class CheckTxMined(txId: String) - case class MultisigTxsInBlockchain(ids: Set[String]) - case object TimeToCheck -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/Transaction.scala b/src/main/scala/encry/net/modifiers/Transaction.scala deleted file mode 100644 index 9359059..0000000 --- a/src/main/scala/encry/net/modifiers/Transaction.scala +++ /dev/null @@ -1,95 +0,0 @@ -package encry.net.modifiers - -import TransactionProto.TransactionProtoMessage -import com.google.protobuf.ByteString -import encry.net.modifiers.box.Box -import encry.net.modifiers.directives.{Directive, DirectiveProtoSerializer} -import scorex.crypto.hash.{Blake2b256, Digest32} -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.modifiers.mempool.transaction.{Input, InputSerializer, Proof, ProofSerializer} -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.prismlang.core.Types -import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} - -import scala.util.Try - -case class Transaction(fee: Long, - timestamp: Long, - inputs: IndexedSeq[Input], - directives: IndexedSeq[Directive], - defaultProofOpt: Option[Proof]) { - - val messageToSign: Array[Byte] = UnsignedEncryTransaction.bytesToSign(fee, timestamp, inputs, directives) - lazy val id: Array[Byte] = Blake2b256.hash(messageToSign) - lazy val newBoxes: IndexedSeq[Box] = - directives.zipWithIndex.flatMap { case (d, idx) => d.boxes(Digest32 !@@ id, idx) } - - val tpe: Types.Product = Types.EncryTransaction - - def asVal: PValue = PValue(PObject(Map( - "inputs" -> PValue(inputs.map(_.boxId.toList), Types.PCollection(Types.PCollection.ofByte)), - "outputs" -> PValue(newBoxes.map(_.asPrism), Types.PCollection(Types.EncryBox)), - "messageToSign" -> PValue(messageToSign, Types.PCollection.ofByte) - ), tpe), tpe) -} - -object Transaction { - - val modifierTypeId: Byte = 2.toByte - - implicit val jsonEncoder: Encoder[Transaction] = (tx: Transaction) => Map( - "id" -> Algos.encode(tx.id).asJson, - "fee" -> tx.fee.asJson, - "timestamp" -> tx.timestamp.asJson, - "inputs" -> tx.inputs.map(_.asJson).asJson, - "directives" -> tx.directives.map(_.asJson).asJson, - "defaultProofOpt" -> tx.defaultProofOpt.map(_.asJson).asJson - ).asJson - - implicit val jsonDecoder: Decoder[Transaction] = (c: HCursor) => { - for { - fee <- c.downField("fee").as[Long] - timestamp <- c.downField("timestamp").as[Long] - inputs <- c.downField("inputs").as[IndexedSeq[Input]] - directives <- c.downField("directives").as[IndexedSeq[Directive]] - defaultProofOpt <- c.downField("defaultProofOpt").as[Option[Proof]] - } yield Transaction( - fee, - timestamp, - inputs, - directives, - defaultProofOpt - ) - } -} - -trait ProtoTransactionSerializer[T] { - - def toProto(message: T): TransactionProtoMessage - - def fromProto(message: TransactionProtoMessage): Try[T] -} - -object TransactionProtoSerializer extends ProtoTransactionSerializer[Transaction] { - - override def toProto(message: Transaction): TransactionProtoMessage = { - val initialTx: TransactionProtoMessage = TransactionProtoMessage() - .withFee(message.fee) - .withTimestamp(message.timestamp) - .withInputs(message.inputs.map(input => ByteString.copyFrom(input.bytes)).to[scala.collection.immutable.IndexedSeq]) - .withDirectives(message.directives.map(_.toDirectiveProto).to[scala.collection.immutable.IndexedSeq]) - message.defaultProofOpt match { - case Some(value) => initialTx.withProof(ByteString.copyFrom(value.bytes)) - case None => initialTx - } - } - - override def fromProto(message: TransactionProtoMessage): Try[Transaction] = Try(Transaction( - message.fee, - message.timestamp, - message.inputs.map(element => InputSerializer.parseBytes(element.toByteArray).get), - message.directives.map(directive => DirectiveProtoSerializer.fromProto(directive).get), - ProofSerializer.parseBytes(message.proof.toByteArray).toOption - )) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/TransactionsFactory.scala b/src/main/scala/encry/net/modifiers/TransactionsFactory.scala deleted file mode 100644 index c4f94c2..0000000 --- a/src/main/scala/encry/net/modifiers/TransactionsFactory.scala +++ /dev/null @@ -1,161 +0,0 @@ -package encry.net.modifiers - -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.transaction.{Input, Proof, PubKeyLockedContract} -import org.encryfoundation.prismlang.compiler.CompiledContract -import org.encryfoundation.prismlang.core.wrapped.BoxedValue -import scorex.crypto.hash.{Blake2b256, Digest32} -import encry.net.modifiers.directives._ -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import encry.net.modifiers.box.MonetaryBox - -import scala.util.Random - -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.isDefinedAt(idx)) 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) - )) -} - -object TransactionsFactory extends StrictLogging { - - def defaultPaymentTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - recipient: String, - amount: Long, - numberOfCreatedDirectives: Int = 1, - 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 - logger.debug(s"howMuchCanTransfer - $howMuchCanTransfer. howMuchWillTransfer - $howMuchWillTransfer. " + - s"Change - $change") - val directives: IndexedSeq[TransferDirective] = - IndexedSeq(TransferDirective(recipient, howMuchWillTransfer, tokenIdOpt)) - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, change, tokenIdOpt) - } - - def defaultPaymentTransactionWithoutRandom(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - recipient: String, - amount: Long, - numberOfCreatedDirectives: Int = 1, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val howMuchCanTransfer: Long = useOutputs.map(_._1.amount).sum - fee - val change: Long = howMuchCanTransfer - amount - val directives: IndexedSeq[TransferDirective] = - IndexedSeq(TransferDirective(recipient, amount, tokenIdOpt)) - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, change, tokenIdOpt) - } - - def scriptedAssetTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - amount: Long, - numberOfCreatedDirectives: Int = 1, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val directives: IndexedSeq[ScriptedAssetDirective] = - (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[ScriptedAssetDirective]) { case (directivesAll, _) => - directivesAll :+ ScriptedAssetDirective(contract.hash, amount, tokenIdOpt) - } - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) - } - - def assetIssuingTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - amount: Long, - numberOfCreatedDirectives: Int = 1, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val directives: IndexedSeq[AssetIssuingDirective] = - (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[AssetIssuingDirective]) { case (directivesAll, _) => - directivesAll :+ AssetIssuingDirective(contract.hash, amount) - } - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) - } - - def dataTransactionScratch(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - contract: CompiledContract, - amount: Long, - data: Array[Byte], - numberOfCreatedDirectives: Int = 1, - tokenIdOpt: Option[ADKey] = None): Transaction = { - val directives: IndexedSeq[DataDirective] = - (1 to numberOfCreatedDirectives).foldLeft(IndexedSeq.empty[DataDirective]) { case (directivesAll, _) => - directivesAll :+ DataDirective(contract.hash, data) - } - prepareTransaction(privKey, fee, timestamp, useOutputs, directives, amount, tokenIdOpt) - } - - private def prepareTransaction(privKey: PrivateKey25519, - fee: Long, - timestamp: Long, - useOutputs: Seq[(MonetaryBox, Option[(CompiledContract, Seq[Proof])])], - directivesSeq: IndexedSeq[Directive], - change: Long, - tokenIdOpt: Option[ADKey] = None): 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)) - } - ) - } - - 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 directives: IndexedSeq[Directive] = - if (change > 0) directivesSeq ++: IndexedSeq(TransferDirective(pubKey.address.address, change, tokenIdOpt)) - else directivesSeq - - val uTransaction: UnsignedEncryTransaction = UnsignedEncryTransaction(fee, timestamp, uInputs, directives) - 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)))) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/AssetBox.scala b/src/main/scala/encry/net/modifiers/box/AssetBox.scala deleted file mode 100644 index 48dfdb1..0000000 --- a/src/main/scala/encry/net/modifiers/box/AssetBox.scala +++ /dev/null @@ -1,84 +0,0 @@ -package encry.net.modifiers.box - -import com.google.common.primitives.{Bytes, Longs, Shorts} -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.Algos -import encry.net.modifiers.box.TokenIssuingBox.TokenId - -import scala.util.Try - -/** Represents monetary asset of some type locked with some `proposition`. - * `tokenIdOpt = None` if the asset is of intrinsic type. */ -case class AssetBox(override val proposition: EncryProposition, - override val nonce: Long, - override val amount: Long, - tokenIdOpt: Option[TokenId] = None) extends EncryBox[EncryProposition] with MonetaryBox { - - override val typeId: Byte = AssetBox.TypeId - - lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty -} - -object AssetBox { - - val IntrinsicTokenId: String = Algos.encode(Algos.hash("intrinsic_token")) - - def apply(proposition: EncryProposition, - nonce: Long, - amount: Long, - tokenIdOpt: Option[TokenId] = None): AssetBox = new AssetBox( - proposition, - nonce, - amount, - tokenIdOpt match { - case Some(id) if Algos.encode(id) == IntrinsicTokenId => Option.empty[TokenId] - case ex => ex - } - ) - - val TypeId: Byte = 1.toByte - - implicit val jsonEncoder: Encoder[AssetBox] = (bx: AssetBox) => Map( - "type" -> TypeId.asJson, - "id" -> Algos.encode(bx.id).asJson, - "proposition" -> bx.proposition.asJson, - "nonce" -> bx.nonce.asJson, - "value" -> bx.amount.asJson, - "tokenId" -> bx.tokenIdOpt.map(id => Algos.encode(id)).asJson - ).asJson - - implicit val jsonDecoder: Decoder[AssetBox] = (c: HCursor) => for { - nonce <- c.downField("nonce").as[Long] - proposition <- c.downField("proposition").as[EncryProposition] - value <- c.downField("value").as[Long] - tokenIdOpt <- c.downField("tokenId") - .as[Option[TokenId]](Decoder.decodeOption(Decoder.decodeString.emapTry(Algos.decode))) - } yield AssetBox.apply(proposition, nonce, value, tokenIdOpt) -} - -object AssetBoxSerializer extends Serializer[AssetBox] { - - override def toBytes(obj: AssetBox): Array[Byte] = { - val propBytes = EncryPropositionSerializer.toBytes(obj.proposition) - Bytes.concat( - Shorts.toByteArray(propBytes.length.toShort), - propBytes, - Longs.toByteArray(obj.nonce), - Longs.toByteArray(obj.amount), - obj.tokenIdOpt.getOrElse(Array.empty) - ) - } - - override def parseBytes(bytes: Array[Byte]): Try[AssetBox] = Try { - val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) - val iBytes: Array[Byte] = bytes.drop(2) - val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get - val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) - val amount: Long = Longs.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 8)) - val tokenIdOpt: Option[TokenId] = - if ((iBytes.length - (propositionLen + 8 + 8)) == 32) Some(iBytes.takeRight(32)) else None - AssetBox(proposition, nonce, amount, tokenIdOpt) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/Box.scala b/src/main/scala/encry/net/modifiers/box/Box.scala deleted file mode 100644 index 7179fd8..0000000 --- a/src/main/scala/encry/net/modifiers/box/Box.scala +++ /dev/null @@ -1,55 +0,0 @@ -package encry.net.modifiers.box - -import com.google.common.primitives.Longs -import io.circe.{Decoder, DecodingFailure, Encoder} -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.prismlang.core.Types -import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} - -trait Box { - - val proposition: EncryProposition - - val typeId: Byte - - val nonce: Long - - lazy val id: ADKey = ADKey @@ Algos.hash(Longs.toByteArray(nonce)).updated(0, typeId) - - def isAmountCarrying: Boolean = this.isInstanceOf[MonetaryBox] - - val tpe: Types.Product = Types.EncryBox - - def asVal: PValue = PValue(asPrism, tpe) - - lazy val baseFields: Map[String, PValue] = Map( - "contractHash" -> PValue(proposition.contractHash, Types.PCollection.ofByte), - "typeId" -> PValue(typeId.toLong, Types.PInt), - "id" -> PValue(id, Types.PCollection.ofByte) - ) - - def asPrism: PObject = PObject(baseFields, tpe) -} - -object Box { - - implicit val jsonEncoder: Encoder[Box] = { - case ab: AssetBox => AssetBox.jsonEncoder(ab) - case db: DataBox => DataBox.jsonEncoder(db) - case aib: TokenIssuingBox => TokenIssuingBox.jsonEncoder(aib) - } - - implicit val jsonDecoder: Decoder[Box] = { - Decoder.instance { c => - c.downField("type").as[Byte] match { - case Right(s) => s match { - case AssetBox.TypeId => AssetBox.jsonDecoder(c) - case DataBox.TypeId => DataBox.jsonDecoder(c) - case _ => Left(DecodingFailure("Incorrect directive typeID", c.history)) - } - case Left(_) => Left(DecodingFailure("None typeId", c.history)) - } - } - } -} diff --git a/src/main/scala/encry/net/modifiers/box/DataBox.scala b/src/main/scala/encry/net/modifiers/box/DataBox.scala deleted file mode 100644 index 4ec49f8..0000000 --- a/src/main/scala/encry/net/modifiers/box/DataBox.scala +++ /dev/null @@ -1,66 +0,0 @@ -package encry.net.modifiers.box - -import com.google.common.primitives.{Bytes, Longs, Shorts} -import io.circe.{Decoder, Encoder, HCursor} -import io.circe.syntax._ -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.Algos - -import scala.util.Try - -/** Stores arbitrary data in EncryTL binary format. */ -case class DataBox(override val proposition: EncryProposition, - override val nonce: Long, - data: Array[Byte]) extends EncryBox[EncryProposition] { - - override val typeId: Byte = DataBox.TypeId -} - -object DataBox { - - val TypeId: Byte = 4.toByte - - implicit val jsonEncoder: Encoder[DataBox] = (bx: DataBox) => Map( - "type" -> TypeId.asJson, - "id" -> Algos.encode(bx.id).asJson, - "proposition" -> bx.proposition.asJson, - "nonce" -> bx.nonce.asJson, - "data" -> Algos.encode(bx.data).asJson, - ).asJson - - implicit val jsonDecoder: Decoder[DataBox] = (c: HCursor) => { - for { - proposition <- c.downField("proposition").as[EncryProposition] - nonce <- c.downField("nonce").as[Long] - data <- c.downField("data").as[String] - } yield DataBox( - proposition, - nonce, - Algos.decode(data).getOrElse(Array.emptyByteArray) - ) - } -} - -object DataBoxSerializer extends Serializer[DataBox] { - - override def toBytes(obj: DataBox): Array[Byte] = { - val propBytes: Array[Byte] = EncryPropositionSerializer.toBytes(obj.proposition) - Bytes.concat( - Shorts.toByteArray(propBytes.length.toShort), - propBytes, - Longs.toByteArray(obj.nonce), - Shorts.toByteArray(obj.data.length.toShort), - obj.data - ) - } - - override def parseBytes(bytes: Array[Byte]): Try[DataBox] = Try { - val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) - val iBytes: Array[Byte] = bytes.drop(2) - val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get - val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) - val dataLen: Short = Shorts.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 2)) - val data: Array[Byte] = iBytes.takeRight(dataLen) - DataBox(proposition, nonce, data) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala b/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala deleted file mode 100644 index 7a2a377..0000000 --- a/src/main/scala/encry/net/modifiers/box/EncryBaseBox.scala +++ /dev/null @@ -1,38 +0,0 @@ -package encry.net.modifiers.box - -import com.google.common.primitives.Longs -import io.circe.Encoder -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} -import org.encryfoundation.prismlang.core.{PConvertible, Types} - -trait EncryBaseBox extends Box with PConvertible { - - val typeId: Byte - - val nonce: Long - - override lazy val id: ADKey = ADKey @@ Algos.hash(Longs.toByteArray(nonce)).updated(0, typeId) - - override val tpe: Types.Product = Types.EncryBox - - override def asVal: PValue = PValue(asPrism, tpe) - - override lazy val baseFields: Map[String, PValue] = Map( - "contractHash" -> PValue(proposition.contractHash, Types.PCollection.ofByte), - "typeId" -> PValue(typeId.toLong, Types.PInt), - "id" -> PValue(id, Types.PCollection.ofByte) - ) - - override def asPrism: PObject = PObject(baseFields, tpe) -} - -object EncryBaseBox { - - implicit val jsonEncoder: Encoder[EncryBaseBox] = { - case ab: AssetBox => AssetBox.jsonEncoder(ab) - case db: DataBox => DataBox.jsonEncoder(db) - case aib: TokenIssuingBox => TokenIssuingBox.jsonEncoder(aib) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBox.scala b/src/main/scala/encry/net/modifiers/box/EncryBox.scala deleted file mode 100644 index 14e6048..0000000 --- a/src/main/scala/encry/net/modifiers/box/EncryBox.scala +++ /dev/null @@ -1,14 +0,0 @@ -package encry.net.modifiers.box - -trait EncryBox[P <: EncryProposition] extends EncryBaseBox { - - override val proposition: P - -} - -object EncryBox { - - type BxTypeId = Byte - - val BoxIdSize = 32 -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala b/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala deleted file mode 100644 index 09087a3..0000000 --- a/src/main/scala/encry/net/modifiers/box/EncryBoxStateChanges.scala +++ /dev/null @@ -1,23 +0,0 @@ -package encry.net.modifiers.box - -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ADKey - -abstract class EncryBoxStateChangeOperation - -case class Removal(boxId: ADKey) extends EncryBoxStateChangeOperation { - - override def toString: String = s"Removal(id: ${Algos.encode(boxId)})" -} - -case class Insertion(box: Box) extends EncryBoxStateChangeOperation { - - override def toString: String = s"Insertion(id: ${Algos.encode(box.id)})" -} - -case class EncryBoxStateChanges(operations: Seq[EncryBoxStateChangeOperation]){ - - def toAppend: Seq[EncryBoxStateChangeOperation] = operations.filter(_.isInstanceOf[Insertion]) - - def toRemove: Seq[EncryBoxStateChangeOperation] = operations.filter(_.isInstanceOf[Removal]) -} diff --git a/src/main/scala/encry/net/modifiers/box/EncryProposition.scala b/src/main/scala/encry/net/modifiers/box/EncryProposition.scala deleted file mode 100644 index 80fd8b6..0000000 --- a/src/main/scala/encry/net/modifiers/box/EncryProposition.scala +++ /dev/null @@ -1,46 +0,0 @@ -package encry.net.modifiers.box - -import io.circe.syntax._ -import io.circe.{Decoder, Encoder} -import org.encryfoundation.common.modifiers.mempool.transaction._ -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import scorex.crypto.signatures.PublicKey - -import scala.util.{Failure, Success, Try} - -case class EncryProposition(contractHash: ContractHash) extends Proposition { - - override type M = EncryProposition - - override def serializer: Serializer[M] = EncryPropositionSerializer -} - -object EncryProposition { - - implicit val jsonEncoder: Encoder[EncryProposition] = (p: EncryProposition) => Map( - "contractHash" -> Algos.encode(p.contractHash).asJson - ).asJson - - implicit val jsonDecoder: Decoder[EncryProposition] = - Decoder.decodeString - .emapTry(Algos.decode) - .map(EncryProposition.apply) - .prepare(_.downField("contractHash")) - - def open: EncryProposition = EncryProposition(OpenContract.contract.hash) - def heightLocked(height: Int): EncryProposition = EncryProposition(HeightLockedContract(height).contract.hash) - def pubKeyLocked(pubKey: PublicKey): EncryProposition = EncryProposition(PubKeyLockedContract(pubKey).contract.hash) - def addressLocked(address: String): EncryProposition = EncryAddress.resolveAddress(address).map { - case p2pk: Pay2PubKeyAddress => EncryProposition(PubKeyLockedContract(p2pk.pubKey).contract.hash) - case p2sh: Pay2ContractHashAddress => EncryProposition(p2sh.contractHash) - }.getOrElse(throw EncryAddress.InvalidAddressException) -} - -object EncryPropositionSerializer extends Serializer[EncryProposition] { - def toBytes(obj: EncryProposition): Array[Byte] = obj.contractHash - def parseBytes(bytes: Array[Byte]): Try[EncryProposition] = - if (bytes.lengthCompare(32) == 0) Success(EncryProposition(bytes)) - else Failure(new Exception("Invalid contract hash length")) -} diff --git a/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala b/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala deleted file mode 100644 index 481d06b..0000000 --- a/src/main/scala/encry/net/modifiers/box/MonetaryBox.scala +++ /dev/null @@ -1,3 +0,0 @@ -package encry.net.modifiers.box - -trait MonetaryBox extends Box { val amount: Long } diff --git a/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala b/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala deleted file mode 100644 index 4708210..0000000 --- a/src/main/scala/encry/net/modifiers/box/TokenIssuingBox.scala +++ /dev/null @@ -1,56 +0,0 @@ -package encry.net.modifiers.box - -import com.google.common.primitives.{Bytes, Longs, Shorts} -import io.circe.Encoder -import io.circe.syntax._ -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.Algos -import encry.net.modifiers.box.TokenIssuingBox.TokenId - -import scala.util.Try - -case class TokenIssuingBox(override val proposition: EncryProposition, - override val nonce: Long, - override val amount: Long, - tokenId: TokenId) extends EncryBox[EncryProposition] with MonetaryBox { - - override val typeId: Byte = AssetBox.TypeId -} - -object TokenIssuingBox { - - type TokenId = Array[Byte] - - val TypeId: Byte = 3.toByte - - implicit val jsonEncoder: Encoder[TokenIssuingBox] = (bx: TokenIssuingBox) => Map( - "type" -> TypeId.asJson, - "id" -> Algos.encode(bx.id).asJson, - "proposition" -> bx.proposition.asJson, - "nonce" -> bx.nonce.asJson - ).asJson -} - -object AssetIssuingBoxSerializer extends Serializer[TokenIssuingBox] { - - override def toBytes(obj: TokenIssuingBox): Array[Byte] = { - val propBytes: Array[Byte] = EncryPropositionSerializer.toBytes(obj.proposition) - Bytes.concat( - Shorts.toByteArray(propBytes.length.toShort), - propBytes, - Longs.toByteArray(obj.nonce), - Longs.toByteArray(obj.amount), - obj.tokenId - ) - } - - override def parseBytes(bytes: Array[Byte]): Try[TokenIssuingBox] = Try { - val propositionLen: Short = Shorts.fromByteArray(bytes.take(2)) - val iBytes: Array[Byte] = bytes.drop(2) - val proposition: EncryProposition = EncryPropositionSerializer.parseBytes(iBytes.take(propositionLen)).get - val nonce: Long = Longs.fromByteArray(iBytes.slice(propositionLen, propositionLen + 8)) - val amount: Long = Longs.fromByteArray(iBytes.slice(propositionLen + 8, propositionLen + 8 + 8)) - val creationId: TokenId = bytes.takeRight(32) - TokenIssuingBox(proposition, nonce, amount, creationId) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala b/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala deleted file mode 100644 index 087fc91..0000000 --- a/src/main/scala/encry/net/modifiers/directives/AssetIssuingDirective.scala +++ /dev/null @@ -1,85 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.AssetIssuingDirectiveProtoMessage -import com.google.common.primitives.{Bytes, Ints, Longs} -import com.google.protobuf.ByteString -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.{Algos, Utils} -import org.encryfoundation.common.utils.constants.TestNetConstants -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import scorex.crypto.hash.Digest32 -import encry.net.modifiers.box.{Box, EncryProposition, TokenIssuingBox} - -import scala.util.Try - -case class AssetIssuingDirective(contractHash: ContractHash, amount: Long) extends Directive { - - override type M = AssetIssuingDirective - override val typeId: Byte = AssetIssuingDirective.TypeId - override lazy val isValid: Boolean = amount > 0 - - override def boxes(digest: Digest32, idx: Int): Seq[Box] = - Seq(TokenIssuingBox( - EncryProposition(contractHash), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), - amount, - Algos.hash(Ints.toByteArray(idx) ++ digest) - )) - - override def serializer: Serializer[M] = AssetIssuingDirectiveSerializer - - override def toDirectiveProto: DirectiveProtoMessage = AssetIssuingDirectiveProtoSerializer.toProto(this) - -} - -object AssetIssuingDirective { - - val TypeId: Byte = 2.toByte - - implicit val jsonEncoder: Encoder[AssetIssuingDirective] = (d: AssetIssuingDirective) => Map( - "typeId" -> d.typeId.asJson, - "contractHash" -> Algos.encode(d.contractHash).asJson, - "amount" -> d.amount.asJson - ).asJson - - implicit val jsonDecoder: Decoder[AssetIssuingDirective] = (c: HCursor) => { - for { - contractHash <- c.downField("contractHash").as[ContractHash](Decoder.decodeString.emapTry(Algos.decode)) - amount <- c.downField("amount").as[Long] - } yield AssetIssuingDirective(contractHash, amount) - } -} - -object AssetIssuingDirectiveProtoSerializer extends ProtoDirectiveSerializer[AssetIssuingDirective] { - - override def toProto(message: AssetIssuingDirective): DirectiveProtoMessage = - DirectiveProtoMessage().withAssetIssuingDirectiveProto(AssetIssuingDirectiveProtoMessage() - .withAmount(message.amount) - .withContractHash(ByteString.copyFrom(message.contractHash)) - ) - - override def fromProto(message: DirectiveProtoMessage): Option[AssetIssuingDirective] = - message.directiveProto.assetIssuingDirectiveProto match { - case Some(value) => Some(AssetIssuingDirective(value.contractHash.toByteArray, value.amount)) - case None => Option.empty[AssetIssuingDirective] - } -} - - -object AssetIssuingDirectiveSerializer extends Serializer[AssetIssuingDirective] { - - override def toBytes(obj: AssetIssuingDirective): Array[Byte] = - Bytes.concat( - obj.contractHash, - Longs.toByteArray(obj.amount) - ) - - override def parseBytes(bytes: Array[Byte]): Try[AssetIssuingDirective] = Try { - val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) - val amount: Long = Longs.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 8)) - AssetIssuingDirective(contractHash, amount) - } -} diff --git a/src/main/scala/encry/net/modifiers/directives/DataDirective.scala b/src/main/scala/encry/net/modifiers/directives/DataDirective.scala deleted file mode 100644 index a7d467e..0000000 --- a/src/main/scala/encry/net/modifiers/directives/DataDirective.scala +++ /dev/null @@ -1,89 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.DataDirectiveProtoMessage -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import com.google.common.primitives.{Bytes, Ints} -import com.google.protobuf.ByteString -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.{Algos, Utils} -import org.encryfoundation.common.utils.constants.TestNetConstants -import scorex.crypto.hash.Digest32 -import encry.net.modifiers.box.{DataBox, EncryProposition} -import encry.net.modifiers.directives.Directive.DTypeId - -import scala.util.Try - -case class DataDirective(contractHash: ContractHash, data: Array[Byte]) extends Directive { - - override type M = DataDirective - - override val typeId: DTypeId = DataDirective.TypeId - - override def boxes(digest: Digest32, idx: Int): Seq[DataBox] = - Seq(DataBox(EncryProposition(contractHash), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), data)) - - val MaxDataLength: Int = 1000 - - override lazy val isValid: Boolean = data.length <= MaxDataLength - - override def serializer: Serializer[M] = DataDirectiveSerializer - - override def toDirectiveProto: DirectiveProtoMessage = DataDirectiveProtoSerializer.toProto(this) - -} - -object DataDirective { - - val TypeId: DTypeId = 5.toByte - - implicit val jsonEncoder: Encoder[DataDirective] = (d: DataDirective) => Map( - "typeId" -> d.typeId.asJson, - "contractHash" -> Algos.encode(d.contractHash).asJson, - "data" -> Algos.encode(d.data).asJson - ).asJson - - implicit val jsonDecoder: Decoder[DataDirective] = (c: HCursor) => { - for { - contractHash <- c.downField("contractHash").as[String] - dataEnc <- c.downField("data").as[String] - } yield Algos.decode(contractHash) - .flatMap(ch => Algos.decode(dataEnc).map(data => DataDirective(ch, data))) - .getOrElse(throw new Exception("Decoding failed")) - } -} - - -object DataDirectiveProtoSerializer extends ProtoDirectiveSerializer[DataDirective] { - - override def toProto(message: DataDirective): DirectiveProtoMessage = DirectiveProtoMessage() - .withDataDirectiveProto(DataDirectiveProtoMessage() - .withContractHash(ByteString.copyFrom(message.contractHash)) - .withData(ByteString.copyFrom(message.data))) - - override def fromProto(message: DirectiveProtoMessage): Option[DataDirective] = - message.directiveProto.dataDirectiveProto match { - case Some(value) => Some(DataDirective(value.contractHash.toByteArray, value.data.toByteArray)) - case None => Option.empty[DataDirective] - } -} - -object DataDirectiveSerializer extends Serializer[DataDirective] { - - override def toBytes(obj: DataDirective): Array[Byte] = - Bytes.concat( - obj.contractHash, - Ints.toByteArray(obj.data.length), - obj.data - ) - - override def parseBytes(bytes: Array[Byte]): Try[DataDirective] = Try { - val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) - val dataLen: Int = Ints.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 4)) - val data: Array[DTypeId] = bytes.slice(TestNetConstants.DigestLength + 4, TestNetConstants.DigestLength + 4 + dataLen) - DataDirective(contractHash, data) - } -} \ No newline at end of file diff --git a/src/main/scala/encry/net/modifiers/directives/Directive.scala b/src/main/scala/encry/net/modifiers/directives/Directive.scala deleted file mode 100644 index 13a3f48..0000000 --- a/src/main/scala/encry/net/modifiers/directives/Directive.scala +++ /dev/null @@ -1,45 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import io.circe._ -import org.encryfoundation.common.serialization.BytesSerializable -import scorex.crypto.hash.Digest32 -import encry.net.modifiers.box.Box - -trait Directive extends BytesSerializable { - - val typeId: Byte - val isValid: Boolean - - def boxes(digest: Digest32, idx: Int): Seq[Box] - - def toDirectiveProto: DirectiveProtoMessage -} - -object Directive { - - type DTypeId = Byte - - implicit val jsonEncoder: Encoder[Directive] = { - case td: TransferDirective => TransferDirective.jsonEncoder(td) - case aid: AssetIssuingDirective => AssetIssuingDirective.jsonEncoder(aid) - case sad: ScriptedAssetDirective => ScriptedAssetDirective.jsonEncoder(sad) - case dad: DataDirective => DataDirective.jsonEncoder(dad) - case _ => throw new Exception("Incorrect directive type") - } - - implicit val jsonDecoder: Decoder[Directive] = { - Decoder.instance { c => - c.downField("typeId").as[DTypeId] match { - case Right(s) => s match { - case TransferDirective.TypeId => TransferDirective.jsonDecoder(c) - case AssetIssuingDirective.TypeId => AssetIssuingDirective.jsonDecoder(c) - case ScriptedAssetDirective.TypeId => ScriptedAssetDirective.jsonDecoder(c) - case DataDirective.TypeId => DataDirective.jsonDecoder(c) - case _ => Left(DecodingFailure("Incorrect directive typeID", c.history)) - } - case Left(_) => Left(DecodingFailure("None typeId", c.history)) - } - } - } -} diff --git a/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala b/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala deleted file mode 100644 index 852ff40..0000000 --- a/src/main/scala/encry/net/modifiers/directives/DirectiveSerializer.scala +++ /dev/null @@ -1,42 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.DirectiveProto - -import scala.util.{Failure, Try} -import org.encryfoundation.common.serialization.Serializer - - -trait ProtoDirectiveSerializer[T] { - - def toProto(message: T): DirectiveProtoMessage - - def fromProto(message: DirectiveProtoMessage): Option[T] -} - -object DirectiveProtoSerializer { - def fromProto(message: DirectiveProtoMessage): Option[Directive] = message.directiveProto match { - case DirectiveProto.AssetIssuingDirectiveProto(_) => AssetIssuingDirectiveProtoSerializer.fromProto(message) - case DirectiveProto.DataDirectiveProto(_) => DataDirectiveProtoSerializer.fromProto(message) - case DirectiveProto.TransferDirectiveProto(_) => TransferDirectiveProtoSerializer.fromProto(message) - case DirectiveProto.ScriptedAssetDirectiveProto(_) => ScriptedAssetDirectiveProtoSerializer.fromProto(message) - case DirectiveProto.Empty => None - } -} - -object DirectiveSerializer extends Serializer[Directive] { - - override def toBytes(obj: Directive): Array[Byte] = obj match { - case td: TransferDirective => TransferDirective.TypeId +: TransferDirectiveSerializer.toBytes(td) - case aid: AssetIssuingDirective => AssetIssuingDirective.TypeId +: AssetIssuingDirectiveSerializer.toBytes(aid) - case sad: ScriptedAssetDirective => ScriptedAssetDirective.TypeId +: ScriptedAssetDirectiveSerializer.toBytes(sad) - case m => throw new Exception(s"Serialization of unknown directive type: $m") - } - - override def parseBytes(bytes: Array[Byte]): Try[Directive] = Try(bytes.head).flatMap { - case TransferDirective.`TypeId` => TransferDirectiveSerializer.parseBytes(bytes.tail) - case AssetIssuingDirective.`TypeId` => AssetIssuingDirectiveSerializer.parseBytes(bytes.tail) - case ScriptedAssetDirective.`TypeId` => ScriptedAssetDirectiveSerializer.parseBytes(bytes.tail) - case t => Failure(new Exception(s"Got unknown typeId: $t")) - } -} diff --git a/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala b/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala deleted file mode 100644 index 5895c93..0000000 --- a/src/main/scala/encry/net/modifiers/directives/ScriptedAssetDirective.scala +++ /dev/null @@ -1,100 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.{ADKeyProto, ScriptedAssetDirectiveProtoMessage} - -import scala.util.Try -import com.google.common.primitives.{Bytes, Ints, Longs} -import com.google.protobuf.ByteString -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.{Algos, Utils} -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.common.utils.constants.TestNetConstants -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import scorex.crypto.hash.Digest32 -import encry.net.modifiers.box.{AssetBox, Box, EncryProposition} - -case class ScriptedAssetDirective(contractHash: ContractHash, - amount: Long, - tokenIdOpt: Option[ADKey] = None) extends Directive { - - override type M = ScriptedAssetDirective - - override val typeId: Byte = ScriptedAssetDirective.TypeId - - override def boxes(digest: Digest32, idx: Int): Seq[Box] = - Seq(AssetBox(EncryProposition(contractHash), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount)) - - override lazy val isValid: Boolean = amount > 0 - - override def serializer: Serializer[M] = ScriptedAssetDirectiveSerializer - - lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty - - override def toDirectiveProto: DirectiveProtoMessage = ScriptedAssetDirectiveProtoSerializer.toProto(this) - -} - -object ScriptedAssetDirective { - - val TypeId: Byte = 3.toByte - - implicit val jsonEncoder: Encoder[ScriptedAssetDirective] = (d: ScriptedAssetDirective) => Map( - "typeId" -> d.typeId.asJson, - "contractHash" -> Algos.encode(d.contractHash).asJson, - "amount" -> d.amount.asJson, - "tokenId" -> d.tokenIdOpt.map(id => Algos.encode(id)).asJson - ).asJson - - implicit val jsonDecoder: Decoder[ScriptedAssetDirective] = (c: HCursor) => for { - contractHash <- c.downField("contractHash").as[ContractHash](Decoder.decodeString.emapTry(Algos.decode)) - amount <- c.downField("amount").as[Long] - tokenIdOpt <- c.downField("tokenId").as[Option[ADKey]](Decoder.decodeOption(Decoder.decodeString.emapTry(Algos.decode).map(ADKey @@ _))) - } yield ScriptedAssetDirective(contractHash, amount, tokenIdOpt) -} - -object ScriptedAssetDirectiveProtoSerializer extends ProtoDirectiveSerializer[ScriptedAssetDirective] { - - override def toProto(message: ScriptedAssetDirective): DirectiveProtoMessage ={ - val initialDirective: ScriptedAssetDirectiveProtoMessage = ScriptedAssetDirectiveProtoMessage() - .withContractHash(ByteString.copyFrom(message.contractHash)) - .withAmount(message.amount) - val saDirective: ScriptedAssetDirectiveProtoMessage = message.tokenIdOpt match { - case Some(value) => initialDirective.withTokenIdOpt( ADKeyProto().withTokenIdOpt(ByteString.copyFrom(value))) - case None => initialDirective - } - DirectiveProtoMessage().withScriptedAssetDirectiveProto(saDirective) - } - - override def fromProto(message: DirectiveProtoMessage): Option[ScriptedAssetDirective] = - message.directiveProto.scriptedAssetDirectiveProto match { - case Some(value) => Some(ScriptedAssetDirective( - value.contractHash.toByteArray, - value.amount, - value.tokenIdOpt.map(x => ADKey @@ x.tokenIdOpt.toByteArray)) - ) - case None => Option.empty[ScriptedAssetDirective] - } -} - -object ScriptedAssetDirectiveSerializer extends Serializer[ScriptedAssetDirective] { - - override def toBytes(obj: ScriptedAssetDirective): Array[Byte] = - Bytes.concat( - obj.contractHash, - Longs.toByteArray(obj.amount), - obj.tokenIdOpt.getOrElse(Array.empty) - ) - - override def parseBytes(bytes: Array[Byte]): Try[ScriptedAssetDirective] = Try { - val contractHash: ContractHash = bytes.take(TestNetConstants.DigestLength) - val amount: Long = Longs.fromByteArray(bytes.slice(TestNetConstants.DigestLength, TestNetConstants.DigestLength + 8)) - val tokenIdOpt: Option[ADKey] = if ((bytes.length - (TestNetConstants.DigestLength + 8)) == TestNetConstants.ModifierIdSize) { - Some(ADKey @@ bytes.takeRight(TestNetConstants.ModifierIdSize)) - } else None - ScriptedAssetDirective(contractHash, amount, tokenIdOpt) - } -} diff --git a/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala b/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala deleted file mode 100644 index dd76aa3..0000000 --- a/src/main/scala/encry/net/modifiers/directives/TransferDirective.scala +++ /dev/null @@ -1,112 +0,0 @@ -package encry.net.modifiers.directives - -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage -import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage.{ADKeyProto, TransferDirectiveProtoMessage} -import com.google.common.primitives.{Bytes, Ints, Longs} -import com.google.protobuf.ByteString -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} -import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress -import org.encryfoundation.common.modifiers.mempool.transaction.EncryAddress.Address -import org.encryfoundation.common.serialization.Serializer -import org.encryfoundation.common.utils.TaggedTypes.ADKey -import org.encryfoundation.common.utils.{Algos, Utils} -import org.encryfoundation.common.utils.constants.{Constants, TestNetConstants} -import scorex.crypto.hash.Digest32 -import encry.net.modifiers.box.{AssetBox, Box, EncryProposition} - -import scala.util.Try - -case class TransferDirective(address: Address, - amount: Long, - tokenIdOpt: Option[ADKey] = None) extends Directive { - - override type M = TransferDirective - - override val typeId: Byte = TransferDirective.TypeId - - override def boxes(digest: Digest32, idx: Int): Seq[Box] = - Seq(AssetBox(EncryProposition.addressLocked(address), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount, tokenIdOpt)) - - override lazy val isValid: Boolean = amount > 0 && EncryAddress.resolveAddress(address).isSuccess - - override def serializer: Serializer[M] = TransferDirectiveSerializer - - lazy val isIntrinsic: Boolean = tokenIdOpt.isEmpty - - override def toDirectiveProto: DirectiveProtoMessage = TransferDirectiveProtoSerializer.toProto(this) - -} - -object TransferDirective { - - val TypeId: Byte = 1.toByte - - implicit val jsonEncoder: Encoder[TransferDirective] = (d: TransferDirective) => Map( - "typeId" -> d.typeId.asJson, - "address" -> d.address.toString.asJson, - "amount" -> d.amount.asJson, - "tokenId" -> d.tokenIdOpt.map(id => Algos.encode(id)).getOrElse("null").asJson - ).asJson - - implicit val jsonDecoder: Decoder[TransferDirective] = (c: HCursor) => { - for { - address <- c.downField("address").as[String] - amount <- c.downField("amount").as[Long] - tokenIdOpt <- c.downField("tokenId").as[Option[String]] - } yield { - TransferDirective( - address, - amount, - tokenIdOpt.flatMap(id => Algos.decode(id).map(ADKey @@ _).toOption) - ) - } - } -} - -object TransferDirectiveProtoSerializer extends ProtoDirectiveSerializer[TransferDirective] { - - override def toProto(message: TransferDirective): DirectiveProtoMessage = { - val initialDirective: TransferDirectiveProtoMessage = TransferDirectiveProtoMessage() - .withAddress(message.address) - .withAmount(message.amount) - val transferDirective: TransferDirectiveProtoMessage = message.tokenIdOpt match { - case Some(value) => initialDirective.withTokenIdOpt(ADKeyProto().withTokenIdOpt(ByteString.copyFrom(value))) - case None => initialDirective - } - DirectiveProtoMessage().withTransferDirectiveProto(transferDirective) - } - - override def fromProto(message: DirectiveProtoMessage): Option[TransferDirective] = - message.directiveProto.transferDirectiveProto match { - case Some(value) => Some(TransferDirective( - value.address, - value.amount, - value.tokenIdOpt.map(x => ADKey @@ x.tokenIdOpt.toByteArray)) - ) - case None => Option.empty[TransferDirective] - } -} - -object TransferDirectiveSerializer extends Serializer[TransferDirective] { - - override def toBytes(obj: TransferDirective): Array[Byte] = { - val address: Array[Byte] = obj.address.getBytes(Algos.charset) - address.length.toByte +: Bytes.concat( - address, - Longs.toByteArray(obj.amount), - obj.tokenIdOpt.getOrElse(Array.empty) - ) - } - - override def parseBytes(bytes: Array[Byte]): Try[TransferDirective] = Try { - val addressLen: Int = bytes.head.toInt - val address: Address = new String(bytes.slice(1, 1 + addressLen), Algos.charset) - val amount: Long = Longs.fromByteArray(bytes.slice(1 + addressLen, 1 + addressLen + 8)) - val tokenIdOpt: Option[ADKey] = if ((bytes.length - (1 + addressLen + 8)) == TestNetConstants.ModifierIdSize) { - Some(ADKey @@ bytes.takeRight(TestNetConstants.ModifierIdSize)) - } else None - TransferDirective(address, amount, tokenIdOpt) - } -} diff --git a/src/main/scala/encry/net/transaction/Contracts.scala b/src/main/scala/encry/net/transaction/Contracts.scala deleted file mode 100644 index f7fcbe5..0000000 --- a/src/main/scala/encry/net/transaction/Contracts.scala +++ /dev/null @@ -1,31 +0,0 @@ -package encry.net.transaction - -import org.encryfoundation.prismlang.compiler.{CompiledContract, PCompiler} -import scorex.crypto.encode.Base58 -import scorex.crypto.signatures.PublicKey - -import scala.util.{Failure, Try} - -object Contracts { - - def multiSigContractScratch(owners: Seq[PublicKey]): Try[CompiledContract] = - if (owners.nonEmpty) { - val contractCode: String = - s""" - |contract (signature: MultiSig, transaction: Transaction) = { - | def isValidSig(signature: MultiSig, message: Array[Byte], key: Array[Byte]): Bool = { - | anyOf(signature.map(lamb (x: Array[Byte]) = checkSig(x, message, key))) - | } - | - | let ownerPubKey = base58'${Base58.encode(owners.head)}' - | let garantPubKey = base58'${Base58.encode(owners(1))}' - | let receiverPubKey = base58'${Base58.encode(owners(2))}' - | let keys = Array(ownerPubKey, garantPubKey, receiverPubKey) - | let all: Array[Int] = keys.map(lamb(x: Array[Byte]) = if(isValidSig(signature, transaction.messageToSign, x)) 1 else 0) - | all.sum > 1 - |} - """.stripMargin - PCompiler.compile(contractCode) - } else Failure(new IllegalArgumentException) - -} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/CoreTaggedTypes.scala b/src/main/scala/encry/net/utils/CoreTaggedTypes.scala deleted file mode 100644 index 194133b..0000000 --- a/src/main/scala/encry/net/utils/CoreTaggedTypes.scala +++ /dev/null @@ -1,14 +0,0 @@ -package encry.net.utils - -import supertagged.TaggedType - -object CoreTaggedTypes { - - object ModifierTypeId extends TaggedType[Byte] - - object ModifierId extends TaggedType[Array[Byte]] - - type ModifierTypeId = ModifierTypeId.Type - - type ModifierId = ModifierId.Type -} \ No newline at end of file diff --git a/src/main/scala/encry/net/utils/Mnemonic.scala b/src/main/scala/encry/net/utils/Mnemonic.scala deleted file mode 100644 index c68d8e1..0000000 --- a/src/main/scala/encry/net/utils/Mnemonic.scala +++ /dev/null @@ -1,40 +0,0 @@ -package encry.net.utils - -import org.encryfoundation.common.crypto.PrivateKey25519 -import org.encryfoundation.common.utils.Algos -import scodec.bits.BitVector -import scorex.crypto.hash.Blake2b256 -import scorex.crypto.signatures.{Curve25519, PrivateKey, PublicKey} - -import scala.io.Source - -object Mnemonic { - - def createPrivKey(seed: Option[String]): PrivateKey25519 = { - val (privateKey: PrivateKey, publicKey: PublicKey) = Curve25519.createKeyPair( - Blake2b256.hash( - seed.map { - seedFromMnemonic(_) - } - .getOrElse { - val phrase: String = entropyToMnemonicCode(scorex.utils.Random.randomBytes(16)) - seedFromMnemonic(phrase) - }) - ) - PrivateKey25519(privateKey, publicKey) - } - - private def seedFromMnemonic(mnemonicCode: String, passPhrase: String = ""): Array[Byte] = - Algos.hash(mnemonicCode + "mnemonic=" + passPhrase) - - private def entropyToMnemonicCode(entropy: Array[Byte]): String = { - val words: Array[String] = - Source.fromInputStream(getClass.getResourceAsStream("/languages/english/words.txt")).getLines.toArray - val checkSum: BitVector = BitVector(Algos.hash(entropy)) - val entropyWithCheckSum: BitVector = BitVector(entropy) ++ checkSum.take(4) - - entropyWithCheckSum.grouped(11).map { i => - words(i.toInt(signed = false)) - }.mkString(" ") - } -} diff --git a/src/main/scala/encry/net/utils/NetworkService.scala b/src/main/scala/encry/net/utils/NetworkService.scala deleted file mode 100644 index 0e67211..0000000 --- a/src/main/scala/encry/net/utils/NetworkService.scala +++ /dev/null @@ -1,79 +0,0 @@ -package encry.net.utils - -import akka.http.scaladsl.Http -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.Host -import akka.util.ByteString -import com.typesafe.scalalogging.StrictLogging -import io.circe.{Decoder, HCursor} -import io.circe.parser.decode -import org.encryfoundation.common.modifiers.mempool.transaction.PubKeyLockedContract -import org.encryfoundation.common.utils.Algos -import encry.net.modifiers.box.Box -import encry.settings.{NetworkSettings, NodeSettings} - -import scala.concurrent.Future -import scala.util.control.NonFatal -import encry.ExplorerApp._ - -object NetworkService extends StrictLogging { - - def requestUtxos(settings: NodeSettings, from: Int, to: Int): Future[List[Box]] = { - val privKey = Mnemonic.createPrivKey(Option(settings.mnemonicKey)) - val contractHash: String = Algos.encode(PubKeyLockedContract(privKey.publicImage.pubKeyBytes).contract.hash) - Http().singleRequest(HttpRequest( - method = HttpMethods.GET, - uri = s"/wallet/$contractHash/boxes/$from/$to" - ).withEffectiveUri(securedConnection = false, Host(settings.explorerHost, settings.explorerPort))) - .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) - .map(_.utf8String) - .map(decode[List[Box]]) - .flatMap(_.fold(Future.failed, Future.successful)) - } - - def checkTxsInBlockchain(node: NetworkSettings, txsToCheck: Vector[String], numberOfBlocks: Int): Future[List[String]] = - Http().singleRequest(HttpRequest( - method = HttpMethods.GET, - uri = s"/history/lastHeaders/$numberOfBlocks" - ).withEffectiveUri(securedConnection = false, Host(node.peerForConnectionHost, node.peerForConnectionApiPort))) - .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) - .map(_.utf8String) - .map(decode[List[HeaderId]]) - .flatMap(_.fold(Future.failed, Future.successful)) - .flatMap { headers => - Future.sequence(headers.map(checkTxsInBlock(node, txsToCheck, _))).map(_.flatten) - } - .flatMap { txs => - if (txs.isEmpty) Future.failed(new RuntimeException) else Future.successful(txs) - } - .recover { - case NonFatal(_) => List.empty - } - - private def checkTxsInBlock(node: NetworkSettings, txsToCheck: Vector[String], headerId: HeaderId): Future[List[String]] = - Http().singleRequest(HttpRequest( - method = HttpMethods.GET, - uri = s"/history/${headerId.id}/transactions" - ).withEffectiveUri(securedConnection = false, Host(node.peerForConnectionHost, node.peerForConnectionApiPort))) - .flatMap(_.entity.dataBytes.runFold(ByteString.empty)(_ ++ _)) - .map(_.utf8String) - .map(decode[List[TransactionId]]) - .flatMap(_.fold(Future.failed, Future.successful)) - .map(_.map(_.id).intersect(txsToCheck)) - .map { txs => - if (txs.nonEmpty) logger.info(s"txs ${txs.mkString(",")} are in a block ${headerId.id}") - txs - }.recoverWith { - case NonFatal(th) => - th.printStackTrace() - Future.failed(th) - } - - case class HeaderId(id: String) - case class TransactionId(id: String) - - implicit val headerIdDecoder: Decoder[HeaderId] = (c: HCursor) => - for { id <- c.downField("id").as[String] } yield HeaderId(id) - implicit val transactionIdDecoder: Decoder[TransactionId] = (c: HCursor) => - for { id <- c.downField("id").as[String] } yield TransactionId(id) -} \ No newline at end of file diff --git a/src/main/scala/encry/net/network/BasicMessagesRepo.scala b/src/main/scala/encry/network/BasicMessagesRepo.scala similarity index 98% rename from src/main/scala/encry/net/network/BasicMessagesRepo.scala rename to src/main/scala/encry/network/BasicMessagesRepo.scala index 32c9593..857088c 100644 --- a/src/main/scala/encry/net/network/BasicMessagesRepo.scala +++ b/src/main/scala/encry/network/BasicMessagesRepo.scala @@ -1,4 +1,4 @@ -package encry.net.network +package encry.network import java.net.InetSocketAddress @@ -12,10 +12,11 @@ import akka.actor.ActorRef import com.google.protobuf.{ByteString => GoogleByteString} import akka.util.{ByteString => AkkaByteString} import com.typesafe.scalalogging.StrictLogging -import encry.net.network.BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} -import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import encry.settings.ExplorerSettings +import BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} +import org.encryfoundation.common.utils.TaggedTypes +import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} import scorex.crypto.hash.Blake2b256 +import supertagged.@@ import scala.util.Try @@ -454,7 +455,7 @@ object BasicMessagesRepo extends StrictLogging { case class SyncInfo(lastHeaderIds: Seq[ModifierId]) { - def startingPoints = lastHeaderIds.map(id => ModifierTypeId @@ (101: Byte) -> id) + def startingPoints: Seq[(Byte @@ TaggedTypes.ModifierTypeId.Tag, ModifierId)] = lastHeaderIds.map(id => ModifierTypeId @@ (101: Byte) -> id) } } \ No newline at end of file diff --git a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala similarity index 54% rename from src/main/scala/encry/net/network/NetworkMessagesHandler.scala rename to src/main/scala/encry/network/NetworkMessagesHandler.scala index 6831717..9a0d635 100644 --- a/src/main/scala/encry/net/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -1,4 +1,4 @@ -package encry.net.network +package encry.network import HeaderProto.HeaderProtoMessage import PayloadProto.PayloadProtoMessage @@ -6,24 +6,16 @@ import TransactionProto.TransactionProtoMessage import akka.actor.{Actor, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos -import encry.net.network.BasicMessagesRepo._ -import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, ReceiveHeader, ReceivePayload, ReceiveTransaction, TransactionForCommit} -import encry.net.modifiers.{Transaction, TransactionProtoSerializer} -import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import encry.net.utils.CoreTaggedTypes +import BasicMessagesRepo._ +import NetworkMessagesHandler._ import org.encryfoundation.common.modifiers.history.{Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} -import supertagged.@@ +import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer} //TODO: replace everywhere encry.net to org.encryfoundation.common class NetworkMessagesHandler() extends Actor with StrictLogging { - var localGeneratedTransactions: Seq[Transaction] = Seq.empty - override def receive: Receive = { - case TransactionForCommit(transaction) => - localGeneratedTransactions :+= transaction - context.parent ! BroadcastInvForTx(transaction) case MessageFromNetwork(message, peerOpt) => message match { @@ -43,31 +35,18 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { modifierTypeId match { case Transaction.modifierTypeId => val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) - tx.foreach(target ! ReceiveTransaction(_)) + tx.foreach(self ! ReceiveTransaction(_)) case Header.modifierTypeId => val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) - header.foreach(target ! ReceiveHeader(_)) + header.foreach(self ! ReceiveHeader(_)) case Payload.modifierTypeId => val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) - payload.foreach(target ! ReceivePayload(_)) + payload.foreach(self ! ReceivePayload(_)) } } - case RequestModifiersNetworkMessage(invData) if invData._1 == Transaction.modifierTypeId => - logger.debug(s"Got request modifiers on NMH") - val tmpInv: Seq[String] = invData._2.map(Algos.encode) - val transactions: Seq[Transaction] = localGeneratedTransactions.filter(tx => tmpInv.contains(Algos.encode(tx.id))) - val forSend: Map[Array[Byte] @@ CoreTaggedTypes.ModifierId.Tag, Array[Byte]] = transactions.map { tx => - ModifierId @@ tx.id -> TransactionProtoSerializer.toProto(tx).toByteArray - }.toMap - sender() ! ModifiersNetworkMessage(ModifierTypeId @@ Transaction.modifierTypeId -> forSend) - val tmpTxs = transactions.map(tx => Algos.encode(tx.id)) - localGeneratedTransactions = localGeneratedTransactions.filter(tx => - !tmpTxs.contains(Algos.encode(tx.id)) - ) - logger.debug(s"Sent modifiers to node.") case _ => } case _ => @@ -82,9 +61,5 @@ object NetworkMessagesHandler { case class ReceivePayload(payload: Payload) - case class TransactionForCommit(tx: Transaction) - - case class BroadcastInvForTx(tx: Transaction) - def props() = Props(new NetworkMessagesHandler()) } \ No newline at end of file diff --git a/src/main/scala/encry/net/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala similarity index 77% rename from src/main/scala/encry/net/network/NetworkServer.scala rename to src/main/scala/encry/network/NetworkServer.scala index 1a8fd44..d8f43fd 100644 --- a/src/main/scala/encry/net/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -1,4 +1,4 @@ -package encry.net.network +package encry.network import java.net.InetSocketAddress @@ -7,18 +7,12 @@ import akka.io.Tcp.SO.KeepAlive import akka.io.Tcp._ import akka.io.{IO, Tcp} import com.typesafe.scalalogging.StrictLogging -import encry.net.network.BasicMessagesRepo.{InvNetworkMessage, Outgoing} -import encry.net.network.NetworkMessagesHandler.{BroadcastInvForTx, TransactionForCommit} -import encry.net.network.NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} -import encry.net.network.PeerHandler._ -import encry.net.modifiers.Transaction -import encry.net.utils.CoreTaggedTypes.{ModifierId, ModifierTypeId} -import encry.net.utils.Mnemonic.createPrivKey -import encry.net.utils.NetworkTimeProvider +import NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} +import PeerHandler._ import encry.settings.NetworkSettings -import scala.concurrent.duration._ import scala.concurrent.ExecutionContextExecutor +import scala.concurrent.duration._ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider) extends Actor with StrictLogging { @@ -51,7 +45,7 @@ class NetworkServer(settings: NetworkSettings, case Connected(remote, _) if !isConnected && remote.getAddress == connectingPeer.getAddress => val handler: ActorRef = context.actorOf( - PeerHandler.props(remote, sender(), settings, timeProvider, Outgoing, messagesHandler) + PeerHandler.props(remote, sender(), settings, timeProvider, messagesHandler) ) logger.info(s"Successfully connected to $remote. Creating handler: $handler.") isConnected = true @@ -78,17 +72,9 @@ class NetworkServer(settings: NetworkSettings, tmpConnectionHandler = None logger.info(s"Disconnected from $peer.") - case BroadcastInvForTx(tx) => - val inv: BasicMessagesRepo.NetworkMessage = - InvNetworkMessage(ModifierTypeId @@ Transaction.modifierTypeId -> Seq(ModifierId @@ tx.id)) - tmpConnectionHandler.foreach(_ ! inv) - logger.debug(s"Send inv message to remote.") - case ConnectionSetupSuccessfully => //logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") - case msg@TransactionForCommit(_) => messagesHandler ! msg - case msg => logger.info(s"Got strange message on NetworkServer: $msg.") } } diff --git a/src/main/scala/encry/net/utils/NetworkTime.scala b/src/main/scala/encry/network/NetworkTime.scala similarity index 97% rename from src/main/scala/encry/net/utils/NetworkTime.scala rename to src/main/scala/encry/network/NetworkTime.scala index bc9516a..ae341d4 100644 --- a/src/main/scala/encry/net/utils/NetworkTime.scala +++ b/src/main/scala/encry/network/NetworkTime.scala @@ -1,12 +1,14 @@ -package encry.net.utils +package encry.network import java.net.InetAddress + import com.typesafe.scalalogging.StrictLogging +import NetworkTime.Time import org.apache.commons.net.ntp.{NTPUDPClient, TimeInfo} -import encry.net.utils.NetworkTime.Time + import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration._ import scala.concurrent.Future +import scala.concurrent.duration._ import scala.util.Left import scala.util.control.NonFatal diff --git a/src/main/scala/encry/net/network/PeerHandler.scala b/src/main/scala/encry/network/PeerHandler.scala similarity index 96% rename from src/main/scala/encry/net/network/PeerHandler.scala rename to src/main/scala/encry/network/PeerHandler.scala index e366ae9..680edfb 100644 --- a/src/main/scala/encry/net/network/PeerHandler.scala +++ b/src/main/scala/encry/network/PeerHandler.scala @@ -1,4 +1,4 @@ -package encry.net.network +package encry.network import java.net.InetSocketAddress import java.nio.ByteOrder @@ -9,10 +9,9 @@ import akka.io.Tcp._ import akka.util.{ByteString, CompactByteString} import com.google.common.primitives.Ints import com.typesafe.scalalogging.StrictLogging -import encry.net.network.PeerHandler._ -import encry.net.network.BasicMessagesRepo._ -import encry.net.network.NetworkServer.ConnectionSetupSuccessfully -import encry.net.utils.NetworkTimeProvider +import PeerHandler._ +import BasicMessagesRepo._ +import NetworkServer.ConnectionSetupSuccessfully import encry.settings.NetworkSettings import scala.annotation.tailrec @@ -24,7 +23,6 @@ class PeerHandler(remoteAddress: InetSocketAddress, listener: ActorRef, settings: NetworkSettings, timeProvider: NetworkTimeProvider, - direction: ConnectionType, receivedMessagesHandler: ActorRef) extends Actor with StrictLogging { context.watch(listener) @@ -229,7 +227,6 @@ object PeerHandler { listener: ActorRef, settings: NetworkSettings, timeProvider: NetworkTimeProvider, - direction: ConnectionType, messagesHandler: ActorRef): Props = - Props(new PeerHandler(remoteAddress, listener, settings, timeProvider, direction, messagesHandler)) + Props(new PeerHandler(remoteAddress, listener, settings, timeProvider, messagesHandler)) } \ No newline at end of file diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/ExplorerSettings.scala index b9d35f4..d53b983 100644 --- a/src/main/scala/encry/settings/ExplorerSettings.scala +++ b/src/main/scala/encry/settings/ExplorerSettings.scala @@ -3,7 +3,7 @@ package encry.settings import java.net.InetSocketAddress import com.typesafe.config.{Config, ConfigFactory} -import encry.net.utils.NetworkTimeProviderSettings +import encry.network.NetworkTimeProviderSettings import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ import net.ceedubs.ficus.readers.ValueReader From 18b8454d77fe2295b432cfd89e6f15f005abbeab Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 16 Oct 2019 13:08:49 +0500 Subject: [PATCH 08/21] remove nodeSettings --- src/main/resources/application.conf | 9 +-------- .../scala/encry/network/NetworkMessagesHandler.scala | 9 +++++++++ src/main/scala/encry/settings/ExplorerSettings.scala | 3 --- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 9ba9788..75f45d7 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1,7 +1,7 @@ explorer { parseSettings { - nodes = [""] + nodes = [] recoverBatchSize = 15 infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts askNode = false # if set false, explorer won't ping the node if it stopped working @@ -20,13 +20,6 @@ explorer { connectionTimeout = 60000 } - nodeSettings { - explorerHost = "172.16.11.11" - explorerPort = 9001 - mnemonicKey = "" - maxRollbackDepth = 100 - } - networkSettings { syncPacketLength = 1000 bindAddressHost = "0.0.0.0" diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 9a0d635..5335844 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -21,6 +21,7 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { case InvNetworkMessage((modifierTypeId, modifierIds)) => logger.debug(s"Got modifiers: $modifierTypeId (${modifierIds.map(Algos.encode).mkString(",")})") + println(s"Got modifier: ${peerOpt.get}") peerOpt.foreach { peer => if (List(Transaction.modifierTypeId, Header.modifierTypeId, Payload.modifierTypeId).contains(modifierTypeId)) { logger.debug(s"Request modifier: $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") @@ -49,6 +50,14 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { case _ => } + + case ReceiveTransaction(tx: Transaction) => println(s"tx: $tx") + + case ReceiveHeader(header: Header) => println(s"header: $header") + + case ReceivePayload(payload: Payload) => println(s"payload: txs ${payload.txs.size}") + + case _ => } } diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/ExplorerSettings.scala index d53b983..9fed4ed 100644 --- a/src/main/scala/encry/settings/ExplorerSettings.scala +++ b/src/main/scala/encry/settings/ExplorerSettings.scala @@ -14,7 +14,6 @@ case class ExplorerSettings(parseSettings: ParseSettings, blackListSettings: BlackListSettings, databaseSettings: DatabaseSettings, ntpSettings: NetworkTimeProviderSettings, - nodeSettings: NodeSettings, networkSettings: NetworkSettings, multisigSettings: MultisigSettings) @@ -30,8 +29,6 @@ case class NetworkSettings(syncPacketLength: Int, declaredAddressHost: String, declaredAddressPort: Int) -case class NodeSettings(explorerHost: String, explorerPort: Int, mnemonicKey: String) - case class MultisigSettings(checkTxMinedPeriod: Int, numberOfBlocksToCheck: Int, mnemonicKeys: List[String]) case class ParseSettings(nodes: List[InetSocketAddress], recoverBatchSize: Int, infinitePing: Boolean, askNode: Boolean, From ef66d23a72e44f497a71fe2d63a5d5aa45afa0a8 Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 16 Oct 2019 16:14:00 +0500 Subject: [PATCH 09/21] akka remote send Modifiers --- build.sbt | 1 + src/main/resources/application.conf | 14 ++++++ .../network/NetworkMessagesHandler.scala | 45 +++++++++++-------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/build.sbt b/build.sbt index ed266da..359b212 100644 --- a/build.sbt +++ b/build.sbt @@ -40,6 +40,7 @@ val loggingDependencies = Seq( libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion, + "com.typesafe.akka" %% "akka-remote" % akkaVersion, "com.iheart" %% "ficus" % "1.4.2", "commons-net" % "commons-net" % "3.3" ) ++ apiDependencies ++ loggingDependencies ++ databaseDependencies diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 75f45d7..f480d01 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -61,11 +61,25 @@ blocking-dispatcher { } throughput = 1 } + akka { log-dead-letters = 0 log-dead-letters-during-shutdown = off loggers = [ "akka.event.slf4j.Slf4jLogger" ] logger-startup-timeout = 60s actor.warn-about-java-serializer-usage = false + + actor { + provider = remote + } + remote { + enabled-transports = ["akka.remote.netty.tcp"] + netty.tcp { + hostname = "127.0.0.1" + port = 0 + } + } + + } diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 5335844..83e18be 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -7,14 +7,21 @@ import akka.actor.{Actor, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos import BasicMessagesRepo._ -import NetworkMessagesHandler._ +import ModifierMessages._ import org.encryfoundation.common.modifiers.history.{Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer} +import scala.concurrent.duration._ +import scala.concurrent.ExecutionContext.Implicits.global + //TODO: replace everywhere encry.net to org.encryfoundation.common class NetworkMessagesHandler() extends Actor with StrictLogging { + val selection = context.actorSelection("akka.tcp://frontendActorSystem@localhost:46619/user/receiver") + + //context.system.scheduler.schedule(1 seconds, 2 seconds) (selection ! ReceiveTransaction(null)) + override def receive: Receive = { case MessageFromNetwork(message, peerOpt) => message match { @@ -36,39 +43,39 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { modifierTypeId match { case Transaction.modifierTypeId => val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) - tx.foreach(self ! ReceiveTransaction(_)) + tx.foreach { tx => + println(s"tx: $tx") + selection ! ModifierTx(tx) + } case Header.modifierTypeId => val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) - header.foreach(self ! ReceiveHeader(_)) + header.foreach { header => + println(s"header: $header") + selection ! ModifierHeader(header) + } case Payload.modifierTypeId => val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) - payload.foreach(self ! ReceivePayload(_)) + payload.foreach { payload => + println(s"payload: txs ${payload.txs.size}") + selection ! ModifierPayload(payload) + } } } case _ => } - - case ReceiveTransaction(tx: Transaction) => println(s"tx: $tx") - - case ReceiveHeader(header: Header) => println(s"header: $header") - - case ReceivePayload(payload: Payload) => println(s"payload: txs ${payload.txs.size}") - - case _ => } } -object NetworkMessagesHandler { - - case class ReceiveTransaction(tx: Transaction) - - case class ReceiveHeader(header: Header) - - case class ReceivePayload(payload: Payload) +object ModifierMessages { + case class ModifierTx(tx: Transaction) + case class ModifierHeader(header: Header) + case class ModifierPayload(payload: Payload) +} +object NetworkMessagesHandler { def props() = Props(new NetworkMessagesHandler()) } \ No newline at end of file From 10251e66c358c977409120583c14d8361f94763c Mon Sep 17 00:00:00 2001 From: capdev Date: Thu, 17 Oct 2019 12:13:57 +0500 Subject: [PATCH 10/21] frontend settings --- src/main/resources/application.conf | 7 ++++++- src/main/scala/encry/ExplorerApp.scala | 2 +- .../network/NetworkMessagesHandler.scala | 20 +++++++++---------- .../scala/encry/network/NetworkServer.scala | 9 ++++----- .../scala/encry/network/NetworkTime.scala | 2 ++ .../encry/settings/ExplorerSettings.scala | 5 +++-- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index f480d01..2e90f9c 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -45,6 +45,11 @@ explorer { updateEvery = 30m timeout = 30s } + + frontendSettings { + host = "127.0.0.1" + port = 5150 + } } parser-dispatcher { @@ -70,7 +75,7 @@ akka { actor.warn-about-java-serializer-usage = false actor { - provider = remote + provider = "akka.remote.RemoteActorRefProvider" } remote { enabled-transports = ["akka.remote.netty.tcp"] diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index d6e379f..e06697c 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -42,7 +42,7 @@ object ExplorerApp extends App { } } *> IO { val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) - val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider), "networkServer") + val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, settings.frontendSettings), "networkServer") // val dbService = DBService(xa) // val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 83e18be..ef6e572 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -16,11 +16,9 @@ import scala.concurrent.ExecutionContext.Implicits.global //TODO: replace everywhere encry.net to org.encryfoundation.common -class NetworkMessagesHandler() extends Actor with StrictLogging { +class NetworkMessagesHandler(frontHost: String, frontPort: Int) extends Actor with StrictLogging { - val selection = context.actorSelection("akka.tcp://frontendActorSystem@localhost:46619/user/receiver") - - //context.system.scheduler.schedule(1 seconds, 2 seconds) (selection ! ReceiveTransaction(null)) + val frontRemoteActor = context.actorSelection(s"akka.tcp://application@$frontHost:$frontPort/user/receiver") override def receive: Receive = { @@ -44,22 +42,22 @@ class NetworkMessagesHandler() extends Actor with StrictLogging { case Transaction.modifierTypeId => val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) tx.foreach { tx => - println(s"tx: $tx") - selection ! ModifierTx(tx) + println(s"tx: ${tx.encodedId}") + frontRemoteActor ! tx } case Header.modifierTypeId => val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) header.foreach { header => - println(s"header: $header") - selection ! ModifierHeader(header) + println(s"header: ${header.encodedId}") + frontRemoteActor ! header } case Payload.modifierTypeId => val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) payload.foreach { payload => - println(s"payload: txs ${payload.txs.size}") - selection ! ModifierPayload(payload) + println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") + frontRemoteActor ! payload } } } @@ -77,5 +75,5 @@ object ModifierMessages { } object NetworkMessagesHandler { - def props() = Props(new NetworkMessagesHandler()) + def props(frontHost: String, frontPort: Int) = Props(new NetworkMessagesHandler(frontHost, frontPort)) } \ No newline at end of file diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index d8f43fd..ed1fc74 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -14,15 +14,14 @@ import encry.settings.NetworkSettings import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration._ -class NetworkServer(settings: NetworkSettings, - timeProvider: NetworkTimeProvider) extends Actor with StrictLogging { +class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider, frontend: FrontendSettings) extends Actor with StrictLogging { implicit val system: ActorSystem = context.system implicit val ec: ExecutionContextExecutor = context.dispatcher var isConnected = false - val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props()) + val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(frontend.host, frontend.port)) var tmpConnectionHandler: Option[ActorRef] = None @@ -83,6 +82,6 @@ object NetworkServer { case object CheckConnection case object ConnectionSetupSuccessfully - def props(settings: NetworkSettings, timeProvider: NetworkTimeProvider): Props = - Props(new NetworkServer(settings, timeProvider)) + def props(settings: NetworkSettings, timeProvider: NetworkTimeProvider, frontend: FrontendSettings): Props = + Props(new NetworkServer(settings, timeProvider, frontend)) } \ No newline at end of file diff --git a/src/main/scala/encry/network/NetworkTime.scala b/src/main/scala/encry/network/NetworkTime.scala index ae341d4..e92d4f0 100644 --- a/src/main/scala/encry/network/NetworkTime.scala +++ b/src/main/scala/encry/network/NetworkTime.scala @@ -23,6 +23,8 @@ protected case class NetworkTime(offset: NetworkTime.Offset, lastUpdate: Network case class NetworkTimeProviderSettings(server: String, updateEvery: FiniteDuration, timeout: FiniteDuration) +case class FrontendSettings(host: String, port: Int) + class NetworkTimeProvider(ntpSettings: NetworkTimeProviderSettings) extends StrictLogging { private var state: State = Right(NetworkTime(0L, 0L)) diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/ExplorerSettings.scala index 9fed4ed..221cb08 100644 --- a/src/main/scala/encry/settings/ExplorerSettings.scala +++ b/src/main/scala/encry/settings/ExplorerSettings.scala @@ -3,7 +3,7 @@ package encry.settings import java.net.InetSocketAddress import com.typesafe.config.{Config, ConfigFactory} -import encry.network.NetworkTimeProviderSettings +import encry.network.{FrontendSettings, NetworkTimeProviderSettings} import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ import net.ceedubs.ficus.readers.ValueReader @@ -15,7 +15,8 @@ case class ExplorerSettings(parseSettings: ParseSettings, databaseSettings: DatabaseSettings, ntpSettings: NetworkTimeProviderSettings, networkSettings: NetworkSettings, - multisigSettings: MultisigSettings) + multisigSettings: MultisigSettings, + frontendSettings: FrontendSettings) case class NetworkSettings(syncPacketLength: Int, bindAddressHost: String, From f2da45fb7ff980cedcdcd4292ba523573e3bafbe Mon Sep 17 00:00:00 2001 From: capdev Date: Thu, 17 Oct 2019 18:48:47 +0500 Subject: [PATCH 11/21] print block txs --- src/main/resources/application.conf | 4 ++-- src/main/scala/encry/network/NetworkMessagesHandler.scala | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 2e90f9c..772624c 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -23,11 +23,11 @@ explorer { networkSettings { syncPacketLength = 1000 bindAddressHost = "0.0.0.0" - bindAddressPort = 8000 + bindAddressPort = 0 nodeName = "explorer" appVersion = 0.9.3 handshakeTimeout = 1m - peerForConnectionHost = "172.16.11.11" + peerForConnectionHost = "172.16.11.18" peerForConnectionPort = 9001 peerForConnectionApiPort = 9051 declaredAddressHost = "" diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index ef6e572..6cdda80 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -57,6 +57,8 @@ class NetworkMessagesHandler(frontHost: String, frontPort: Int) extends Actor wi val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) payload.foreach { payload => println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") + payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) + println(s"payload.end") frontRemoteActor ! payload } } From 61f277567ef042ee5be1819838ddb58eba576c81 Mon Sep 17 00:00:00 2001 From: capdev Date: Fri, 18 Oct 2019 18:45:38 +0500 Subject: [PATCH 12/21] move frontendRemoteActor DBActor on --- src/main/resources/application.conf | 2 +- src/main/scala/encry/ExplorerApp.scala | 14 +++++++---- .../network/NetworkMessagesHandler.scala | 23 +++++++------------ .../scala/encry/network/NetworkServer.scala | 12 ++++++---- .../{ => parser}/ParsersController.scala | 11 ++++----- .../scala/encry/parser/SimpleNodeParser.scala | 4 +++- 6 files changed, 34 insertions(+), 32 deletions(-) rename src/main/scala/encry/{ => parser}/ParsersController.scala (96%) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 772624c..108b7a4 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -27,7 +27,7 @@ explorer { nodeName = "explorer" appVersion = 0.9.3 handshakeTimeout = 1m - peerForConnectionHost = "172.16.11.18" + peerForConnectionHost = "172.16.11.19" peerForConnectionPort = 9001 peerForConnectionApiPort = 9051 declaredAddressHost = "" diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index e06697c..43e5492 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -6,6 +6,7 @@ import cats.effect.{Blocker, ContextShift, IO} import cats.implicits._ import doobie.hikari.HikariTransactor import doobie.util.ExecutionContexts +import encry.database.{DBActor, DBService} import encry.network.{NetworkServer, NetworkTimeProvider} import encry.settings.ExplorerSettings @@ -17,6 +18,9 @@ object ExplorerApp extends App { implicit val materializer: ActorMaterializer = ActorMaterializer() implicit val ec: ExecutionContextExecutor = system.dispatcher + val frontRemoteActor = + system.actorSelection(s"akka.tcp://application@${settings.frontendSettings.host}:${settings.frontendSettings.port}/user/receiver") + val settings = ExplorerSettings.read implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) @@ -41,12 +45,14 @@ object ExplorerApp extends App { ds.setConnectionTimeout(settings.databaseSettings.connectionTimeout) } } *> IO { + + val dbService = DBService(xa) + val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") + //system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") + val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) - val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, settings.frontendSettings), "networkServer") + val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, frontRemoteActor), "networkServer") -// val dbService = DBService(xa) -// val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") -// system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") } *> IO.never }.unsafeRunSync() } diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 6cdda80..248d4c8 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -3,22 +3,17 @@ package encry.network import HeaderProto.HeaderProtoMessage import PayloadProto.PayloadProtoMessage import TransactionProto.TransactionProtoMessage -import akka.actor.{Actor, Props} +import akka.actor.{Actor, ActorSelection, Props} import com.typesafe.scalalogging.StrictLogging import org.encryfoundation.common.utils.Algos import BasicMessagesRepo._ -import ModifierMessages._ -import org.encryfoundation.common.modifiers.history.{Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} +import org.encryfoundation.common.modifiers.history.{Block, Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer} import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global -//TODO: replace everywhere encry.net to org.encryfoundation.common - -class NetworkMessagesHandler(frontHost: String, frontPort: Int) extends Actor with StrictLogging { - - val frontRemoteActor = context.actorSelection(s"akka.tcp://application@$frontHost:$frontPort/user/receiver") +class NetworkMessagesHandler(frontRemoteActor: ActorSelection) extends Actor with StrictLogging { override def receive: Receive = { @@ -55,6 +50,7 @@ class NetworkMessagesHandler(frontHost: String, frontPort: Int) extends Actor wi case Payload.modifierTypeId => val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) + payload.foreach { payload => println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) @@ -70,12 +66,9 @@ class NetworkMessagesHandler(frontHost: String, frontPort: Int) extends Actor wi } } -object ModifierMessages { - case class ModifierTx(tx: Transaction) - case class ModifierHeader(header: Header) - case class ModifierPayload(payload: Payload) -} - object NetworkMessagesHandler { - def props(frontHost: String, frontPort: Int) = Props(new NetworkMessagesHandler(frontHost, frontPort)) + case class Transaction(tx: Transaction) + case class Block(block: Block) + + def props(frontRemoteActor: ActorSelection) = Props(new NetworkMessagesHandler(frontRemoteActor)) } \ No newline at end of file diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index ed1fc74..c3754da 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -2,7 +2,7 @@ package encry.network import java.net.InetSocketAddress -import akka.actor.{Actor, ActorRef, ActorSystem, Props} +import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props} import akka.io.Tcp.SO.KeepAlive import akka.io.Tcp._ import akka.io.{IO, Tcp} @@ -14,14 +14,15 @@ import encry.settings.NetworkSettings import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration._ -class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider, frontend: FrontendSettings) extends Actor with StrictLogging { +class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider, + dbActor: ActorRef, frontRemoteActor: ActorSelection) extends Actor with StrictLogging { implicit val system: ActorSystem = context.system implicit val ec: ExecutionContextExecutor = context.dispatcher var isConnected = false - val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(frontend.host, frontend.port)) + val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(frontRemoteActor: ActorSelection)) var tmpConnectionHandler: Option[ActorRef] = None @@ -34,6 +35,7 @@ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider IO(Tcp) ! Bind(self, selfPeer) override def receive: Receive = { + case Bound(localAddress) => logger.info(s"Local app was successfully bound to $localAddress!") context.system.scheduler.schedule(5.seconds, 30.seconds, self, CheckConnection) @@ -82,6 +84,6 @@ object NetworkServer { case object CheckConnection case object ConnectionSetupSuccessfully - def props(settings: NetworkSettings, timeProvider: NetworkTimeProvider, frontend: FrontendSettings): Props = - Props(new NetworkServer(settings, timeProvider, frontend)) + def props(settings: NetworkSettings, timeProvider: NetworkTimeProvider, frontRemoteActor: ActorSelection): Props = + Props(new NetworkServer(settings, timeProvider, frontRemoteActor)) } \ No newline at end of file diff --git a/src/main/scala/encry/ParsersController.scala b/src/main/scala/encry/parser/ParsersController.scala similarity index 96% rename from src/main/scala/encry/ParsersController.scala rename to src/main/scala/encry/parser/ParsersController.scala index 9ce9642..7533556 100644 --- a/src/main/scala/encry/ParsersController.scala +++ b/src/main/scala/encry/parser/ParsersController.scala @@ -1,17 +1,16 @@ -package encry +package encry.parser import java.net.{InetAddress, InetSocketAddress} -import scala.concurrent.ExecutionContext.Implicits.global -import akka.actor.{Actor, ActorRef, OneForOneStrategy, Props, SupervisorStrategy} -import encry.parser.{NodeParser, SimpleNodeParser} -import encry.settings.{BlackListSettings, ParseSettings} import akka.actor.SupervisorStrategy.Stop +import akka.actor.{Actor, ActorRef, OneForOneStrategy, Props, SupervisorStrategy} import com.typesafe.scalalogging.StrictLogging -import encry.ParsersController.{BadPeer, RemoveBadPeer} import encry.database.DBActor.RecoveryMode import encry.parser.NodeParser.PeersFromApi +import encry.parser.ParsersController.{BadPeer, RemoveBadPeer} +import encry.settings.{BlackListSettings, ParseSettings} +import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ class ParsersController(settings: ParseSettings, diff --git a/src/main/scala/encry/parser/SimpleNodeParser.scala b/src/main/scala/encry/parser/SimpleNodeParser.scala index 658d91a..2912910 100644 --- a/src/main/scala/encry/parser/SimpleNodeParser.scala +++ b/src/main/scala/encry/parser/SimpleNodeParser.scala @@ -1,13 +1,15 @@ package encry.parser import java.net.{InetAddress, InetSocketAddress} + import akka.actor.{Actor, ActorRef, Props} import com.typesafe.scalalogging.StrictLogging -import encry.ParsersController.BadPeer import encry.blockchain.nodeRoutes.InfoRoute import encry.database.DBActor.UpdatedInfoAboutNode import encry.parser.NodeParser.{PeersFromApi, PingNode} +import encry.parser.ParsersController.BadPeer import encry.settings.ParseSettings + import scala.concurrent.duration._ class SimpleNodeParser(node: InetSocketAddress, From 02879c2e3affe21674035b3eeeaf3202fbd632d1 Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 21 Oct 2019 10:16:02 +0500 Subject: [PATCH 13/21] send tx via NetworkServer ParsersController on --- src/main/scala/encry/ExplorerApp.scala | 7 +-- .../network/NetworkMessagesHandler.scala | 51 +++++++++---------- .../scala/encry/network/NetworkServer.scala | 12 ++++- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index 43e5492..bf30834 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -8,6 +8,7 @@ import doobie.hikari.HikariTransactor import doobie.util.ExecutionContexts import encry.database.{DBActor, DBService} import encry.network.{NetworkServer, NetworkTimeProvider} +import encry.parser.ParsersController import encry.settings.ExplorerSettings import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} @@ -18,11 +19,11 @@ object ExplorerApp extends App { implicit val materializer: ActorMaterializer = ActorMaterializer() implicit val ec: ExecutionContextExecutor = system.dispatcher + val settings = ExplorerSettings.read + val frontRemoteActor = system.actorSelection(s"akka.tcp://application@${settings.frontendSettings.host}:${settings.frontendSettings.port}/user/receiver") - val settings = ExplorerSettings.read - implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global) val pgTransactor = for { @@ -48,7 +49,7 @@ object ExplorerApp extends App { val dbService = DBService(xa) val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") - //system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") + val parsersController = system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, frontRemoteActor), "networkServer") diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 248d4c8..2d6d6a5 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -1,19 +1,14 @@ package encry.network -import HeaderProto.HeaderProtoMessage -import PayloadProto.PayloadProtoMessage import TransactionProto.TransactionProtoMessage -import akka.actor.{Actor, ActorSelection, Props} +import akka.actor.{Actor, ActorRef, Props} import com.typesafe.scalalogging.StrictLogging -import org.encryfoundation.common.utils.Algos -import BasicMessagesRepo._ -import org.encryfoundation.common.modifiers.history.{Block, Header, HeaderProtoSerializer, Payload, PayloadProtoSerializer} +import encry.network.BasicMessagesRepo._ +import org.encryfoundation.common.modifiers.history.{Header, Payload} import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer} +import org.encryfoundation.common.utils.Algos -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -class NetworkMessagesHandler(frontRemoteActor: ActorSelection) extends Actor with StrictLogging { +class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictLogging { override def receive: Receive = { @@ -38,25 +33,25 @@ class NetworkMessagesHandler(frontRemoteActor: ActorSelection) extends Actor wit val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) tx.foreach { tx => println(s"tx: ${tx.encodedId}") - frontRemoteActor ! tx + networkServer ! tx } - case Header.modifierTypeId => - val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) - header.foreach { header => - println(s"header: ${header.encodedId}") - frontRemoteActor ! header - } - - case Payload.modifierTypeId => - val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) - - payload.foreach { payload => - println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") - payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) - println(s"payload.end") - frontRemoteActor ! payload - } +// case Header.modifierTypeId => +// val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) +// header.foreach { header => +// println(s"header: ${header.encodedId}") +// networkServer ! header +// } +// +// case Payload.modifierTypeId => +// val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) +// +// payload.foreach { payload => +// println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") +// payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) +// println(s"payload.end") +// networkServer ! payload +// } } } @@ -70,5 +65,5 @@ object NetworkMessagesHandler { case class Transaction(tx: Transaction) case class Block(block: Block) - def props(frontRemoteActor: ActorSelection) = Props(new NetworkMessagesHandler(frontRemoteActor)) + def props(networkServer: ActorRef) = Props(new NetworkMessagesHandler(networkServer)) } \ No newline at end of file diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index c3754da..87257a1 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -9,20 +9,22 @@ import akka.io.{IO, Tcp} import com.typesafe.scalalogging.StrictLogging import NetworkServer.{CheckConnection, ConnectionSetupSuccessfully} import PeerHandler._ +import encry.parser.NodeParser.BlockFromNode import encry.settings.NetworkSettings +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import scala.concurrent.ExecutionContextExecutor import scala.concurrent.duration._ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider, - dbActor: ActorRef, frontRemoteActor: ActorSelection) extends Actor with StrictLogging { + frontRemoteActor: ActorSelection) extends Actor with StrictLogging { implicit val system: ActorSystem = context.system implicit val ec: ExecutionContextExecutor = context.dispatcher var isConnected = false - val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(frontRemoteActor: ActorSelection)) + val messagesHandler: ActorRef = context.actorOf(NetworkMessagesHandler.props(self)) var tmpConnectionHandler: Option[ActorRef] = None @@ -76,6 +78,12 @@ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider case ConnectionSetupSuccessfully => //logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") + case tx: Transaction => + frontRemoteActor ! tx + + case BlockFromNode(block, nodeAddr, nodeInfo) => + frontRemoteActor ! block.payload + case msg => logger.info(s"Got strange message on NetworkServer: $msg.") } } From fec35ef5d95601921e81afad86e5e257ddc3854b Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 21 Oct 2019 10:21:48 +0500 Subject: [PATCH 14/21] NodeParser.props --- src/main/scala/encry/parser/NodeParser.scala | 9 +++++++-- src/main/scala/encry/parser/ParsersController.scala | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/scala/encry/parser/NodeParser.scala b/src/main/scala/encry/parser/NodeParser.scala index 348b2c0..dd26c7d 100644 --- a/src/main/scala/encry/parser/NodeParser.scala +++ b/src/main/scala/encry/parser/NodeParser.scala @@ -3,7 +3,7 @@ package encry.parser import java.net.{InetAddress, InetSocketAddress} import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} -import akka.actor.{Actor, ActorRef} +import akka.actor.{Actor, ActorRef, Props} import com.typesafe.scalalogging.StrictLogging import encry.blockchain.modifiers.{Block, Header} import encry.blockchain.nodeRoutes.InfoRoute @@ -282,7 +282,6 @@ class NodeParser(node: InetSocketAddress, object NodeParser { - case class PeersFromApi(peers: Set[InetAddress]) case object PingNode @@ -300,4 +299,10 @@ object NodeParser { case object Recover case object BecomeAwaitDB + + def props(node: InetSocketAddress, + parserController: ActorRef, + dbActor: ActorRef, + settings: ParseSettings): Props = + Props(new NodeParser(node, parserController, dbActor, settings)).withDispatcher("parser-dispatcher") } \ No newline at end of file diff --git a/src/main/scala/encry/parser/ParsersController.scala b/src/main/scala/encry/parser/ParsersController.scala index 7533556..ddb6efe 100644 --- a/src/main/scala/encry/parser/ParsersController.scala +++ b/src/main/scala/encry/parser/ParsersController.scala @@ -32,7 +32,7 @@ class ParsersController(settings: ParseSettings, context.system.scheduler.scheduleOnce(blackListSettings.cleanupTime, self, RemoveBadPeer) logger.info(s"Starting Parsing controller. Try to create listeners for: ${settings.nodes.mkString(",")}") settings.nodes.foreach(node => - context.actorOf(Props(new NodeParser(node, self, dbActor, settings)).withDispatcher("parser-dispatcher")) + context.actorOf(NodeParser.props(node, self, dbActor, settings)) ) val initialPeers: Set[InetAddress] = settings.nodes.map(_.getAddress).toSet logger.info(s"Initial peers are: ${initialPeers.mkString(",")}. Starting main behaviour...") From a07f002570dc9110e1d2aa2e2ec58d62a4a62792 Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 21 Oct 2019 10:41:44 +0500 Subject: [PATCH 15/21] send BlockFromNode to networkServer --- src/main/scala/encry/ExplorerApp.scala | 5 ++++- .../scala/encry/network/NetworkMessagesHandler.scala | 5 +---- src/main/scala/encry/network/NetworkServer.scala | 4 ++++ src/main/scala/encry/parser/NodeParser.scala | 4 ++-- src/main/scala/encry/parser/ParsersController.scala | 10 ++++++++-- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index bf30834..2458fca 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -49,11 +49,14 @@ object ExplorerApp extends App { val dbService = DBService(xa) val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") - val parsersController = system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor)), s"parserController") val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, frontRemoteActor), "networkServer") + val parsersController = system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor, networkServer)), + s"parserController") + + } *> IO.never }.unsafeRunSync() } diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 2d6d6a5..f661ff1 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -31,10 +31,7 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL modifierTypeId match { case Transaction.modifierTypeId => val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) - tx.foreach { tx => - println(s"tx: ${tx.encodedId}") - networkServer ! tx - } + tx.foreach(networkServer ! _) // case Header.modifierTypeId => // val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index 87257a1..e514802 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -79,9 +79,13 @@ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider //logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") case tx: Transaction => + println(s"tx: ${tx.encodedId}") frontRemoteActor ! tx case BlockFromNode(block, nodeAddr, nodeInfo) => + println(s"payload: ${block.payload.headerId} txs ${block.payload.txs.size}") + block.payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) + println(s"payload.end") frontRemoteActor ! block.payload case msg => logger.info(s"Got strange message on NetworkServer: $msg.") diff --git a/src/main/scala/encry/parser/NodeParser.scala b/src/main/scala/encry/parser/NodeParser.scala index dd26c7d..1730c85 100644 --- a/src/main/scala/encry/parser/NodeParser.scala +++ b/src/main/scala/encry/parser/NodeParser.scala @@ -195,7 +195,7 @@ class NodeParser(node: InetSocketAddress, parserRequests.getBlock(blockId).map { block => blocksToReask -= height blocksToWrite += blockId -> (System.nanoTime(), height) - dbActor ! BlockFromNode(block, node, currentNodeInfo) + parserController ! BlockFromNode(block, node, currentNodeInfo) } } } @@ -237,7 +237,7 @@ class NodeParser(node: InetSocketAddress, currentNodeBestBlockId = block.header.id currentBestBlockHeight.set(block.header.height) blocksToWrite += blockId -> (System.nanoTime(), height) - dbActor ! BlockFromNode(block, node, currentNodeInfo) + parserController ! BlockFromNode(block, node, currentNodeInfo) } }} } diff --git a/src/main/scala/encry/parser/ParsersController.scala b/src/main/scala/encry/parser/ParsersController.scala index ddb6efe..9163cdc 100644 --- a/src/main/scala/encry/parser/ParsersController.scala +++ b/src/main/scala/encry/parser/ParsersController.scala @@ -6,7 +6,8 @@ import akka.actor.SupervisorStrategy.Stop import akka.actor.{Actor, ActorRef, OneForOneStrategy, Props, SupervisorStrategy} import com.typesafe.scalalogging.StrictLogging import encry.database.DBActor.RecoveryMode -import encry.parser.NodeParser.PeersFromApi +import encry.network.NetworkServer +import encry.parser.NodeParser.{BlockFromNode, PeersFromApi} import encry.parser.ParsersController.{BadPeer, RemoveBadPeer} import encry.settings.{BlackListSettings, ParseSettings} @@ -15,7 +16,8 @@ import scala.concurrent.duration._ class ParsersController(settings: ParseSettings, blackListSettings: BlackListSettings, - dbActor: ActorRef) extends Actor with StrictLogging { + dbActor: ActorRef, + networkServer: ActorRef) extends Actor with StrictLogging { var peerReconnects: Map[InetAddress, Int] = Map.empty[InetAddress, Int] @@ -76,6 +78,10 @@ class ParsersController(settings: ParseSettings, context.system.scheduler.scheduleOnce(blackListSettings.cleanupTime, self, RemoveBadPeer) context.become(mainBehaviour(knownPeers -- peersForRemove.map(_._1))) + case msg @ BlockFromNode(block, node, currentNodeInfo) => + dbActor ! msg + networkServer ! msg + case msg => logger.info(s"Got strange message on ParserController: $msg.") } From b25c795e7088478bfa782d39956dac83642c67df Mon Sep 17 00:00:00 2001 From: capdev Date: Mon, 21 Oct 2019 15:18:09 +0500 Subject: [PATCH 16/21] send txIds to remote --- src/main/resources/application.conf | 2 +- src/main/scala/encry/network/NetworkMessagesHandler.scala | 2 ++ src/main/scala/encry/network/NetworkServer.scala | 5 +++-- src/main/scala/encry/parser/NodeParser.scala | 2 ++ src/main/scala/encry/parser/ParsersController.scala | 1 - 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 108b7a4..d0519f0 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1,7 +1,7 @@ explorer { parseSettings { - nodes = [] + nodes = ["172.16.11.19:9051"] recoverBatchSize = 15 infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts askNode = false # if set false, explorer won't ping the node if it stopped working diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index f661ff1..0ff731d 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -33,6 +33,8 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) tx.foreach(networkServer ! _) + case _ => + // case Header.modifierTypeId => // val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) // header.foreach { header => diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index e514802..f3b6497 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -84,9 +84,10 @@ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider case BlockFromNode(block, nodeAddr, nodeInfo) => println(s"payload: ${block.payload.headerId} txs ${block.payload.txs.size}") - block.payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) + block.payload.txs.foreach(tx => println(s"payload.tx: ${tx.id}")) println(s"payload.end") - frontRemoteActor ! block.payload + val txIds = block.payload.txs.map(_.id) + frontRemoteActor ! txIds case msg => logger.info(s"Got strange message on NetworkServer: $msg.") } diff --git a/src/main/scala/encry/parser/NodeParser.scala b/src/main/scala/encry/parser/NodeParser.scala index 1730c85..afbc590 100644 --- a/src/main/scala/encry/parser/NodeParser.scala +++ b/src/main/scala/encry/parser/NodeParser.scala @@ -195,6 +195,7 @@ class NodeParser(node: InetSocketAddress, parserRequests.getBlock(blockId).map { block => blocksToReask -= height blocksToWrite += blockId -> (System.nanoTime(), height) + dbActor ! BlockFromNode(block, node, currentNodeInfo) parserController ! BlockFromNode(block, node, currentNodeInfo) } } @@ -237,6 +238,7 @@ class NodeParser(node: InetSocketAddress, currentNodeBestBlockId = block.header.id currentBestBlockHeight.set(block.header.height) blocksToWrite += blockId -> (System.nanoTime(), height) + dbActor ! BlockFromNode(block, node, currentNodeInfo) parserController ! BlockFromNode(block, node, currentNodeInfo) } }} diff --git a/src/main/scala/encry/parser/ParsersController.scala b/src/main/scala/encry/parser/ParsersController.scala index 9163cdc..625c6e5 100644 --- a/src/main/scala/encry/parser/ParsersController.scala +++ b/src/main/scala/encry/parser/ParsersController.scala @@ -79,7 +79,6 @@ class ParsersController(settings: ParseSettings, context.become(mainBehaviour(knownPeers -- peersForRemove.map(_._1))) case msg @ BlockFromNode(block, node, currentNodeInfo) => - dbActor ! msg networkServer ! msg case msg => logger.info(s"Got strange message on ParserController: $msg.") From 6556f9bf3aa531a273ed20887dc28ec7fca51f53 Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 23 Oct 2019 10:26:00 +0500 Subject: [PATCH 17/21] settings --- src/main/resources/application.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index d0519f0..8569614 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -50,6 +50,9 @@ explorer { host = "127.0.0.1" port = 5150 } + remote { + host = "127.0.0.1" + } } parser-dispatcher { @@ -80,7 +83,7 @@ akka { remote { enabled-transports = ["akka.remote.netty.tcp"] netty.tcp { - hostname = "127.0.0.1" + hostname = ${explorer.remote.host} port = 0 } } From dc339fb88ebffcbb29910aea63c7a44fac1f558c Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 23 Oct 2019 11:51:51 +0500 Subject: [PATCH 18/21] off Header, Payload Request format settings --- src/main/resources/application.conf | 112 +++++++++--------- .../network/NetworkMessagesHandler.scala | 7 +- 2 files changed, 56 insertions(+), 63 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 8569614..3bea7c0 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -1,58 +1,54 @@ explorer { + parseSettings { + nodes = [] + recoverBatchSize = 15 + infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts + askNode = false # if set false, explorer won't ping the node if it stopped working + } - parseSettings { - nodes = ["172.16.11.19:9051"] - recoverBatchSize = 15 - infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts - askNode = false # if set false, explorer won't ping the node if it stopped working - } - - blackListSettings { - banTime = 60m - cleanupTime = 5s - } + blackListSettings { + banTime = 60m + cleanupTime = 5s + } - databaseSettings { - host = "" - user = "" - password = "" - maxPoolSize = 5 - connectionTimeout = 60000 - } + databaseSettings { + host = "" + user = "" + password = "" + maxPoolSize = 5 + connectionTimeout = 60000 + } - networkSettings { - syncPacketLength = 1000 - bindAddressHost = "0.0.0.0" - bindAddressPort = 0 - nodeName = "explorer" - appVersion = 0.9.3 - handshakeTimeout = 1m - peerForConnectionHost = "172.16.11.19" - peerForConnectionPort = 9001 - peerForConnectionApiPort = 9051 - declaredAddressHost = "" - declaredAddressPort = 0 - } + networkSettings { + syncPacketLength = 1000 + bindAddressHost = "0.0.0.0" + bindAddressPort = 0 + nodeName = "explorer" + appVersion = 0.9.3 + handshakeTimeout = 1m + peerForConnectionHost = "" + peerForConnectionPort = 9001 + peerForConnectionApiPort = 9051 + declaredAddressHost = "" + declaredAddressPort = 0 + } - multisigSettings { - checkTxMinedPeriod = 30 - numberOfBlocksToCheck = 3 - mnemonicKeys = [] - } + multisigSettings { + checkTxMinedPeriod = 30 + numberOfBlocksToCheck = 3 + mnemonicKeys = [] + } - ntpSettings { - server = "pool.ntp.org" - updateEvery = 30m - timeout = 30s - } + ntpSettings { + server = "pool.ntp.org" + updateEvery = 30m + timeout = 30s + } - frontendSettings { - host = "127.0.0.1" - port = 5150 - } - remote { - host = "127.0.0.1" - } + frontendSettings { + host = "" + port = 5150 + } } parser-dispatcher { @@ -77,17 +73,15 @@ akka { logger-startup-timeout = 60s actor.warn-about-java-serializer-usage = false - actor { - provider = "akka.remote.RemoteActorRefProvider" - } - remote { - enabled-transports = ["akka.remote.netty.tcp"] - netty.tcp { - hostname = ${explorer.remote.host} - port = 0 - } + actor { + provider = "akka.remote.RemoteActorRefProvider" + } + remote { + enabled-transports = ["akka.remote.netty.tcp"] + netty.tcp { + hostname = "127.0.0.1" + port = 0 } - - + } } diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 0ff731d..fd916e9 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -18,7 +18,7 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL logger.debug(s"Got modifiers: $modifierTypeId (${modifierIds.map(Algos.encode).mkString(",")})") println(s"Got modifier: ${peerOpt.get}") peerOpt.foreach { peer => - if (List(Transaction.modifierTypeId, Header.modifierTypeId, Payload.modifierTypeId).contains(modifierTypeId)) { + if (List(Transaction.modifierTypeId/*, Header.modifierTypeId, Payload.modifierTypeId*/).contains(modifierTypeId)) { logger.debug(s"Request modifier: $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") peer.handlerRef ! RequestModifiersNetworkMessage((modifierTypeId, modifierIds)) } @@ -33,8 +33,6 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) tx.foreach(networkServer ! _) - case _ => - // case Header.modifierTypeId => // val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) // header.foreach { header => @@ -44,13 +42,14 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL // // case Payload.modifierTypeId => // val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) -// // payload.foreach { payload => // println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") // payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) // println(s"payload.end") // networkServer ! payload // } + + case _ => } } From d9887f41f3223cd78e611c65a80954bafc8dcf15 Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 23 Oct 2019 12:09:21 +0500 Subject: [PATCH 19/21] refactoring --- src/main/protobuf/NetworkMessagesProto.proto | 55 ------------------- src/main/protobuf/SyntaxMessageProto.proto | 6 -- src/main/protobuf/TransactionProto.proto | 48 ---------------- src/main/resources/application.conf | 6 -- src/main/scala/encry/ExplorerApp.scala | 5 +- .../encry/parser/ParsersController.scala | 4 +- .../scala/encry/parser/SimpleNodeParser.scala | 4 +- src/main/scala/encry/settings/Constants.scala | 1 + ...{ExplorerSettings.scala => Settings.scala} | 0 9 files changed, 5 insertions(+), 124 deletions(-) delete mode 100644 src/main/protobuf/NetworkMessagesProto.proto delete mode 100644 src/main/protobuf/SyntaxMessageProto.proto delete mode 100644 src/main/protobuf/TransactionProto.proto rename src/main/scala/encry/settings/{ExplorerSettings.scala => Settings.scala} (100%) diff --git a/src/main/protobuf/NetworkMessagesProto.proto b/src/main/protobuf/NetworkMessagesProto.proto deleted file mode 100644 index 76ebfbc..0000000 --- a/src/main/protobuf/NetworkMessagesProto.proto +++ /dev/null @@ -1,55 +0,0 @@ -syntax = "proto3"; -import "SyntaxMessageProto.proto"; - -message GeneralizedNetworkProtoMessage { - - message SyncInfoProtoMessage { - repeated bytes lastHeaderIds = 1; - } - - message InvProtoMessage { - bytes modifierTypeId = 1; - repeated bytes modifiers = 2; - } - - message RequestModifiersProtoMessage { - bytes modifierTypeId = 1; - repeated bytes modifiers = 2; - } - - message ModifiersProtoMessage { - - message MapFieldEntry { - bytes key = 1; - bytes value = 2; - } - - bytes modifierTypeId = 1; - repeated MapFieldEntry map = 2; - } - - message GetPeersProtoMessage { } - - message PeersProtoMessage { - repeated InetSocketAddressProtoMessage peers = 1; - } - - message HandshakeProtoMessage { - bytes protocolVersion = 1; - string nodeName = 2; - InetSocketAddressProtoMessage declaredAddress = 3; - uint64 time = 4; - } - - bytes magic = 1; - bytes checksum = 2; - oneof innerMessage { - SyncInfoProtoMessage syncInfoProtoMessage = 3; - InvProtoMessage invProtoMessage = 4; - RequestModifiersProtoMessage requestModifiersProtoMessage = 5; - ModifiersProtoMessage modifiersProtoMessage = 6; - GetPeersProtoMessage getPeersProtoMessage = 7; - PeersProtoMessage peersProtoMessage = 8; - HandshakeProtoMessage handshakeProtoMessage = 9; - } -} \ No newline at end of file diff --git a/src/main/protobuf/SyntaxMessageProto.proto b/src/main/protobuf/SyntaxMessageProto.proto deleted file mode 100644 index 891c2fb..0000000 --- a/src/main/protobuf/SyntaxMessageProto.proto +++ /dev/null @@ -1,6 +0,0 @@ -syntax = "proto3"; - -message InetSocketAddressProtoMessage { - string host = 1; - uint32 port = 2; -} \ No newline at end of file diff --git a/src/main/protobuf/TransactionProto.proto b/src/main/protobuf/TransactionProto.proto deleted file mode 100644 index 083a94b..0000000 --- a/src/main/protobuf/TransactionProto.proto +++ /dev/null @@ -1,48 +0,0 @@ -syntax = "proto3"; -import "scalapb/scalapb.proto"; - -message TransactionProtoMessage { - - message DirectiveProtoMessage { - - message ADKeyProto { - bytes tokenIdOpt = 1; - } - - message TransferDirectiveProtoMessage { - string address = 1; - uint64 amount = 2; - ADKeyProto tokenIdOpt = 3; - } - - message AssetIssuingDirectiveProtoMessage { - bytes contractHash = 1; - uint64 amount = 2; - } - - message ScriptedAssetDirectiveProtoMessage { - - bytes contractHash = 1; - uint64 amount = 2; - ADKeyProto tokenIdOpt = 3; - } - - message DataDirectiveProtoMessage { - bytes contractHash = 1; - bytes data = 2; - } - - oneof directiveProto { - TransferDirectiveProtoMessage transferDirectiveProto = 1; - AssetIssuingDirectiveProtoMessage assetIssuingDirectiveProto = 2; - ScriptedAssetDirectiveProtoMessage scriptedAssetDirectiveProto = 3; - DataDirectiveProtoMessage dataDirectiveProto = 4; - } - } - - uint64 fee = 1; - uint64 timestamp = 2; - repeated bytes inputs = 3 [(scalapb.field).collection_type = "scala.collection.immutable.IndexedSeq"]; - repeated DirectiveProtoMessage directives = 4 [(scalapb.field).collection_type = "scala.collection.immutable.IndexedSeq"]; - bytes proof = 5; -} \ No newline at end of file diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 3bea7c0..ff87471 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -5,12 +5,10 @@ explorer { infinitePing = true # if set to true, explorer will continue to ping node infinetely, otherwise it stops after #numberOfAttempts attempts askNode = false # if set false, explorer won't ping the node if it stopped working } - blackListSettings { banTime = 60m cleanupTime = 5s } - databaseSettings { host = "" user = "" @@ -18,7 +16,6 @@ explorer { maxPoolSize = 5 connectionTimeout = 60000 } - networkSettings { syncPacketLength = 1000 bindAddressHost = "0.0.0.0" @@ -32,19 +29,16 @@ explorer { declaredAddressHost = "" declaredAddressPort = 0 } - multisigSettings { checkTxMinedPeriod = 30 numberOfBlocksToCheck = 3 mnemonicKeys = [] } - ntpSettings { server = "pool.ntp.org" updateEvery = 30m timeout = 30s } - frontendSettings { host = "" port = 5150 diff --git a/src/main/scala/encry/ExplorerApp.scala b/src/main/scala/encry/ExplorerApp.scala index 2458fca..a6f4151 100644 --- a/src/main/scala/encry/ExplorerApp.scala +++ b/src/main/scala/encry/ExplorerApp.scala @@ -46,17 +46,14 @@ object ExplorerApp extends App { ds.setConnectionTimeout(settings.databaseSettings.connectionTimeout) } } *> IO { - val dbService = DBService(xa) val dbActor = system.actorOf(Props(new DBActor(dbService)), s"dbActor") val timeProvider: NetworkTimeProvider = new NetworkTimeProvider(settings.ntpSettings) val networkServer: ActorRef = system.actorOf(NetworkServer.props(settings.networkSettings, timeProvider, frontRemoteActor), "networkServer") - val parsersController = system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor, networkServer)), + system.actorOf(Props(new ParsersController(settings.parseSettings, settings.blackListSettings, dbActor, networkServer)), s"parserController") - - } *> IO.never }.unsafeRunSync() } diff --git a/src/main/scala/encry/parser/ParsersController.scala b/src/main/scala/encry/parser/ParsersController.scala index 625c6e5..bb66203 100644 --- a/src/main/scala/encry/parser/ParsersController.scala +++ b/src/main/scala/encry/parser/ParsersController.scala @@ -78,8 +78,8 @@ class ParsersController(settings: ParseSettings, context.system.scheduler.scheduleOnce(blackListSettings.cleanupTime, self, RemoveBadPeer) context.become(mainBehaviour(knownPeers -- peersForRemove.map(_._1))) - case msg @ BlockFromNode(block, node, currentNodeInfo) => - networkServer ! msg + case blockFromNode: BlockFromNode => + networkServer ! blockFromNode case msg => logger.info(s"Got strange message on ParserController: $msg.") } diff --git a/src/main/scala/encry/parser/SimpleNodeParser.scala b/src/main/scala/encry/parser/SimpleNodeParser.scala index 2912910..822a0fc 100644 --- a/src/main/scala/encry/parser/SimpleNodeParser.scala +++ b/src/main/scala/encry/parser/SimpleNodeParser.scala @@ -1,15 +1,13 @@ package encry.parser import java.net.{InetAddress, InetSocketAddress} - import akka.actor.{Actor, ActorRef, Props} import com.typesafe.scalalogging.StrictLogging +import encry.parser.ParsersController.BadPeer import encry.blockchain.nodeRoutes.InfoRoute import encry.database.DBActor.UpdatedInfoAboutNode import encry.parser.NodeParser.{PeersFromApi, PingNode} -import encry.parser.ParsersController.BadPeer import encry.settings.ParseSettings - import scala.concurrent.duration._ class SimpleNodeParser(node: InetSocketAddress, diff --git a/src/main/scala/encry/settings/Constants.scala b/src/main/scala/encry/settings/Constants.scala index a2f7b58..c723456 100644 --- a/src/main/scala/encry/settings/Constants.scala +++ b/src/main/scala/encry/settings/Constants.scala @@ -4,5 +4,6 @@ import org.encryfoundation.common.utils.Algos object Constants { val IntrinsicTokenId: Array[Byte] = Algos.hash("intrinsic_token") + val EttTokenId: String = Algos.encode(IntrinsicTokenId) } diff --git a/src/main/scala/encry/settings/ExplorerSettings.scala b/src/main/scala/encry/settings/Settings.scala similarity index 100% rename from src/main/scala/encry/settings/ExplorerSettings.scala rename to src/main/scala/encry/settings/Settings.scala From 74e66802a47232b7588cff4fc569813d96ff99b5 Mon Sep 17 00:00:00 2001 From: capdev Date: Wed, 23 Oct 2019 18:59:04 +0500 Subject: [PATCH 20/21] replace BasicMessagesRepo to import org.encryfoundation.common.network.BasicMessagesRepo --- .../encry/network/BasicMessagesRepo.scala | 461 ------------------ .../network/NetworkMessagesHandler.scala | 33 +- .../scala/encry/network/PeerHandler.scala | 23 +- 3 files changed, 28 insertions(+), 489 deletions(-) delete mode 100644 src/main/scala/encry/network/BasicMessagesRepo.scala diff --git a/src/main/scala/encry/network/BasicMessagesRepo.scala b/src/main/scala/encry/network/BasicMessagesRepo.scala deleted file mode 100644 index 857088c..0000000 --- a/src/main/scala/encry/network/BasicMessagesRepo.scala +++ /dev/null @@ -1,461 +0,0 @@ -package encry.network - -import java.net.InetSocketAddress - -import NetworkMessagesProto.GeneralizedNetworkProtoMessage -import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage -import NetworkMessagesProto.GeneralizedNetworkProtoMessage.InnerMessage._ -import NetworkMessagesProto.GeneralizedNetworkProtoMessage.ModifiersProtoMessage.MapFieldEntry -import NetworkMessagesProto.GeneralizedNetworkProtoMessage.{GetPeersProtoMessage => GetPeersProto, HandshakeProtoMessage => hPM, InvProtoMessage => InvPM, ModifiersProtoMessage => ModifiersPM, PeersProtoMessage => PeersPM, RequestModifiersProtoMessage => rModsPM, SyncInfoProtoMessage => sIPM} -import SyntaxMessageProto.InetSocketAddressProtoMessage -import akka.actor.ActorRef -import com.google.protobuf.{ByteString => GoogleByteString} -import akka.util.{ByteString => AkkaByteString} -import com.typesafe.scalalogging.StrictLogging -import BasicMessagesRepo.BasicMsgDataTypes.{InvData, ModifiersData} -import org.encryfoundation.common.utils.TaggedTypes -import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} -import scorex.crypto.hash.Blake2b256 -import supertagged.@@ - -import scala.util.Try - -object BasicMessagesRepo extends StrictLogging { - - object BasicMsgDataTypes { - type InvData = (ModifierTypeId, Seq[ModifierId]) - type ModifiersData = (ModifierTypeId, Map[ModifierId, Array[Byte]]) - } - - sealed trait NetworkMessage { - - val messageName: String - - val NetworkMessageTypeID: Byte - - def checkSumBytes(innerMessage: InnerMessage): Array[Byte] - - def toInnerMessage: InnerMessage - - def isValid(syncPacketLength: Int): Boolean - } - - sealed trait ProtoNetworkMessagesSerializer[T] { - - def toProto(message: T): InnerMessage - - def fromProto(message: InnerMessage): Option[T] - } - - object MessageOptions { - - val MAGIC: GoogleByteString = GoogleByteString.copyFrom(Array[Byte](0x12: Byte, 0x34: Byte, 0x56: Byte, 0x78: Byte)) - - val ChecksumLength: Int = 4 - - def calculateCheckSum(bytes: Array[Byte]): GoogleByteString = - GoogleByteString.copyFrom(Blake2b256.hash(bytes).take(ChecksumLength)) - } - - /** - * @param message - message, received from network - * @param source - sender of received message - * - * This case class transfers network message from PeerConnectionHandler actor to the NetworkController. - * Main duty is to transfer message from network with sender of it message to the NetworkController as an end point. - */ - - case class MessageFromNetwork(message: NetworkMessage, source: Option[ConnectedPeer]) - - /** - * This object contains functions, connected with protobuf serialization to the generalized network message. - * - * toProto function first computes checkSum as a hash from NetworkMessageProtoSerialized bytes. Next, - * assembles GeneralizedMessage, which contains from first dour calculated checkSum bytes, MAGIC constant, network message. - * - * fromProto function tries to serialize raw bytes to GeneralizedMessage and compare - * magic bytes. Next, tries to collect networkMessage. - */ - - object GeneralizedNetworkMessage { - - def toProto(message: NetworkMessage): GeneralizedNetworkProtoMessage = { - val innerMessage: InnerMessage = message.toInnerMessage - val calculatedCheckSum: GoogleByteString = MessageOptions.calculateCheckSum(message.checkSumBytes(innerMessage)) - GeneralizedNetworkProtoMessage() - .withMagic(MessageOptions.MAGIC) - .withChecksum(calculatedCheckSum) - .withInnerMessage(innerMessage) - } - - def fromProto(message: AkkaByteString): Try[NetworkMessage] = Try { - val netMessage: GeneralizedNetworkProtoMessage = - GeneralizedNetworkProtoMessage.parseFrom(message.toArray) - require(netMessage.magic.toByteArray.sameElements(MessageOptions.MAGIC.toByteArray), - s"Wrong MAGIC! Got ${netMessage.magic.toByteArray.mkString(",")}") - netMessage.innerMessage match { - case InnerMessage.SyncInfoProtoMessage(_) => - checkMessageValidity(SyncInfoNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.InvProtoMessage(_) => - checkMessageValidity(InvNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.RequestModifiersProtoMessage(_) => - checkMessageValidity(RequestModifiersSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.ModifiersProtoMessage(_) => - checkMessageValidity(ModifiersNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.GetPeersProtoMessage(_) => - checkMessageValidity(GetPeersNetworkMessage.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.PeersProtoMessage(_) => - checkMessageValidity(PeersNetworkMessageSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.HandshakeProtoMessage(_) => - checkMessageValidity(HandshakeSerializer.fromProto, netMessage.innerMessage, netMessage.checksum) - case InnerMessage.Empty => throw new RuntimeException("Empty inner message!") - case _ => throw new RuntimeException("Can't find serializer for received message!") - } - }.flatten - - /** - * @param serializer - function, which takes as a parameter other function, which provides serialisation to NetworkMessage. - * As a result it gives serialized network message contained in option. - * @param innerMessage - type of protobuf generalized nested message. - * @param requiredBytes - checkSum, stored in received message. - * @return - serialized network message contained in option. - * - * This function provides validation check for inner message parsing and compares checkSum bytes. - */ - - def checkMessageValidity(serializer: InnerMessage => Option[NetworkMessage], - innerMessage: InnerMessage, - requiredBytes: GoogleByteString): Try[NetworkMessage] = Try { - val serializedMessage: Option[NetworkMessage] = serializer(innerMessage) - require(serializedMessage.isDefined, "Nested message is invalid!") - val networkMessage: NetworkMessage = serializedMessage.get - val checkSumBytes: Array[Byte] = networkMessage.checkSumBytes(innerMessage) - val calculatedCheckSumBytes = MessageOptions.calculateCheckSum(checkSumBytes) - require(calculatedCheckSumBytes.toByteArray.sameElements(requiredBytes.toByteArray), - "Checksum of received message is invalid!") - networkMessage - } - } - - /** - * @param esi - EncrySyncInfo case class which contains sequence of modifiers ids/ - * - * This message is a nested type of generalized network message. It's sent with the aim to show other peer, - * which last N modifiers this peer has. - * Response for this message is an InvMessage which contains all modifiers older than local. - */ - - case class SyncInfoNetworkMessage(esi: SyncInfo) extends NetworkMessage { - - override val messageName: String = "Sync" - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.syncInfoProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override def toInnerMessage: InnerMessage = SyncInfoNetworkMessageSerializer.toProto(this) - - override val NetworkMessageTypeID: Byte = SyncInfoNetworkMessage.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = - if (esi.lastHeaderIds.size <= syncPacketLength) true else false - } - - object SyncInfoNetworkMessage { - - val NetworkMessageTypeID: Byte = 65: Byte - } - - object SyncInfoNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[SyncInfoNetworkMessage] { - - override def toProto(message: SyncInfoNetworkMessage): InnerMessage = - SyncInfoProtoMessage(sIPM().withLastHeaderIds(message.esi.lastHeaderIds.map(GoogleByteString.copyFrom))) - - override def fromProto(message: InnerMessage): Option[SyncInfoNetworkMessage] = message.syncInfoProtoMessage match { - case Some(value) => - Some(SyncInfoNetworkMessage(SyncInfo(value.lastHeaderIds.map(modId => ModifierId @@ modId.toByteArray)))) - case None => Option.empty[SyncInfoNetworkMessage] - } - } - - /** - * @param data - modifiersIds sequence. - * - * This message sends as a respons for SyncInfoMessage or to show other peers locally generated modifier. - */ - - case class InvNetworkMessage(data: InvData) extends NetworkMessage { - - override val messageName: String = "Inv" - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.invProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override def toInnerMessage: InnerMessage = InvNetworkMessageSerializer.toProto(this) - - override val NetworkMessageTypeID: Byte = InvNetworkMessage.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = - if (data._2.size <= syncPacketLength) true else false - } - - object InvNetworkMessage { - - val NetworkMessageTypeID: Byte = 55: Byte - } - - object InvNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[InvNetworkMessage] { - - def toProto(message: InvNetworkMessage): InnerMessage = InvProtoMessage(InvPM() - .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) - .withModifiers(message.data._2.map(elem => GoogleByteString.copyFrom(elem))) - ) - - def fromProto(message: InnerMessage): Option[InvNetworkMessage] = message.invProtoMessage match { - case Some(value) => value.modifiers match { - case mods: Seq[_] if mods.nonEmpty => Some(InvNetworkMessage( - ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> value.modifiers.map(x => ModifierId @@ x.toByteArray))) - case _ => Option.empty[InvNetworkMessage] - } - case None => Option.empty[InvNetworkMessage] - } - } - - /** - * @param data - modifiersIds sequence. - * - * This message sends to the peer to request missing in local history modifiers. - */ - - case class RequestModifiersNetworkMessage(data: InvData) extends NetworkMessage { - - override val messageName: String = "RequestModifier" - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.requestModifiersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override def toInnerMessage: InnerMessage = RequestModifiersSerializer.toProto(this) - - override val NetworkMessageTypeID: Byte = RequestModifiersNetworkMessage.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = - if (data._2.size <= syncPacketLength) true else false - } - - object RequestModifiersNetworkMessage { - - val NetworkMessageTypeID: Byte = 22: Byte - } - - object RequestModifiersSerializer extends ProtoNetworkMessagesSerializer[RequestModifiersNetworkMessage] { - - override def toProto(message: RequestModifiersNetworkMessage): InnerMessage = - RequestModifiersProtoMessage( - rModsPM() - .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) - .withModifiers(message.data._2.map(elem => GoogleByteString.copyFrom(elem))) - ) - - override def fromProto(message: InnerMessage): Option[RequestModifiersNetworkMessage] = - message.requestModifiersProtoMessage match { - case Some(value) => value.modifiers match { - case mods: Seq[_] if mods.nonEmpty => Some(RequestModifiersNetworkMessage( - ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> value.modifiers.map(x => ModifierId @@ x.toByteArray))) - case _ => Option.empty[RequestModifiersNetworkMessage] - } - case None => Option.empty[RequestModifiersNetworkMessage] - } - } - - /** - * @param data - map with modifierId as a key and serialized to protobuf modifiers as a value. - * - * This message sends as a RESPONSE ONLY to RequestModifiers message. - */ - - case class ModifiersNetworkMessage(data: ModifiersData) extends NetworkMessage { - - override val messageName: String = "Modifier" - - override def toInnerMessage: InnerMessage = ModifiersNetworkMessageSerializer.toProto(this) - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.modifiersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override val NetworkMessageTypeID: Byte = ModifiersNetworkMessage.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = - if (data._2.size <= syncPacketLength) true else false - } - - object ModifiersNetworkMessage { - - val NetworkMessageTypeID: Byte = 33: Byte - } - - object ModifiersNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[ModifiersNetworkMessage] { - - override def toProto(message: ModifiersNetworkMessage): InnerMessage = ModifiersProtoMessage(ModifiersPM() - .withModifierTypeId(GoogleByteString.copyFrom(Array(message.data._1))) - .withMap(message.data._2.map(element => - MapFieldEntry().withKey(GoogleByteString.copyFrom(element._1)).withValue(GoogleByteString.copyFrom(element._2))).toSeq)) - - override def fromProto(message: InnerMessage): Option[ModifiersNetworkMessage] = message.modifiersProtoMessage match { - case Some(value) => Some(ModifiersNetworkMessage(ModifierTypeId @@ value.modifierTypeId.toByteArray.head -> - value.map.map(element => ModifierId @@ element.key.toByteArray -> element.value.toByteArray).toMap)) - case None => Option.empty[ModifiersNetworkMessage] - } - } - - /** - * This network message sends to a random peer as a request for receiver's known peers. - */ - - case object GetPeersNetworkMessage extends NetworkMessage { - - override val messageName: String = "GetPeers message" - - override def toInnerMessage: InnerMessage = toProto - - def toProto: InnerMessage = GetPeersProtoMessage(GetPeersProto()) - - def fromProto(message: InnerMessage): Option[GetPeersNetworkMessage.type] = message.getPeersProtoMessage match { - case Some(_) => Some(GetPeersNetworkMessage) - case None => Option.empty[GetPeersNetworkMessage.type] - } - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.getPeersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override val NetworkMessageTypeID: Byte = 1: Byte - - override def isValid(syncPacketLength: Int): Boolean = true - } - - /** - * @param peers - sequence of known by this peer other peers. - * - * This network message sends directly to the sender of 'GetPeers' message. - */ - - case class PeersNetworkMessage(peers: Seq[InetSocketAddress]) extends NetworkMessage { - - override val messageName: String = "Peers message" - - override def toInnerMessage: InnerMessage = PeersNetworkMessageSerializer.toProto(this) - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.peersProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override val NetworkMessageTypeID: Byte = PeersNetworkMessage.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = true - } - - object PeersNetworkMessage { - - val NetworkMessageTypeID: Byte = 2: Byte - } - - object PeersNetworkMessageSerializer extends ProtoNetworkMessagesSerializer[PeersNetworkMessage] { - - override def toProto(message: PeersNetworkMessage): InnerMessage = PeersProtoMessage( - PeersPM().withPeers( - message.peers.map(element => InetSocketAddressProtoMessage().withHost(element.getHostName).withPort(element.getPort)) - )) - - override def fromProto(message: InnerMessage): Option[PeersNetworkMessage] = message.peersProtoMessage match { - case Some(value) => value.peers match { - case peers: Seq[_] if peers.nonEmpty => - Some(PeersNetworkMessage(value.peers.map(element => new InetSocketAddress(element.host, element.port)))) - case _ => Option.empty[PeersNetworkMessage] - } - case None => Option.empty[PeersNetworkMessage] - } - } - - /** - * @param protocolVersion - peer network communication protocol version - * @param nodeName - peer name - * @param declaredAddress - peer address - * @param time - handshake creation time - * - * This network message are using for set up network connection between two peers. - * First peer sends this message to the second one. Second peer processes this message - * and send back response with it's own handshake. After both - * peers received handshakes from each other, network connection raises. - */ - - case class Handshake(protocolVersion: Array[Byte], - nodeName: String, - declaredAddress: Option[InetSocketAddress], - time: Long) extends NetworkMessage { - - require(protocolVersion.length > 0, "Empty protocol version!") - - override val messageName: String = "Handshake" - - override def toInnerMessage: InnerMessage = HandshakeSerializer.toProto(this) - - override def checkSumBytes(innerMessage: InnerMessage): Array[Byte] = - innerMessage.handshakeProtoMessage.map(_.toByteArray).getOrElse(Array.emptyByteArray) - - override val NetworkMessageTypeID: Byte = Handshake.NetworkMessageTypeID - - override def isValid(syncPacketLength: Int): Boolean = true - } - - object Handshake { - - val NetworkMessageTypeID: Byte = 75: Byte - } - - object HandshakeSerializer extends ProtoNetworkMessagesSerializer[Handshake] { - - override def toProto(message: Handshake): InnerMessage = { - val initialHandshakeProto: hPM = hPM() - .withProtocolVersion(GoogleByteString.copyFrom(message.protocolVersion)) - .withNodeName(message.nodeName) - .withTime(message.time) - val updatedHandshakeProto: hPM = message.declaredAddress match { - case Some(value) => initialHandshakeProto.withDeclaredAddress(InetSocketAddressProtoMessage() - .withHost(value.getHostName) - .withPort(value.getPort) - ) - case None => initialHandshakeProto - } - HandshakeProtoMessage(updatedHandshakeProto) - } - - override def fromProto(message: InnerMessage): Option[Handshake] = message.handshakeProtoMessage match { - case Some(value) => value.nodeName match { - case name: String if name.nonEmpty => Some( - Handshake( - value.protocolVersion.toByteArray, - value.nodeName, - value.declaredAddress.map(element => new InetSocketAddress(element.host, element.port)), - value.time - )) - case _ => Option.empty[Handshake] - } - case None => Option.empty[Handshake] - } - } - - sealed trait ConnectionType - case object Incoming extends ConnectionType - case object Outgoing extends ConnectionType - - case class ConnectedPeer(socketAddress: InetSocketAddress, - handlerRef: ActorRef, - direction: ConnectionType, - handshake: Handshake) { - - override def toString: String = s"ConnectedPeer($socketAddress)" - } - - case class SyncInfo(lastHeaderIds: Seq[ModifierId]) { - - def startingPoints: Seq[(Byte @@ TaggedTypes.ModifierTypeId.Tag, ModifierId)] = lastHeaderIds.map(id => ModifierTypeId @@ (101: Byte) -> id) - - } -} \ No newline at end of file diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index fd916e9..03868a0 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -3,9 +3,10 @@ package encry.network import TransactionProto.TransactionProtoMessage import akka.actor.{Actor, ActorRef, Props} import com.typesafe.scalalogging.StrictLogging -import encry.network.BasicMessagesRepo._ -import org.encryfoundation.common.modifiers.history.{Header, Payload} +import encry.network.NetworkMessagesHandler.MessageFromNetwork +import encry.network.PeerHandler.ConnectedPeer import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer} +import org.encryfoundation.common.network.BasicMessagesRepo._ import org.encryfoundation.common.utils.Algos class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictLogging { @@ -18,7 +19,7 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL logger.debug(s"Got modifiers: $modifierTypeId (${modifierIds.map(Algos.encode).mkString(",")})") println(s"Got modifier: ${peerOpt.get}") peerOpt.foreach { peer => - if (List(Transaction.modifierTypeId/*, Header.modifierTypeId, Payload.modifierTypeId*/).contains(modifierTypeId)) { + if (Transaction.modifierTypeId == modifierTypeId) { logger.debug(s"Request modifier: $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") peer.handlerRef ! RequestModifiersNetworkMessage((modifierTypeId, modifierIds)) } @@ -33,22 +34,6 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL val tx = TransactionProtoSerializer.fromProto(TransactionProtoMessage.parseFrom(bytes)) tx.foreach(networkServer ! _) -// case Header.modifierTypeId => -// val header = HeaderProtoSerializer.fromProto(HeaderProtoMessage.parseFrom(bytes)) -// header.foreach { header => -// println(s"header: ${header.encodedId}") -// networkServer ! header -// } -// -// case Payload.modifierTypeId => -// val payload = PayloadProtoSerializer.fromProto(PayloadProtoMessage.parseFrom(bytes)) -// payload.foreach { payload => -// println(s"payload: ${payload.encodedId} txs ${payload.txs.size}") -// payload.txs.foreach(tx => println(s"payload.tx: ${tx.encodedId}")) -// println(s"payload.end") -// networkServer ! payload -// } - case _ => } } @@ -60,8 +45,18 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL } object NetworkMessagesHandler { + case class Transaction(tx: Transaction) case class Block(block: Block) + /** + * @param message - message, received from network + * @param source - sender of received message + * + * This case class transfers network message from PeerConnectionHandler actor to the NetworkController. + * Main duty is to transfer message from network with sender of it message to the NetworkController as an end point. + */ + case class MessageFromNetwork(message: NetworkMessage, source: Option[ConnectedPeer]) + def props(networkServer: ActorRef) = Props(new NetworkMessagesHandler(networkServer)) } \ No newline at end of file diff --git a/src/main/scala/encry/network/PeerHandler.scala b/src/main/scala/encry/network/PeerHandler.scala index 680edfb..68184e6 100644 --- a/src/main/scala/encry/network/PeerHandler.scala +++ b/src/main/scala/encry/network/PeerHandler.scala @@ -9,9 +9,10 @@ import akka.io.Tcp._ import akka.util.{ByteString, CompactByteString} import com.google.common.primitives.Ints import com.typesafe.scalalogging.StrictLogging -import PeerHandler._ -import BasicMessagesRepo._ +import org.encryfoundation.common.network.BasicMessagesRepo._ import NetworkServer.ConnectionSetupSuccessfully +import encry.network.NetworkMessagesHandler.MessageFromNetwork +import encry.network.PeerHandler.{Ack, ConnectedPeer, HandshakeDone, HandshakeTimeout, Outgoing, RemovePeerFromConnectionList, StartIteration} import encry.settings.NetworkSettings import scala.annotation.tailrec @@ -76,7 +77,7 @@ class PeerHandler(remoteAddress: InetSocketAddress, timeout.foreach(_.cancel()) context.become(workingCycleWriting(ConnectedPeer(remoteAddress, self, Outgoing, receivedHandshake.get))) - case Received(data) => GeneralizedNetworkMessage.fromProto(data) match { + case Received(data) => GeneralizedNetworkMessage.fromProto(data.toArray[Byte]) match { case Success(value) => value match { case handshake: Handshake => logger.info(s"Got a Handshake from $remoteAddress.") @@ -141,7 +142,7 @@ class PeerHandler(remoteAddress: InetSocketAddress, val packet: (List[ByteString], ByteString) = getPacket(chunksBuffer ++ data) chunksBuffer = packet._2 packet._1.find { packet => - GeneralizedNetworkMessage.fromProto(packet) match { + GeneralizedNetworkMessage.fromProto(packet.toArray[Byte]) match { case Success(message) => receivedMessagesHandler ! MessageFromNetwork(message, Some(cp)) logger.debug("Received message " + message.messageName + " from " + remoteAddress) @@ -211,18 +212,22 @@ class PeerHandler(remoteAddress: InetSocketAddress, object PeerHandler { - case object StartIteration + sealed trait ConnectionType + case object Incoming extends ConnectionType + case object Outgoing extends ConnectionType - sealed trait ConnectionMessages + case class ConnectedPeer(socketAddress: InetSocketAddress, handlerRef: ActorRef, direction: ConnectionType, handshake: Handshake) { + override def toString: String = s"ConnectedPeer($socketAddress)" + } + sealed trait ConnectionMessages case object HandshakeTimeout extends ConnectionMessages - case object HandshakeDone extends ConnectionMessages + case object StartIteration + final case class Ack(offset: Long) extends Tcp.Event case class RemovePeerFromConnectionList(peer: InetSocketAddress) extends ConnectionMessages - final case class Ack(offset: Long) extends Tcp.Event - def props(remoteAddress: InetSocketAddress, listener: ActorRef, settings: NetworkSettings, From 734019e5b37cf9d15502b8ec508361e7ac0ddef0 Mon Sep 17 00:00:00 2001 From: SunFrost Date: Mon, 28 Oct 2019 15:05:58 +0500 Subject: [PATCH 21/21] PR fixes --- .../scala/encry/network/NetworkMessagesHandler.scala | 3 --- src/main/scala/encry/network/NetworkServer.scala | 10 ++-------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/scala/encry/network/NetworkMessagesHandler.scala b/src/main/scala/encry/network/NetworkMessagesHandler.scala index 03868a0..26a1552 100644 --- a/src/main/scala/encry/network/NetworkMessagesHandler.scala +++ b/src/main/scala/encry/network/NetworkMessagesHandler.scala @@ -16,8 +16,6 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL case MessageFromNetwork(message, peerOpt) => message match { case InvNetworkMessage((modifierTypeId, modifierIds)) => - logger.debug(s"Got modifiers: $modifierTypeId (${modifierIds.map(Algos.encode).mkString(",")})") - println(s"Got modifier: ${peerOpt.get}") peerOpt.foreach { peer => if (Transaction.modifierTypeId == modifierTypeId) { logger.debug(s"Request modifier: $modifierTypeId ${modifierIds.map(Algos.encode).mkString(",")}") @@ -27,7 +25,6 @@ class NetworkMessagesHandler(networkServer: ActorRef) extends Actor with StrictL case ModifiersNetworkMessage((modifierTypeId, modifierMap)) => logger.debug(s"Response modifiers: $modifierTypeId size ${modifierMap.size}") - modifierMap.foreach { case (modifierId, bytes) => modifierTypeId match { case Transaction.modifierTypeId => diff --git a/src/main/scala/encry/network/NetworkServer.scala b/src/main/scala/encry/network/NetworkServer.scala index f3b6497..5dd5788 100644 --- a/src/main/scala/encry/network/NetworkServer.scala +++ b/src/main/scala/encry/network/NetworkServer.scala @@ -75,21 +75,15 @@ class NetworkServer(settings: NetworkSettings, timeProvider: NetworkTimeProvider tmpConnectionHandler = None logger.info(s"Disconnected from $peer.") - case ConnectionSetupSuccessfully => - //logger.info(s"Created generator actor for ${peer.explorerHost}:${peer.explorerPort}.") - case tx: Transaction => - println(s"tx: ${tx.encodedId}") frontRemoteActor ! tx case BlockFromNode(block, nodeAddr, nodeInfo) => - println(s"payload: ${block.payload.headerId} txs ${block.payload.txs.size}") - block.payload.txs.foreach(tx => println(s"payload.tx: ${tx.id}")) - println(s"payload.end") val txIds = block.payload.txs.map(_.id) frontRemoteActor ! txIds - case msg => logger.info(s"Got strange message on NetworkServer: $msg.") + case msg => + logger.info(s"Got strange message on NetworkServer: $msg.") } }