diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a33887508b..98448bccfa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,9 +36,16 @@ jobs: env: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} - - name: Publish release + - name: Publish ergo-wallet run: sbt +ergoWallet/publishSigned sonatypeBundleRelease env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + + - name: Publish ergo-core + run: sbt +ergoCore/publishSigned sonatypeBundleRelease + env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/Dockerfile b/Dockerfile index 95b719cb24..17be1a16aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,10 @@ COPY build.sbt findbugs-exclude.xml ./ COPY project/ project/ COPY avldb/build.sbt avldb/build.sbt COPY avldb/project/ avldb/project/ +COPY ergo-core/build.sbt ergo-core/build.sbt +COPY ergo-core/project/ ergo-core/project/ COPY ergo-wallet/build.sbt ergo-wallet/build.sbt COPY ergo-wallet/project/ ergo-wallet/project/ -COPY benchmarks/build.sbt benchmarks/build.sbt RUN sbt update COPY . ./ RUN sbt assembly diff --git a/avldb/project/build.properties b/avldb/project/build.properties index 31334bbd3d..c0bab04941 100644 --- a/avldb/project/build.properties +++ b/avldb/project/build.properties @@ -1 +1 @@ -sbt.version=1.1.1 +sbt.version=1.2.8 diff --git a/avldb/project/plugins.sbt b/avldb/project/plugins.sbt index a083810279..0d12040ca8 100644 --- a/avldb/project/plugins.sbt +++ b/avldb/project/plugins.sbt @@ -2,8 +2,6 @@ logLevel := Level.Warn addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") -addSbtPlugin("com.github.tkawachi" % "sbt-lock" % "0.4.0") - addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") diff --git a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala b/avldb/src/main/scala/org/ergoplatform/serialization/ErgoSerializer.scala similarity index 95% rename from avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala rename to avldb/src/main/scala/org/ergoplatform/serialization/ErgoSerializer.scala index 972ac73792..cfc13412dc 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala +++ b/avldb/src/main/scala/org/ergoplatform/serialization/ErgoSerializer.scala @@ -1,4 +1,4 @@ -package scorex.core.serialization +package org.ergoplatform.serialization import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/org/ergoplatform/serialization/ManifestSerializer.scala similarity index 98% rename from avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala rename to avldb/src/main/scala/org/ergoplatform/serialization/ManifestSerializer.scala index 70bced053d..985eb421fe 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/org/ergoplatform/serialization/ManifestSerializer.scala @@ -1,4 +1,4 @@ -package scorex.core.serialization +package org.ergoplatform.serialization import scorex.crypto.authds.avltree.batch.Constants.DigestType import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, ProxyInternalNode} diff --git a/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala b/avldb/src/main/scala/org/ergoplatform/serialization/SubtreeSerializer.scala similarity index 97% rename from avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala rename to avldb/src/main/scala/org/ergoplatform/serialization/SubtreeSerializer.scala index 0a1f6df970..f4822bbe3e 100644 --- a/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala +++ b/avldb/src/main/scala/org/ergoplatform/serialization/SubtreeSerializer.scala @@ -1,4 +1,4 @@ -package scorex.core.serialization +package org.ergoplatform.serialization import scorex.crypto.authds.avltree.batch.Constants.DigestType import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes, VersionedLDBAVLStorage} diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala index a2bffc75ca..9199acdd8f 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala @@ -1,7 +1,7 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.{ADKey, ADValue, Balance} import scorex.crypto.authds.avltree.batch.Constants.{DigestType, hashFn} import scorex.crypto.authds.avltree.batch.serialization.ProxyInternalNode diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageSpecification.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageSpecification.scala index 1c72d26b84..810e986b00 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageSpecification.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageSpecification.scala @@ -6,7 +6,7 @@ import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.core.serialization.{ManifestSerializer, SubtreeSerializer} +import org.ergoplatform.serialization.{ManifestSerializer, SubtreeSerializer} import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 diff --git a/benchmarks/Jenkinsfile b/benchmarks/Jenkinsfile deleted file mode 100644 index c15fda9f89..0000000000 --- a/benchmarks/Jenkinsfile +++ /dev/null @@ -1,11 +0,0 @@ -pipeline { - agent { label 'slave' } - stages { - - stage('org.ergoplatform.bench.BenchRunner') { - steps { - sh('sbt "benchmarks/test:runMain org.ergoplatform.bench.BenchRunner"') - } - } - } -} diff --git a/benchmarks/build.sbt b/benchmarks/build.sbt deleted file mode 100644 index 6e115c6490..0000000000 --- a/benchmarks/build.sbt +++ /dev/null @@ -1,6 +0,0 @@ -sourceDirectory in Jmh := (sourceDirectory in Test).value -classDirectory in Jmh := (classDirectory in Test).value -dependencyClasspath in Jmh := (dependencyClasspath in Test).value -// rewire tasks, so that 'jmh:run' automatically invokes 'jmh:compile' (otherwise a clean 'jmh:run' would fail) -compile in Jmh := (compile in Jmh).dependsOn(compile in Test).value -run in Jmh := (run in Jmh).dependsOn(Keys.compile in Jmh).evaluated \ No newline at end of file diff --git a/benchmarks/src/test/resources/logback-bench.xml b/benchmarks/src/test/resources/logback-bench.xml deleted file mode 100644 index 550b67da3a..0000000000 --- a/benchmarks/src/test/resources/logback-bench.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - System.out - - ERROR - - - [%thread] >> [%-5level] %logger{36} >> %d{HH:mm:ss.SSS} %msg%n - - - - - - - - - - diff --git a/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala b/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala deleted file mode 100644 index 60c2837582..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala +++ /dev/null @@ -1,98 +0,0 @@ -package org.ergoplatform - -import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.extension.Extension -import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.{ErgoModifiersCache, NVBenchmark} -import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.utils.HistoryTestHelpers - -import scala.annotation.tailrec - -object ModifiersApplicationBench extends HistoryTestHelpers with NVBenchmark { - - def main(args: Array[String]): Unit = { - - val startTs = System.currentTimeMillis() - - val cache = new ErgoModifiersCache(maxSize = 1024) - - val headers: Seq[Header] = readHeaders - val payloads: Seq[BlockTransactions] = readPayloads - val extensions: Seq[Extension] = readExtensions - - def bench(benchCase: String) - (applicator: (Seq[BlockSection], ErgoHistory) => Any, - mods: Seq[BlockSection]): (String, Long) = { - val preparedHistory = applyModifiers(headers.take(mods.size / 2), unlockedHistory())._1 - val et = Utils.time(applicator(mods, preparedHistory)).toLong - assert(preparedHistory.fullBlockHeight == mods.size / 2) - s"Performance of `$benchCase`: $et ms" -> et - } - - def applyModifiersWithCache(mods: Seq[BlockSection], his: ErgoHistory): (ErgoHistory, Int) = { - mods.foreach(m => cache.put(m.id, m)) - @tailrec def applyLoop(applied: Seq[BlockSection]): Seq[BlockSection] = { - cache.popCandidate(his) match { - case Some(mod) => - his.append(mod).get - applyLoop(mod +: applied) - case None => - applied - } - } - - val appliedModsQty = applyLoop(Seq()).size - his -> appliedModsQty - } - - def applyModifiers(mods: Seq[BlockSection], his: ErgoHistory): (ErgoHistory, Int) = { - @tailrec def applyLoop(rem: Seq[BlockSection], - applied: Seq[BlockSection]): Seq[BlockSection] = { - rem match { - case m :: tail => - his.applicableTry(m) - his.append(m) - applyLoop(tail, m +: applied) - case Nil => - applied - } - } - - val appliedModsQty = applyLoop(mods, Seq()).size - his -> appliedModsQty - } - - val modifiersDirectOrd = payloads ++ extensions - val modifiersReversedOrd = modifiersDirectOrd.reverse - - val report0 = bench("Modifiers application in direct order")(applyModifiers, modifiersDirectOrd) - val report1 = bench("Modifiers application in direct order (cache)")(applyModifiersWithCache, modifiersDirectOrd) - val report2 = bench("Modifiers application in reversed order (cache)")(applyModifiersWithCache, modifiersReversedOrd) - - println(report0._1) - println(report1._1) - println(report2._1) - - val reports = Seq(report0, report1, report2).map { case (repStr, et) => - BenchReport(repStr, et) - } - - Utils.dumpToFile("ModifiersApplicationBench", startTs, reports) - - System.exit(0) - } - - def history(): ErgoHistory = generateHistory(verifyTransactions = true, StateType.Utxo, - PoPoWBootstrap = false, blocksToKeep = -1) - - def unlockedHistory(): ErgoHistory = { - val h = history() - HistoryTestHelpers.allowToApplyOldBlocks(h) - h - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/Utils.scala b/benchmarks/src/test/scala/org/ergoplatform/Utils.scala deleted file mode 100644 index 70eaec61bf..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/Utils.scala +++ /dev/null @@ -1,59 +0,0 @@ -package org.ergoplatform - -import java.io.{File, InputStream, PrintWriter} -import java.net.URL - -import com.google.common.primitives.Ints -import javax.net.ssl.HttpsURLConnection -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.HistoryModifierSerializer - -object Utils { - - final case class BenchReport(benchCase: String, et: Long) - - def getUrlInputStream(url: String, - connectTimeout: Int = 5000, - readTimeout: Int = 5000, - requestMethod: String = "GET"): InputStream = { - val u = new URL(url) - val conn = u.openConnection.asInstanceOf[HttpsURLConnection] - conn.setConnectTimeout(connectTimeout) - conn.setReadTimeout(readTimeout) - conn.setRequestMethod(requestMethod) - conn.connect() - conn.getInputStream - } - - def readLength(implicit fis: InputStream): Option[Int] = - Some(Stream.continually(fis.read().toByte).take(4).toArray).map(Ints.fromByteArray) - - def readBytes(length: Int)(implicit fis: InputStream): Option[Array[Byte]] = - Some(Stream.continually(fis.read().toByte).take(length).toArray) - - def readModifier[M <: BlockSection](implicit fis: InputStream): Option[M] = { - for { - length <- readLength - bytes <- readBytes(length) - mod <- HistoryModifierSerializer.parseBytesTry(bytes).toOption.map(_.asInstanceOf[M]) - } yield mod - } - - def dumpToFile(benchName: String, startTs: Long, reports: Seq[BenchReport]): Unit = { - new File(s"target/bench/").mkdirs() - val outWriter = new PrintWriter(new File(s"target/bench/bench-report.json")) - val reportsStr = reports.map { case BenchReport(benchCase, et) => - s"""{"benchCase":"$benchCase","elapsedTime":$et}""" - }.mkString(",") - outWriter.write(s"""{"benchName":"$benchName","startTs":$startTs,"reports":[$reportsStr]}""") - outWriter.close() - } - - def time[R](block: => R): Double = { - val t0 = System.nanoTime() - block // call-by-name - val t1 = System.nanoTime() - (t1.toDouble - t0) / 1000000 - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchActor.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/BenchActor.scala deleted file mode 100644 index c591aba9c9..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchActor.scala +++ /dev/null @@ -1,63 +0,0 @@ -package org.ergoplatform.bench - -import akka.actor.{Actor, ActorRef, ActorSystem, Props} -import org.ergoplatform.Utils -import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied -import scorex.util.ScorexLogging - -import scala.concurrent.ExecutionContext -import scala.concurrent.duration._ -import scala.language.postfixOps - -class BenchActor(threshold: Int) extends Actor with ScorexLogging { - - implicit val ec: ExecutionContext = context.dispatcher - - var counter = 0 - var start = 0L - var finish = 0L - - val timeout: FiniteDuration = 2 hours - - override def preStart(): Unit = { - context.system.eventStream.subscribe(self, classOf[FullBlockApplied]) - context.system.scheduler.scheduleOnce(timeout, self, BenchActor.Timeout) - () - } - - override def receive: Receive = { - case BenchActor.Start => - start = System.nanoTime() - log.info(s"Starting bench..") - case FullBlockApplied(_) => - self ! BenchActor.Inc - case BenchActor.Inc => - counter += 1 - if (counter % 100 == 0 ) log.info(s"counter is $counter") - if (counter >= threshold) { - finish = System.nanoTime() - val et = (finish - start) / 1000000 - log.info(s"$threshold modifiers applied, elapsed time: $et ms") - Utils.dumpToFile("NodeViewHolder modifiers application", start, Seq(BenchReport(s"$threshold modifiers", et))) - System.exit(0) - } - case BenchActor.Timeout => - log.error("Bench is taking too long. Shutting down on timeout.") - System.exit(1) - } - -} - -object BenchActor { - - def apply(threshold: Int)(implicit ac: ActorSystem): ActorRef = { - ac.actorOf(Props.apply(classOf[BenchActor], threshold)) - } - - case object Timeout - - case object Inc - - case object Start -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala deleted file mode 100644 index 2dfdfbfd0e..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala +++ /dev/null @@ -1,72 +0,0 @@ -package org.ergoplatform.bench - -import akka.actor.{ActorRef, ActorSystem} -import org.ergoplatform.bench.misc.TempDir -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.storage.modifierprocessors.{FullBlockPruningProcessor, ToDownloadProcessor} -import org.ergoplatform.nodeView.state.{ErgoState, StateType} -import org.ergoplatform.nodeView.{ErgoNodeViewRef, NVBenchmark} -import org.ergoplatform.settings.{Args, ErgoSettings} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.CurrentView -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetDataFromCurrentView, LocallyGeneratedModifier} -import scorex.util.ScorexLogging -import scala.concurrent.ExecutionContextExecutor - -object BenchRunner extends ScorexLogging with NVBenchmark { - - implicit val system: ActorSystem = ActorSystem("bench") - implicit val ec: ExecutionContextExecutor = system.dispatcher - - def main(args: Array[String]): Unit = { - val threshold = args.headOption.getOrElse("1000").toInt - val isUtxo = args.lift(2).isEmpty - val state = if (isUtxo) StateType.Utxo else StateType.Digest - val benchRef = BenchActor(threshold) - val userDir = TempDir.createTempDir - - log.info(s"User dir is $userDir") - log.info("Starting benchmark.") - - val realNetworkSettings = ErgoSettings.read(Args(Some("src/main/resources/application.conf"), None)) - val nodeSettings = realNetworkSettings.nodeSettings.copy(stateType = state) - - lazy val ergoSettings: ErgoSettings = realNetworkSettings - .copy(directory = userDir.getAbsolutePath, nodeSettings = nodeSettings) - - log.info(s"Setting that being used:") - log.info(s"$ergoSettings") - - val nodeViewHolderRef: ActorRef = ErgoNodeViewRef(ergoSettings) - - /** - * It's a hack to set minimalFullBlockHeightVar to 0 and to avoid "Header Is Not Synced" error, cause - * in our case we are considering only locally pre-generated modifiers. - */ - nodeViewHolderRef ! GetDataFromCurrentView[ErgoState[_], Unit](adjust) - - log.info("Starting to read modifiers.") - log.info("Finished read modifiers, starting to bench.") - log.info(s"$threshold modifiers to go") - runBench(benchRef, nodeViewHolderRef, (readHeaders ++ readExtensions ++ readPayloads).toVector) - } - - private def adjust(v: CurrentView[ErgoState[_]]): Unit = { - import scala.reflect.runtime.{universe => ru} - val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader) - val procInstance = runtimeMirror.reflect(v.history.asInstanceOf[ToDownloadProcessor]) - val ppM = ru.typeOf[ToDownloadProcessor].member(ru.TermName("pruningProcessor")).asMethod - val pp = procInstance.reflectMethod(ppM).apply().asInstanceOf[FullBlockPruningProcessor] - val f = ru.typeOf[FullBlockPruningProcessor].member(ru.TermName("minimalFullBlockHeightVar")).asTerm.accessed.asTerm - runtimeMirror.reflect(pp).reflectField(f).set(ErgoHistory.GenesisHeight) - val f2 = ru.typeOf[FullBlockPruningProcessor].member(ru.TermName("isHeadersChainSyncedVar")).asTerm.accessed.asTerm - runtimeMirror.reflect(pp).reflectField(f2).set(true: Boolean) - () - } - - private def runBench(benchRef: ActorRef, nodeRef: ActorRef, modifiers: Vector[BlockSection]): Unit = { - benchRef ! BenchActor.Start - modifiers.foreach { m => nodeRef ! LocallyGeneratedModifier(m) } - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/HistoryExtractor.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/HistoryExtractor.scala deleted file mode 100644 index 04e0540995..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/HistoryExtractor.scala +++ /dev/null @@ -1,36 +0,0 @@ -package org.ergoplatform.bench - -import java.io.FileOutputStream - -import org.ergoplatform.bench.misc.ModifierWriter -import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.settings.{Args, ErgoSettings} -import scorex.util.ScorexLogging - - -object HistoryExtractor extends ScorexLogging { - - def main(args: Array[String]): Unit = { - - lazy val cfgPath: Option[String] = args.headOption - lazy val outputFile: String = args.lift(1).getOrElse("blocks.dat") - lazy val ergoSettings: ErgoSettings = ErgoSettings.read(Args(cfgPath, None)) - - val os = new FileOutputStream(outputFile) - val h = ErgoHistory.readOrGenerate(ergoSettings)(null) - val wholeChain = h.chainToHeader(None, h.bestHeaderOpt.get) - - var counter = 0 - wholeChain._2.headers.take(10000).foreach { header => - counter += 1 - if (counter % 100 == 0) { log.info(s"Processed $counter blocks.")} - val b = h.getFullBlock(header).get - ModifierWriter.write(b.header)(os) - ModifierWriter.write(b.blockTransactions)(os) - b.adProofs.foreach { p => ModifierWriter.write(p)(os) } - os.flush() - } - os.close() - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/CrawlerConfig.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/CrawlerConfig.scala deleted file mode 100644 index 7c8d7b7366..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/CrawlerConfig.scala +++ /dev/null @@ -1,3 +0,0 @@ -package org.ergoplatform.bench.misc - -case class CrawlerConfig(file: String, threshold: Int) diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala deleted file mode 100644 index 50e37cedd8..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ /dev/null @@ -1,41 +0,0 @@ -package org.ergoplatform.bench.misc - -import java.io.{InputStream, OutputStream} -import com.google.common.primitives.Ints -import org.ergoplatform.Utils._ -import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId} -import org.ergoplatform.modifiers.history._ -import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import scorex.core.serialization.ErgoSerializer -import scorex.core.NodeViewModifier - -object ModifierWriter { - - val modifierSerializers: Map[NetworkObjectTypeId.Value, ErgoSerializer[_ <: BlockSection]] = - Map(Header.modifierTypeId -> HeaderSerializer, - BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, - ADProofs.modifierTypeId -> ADProofsSerializer) - - def write(mod: NodeViewModifier)(implicit fos: OutputStream): Unit = { - val typeId = mod.modifierTypeId - val bytes = mod.bytes - val length: Int = bytes.length - val lengthBytes = Ints.toByteArray(length) - val bytesToWrite = typeId +: (lengthBytes ++ bytes) - fos.write(bytesToWrite) - fos.flush() - } - - def read(implicit fis: InputStream): Option[BlockSection] = for { - typeId <- readModId - length <- readLength - bytes <- readBytes(length) - mod <- modifierSerializers(typeId).parseBytesTry(bytes).toOption - } yield mod - - private def readModId(implicit fis: InputStream): Option[NetworkObjectTypeId.Value] = { - val int = fis.read() - if (int == -1) { None } else { Some(NetworkObjectTypeId.fromByte(int.toByte)) } - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/TempDir.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/TempDir.scala deleted file mode 100644 index 35e5c2b9a4..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/TempDir.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.ergoplatform.bench.misc - -import org.ergoplatform.wallet.utils.TestFileUtils - -object TempDir extends TestFileUtils diff --git a/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala b/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala deleted file mode 100644 index cb21d133ab..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala +++ /dev/null @@ -1,95 +0,0 @@ -package org.ergoplatform.db - -import com.google.common.primitives.Longs -import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.settings.Algos -import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import org.ergoplatform.wallet.utils.TestFileUtils -import org.iq80.leveldb.Options -import org.scalameter.KeyValue -import org.scalameter.api.{Bench, Gen, _} -import org.scalameter.picklers.Implicits._ -import scorex.crypto.hash.Digest32 -import scorex.util.idToBytes -import scorex.db.LDBFactory.factory -import scorex.db.{LDBKVStore, LDBVersionedStore} - -import scala.util.Random - -object LDBStoreBench - extends Bench.ForkedTime - with ErgoTransactionGenerators - with TestFileUtils { - - private val options = new Options() - options.createIfMissing(true) - private val db1 = factory.open(createTempDir, options) - - private def storeLDB() = new LDBKVStore(db1) - private def storeLVDB() = new LDBVersionedStore(createTempDir, initialKeepVersions = 400) - - private val modsNumGen = Gen.enumeration("modifiers number")(1000) - - val txsGen: Gen[Seq[BlockTransactions]] = modsNumGen.map { num => - (0 to num).flatMap { _ => - invalidBlockTransactionsGen(defaultMinerPk, 10).sample - } - } - - val txsWithDbGen: Gen[(Seq[BlockTransactions], LDBKVStore)] = txsGen.map { bts => - val toInsert = bts.map(bt => idToBytes(bt.headerId) -> bt.bytes).toArray - val db = storeLDB() - toInsert.grouped(5).foreach(kv => db.insert(kv.map(_._1), kv.map(_._2)).get) - bts -> storeLDB - } - - private val config = Seq[KeyValue]( - exec.minWarmupRuns -> 1, - exec.maxWarmupRuns -> 1, - exec.benchRuns -> 10, - exec.requireGC -> true - ) - - private def randomVersion: Digest32 = Algos.hash(Longs.toByteArray(Random.nextLong())) - - private def benchWriteLDB(bts: Seq[BlockTransactions]): Unit = { - val toInsert = bts.map(bt => idToBytes(bt.headerId) -> bt.bytes).toArray - val db = storeLDB() - toInsert.grouped(5).foreach(kv => db.insert(kv.map(_._1), kv.map(_._2)).get) - } - - private def benchReadLDB(bts: Seq[BlockTransactions], db: LDBKVStore): Unit = { - bts.foreach { bt => db.get(idToBytes(bt.headerId)) } - } - - private def benchWriteLVDB(bts: Seq[BlockTransactions]): Unit = { - val toInsert = bts.map(bt => idToBytes(bt.headerId) -> bt.bytes) - val db = storeLVDB() - db.update(randomVersion, List.empty, toInsert).get - } - - private def benchWriteReadLVDB(bts: Seq[BlockTransactions]): Unit = { - val toInsert = bts.map(bt => idToBytes(bt.headerId) -> bt.bytes) - val db = storeLVDB() - db.update(randomVersion, List.empty, toInsert).get - bts.foreach { bt => db.get(idToBytes(bt.headerId)) } - } - - - performance of "LDBStore" in { - performance of "LDBStore write" in { - using(txsGen) config(config: _*) in (bts => benchWriteLDB(bts)) - } - performance of "LDBStore read" in { - using(txsWithDbGen) config(config: _*) in { case (bts, db) => benchReadLDB(bts, db) } - } - - performance of "LVDBStore write" in { - using(txsGen) config(config: _*) in (bts => benchWriteLVDB(bts)) - } - - performance of "LVDBStore write/read" in { - using(txsGen) config(config: _*) in (bts => benchWriteReadLVDB(bts)) - } - } -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/db/LDBVersionedStoreRollbackBench.scala b/benchmarks/src/test/scala/org/ergoplatform/db/LDBVersionedStoreRollbackBench.scala deleted file mode 100644 index d0301ac45f..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/db/LDBVersionedStoreRollbackBench.scala +++ /dev/null @@ -1,38 +0,0 @@ -package org.ergoplatform.db - -import com.google.common.primitives.Ints -import org.ergoplatform.Utils -import org.ergoplatform.settings.Algos -import org.ergoplatform.wallet.utils.TestFileUtils -import scorex.util.Random -import scorex.db.LDBVersionedStore - -object LDBVersionedStoreRollbackBench extends App with TestFileUtils { - - private val store = new LDBVersionedStore(createTempDir, initialKeepVersions = 400) - - private val numEpochs = 10000 - private val elemsAtStep = 500 - - private def key(i: Int): Array[Byte] = Algos.hash(Ints.toByteArray(i)) - - (0 to numEpochs).foreach { i => - val toInsert = (0 to elemsAtStep).map { i0 => - key(i0 + i * elemsAtStep) -> Random.randomBytes(1000) - } - val toRemove = - if (i > 1) (0 to (elemsAtStep / 2)).map(_ => key(scala.util.Random.nextInt((i - 1) * elemsAtStep))) else Seq.empty - val version = Algos.hash(key(i)) - store.update(version, toRemove, toInsert).get - } - - private val version = store.rollbackVersions().head - - val et = Utils.time { - val result = store.rollbackTo(version) - require(result.isSuccess) - } - - println(s"Performance of `LDBVersionedStore.rollback (max depth)` ($numEpochs epochs): $et ms") - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala deleted file mode 100644 index 96ba461c5a..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala +++ /dev/null @@ -1,30 +0,0 @@ -package org.ergoplatform.nodeView - -import org.ergoplatform.Utils -import org.ergoplatform.modifiers.history.extension.Extension -import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} -import org.ergoplatform.modifiers.history.BlockTransactions - -trait NVBenchmark { - - val resourceUrlPrefix = "https://github.com/ergoplatform/static-data/raw/master/v2" - - def readHeaders: Seq[Header] = readModifiers[Header](s"$resourceUrlPrefix/headers.dat") - def readPayloads: Seq[BlockTransactions] = readModifiers[BlockTransactions](s"$resourceUrlPrefix/payloads.dat") - def readExtensions: Seq[Extension] = readModifiers[Extension](s"$resourceUrlPrefix/extensions.dat") - def readBlocks: Seq[ErgoFullBlock] = readHeaders.zip(readPayloads).zip(readExtensions) - .map { case ((h, txs), ext) => ErgoFullBlock(h, txs, ext, None) } - - def readModifiers[M <: BlockSection](path: String): Seq[M] = { - val is = Utils.getUrlInputStream(path) - Stream - .continually { - Utils.readModifier[M](is) - } - .takeWhile(_.isDefined) - .flatten - .toList - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala deleted file mode 100644 index 1e8802027a..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolBenchmark.scala +++ /dev/null @@ -1,63 +0,0 @@ -package org.ergoplatform.nodeView.mempool - -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import org.scalameter.KeyValue -import org.scalameter.api._ -import org.scalameter.picklers.Implicits._ - -import scala.util.{Random => Rng} - -object ErgoMemPoolBenchmark - extends Bench.ForkedTime - with ErgoTransactionGenerators { - - private val blockSizes = Gen.enumeration("txs in block")(50, 500, 1000) - private val waitingSizes = Gen.enumeration("waitings")(1, 10) - - private def waitForTransactionsInSequence(txIncomeOrder: Seq[Seq[ErgoTransaction]] => Seq[ErgoTransaction]): Gen[Seq[ErgoTransaction]] = for { - waitingSize <- waitingSizes - transactionsPerBlock <- blockSizes - } yield { - val txsByWaitingGroups = for {_ <- 0 until waitingSize} - yield { - (for {_ <- 0 until transactionsPerBlock} yield { - invalidErgoTransactionGen.sample - }).flatten - } - txIncomeOrder(txsByWaitingGroups) - } - - private val bestCaseGenerator = waitForTransactionsInSequence(_.flatten) - private val avgCaseGenerator = waitForTransactionsInSequence(txs => Rng.shuffle(txs.flatten)) - private val worstCaseGenerator = waitForTransactionsInSequence(groups => { - groups.flatMap(_.init) ++ groups.map(_.last) - }) - - private val config = Seq[KeyValue]( - exec.minWarmupRuns -> 10, - exec.maxWarmupRuns -> 30, - exec.benchRuns -> 20, - exec.requireGC -> true - ) - - private def bench(txsInIncomeOrder: Seq[ErgoTransaction]): Unit = { - var pool = ErgoMemPool.empty(settings) - txsInIncomeOrder.foreach(tx => pool = pool.put(UnconfirmedTransaction(tx, None))) - } - - performance of "ErgoMemPool awaiting" in { - performance of "best case" in { - using(bestCaseGenerator) config (config: _*) in (txsInIncomeOrder => bench(txsInIncomeOrder)) - } - - performance of "avg case" in { - using(avgCaseGenerator) config (config: _*) in (txsInIncomeOrder => bench(txsInIncomeOrder)) - } - - performance of "worst case" in { - using(worstCaseGenerator) config (config: _*) in (txsInIncomeOrder => bench(txsInIncomeOrder)) - } - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala deleted file mode 100644 index c15db166f0..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/mempool/MempoolPerformanceBench.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.ergoplatform.nodeView.mempool - -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.utils.generators.{ErgoGenerators, ErgoTransactionGenerators} -import org.scalacheck.Gen -import org.scalatest.propspec.AnyPropSpec -import scorex.testkit.properties.mempool.MempoolFilterPerformanceTest - -class MempoolPerformanceBench extends AnyPropSpec - with MempoolFilterPerformanceTest - with ErgoGenerators - with ErgoTransactionGenerators { - - override val memPool: ErgoMemPool = ErgoMemPool.empty(settings) - override val memPoolGenerator: Gen[ErgoMemPool] = emptyMemPoolGen - override val transactionGenerator: Gen[ErgoTransaction] = invalidErgoTransactionGen - override val unconfirmedTxGenerator: Gen[UnconfirmedTransaction] = - invalidErgoTransactionGen.map(tx => UnconfirmedTransaction(tx, None)) -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/TransactionExecutionBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/TransactionExecutionBenchmark.scala deleted file mode 100644 index bb5e4b7888..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/TransactionExecutionBenchmark.scala +++ /dev/null @@ -1,58 +0,0 @@ -package org.ergoplatform.nodeView.state - -import org.ergoplatform.Utils -import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.nodeView.NVBenchmark -import org.ergoplatform.settings.{ErgoValidationSettingsUpdate, Parameters} -import org.ergoplatform.settings.Parameters.MaxBlockCostIncrease -import org.ergoplatform.utils.{HistoryTestHelpers, RandomWrapper} -import scorex.core.validation.ValidationResult.Valid -import scorex.db.ByteArrayWrapper - -import scala.collection.mutable -import scala.util.Try - -object TransactionExecutionBenchmark extends HistoryTestHelpers with NVBenchmark { - - val WarmupRuns = 3 - - override val parameters = - new Parameters(height = 0, - parametersTable = Parameters.DefaultParameters + (MaxBlockCostIncrease -> Int.MaxValue), - proposedUpdate = ErgoValidationSettingsUpdate.empty - ) - - def main(args: Array[String]): Unit = { - - val startTs = System.currentTimeMillis() - - val bh = BoxHolder(genesisBoxes) - val txs = (1 to 5000).foldLeft(mutable.WrappedArray.newBuilder[ErgoTransaction]) { case (txAcc, _) => - val (transactions, _) = validTransactionsFromBoxes(10000, bh.boxes.values.toVector, new RandomWrapper) - val allBoxIds = bh.boxes.keys.toSet - val txsFromBoxesOnly = transactions.filter { tx => - tx.inputs.map(i => ByteArrayWrapper(i.boxId)).forall(allBoxIds.contains) && - tx.dataInputs.map(i => ByteArrayWrapper(i.boxId)).forall(allBoxIds.contains) - } - txAcc ++= txsFromBoxesOnly - }.result() - - val boxes = bh.boxes - def bench: Long = - Utils.time { - assert(ErgoState.execTransactions(txs, emptyStateContext)(id => Try(boxes(ByteArrayWrapper(id)))) == Valid(178665000)) - }.toLong - - (0 to WarmupRuns).foreach(_ => bench) - val et = bench - - println(s"Performance of `${txs.size} transactions execution`: $et ms") - - Utils.dumpToFile("TransactionExecutionBenchmark", startTs, Seq(BenchReport(s"${txs.size} transactions execution", et))) - - System.exit(0) - - } - -} diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala deleted file mode 100644 index 5a257b0420..0000000000 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala +++ /dev/null @@ -1,45 +0,0 @@ -package org.ergoplatform.nodeView.state - -import org.ergoplatform.Utils -import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.nodeView.NVBenchmark -import org.ergoplatform.settings.{Args, ErgoSettings} -import org.ergoplatform.utils.HistoryTestHelpers - -object UtxoStateBenchmark extends HistoryTestHelpers with NVBenchmark { - - val WarmupRuns = 2 - - def main(args: Array[String]): Unit = { - - val startTs = System.currentTimeMillis() - - val realNetworkSetting = ErgoSettings.read(Args(Some("src/main/resources/application.conf"), None)) - - val blocks = readBlocks - - val transactionsQty = blocks.flatMap(_.transactions).size - - def bench(mods: Seq[BlockSection]): Long = { - val state = ErgoState.generateGenesisUtxoState(createTempDir, realNetworkSetting)._1 - Utils.time { - mods.foldLeft(state) { case (st, mod) => - st.applyModifier(mod, None)(_ => ()).get - } - }.toLong - } - - (0 to WarmupRuns).foreach(_ => bench(blocks)) - - val et = bench(blocks) - - println(s"Performance of `$transactionsQty transactions application`: $et ms") - - Utils.dumpToFile("UtxoStateBenchmark", startTs, Seq(BenchReport(s"$transactionsQty transactions application", et))) - - System.exit(0) - - } - -} diff --git a/build.sbt b/build.sbt index d38fbc514d..dccd41e4b1 100644 --- a/build.sbt +++ b/build.sbt @@ -38,61 +38,32 @@ val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" val sigmaStateVersion = "5.0.13" +val ficusVersion = "1.4.7" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) val effectiveSigma = "org.scorexfoundation" %% "sigma-state" % effectiveSigmaStateVersion -val apiDependencies = Seq( - "io.circe" %% "circe-core" % circeVersion, - "io.circe" %% "circe-generic" % circeVersion, - "io.circe" %% "circe-parser" % circeVersion, - "de.heikoseeberger" %% "akka-http-circe" % "1.20.0" -) libraryDependencies ++= Seq( effectiveSigma.force() .exclude("ch.qos.logback", "logback-classic") .exclude("org.scorexfoundation", "scrypto"), - - // network dependencies - "com.typesafe.akka" %% "akka-actor" % akkaVersion, - "com.typesafe.akka" %% "akka-http-core" % akkaHttpVersion, - "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, - "com.typesafe.akka" %% "akka-parsing" % akkaHttpVersion, - "com.typesafe.akka" %% "akka-stream" % akkaVersion, - "org.bitlet" % "weupnp" % "0.1.4", // api dependencies "io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, "io.circe" %% "circe-parser" % circeVersion, - "de.heikoseeberger" %% "akka-http-circe" % "1.20.0", - - "org.ethereum" % "leveldbjni-all" % "1.18.3", - //the following pure-java leveldb implementation is needed only on specific platforms, such as 32-bit Raspberry Pi - //in future, it could be reasonable to have special builds with this Java db only, and for most of platforms use - //jni wrapper over native library included in leveldbjni-all - "org.iq80.leveldb" % "leveldb" % "0.12", - - "javax.xml.bind" % "jaxb-api" % "2.4.0-b180830.0359", - "com.iheart" %% "ficus" % "1.4.7", + "ch.qos.logback" % "logback-classic" % "1.3.5", - "com.google.guava" % "guava" % "21.0", - "com.github.ben-manes.caffeine" % "caffeine" % "2.9.3", // use 3.x only for java 11+ - "com.github.scopt" %% "scopt" % "4.0.1", + // test dependencies "org.scala-lang.modules" %% "scala-async" % "0.9.7" % "test", - "com.storm-enroute" %% "scalameter" % "0.8.+" % "test", "org.scalactic" %% "scalactic" % "3.0.3" % "test", "org.scalatest" %% "scalatest" % "3.2.10" % "test,it", "org.scalacheck" %% "scalacheck" % "1.14.+" % "test", "org.scalatestplus" %% "scalatestplus-scalacheck" % "3.1.0.0-RC2" % Test, - "com.typesafe.akka" %% "akka-http-core" % akkaHttpVersion, - "io.circe" %% "circe-core" % circeVersion, - "io.circe" %% "circe-core" % circeVersion % "test", - "com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test", "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpVersion % "test", @@ -130,6 +101,8 @@ val opts = Seq( javaOptions in run ++= opts scalacOptions --= Seq("-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Ywarn-unused:params", "-Xcheckinit") +val scalacOpts = Seq("-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Ywarn-unused:params", "-Xcheckinit") + sourceGenerators in Compile += Def.task { val versionFile = (sourceManaged in Compile).value / "org" / "ergoplatform" / "Version.scala" @@ -244,6 +217,14 @@ lazy val avldb = (project in file("avldb")) // see https://github.com/scala/community-builds/issues/796#issuecomment-423395500 scalacOptions in(Compile, compile) ++= Seq("-release", "8"), javacOptions in(Compile, compile) ++= javacReleaseOption, + libraryDependencies ++= Seq( + // database dependencies + "org.ethereum" % "leveldbjni-all" % "1.18.3", + //the following pure-java leveldb implementation is needed only on specific platforms, such as 32-bit Raspberry Pi + //in future, it could be reasonable to have special builds with this Java db only, and for most of platforms use + //jni wrapper over native library included in leveldbjni-all + "org.iq80.leveldb" % "leveldb" % "0.12" + ) ) lazy val avldb_benchmarks = (project in file("avldb/benchmarks")) @@ -262,6 +243,21 @@ lazy val avldb_benchmarks = (project in file("avldb/benchmarks")) .dependsOn(avldb) .enablePlugins(JmhPlugin) +lazy val ergoCore = (project in file("ergo-core")) + .dependsOn(avldb % "test->test;compile->compile") + .dependsOn(ergoWallet % "test->test;compile->compile") + .settings( + commonSettings, + name := "ergo-core", + libraryDependencies ++= Seq( + "com.iheart" %% "ficus" % ficusVersion, + effectiveSigma, + (effectiveSigma % Test).classifier("tests") + ), + scalacOptions in(Compile, compile) ++= Seq("-release", "8"), + scalacOptions in(Compile, compile) --= scalacOpts, + ) + lazy val ergoWallet = (project in file("ergo-wallet")) .disablePlugins(ScapegoatSbtPlugin) // not compatible with crossScalaVersions .settings( @@ -288,23 +284,42 @@ inConfig(It2Test)(Defaults.testSettings ++ Seq( lazy val ergo = (project in file(".")) .settings( - commonSettings, + commonSettings, name := "ergo", // set bytecode version to 8 to fix NoSuchMethodError for various ByteBuffer methods // see https://github.com/eclipse/jetty.project/issues/3244 // these options applied only in "compile" task since scalac crashes on scaladoc compilation with "-release 8" // see https://github.com/scala/community-builds/issues/796#issuecomment-423395500 scalacOptions in(Compile, compile) ++= Seq("-release", "8"), - javacOptions in(Compile, compile) ++= javacReleaseOption + javacOptions in(Compile, compile) ++= javacReleaseOption, + libraryDependencies ++= Seq( + // network dependencies + "com.typesafe.akka" %% "akka-stream" % akkaVersion, // required for akka-http to compile + "com.typesafe.akka" %% "akka-actor" % akkaVersion, // required for akka-http to compile + "com.typesafe.akka" %% "akka-http" % akkaHttpVersion, + "com.typesafe.akka" %% "akka-http-core" % akkaHttpVersion, + "com.typesafe.akka" %% "akka-parsing" % akkaHttpVersion, + "org.bitlet" % "weupnp" % "0.1.4", + // command line args parsing + "com.github.scopt" %% "scopt" % "4.0.1", + + // API dependencies + "de.heikoseeberger" %% "akka-http-circe" % "1.20.0", + + // app dependencies + // jaxb-api is included only to avoid a runtime exception + "javax.xml.bind" % "jaxb-api" % "2.4.0-b180830.0359", + + // caching, bloom filters, Longs/Ints + "com.google.guava" % "guava" % "21.0", + "com.github.ben-manes.caffeine" % "caffeine" % "2.9.3" // use 3.x only for java 11+ + ) ) + .dependsOn(ergoCore % "test->test;compile->compile") .dependsOn(ergoWallet % "test->test;compile->compile") .dependsOn(avldb % "test->test;compile->compile") .configs(It2Test) -lazy val benchmarks = (project in file("benchmarks")) - .settings(commonSettings, name := "ergo-benchmarks") - .dependsOn(ergo % "test->test") - .enablePlugins(JmhPlugin) // PGP key for signing a release build published to sonatype // signing is done by sbt-pgp plugin diff --git a/ergo-core/build.sbt b/ergo-core/build.sbt new file mode 100644 index 0000000000..b92786aef0 --- /dev/null +++ b/ergo-core/build.sbt @@ -0,0 +1,4 @@ +// this values should be in sync with root (i.e. ../build.sbt) +val scala211 = "2.11.12" +val scala212 = "2.12.10" +val scala213 = "2.13.8" \ No newline at end of file diff --git a/ergo-core/project/build.properties b/ergo-core/project/build.properties new file mode 100644 index 0000000000..7609b47819 --- /dev/null +++ b/ergo-core/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.2.8 \ No newline at end of file diff --git a/ergo-core/project/plugins.sbt b/ergo-core/project/plugins.sbt new file mode 100644 index 0000000000..bcd4c6d604 --- /dev/null +++ b/ergo-core/project/plugins.sbt @@ -0,0 +1,3 @@ +addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0") +addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8") diff --git a/ergo-core/src/main/scala/org/ergoplatform/CriticalSystemException.scala b/ergo-core/src/main/scala/org/ergoplatform/CriticalSystemException.scala new file mode 100644 index 0000000000..776e3f8b3a --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/CriticalSystemException.scala @@ -0,0 +1,4 @@ +package org.ergoplatform + +/** Exception that triggers system shutdown */ +case class CriticalSystemException(message: String) extends Exception(message) diff --git a/src/main/scala/scorex/core/NodeViewComponent.scala b/ergo-core/src/main/scala/org/ergoplatform/NodeViewComponent.scala similarity index 50% rename from src/main/scala/scorex/core/NodeViewComponent.scala rename to ergo-core/src/main/scala/org/ergoplatform/NodeViewComponent.scala index 30573a7d29..23c39dc154 100644 --- a/src/main/scala/scorex/core/NodeViewComponent.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/NodeViewComponent.scala @@ -1,3 +1,3 @@ -package scorex.core +package org.ergoplatform trait NodeViewComponent diff --git a/src/main/scala/scorex/core/NodeViewModifier.scala b/ergo-core/src/main/scala/org/ergoplatform/NodeViewModifier.scala similarity index 93% rename from src/main/scala/scorex/core/NodeViewModifier.scala rename to ergo-core/src/main/scala/org/ergoplatform/NodeViewModifier.scala index c11288a0f6..cecf7f01f8 100644 --- a/src/main/scala/scorex/core/NodeViewModifier.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/NodeViewModifier.scala @@ -1,9 +1,9 @@ -package scorex.core +package org.ergoplatform +import org.ergoplatform.core.BytesSerializable import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.modifiers.mempool.ErgoTransaction -import scorex.core.serialization.BytesSerializable -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.utils.ScorexEncoding sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {self => diff --git a/src/main/scala/scorex/core/consensus/ContainsModifiers.scala b/ergo-core/src/main/scala/org/ergoplatform/consensus/ContainsModifiers.scala similarity index 95% rename from src/main/scala/scorex/core/consensus/ContainsModifiers.scala rename to ergo-core/src/main/scala/org/ergoplatform/consensus/ContainsModifiers.scala index 55a2af0107..84edb9eb46 100644 --- a/src/main/scala/scorex/core/consensus/ContainsModifiers.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/consensus/ContainsModifiers.scala @@ -1,4 +1,4 @@ -package scorex.core.consensus +package org.ergoplatform.consensus import org.ergoplatform.modifiers.ErgoNodeViewModifier import scorex.util.ModifierId diff --git a/src/main/scala/scorex/core/consensus/ModifierSemanticValidity.scala b/ergo-core/src/main/scala/org/ergoplatform/consensus/ModifierSemanticValidity.scala similarity index 93% rename from src/main/scala/scorex/core/consensus/ModifierSemanticValidity.scala rename to ergo-core/src/main/scala/org/ergoplatform/consensus/ModifierSemanticValidity.scala index a88acfbae1..04ea984ba8 100644 --- a/src/main/scala/scorex/core/consensus/ModifierSemanticValidity.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/consensus/ModifierSemanticValidity.scala @@ -1,4 +1,4 @@ -package scorex.core.consensus +package org.ergoplatform.consensus /** * Outcome of modifier semantic validation diff --git a/src/main/scala/scorex/core/consensus/PeerChainStatus.scala b/ergo-core/src/main/scala/org/ergoplatform/consensus/PeerChainStatus.scala similarity index 95% rename from src/main/scala/scorex/core/consensus/PeerChainStatus.scala rename to ergo-core/src/main/scala/org/ergoplatform/consensus/PeerChainStatus.scala index 5cc2da6361..70a2e87a70 100644 --- a/src/main/scala/scorex/core/consensus/PeerChainStatus.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/consensus/PeerChainStatus.scala @@ -1,5 +1,4 @@ -package scorex.core.consensus - +package org.ergoplatform.consensus /** * Status of a peer's chain relatively to our diff --git a/src/main/scala/scorex/core/consensus/ProgressInfo.scala b/ergo-core/src/main/scala/org/ergoplatform/consensus/ProgressInfo.scala similarity index 90% rename from src/main/scala/scorex/core/consensus/ProgressInfo.scala rename to ergo-core/src/main/scala/org/ergoplatform/consensus/ProgressInfo.scala index c292b3760f..1e0fb3249d 100644 --- a/src/main/scala/scorex/core/consensus/ProgressInfo.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/consensus/ProgressInfo.scala @@ -1,8 +1,8 @@ -package scorex.core.consensus +package org.ergoplatform.consensus +import org.ergoplatform.PersistentNodeViewModifier import org.ergoplatform.modifiers.NetworkObjectTypeId -import scorex.core.utils.ScorexEncoder -import scorex.core.PersistentNodeViewModifier +import org.ergoplatform.utils.ScorexEncoder import scorex.util.ModifierId /** diff --git a/src/main/scala/scorex/core/consensus/SyncInfo.scala b/ergo-core/src/main/scala/org/ergoplatform/consensus/SyncInfo.scala similarity index 68% rename from src/main/scala/scorex/core/consensus/SyncInfo.scala rename to ergo-core/src/main/scala/org/ergoplatform/consensus/SyncInfo.scala index 128b643fd9..9503673199 100644 --- a/src/main/scala/scorex/core/consensus/SyncInfo.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/consensus/SyncInfo.scala @@ -1,6 +1,6 @@ -package scorex.core.consensus +package org.ergoplatform.consensus -import scorex.core.serialization.BytesSerializable +import org.ergoplatform.core.BytesSerializable /** * Syncing info provides information about starting points this node recommends another to start diff --git a/src/main/scala/scorex/core/serialization/BytesSerializable.scala b/ergo-core/src/main/scala/org/ergoplatform/core/BytesSerializable.scala similarity index 79% rename from src/main/scala/scorex/core/serialization/BytesSerializable.scala rename to ergo-core/src/main/scala/org/ergoplatform/core/BytesSerializable.scala index 27f8f6a0d9..e635675165 100644 --- a/src/main/scala/scorex/core/serialization/BytesSerializable.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/core/BytesSerializable.scala @@ -1,4 +1,6 @@ -package scorex.core.serialization +package org.ergoplatform.core + +import org.ergoplatform.serialization.ErgoSerializer /** * Basic interface for objects which can be represented as bytes diff --git a/src/main/scala/scorex/core/core.scala b/ergo-core/src/main/scala/org/ergoplatform/core/core.scala similarity index 90% rename from src/main/scala/scorex/core/core.scala rename to ergo-core/src/main/scala/org/ergoplatform/core/core.scala index 1bc9628cfd..59d6d0ffdd 100644 --- a/src/main/scala/scorex/core/core.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/core/core.scala @@ -1,8 +1,9 @@ -package scorex +package org.ergoplatform import org.ergoplatform.modifiers.NetworkObjectTypeId -import scorex.core.network.message.InvData -import scorex.core.utils.ScorexEncoder +import org.ergoplatform.network.message.InvData +import org.ergoplatform.utils.ScorexEncoder +import scorex.util import scorex.util.encode.Base16 import supertagged.TaggedType diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/ergo-core/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala similarity index 66% rename from src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala rename to ergo-core/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 424c279c99..625531218f 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -6,21 +6,16 @@ import org.bouncycastle.util.BigIntegers import org.ergoplatform.ErgoBox.RegisterId import org.ergoplatform._ import org.ergoplatform.http.api.ApiEncoderOption.Detalization -import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest, HintExtractionRequest} import org.ergoplatform.mining.{groupElemFromBytes, groupElemToBytes} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty -import org.ergoplatform.nodeView.history.extra.ExtraIndexer.getAddress -import org.ergoplatform.nodeView.history.extra.{BalanceInfo, IndexedErgoBox, IndexedErgoTransaction, IndexedToken} -import org.ergoplatform.nodeView.wallet.persistence.WalletDigest -import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, GenerateCommitmentsRequest, TransactionSigningRequest} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Difficulty import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.settings.{Algos, ErgoAlgos} import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import scorex.core.validation.ValidationResult +import org.ergoplatform.validation.ValidationResult import scorex.crypto.authds.merkle.MerkleProof import scorex.crypto.authds.{LeafData, Side} import scorex.crypto.hash.Digest @@ -33,9 +28,6 @@ import sigmastate.crypto.VerifierMessage.Challenge import sigmastate.crypto._ import sigmastate.interpreter._ import sigmastate.serialization.OpCodes -import sigma.AnyValue -import org.ergoplatform.nodeView.state.SnapshotsInfo -import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.sdk.JsonCodecs import sigmastate.eval.Extensions.ArrayOps @@ -88,15 +80,6 @@ trait ApiCodecs extends JsonCodecs { implicit val proveDlogEncoder: Encoder[ProveDlog] = _.pkBytes.asJson - implicit val balancesSnapshotEncoder: Encoder[WalletDigest] = { v => - import v._ - Json.obj( - "height" -> height.asJson, - "balance" -> walletBalance.asJson, - "assets" -> walletAssetBalances.toMap.map(x => (x._1: String, x._2)).asJson //toMap to have assets as JSON map - ) - } - // this val is named "anyRegisterIdEncoder" because parent trait already contains // "registerIdEncoder" which is actually a KeyEncoder for NonMandatoryRegisterId // todo: rename "registerIdEncoder" into "nonMandatoryRegisterId" in parent trait in sigma repo @@ -246,25 +229,7 @@ trait ApiCodecs extends JsonCodecs { } } - implicit val hintExtractionRequestEncoder: Encoder[HintExtractionRequest] = {hr => - Map( - "tx" -> hr.tx.asJson, - "real" -> hr.real.asJson, - "simulated" -> hr.simulated.asJson, - "inputsRaw" -> hr.inputs.asJson, - "dataInputsRaw" -> hr.dataInputs.asJson - ).asJson - } - implicit val hintExtractionRequestDecoder: Decoder[HintExtractionRequest] = {cursor => - for { - tx <- cursor.downField("tx").as[ErgoTransaction] - real <- cursor.downField("real").as[Seq[SigmaBoolean]] - simulated <- cursor.downField("simulated").as[Seq[SigmaBoolean]] - inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] - dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] - } yield HintExtractionRequest(tx, real, simulated, inputs, dataInputs) - } implicit val firstProverMessageEncoder: Encoder[FirstProverMessage] = { case cmtDlog: FirstDLogProverMessage => @@ -424,160 +389,6 @@ trait ApiCodecs extends JsonCodecs { publicHints <- Decoder.decodeMap[Int, Seq[Hint]].tryDecode(cursor.downField("publicHints")) } yield TransactionHintsBag(secretHints.mapValues(HintsBag.apply), publicHints.mapValues(HintsBag.apply)) } - - implicit val SnapshotInfoEncoder: Encoder[SnapshotsInfo] = { si => - Json.obj( - "availableManifests" -> si.availableManifests.map { case (height, manifest) => - height -> manifest - }.asJson - ) - } - - implicit val SnapshotInfoDecoder: Decoder[SnapshotsInfo] = { cursor => - for { - availableManifests <- Decoder.decodeMap[Int, ManifestId].tryDecode(cursor.downField("availableManifests")) - } yield new SnapshotsInfo(availableManifests) - } - - implicit val transactionSigningRequestEncoder: Encoder[TransactionSigningRequest] = { tsr => - Json.obj( - "tx" -> tsr.unsignedTx.asJson, - "secrets" -> Json.obj( - "dlog" -> tsr.dlogs.asJson, - "dht" -> tsr.dhts.asJson - ), - "hints" -> tsr.hints.asJson, - "inputsRaw" -> tsr.inputs.asJson, - "dataInputsRaw" -> tsr.dataInputs.asJson - ) - } - - implicit val transactionSigningRequestDecoder: Decoder[TransactionSigningRequest] = { cursor => - for { - tx <- cursor.downField("tx").as[UnsignedErgoTransaction] - dlogs <- cursor.downField("secrets").downField("dlog").as[Option[Seq[DlogSecretKey]]] - dhts <- cursor.downField("secrets").downField("dht").as[Option[Seq[DhtSecretKey]]] - hints <- cursor.downField("hints").as[Option[TransactionHintsBag]] - inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] - dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] - secrets = (dlogs.getOrElse(Seq.empty) ++ dhts.getOrElse(Seq.empty)).map(ExternalSecret.apply) - } yield TransactionSigningRequest(tx, hints.getOrElse(TransactionHintsBag.empty), secrets, inputs, dataInputs) - } - - implicit val generateCommitmentsRequestEncoder: Encoder[GenerateCommitmentsRequest] = { gcr => - Json.obj( - "tx" -> gcr.unsignedTx.asJson, - "secrets" -> Json.obj( - "dlog" -> gcr.dlogs.asJson, - "dht" -> gcr.dhts.asJson, - "inputsRaw" -> gcr.inputs.asJson, - "dataInputsRaw" -> gcr.dataInputs.asJson - ) - ) - } - - implicit val generateCommitmentsRequestDecoder: Decoder[GenerateCommitmentsRequest] = { cursor => - for { - tx <- cursor.downField("tx").as[UnsignedErgoTransaction] - dlogs <- cursor.downField("secrets").downField("dlog").as[Option[Seq[DlogSecretKey]]] - dhts <- cursor.downField("secrets").downField("dht").as[Option[Seq[DhtSecretKey]]] - secrets = (dlogs.getOrElse(Seq.empty) ++ dhts.getOrElse(Seq.empty)).map(ExternalSecret.apply) - secretsOpt = if(secrets.isEmpty) None else Some(secrets) - inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] - dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] - } yield GenerateCommitmentsRequest(tx, secretsOpt, inputs, dataInputs) - } - - implicit val executeRequestDecoder = new Decoder[ExecuteRequest] { - def apply(cursor: HCursor): Decoder.Result[ExecuteRequest] = { - for { - script <- cursor.downField("script").as[String] - env <- cursor.downField("namedConstants").as[Map[String,AnyValue]] - ctx <- cursor.downField("context").as[ErgoLikeContext] - } yield ExecuteRequest(script, env.map({ case (k,v) => k -> v.value }), ctx) - } - } - - implicit val cryptResultEncoder: Encoder[CryptoResult] = { - res => - val fields = Map( - "value" -> res.value.asJson, - "cost" -> res.cost.asJson - ) - fields.asJson - } - - implicit val indexedBoxEncoder: Encoder[IndexedErgoBox] = { iEb => - iEb.box.asJson.deepMerge(Json.obj( - "globalIndex" -> iEb.globalIndex.asJson, - "inclusionHeight" -> iEb.inclusionHeight.asJson, - "address" -> ergoAddressEncoder.toString(getAddress(iEb.box.ergoTree)).asJson, - "spentTransactionId" -> iEb.spendingTxIdOpt.asJson - )) - } - - implicit val indexedBoxSeqEncoder: Encoder[(Seq[IndexedErgoBox],Long)] = { iEbSeq => - Json.obj( - "items" -> iEbSeq._1.asJson, - "total" -> iEbSeq._2.asJson - ) - } - - implicit val indexedTxEncoder: Encoder[IndexedErgoTransaction] = { iEt => - Json.obj( - "id" -> iEt.txid.asJson, - "blockId" -> iEt.blockId.asJson, - "inclusionHeight" -> iEt.inclusionHeight.asJson, - "timestamp" -> iEt.timestamp.asJson, - "index" -> iEt.index.asJson, - "globalIndex" -> iEt.globalIndex.asJson, - "numConfirmations" -> iEt.numConfirmations.asJson, - "inputs" -> iEt.inputs.asJson, - "dataInputs" -> iEt.dataInputs.asJson, - "outputs" -> iEt.outputs.asJson, - "size" -> iEt.txSize.asJson - ) - } - - implicit val indexedTxSeqEncoder: Encoder[(Seq[IndexedErgoTransaction],Long)] = { iEtSeq => - Json.obj( - "items" -> iEtSeq._1.asJson, - "total" -> iEtSeq._2.asJson - ) - } - - implicit val IndexedTokenEncoder: Encoder[IndexedToken] = { token => - Json.obj( - "id" -> token.tokenId.asJson, - "boxId" -> token.boxId.asJson, - "emissionAmount" -> token.amount.asJson, - "name" -> token.name.asJson, - "description" -> token.description.asJson, - "decimals" -> token.decimals.asJson - ) - } - - implicit val BalanceInfoEncoder: Encoder[BalanceInfo] = { bal => - Json.obj( - "nanoErgs" -> bal.nanoErgs.asJson, - "tokens" -> bal.tokens.map(token => { - Json.obj( - "tokenId" -> token._1.asJson, - "amount" -> token._2.asJson, - "decimals" -> bal.additionalTokenInfo(token._1)._2.asJson, - "name" -> bal.additionalTokenInfo(token._1)._1.asJson - ) - }).asJson - ) - } - - implicit val TotalBalanceInfoEncoder: Encoder[(BalanceInfo,BalanceInfo)] = { tBal => - Json.obj( - "confirmed" -> tBal._1.asJson, - "unconfirmed" -> tBal._2.asJson - ) - } - } trait ApiEncoderOption diff --git a/src/main/scala/org/ergoplatform/local/NipopowVerificationResult.scala b/ergo-core/src/main/scala/org/ergoplatform/local/NipopowVerificationResult.scala similarity index 100% rename from src/main/scala/org/ergoplatform/local/NipopowVerificationResult.scala rename to ergo-core/src/main/scala/org/ergoplatform/local/NipopowVerificationResult.scala diff --git a/src/main/scala/org/ergoplatform/local/NipopowVerifier.scala b/ergo-core/src/main/scala/org/ergoplatform/local/NipopowVerifier.scala similarity index 100% rename from src/main/scala/org/ergoplatform/local/NipopowVerifier.scala rename to ergo-core/src/main/scala/org/ergoplatform/local/NipopowVerifier.scala diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala similarity index 98% rename from src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala index ad5af554a6..5dd710da26 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala @@ -10,7 +10,7 @@ import org.ergoplatform.modifiers.history.extension.ExtensionCandidate import org.ergoplatform.modifiers.history.header.Header.{Timestamp, Version} import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer, HeaderWithoutPow} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.GenesisHeight import org.ergoplatform.nodeView.mempool.TransactionMembershipProof import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.{Blake2b256, Digest32} @@ -45,7 +45,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { /** * Number of elements in a table to find k-sum problem solution on top of */ - val NBase: Int = Math.pow(2, n).toInt + val NBase: Int = Math.pow(2, n.toDouble).toInt /** * Initial height since which table (`N` value) starting to increase by 5% per `IncreasePeriodForN` blocks @@ -91,7 +91,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { /** * Constant data to be added to hash function to increase its calculation time */ - val M: Array[Byte] = (0 until 1024).toArray.flatMap(i => Longs.toByteArray(i)) + val M: Array[Byte] = (0 until 1024).toArray.flatMap(i => Longs.toByteArray(i.toLong)) /** * Checks that `header` contains correct solution of the Autolykos PoW puzzle. @@ -387,7 +387,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { def deriveExternalCandidate(blockCandidate: CandidateBlock, pk: ProveDlog, mandatoryTxIds: Seq[ModifierId]): WorkMessage = { - val headerCandidate = CandidateGenerator.deriveUnprovenHeader(blockCandidate) + val headerCandidate = CandidateUtils.deriveUnprovenHeader(blockCandidate) val msg = msgByHeader(headerCandidate) val b = getB(blockCandidate.nBits) val hOpt = if (blockCandidate.version == 1) { @@ -417,7 +417,7 @@ object AutolykosPowScheme { */ def derivedHeaderFields(parentOpt: Option[Header]): (ModifierId, Int) = { - val height = parentOpt.map(parent => parent.height + 1).getOrElse(ErgoHistory.GenesisHeight) + val height = parentOpt.map(parent => parent.height + 1).getOrElse(GenesisHeight) val parentId: ModifierId = parentOpt.map(_.id).getOrElse(Header.GenesisParentId) diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala similarity index 98% rename from src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala index adeff25edc..b448135411 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala @@ -6,7 +6,7 @@ import org.bouncycastle.util.BigIntegers import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.header.Header.Version import org.ergoplatform.settings.Algos -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.crypto.CryptoConstants import sigmastate.crypto.CryptoConstants.EcPointType diff --git a/src/main/scala/org/ergoplatform/mining/CandidateBlock.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/CandidateBlock.scala similarity index 100% rename from src/main/scala/org/ergoplatform/mining/CandidateBlock.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/CandidateBlock.scala diff --git a/ergo-core/src/main/scala/org/ergoplatform/mining/CandidateUtils.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/CandidateUtils.scala new file mode 100644 index 0000000000..0ad1d87624 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/CandidateUtils.scala @@ -0,0 +1,35 @@ +package org.ergoplatform.mining + +import org.ergoplatform.mining.AutolykosPowScheme.derivedHeaderFields +import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} +import org.ergoplatform.modifiers.history.header.HeaderWithoutPow +import scorex.crypto.hash.Digest32 + +/** + * Functions related to block candidate generation + */ +object CandidateUtils { + /** + * Derives header without pow from [[CandidateBlock]]. + */ + def deriveUnprovenHeader(candidate: CandidateBlock): HeaderWithoutPow = { + val (parentId, height) = derivedHeaderFields(candidate.parentOpt) + val transactionsRoot = + BlockTransactions.transactionsRoot(candidate.transactions, candidate.version) + val adProofsRoot = ADProofs.proofDigest(candidate.adProofBytes) + val extensionRoot: Digest32 = candidate.extension.digest + + HeaderWithoutPow( + candidate.version, + parentId, + adProofsRoot, + candidate.stateRoot, + transactionsRoot, + candidate.timestamp, + candidate.nBits, + height, + extensionRoot, + candidate.votes + ) + } +} diff --git a/src/main/scala/org/ergoplatform/mining/NumericHash.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/NumericHash.scala similarity index 100% rename from src/main/scala/org/ergoplatform/mining/NumericHash.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/NumericHash.scala diff --git a/src/main/scala/org/ergoplatform/mining/ProofOfUpcomingTransactions.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/ProofOfUpcomingTransactions.scala similarity index 100% rename from src/main/scala/org/ergoplatform/mining/ProofOfUpcomingTransactions.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/ProofOfUpcomingTransactions.scala diff --git a/src/main/scala/org/ergoplatform/mining/WorkMessage.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/WorkMessage.scala similarity index 95% rename from src/main/scala/org/ergoplatform/mining/WorkMessage.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/WorkMessage.scala index 3c815d44fc..4ca8f4641d 100644 --- a/src/main/scala/org/ergoplatform/mining/WorkMessage.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/WorkMessage.scala @@ -3,7 +3,7 @@ package org.ergoplatform.mining import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height import sigmastate.crypto.DLogProtocol.ProveDlog diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala similarity index 98% rename from src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala index 1e2ed12deb..e61e45c952 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala @@ -1,7 +1,7 @@ package org.ergoplatform.mining.difficulty import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory.{Difficulty, Height} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.{Difficulty, Height} import org.ergoplatform.settings.ChainSettings import scorex.util.ScorexLogging import scala.concurrent.duration.FiniteDuration diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala similarity index 97% rename from src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala index 6d3a2407f7..d719f9a95c 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/difficulty/DifficultySerializer.scala @@ -2,8 +2,8 @@ package org.ergoplatform.mining.difficulty import java.math.BigInteger -import org.ergoplatform.nodeView.history.ErgoHistory._ -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} /** diff --git a/src/main/scala/org/ergoplatform/mining/mining.scala b/ergo-core/src/main/scala/org/ergoplatform/mining/mining.scala similarity index 98% rename from src/main/scala/org/ergoplatform/mining/mining.scala rename to ergo-core/src/main/scala/org/ergoplatform/mining/mining.scala index 526d2eeba2..e469225906 100644 --- a/src/main/scala/org/ergoplatform/mining/mining.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/mining/mining.scala @@ -11,7 +11,7 @@ package object mining { type PrivateKey = BigInt - val PublicKeyLength: Byte = 33 + val PublicKeyLength: Int = 33 val group: BcDlogGroup = CryptoConstants.dlogGroup diff --git a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala similarity index 94% rename from src/main/scala/org/ergoplatform/modifiers/BlockSection.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala index fea7be63e3..8ab1914591 100644 --- a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala @@ -1,10 +1,10 @@ package org.ergoplatform.modifiers import io.circe.Encoder +import org.ergoplatform.PersistentNodeViewModifier import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} -import scorex.core.PersistentNodeViewModifier /** diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala similarity index 95% rename from src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 42fd2c286e..23ca8e6c91 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -2,13 +2,13 @@ package org.ergoplatform.modifiers import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor, Json} +import org.ergoplatform.TransactionsCarryingPersistentNodeViewModifier import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import scorex.core.serialization.ErgoSerializer -import scorex.core.TransactionsCarryingPersistentNodeViewModifier +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.ModifierId case class ErgoFullBlock(header: Header, diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala similarity index 85% rename from src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala index 635021bac1..34fff14e05 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/ErgoNodeViewModifier.scala @@ -1,6 +1,6 @@ package org.ergoplatform.modifiers -import scorex.core.serialization.BytesSerializable +import org.ergoplatform.core.BytesSerializable import scorex.util.{ModifierId, bytesToId} trait ErgoNodeViewModifier { self: BytesSerializable => diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala similarity index 97% rename from src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala index 14a4f1de15..f6404d0101 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala @@ -7,7 +7,7 @@ import org.ergoplatform.modifiers.{NetworkObjectTypeId, NonHeaderBlockSection, P import org.ergoplatform.modifiers.state._ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, Constants} -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.avltree.batch.{Lookup => _, _} import scorex.crypto.authds.{ADDigest, ADValue, SerializedAdProof} import scorex.crypto.hash.Digest32 @@ -97,7 +97,7 @@ object ADProofsSerializer extends ErgoSerializer[ADProofs] { override def serialize(obj: ADProofs, w: Writer): Unit = { w.putBytes(idToBytes(obj.headerId)) - w.putUInt(obj.proofBytes.size) + w.putUInt(obj.proofBytes.size.toLong) w.putBytes(obj.proofBytes) } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala similarity index 89% rename from src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala index 4717faf501..7e52ede09a 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala @@ -2,15 +2,15 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} +import org.ergoplatform.TransactionsCarryingPersistentNodeViewModifier import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.{BlockTransactionsTypeId, NetworkObjectTypeId, NonHeaderBlockSection} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.nodeView.mempool.TransactionMembershipProof import org.ergoplatform.settings.{Algos, Constants} -import scorex.core._ import org.ergoplatform.modifiers.history.header.Header.Version -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.{Leaf, MerkleProof, MerkleTree} import scorex.crypto.hash.Digest32 @@ -137,9 +137,9 @@ object BlockTransactionsSerializer extends ErgoSerializer[BlockTransactions] { override def serialize(bt: BlockTransactions, w: Writer): Unit = { w.putBytes(idToBytes(bt.headerId)) if (bt.blockVersion > 1) { - w.putUInt(MaxTransactionsInBlock + bt.blockVersion) + w.putUInt(MaxTransactionsInBlock.toLong + bt.blockVersion) } - w.putUInt(bt.txs.size) + w.putUInt(bt.txs.size.toLong) bt.txs.foreach { tx => ErgoTransactionSerializer.serialize(tx, w) } @@ -150,15 +150,15 @@ object BlockTransactionsSerializer extends ErgoSerializer[BlockTransactions] { val headerId: ModifierId = bytesToId(r.getBytes(Constants.ModifierIdSize)) val verOrCount = r.getUInt().toIntExact - /** - * A hack to avoid need for a database rescan if older version of the serializer was used to put. - * block transactions into. - * - * We consider that in a block there could be no more than 10,000,000 transactions. - * - * Then the new serializer puts 10,000,000 + block version (while the old one just puts tx count with no version), - * and the reader knows that a new serializer was used if the first unsigned integer read is more than 10,000,000. - */ + /* + * A hack to avoid need for a database rescan if older version of the serializer was used to put. + * block transactions into. + * + * We consider that in a block there could be no more than 10,000,000 transactions. + * + * Then the new serializer puts 10,000,000 + block version (while the old one just puts tx count with no version), + * and the reader knows that a new serializer was used if the first unsigned integer read is more than 10,000,000. + */ var blockVersion = 1: Byte var txCount = verOrCount if (verOrCount > MaxTransactionsInBlock) { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/HeaderChain.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/HeaderChain.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/history/HeaderChain.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/HeaderChain.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala similarity index 96% rename from src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala index 2d4e8ac6fd..5f6a72ff2a 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSerializer} import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} object HistoryModifierSerializer extends ErgoSerializer[BlockSection] { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala similarity index 95% rename from src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala index da183f33eb..7710383183 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.header.Header._ -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.Constants import scorex.util._ import sigmastate.crypto.CryptoConstants.EcPointType @@ -72,7 +72,7 @@ object PreHeader { parentId = Header.GenesisParentId, timestamp = 0, nBits = Constants.InitialNBits, - height = ErgoHistory.EmptyHistoryHeight, + height = EmptyHistoryHeight, votes = Array.fill(3)(0.toByte), minerPk = org.ergoplatform.mining.group.generator ) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala similarity index 98% rename from src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index c402d88342..c5dede1165 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala @@ -6,7 +6,7 @@ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.{ExtensionTypeId, NetworkObjectTypeId, NonHeaderBlockSection} import org.ergoplatform.settings.Algos -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.MerkleTree import scorex.crypto.hash.Digest32 diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionCandidate.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionCandidate.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionCandidate.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionCandidate.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala similarity index 95% rename from src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala index 7207b9c056..137e1813dd 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala @@ -1,7 +1,7 @@ package org.ergoplatform.modifiers.history.extension import org.ergoplatform.settings.Constants -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.{bytesToId, idToBytes} diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala similarity index 97% rename from src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index df31f80686..24d5887ecd 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -8,11 +8,10 @@ import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions, PreHeader} import org.ergoplatform.modifiers.{BlockSection, HeaderTypeId, NetworkObjectTypeId, NonHeaderBlockSection} -import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util._ @@ -90,7 +89,7 @@ case class Header(override val version: Header.Version, override lazy val serializer: ErgoSerializer[Header] = HeaderSerializer - lazy val isGenesis: Boolean = height == ErgoHistory.GenesisHeight + lazy val isGenesis: Boolean = height == GenesisHeight /** * Checks that modifier m corresponds to this header diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala similarity index 94% rename from src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala index 0e09bd7714..b6123e1141 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala @@ -1,9 +1,9 @@ package org.ergoplatform.modifiers.history.header +import org.ergoplatform.core.idToBytes import org.ergoplatform.mining.AutolykosSolutionSerializer import org.ergoplatform.mining.difficulty.DifficultySerializer -import scorex.core.idToBytes -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, VLQByteBufferWriter, Writer} @@ -26,13 +26,13 @@ object HeaderSerializer extends ErgoSerializer[Header] { w.putULong(h.timestamp) w.putBytes(h.extensionRoot) DifficultySerializer.serialize(h.nBits, w) - w.putUInt(h.height) + w.putUInt(h.height.toLong) w.putBytes(h.votes) // For block version >= 2, this new byte encodes length of possible new fields. // Set to 0 for now, so no new fields. if (h.version > Header.InitialVersion) { - w.putUByte(0: Byte) + w.putUByte(0) } } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderWithoutPow.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderWithoutPow.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/history/header/HeaderWithoutPow.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderWithoutPow.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala similarity index 77% rename from src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala index 6fbf74dc3e..7cebffbc5e 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/header/PreGenesisHeader.scala @@ -1,7 +1,7 @@ package org.ergoplatform.modifiers.history.header -import org.ergoplatform.nodeView.history.ErgoHistory -import scorex.core.idToBytes +import org.ergoplatform.core.idToBytes +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.EmptyHistoryHeight /** * A fake header that is used to fill the chain that starts from the beginning @@ -14,7 +14,7 @@ object PreGenesisHeader extends Header( transactionsRoot = null, timestamp = 0L, nBits = 0L, - height = ErgoHistory.EmptyHistoryHeight, + height = EmptyHistoryHeight, extensionRoot = null, powSolution = null, votes = null, diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala similarity index 66% rename from src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala index 16ac25d95c..a441cfe9ff 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala @@ -1,17 +1,15 @@ package org.ergoplatform.modifiers.history.popow import org.ergoplatform.mining.AutolykosPowScheme -import org.ergoplatform.mining.difficulty.{DifficultyAdjustment, DifficultySerializer} +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import org.ergoplatform.modifiers.history.extension.Extension.InterlinksVectorPrefix import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.settings.{Algos, ChainSettings, Constants} import scorex.crypto.authds.merkle.BatchMerkleProof import scorex.crypto.hash.Digest32 import scorex.util.{ModifierId, bytesToId, idToBytes} -import scala.collection.mutable import scala.util.{Failure, Success, Try} /** @@ -31,9 +29,6 @@ class NipopowAlgos(val chainSettings: ChainSettings) { import NipopowAlgos._ private def powScheme: AutolykosPowScheme = chainSettings.powScheme - - private val diffAdjustment = new DifficultyAdjustment(chainSettings) - /** * Computes interlinks vector for a header next to `prevHeader`. */ @@ -111,7 +106,7 @@ class NipopowAlgos(val chainSettings: ChainSettings) { } loop(level = 0).map { case (lvl, size) => - math.pow(2, lvl) * size // 2^µ * |C↑µ| + math.pow(2, lvl.toDouble) * size // 2^µ * |C↑µ| }.max.toInt } @@ -163,106 +158,6 @@ class NipopowAlgos(val chainSettings: ChainSettings) { NipopowProof(this, m, k, prefix, suffixHead, suffixTail, params.continuous) } - /** - * Computes NiPoPow proof for the chain stored in `histReader`'s database, - * or a prefix of the chain which contains a specific header (if `headerIdOpt` is specified). - * In the latter case, header will be the first header of the suffix of max length `k` - * (`suffixHead` field of the result). - */ - def prove(histReader: ErgoHistoryReader, - headerIdOpt: Option[ModifierId] = None)(params: PoPowParams): Try[NipopowProof] = Try { - type Height = Int - - val k = params.k - val m = params.m - - require(params.k >= 1, s"$k < 1") - require(histReader.headersHeight >= k + m, s"Can not prove chain of size < ${k + m}") - - def linksWithIndexes(header: PoPowHeader): Seq[(ModifierId, Int)] = header.interlinks.tail.reverse.zipWithIndex - - def previousHeaderIdAtLevel(level: Int, currentHeader: PoPowHeader): Option[ModifierId] = { - linksWithIndexes(currentHeader).find(_._2 == level).map(_._1) - } - - @scala.annotation.tailrec - def collectLevel(prevHeaderId: ModifierId, - level: Int, - anchoringHeight: Height, - acc: Seq[PoPowHeader] = Seq.empty): Seq[PoPowHeader] = { - val prevHeader = histReader.popowHeader(prevHeaderId).get // to be caught in outer (prove's) Try - if (prevHeader.height < anchoringHeight) { - acc - } else { - val newAcc = prevHeader +: acc - previousHeaderIdAtLevel(level, prevHeader) match { - case Some(newPrevHeaderId) => collectLevel(newPrevHeaderId, level, anchoringHeight, newAcc) - case None => newAcc - } - } - } - - def provePrefix(initAnchoringHeight: Height, - lastHeader: PoPowHeader): Seq[PoPowHeader] = { - - val collected = mutable.TreeMap[ModifierId, PoPowHeader]() - - val levels = linksWithIndexes(lastHeader) - levels.foldRight(initAnchoringHeight) { case ((prevHeaderId, levelIdx), anchoringHeight) => - val levelHeaders = collectLevel(prevHeaderId, levelIdx, anchoringHeight) - levelHeaders.foreach(ph => collected.update(ph.id, ph)) - if (m < levelHeaders.length) { - levelHeaders(levelHeaders.length - m).height - } else { - anchoringHeight - } - } - collected.values.toSeq - } - - val (suffixHead, suffixTail) = headerIdOpt match { - case Some(headerId) => - val suffixHead = histReader.popowHeader(headerId).get // to be caught in outer (prove's) Try - val suffixTail = histReader.bestHeadersAfter(suffixHead.header, k - 1) - suffixHead -> suffixTail - case None => - val suffix = histReader.lastHeaders(k).headers - histReader.popowHeader(suffix.head.id).get -> suffix.tail // .get to be caught in outer (prove's) Try - } - - val storedHeights = mutable.Set[Height]() // cache to filter out duplicate headers - val prefixBuilder = mutable.ArrayBuilder.make[PoPowHeader]() - - val genesisHeight = 1 - prefixBuilder += histReader.popowHeader(genesisHeight).get // to be caught in outer (prove's) Try - storedHeights += genesisHeight - - if (params.continuous) { - // put headers needed to check difficulty of new blocks after suffix into prefix - val epochLength = chainSettings.eip37EpochLength.getOrElse(chainSettings.epochLength) - diffAdjustment.heightsForNextRecalculation(suffixHead.height, epochLength).foreach { height => - // check that header in or after suffix is not included, otherwise, sorting by height would be broken - if (height < suffixHead.height) { - histReader.popowHeader(height).foreach { ph => - prefixBuilder += ph - storedHeights += ph.height - } - } - } - } - - provePrefix(genesisHeight, suffixHead).foreach { ph => - if (!storedHeights.contains(ph.height)) { - prefixBuilder += ph - storedHeights += ph.height - } - } - - val prefix = prefixBuilder.result().sortBy(_.height) - - NipopowProof(this, m, k, prefix, suffixHead, suffixTail, params.continuous) - } - } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala similarity index 96% rename from src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala index 21102217d3..a94aec7626 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history.popow import io.circe.{Decoder, Encoder} import org.ergoplatform.mining.difficulty.DifficultyAdjustment import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions.LongOps @@ -178,21 +178,21 @@ object NipopowProof { class NipopowProofSerializer(poPowAlgos: NipopowAlgos) extends ErgoSerializer[NipopowProof] { override def serialize(obj: NipopowProof, w: Writer): Unit = { - w.putUInt(obj.m) - w.putUInt(obj.k) - w.putUInt(obj.prefix.size) + w.putUInt(obj.m.toLong) + w.putUInt(obj.k.toLong) + w.putUInt(obj.prefix.size.toLong) obj.prefix.foreach { h => val hBytes = h.bytes - w.putUInt(hBytes.length) + w.putUInt(hBytes.length.toLong) w.putBytes(hBytes) } val suffixHeadBytes = obj.suffixHead.bytes - w.putUInt(suffixHeadBytes.length) + w.putUInt(suffixHeadBytes.length.toLong) w.putBytes(suffixHeadBytes) - w.putUInt(obj.suffixTail.size) + w.putUInt(obj.suffixTail.size.toLong) obj.suffixTail.foreach { h => val hBytes = h.bytes - w.putUInt(hBytes.length) + w.putUInt(hBytes.length.toLong) w.putBytes(hBytes) } w.put(if (obj.continuous) 1 else 0) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala similarity index 96% rename from src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala index 95aad0a2c9..bdd82ded9c 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala @@ -3,12 +3,13 @@ package org.ergoplatform.modifiers.history.popow import cats.Traverse import cats.implicits.{catsStdInstancesForEither, catsStdInstancesForList} import io.circe.{Decoder, Encoder, Json} +import org.ergoplatform.core.BytesSerializable import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.extension.Extension.merkleTree import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.authds.Side import scorex.crypto.authds.merkle.BatchMerkleProof import scorex.crypto.authds.merkle.serialization.BatchMerkleProofSerializer @@ -144,12 +145,12 @@ object PoPowHeaderSerializer extends ErgoSerializer[PoPowHeader] { override def serialize(obj: PoPowHeader, w: Writer): Unit = { val headerBytes = obj.header.bytes - w.putUInt(headerBytes.length) + w.putUInt(headerBytes.length.toLong) w.putBytes(headerBytes) - w.putUInt(obj.interlinks.size) + w.putUInt(obj.interlinks.size.toLong) obj.interlinks.foreach(x => w.putBytes(idToBytes(x))) val proofBytes = merkleProofSerializer.serialize(obj.interlinksProof) - w.putUInt(proofBytes.length) + w.putUInt(proofBytes.length.toLong) w.putBytes(proofBytes) } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowParams.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowParams.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowParams.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowParams.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala similarity index 96% rename from src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 55fa39b005..02cc3ed275 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -3,11 +3,12 @@ package org.ergoplatform.modifiers.mempool import io.circe.syntax._ import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} -import org.ergoplatform._ +import org.ergoplatform.{EphemerealNodeViewModifier, _} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction.unresolvedIndices +import org.ergoplatform.modifiers.transaction.Transaction import org.ergoplatform.modifiers.{ErgoNodeViewModifier, NetworkObjectTypeId, TransactionTypeId} import org.ergoplatform.nodeView.ErgoContext import org.ergoplatform.nodeView.state.ErgoStateContext @@ -15,17 +16,14 @@ import org.ergoplatform.sdk.utils.ArithUtils.{addExact, multiplyExact} import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings.{Algos, ErgoValidationSettings} -import org.ergoplatform.utils.BoxUtils +import org.ergoplatform.utils.{BoxUtils, ScorexEncoding} import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.wallet.protocol.context.InputContext import org.ergoplatform.wallet.serialization.JsonCodecsWrapper -import scorex.core.EphemerealNodeViewModifier -import scorex.core.serialization.ErgoSerializer -import scorex.core.transaction.Transaction -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.ValidationResult.fromValidationState -import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} +import org.ergoplatform.serialization.ErgoSerializer +import org.ergoplatform.validation.ValidationResult.fromValidationState +import org.ergoplatform.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} import scorex.db.ByteArrayUtils import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, ScorexLogging, bytesToId} @@ -118,7 +116,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], // Cost limit per block val maxCost = stateContext.currentParameters.maxBlockCost.toLong - val input = inputs(inputIndex) + val input = inputs(inputIndex.toInt) // Just in case, should always be true if client implementation is correct. if (!box.id.sameElements(input.boxId)) { @@ -191,7 +189,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], val totalAssetsAccessCost = ErgoBoxAssetExtractor.totalAssetsAccessCost(inAssetsNum, inAssets.size, outAssetsNum, outAssets.size, tokenAccessCost) - val newCost = addExact(currentTxCost, totalAssetsAccessCost) + val newCost = addExact(currentTxCost, totalAssetsAccessCost.toLong) validationBefore // Check that transaction is not too costly considering all the assets @@ -225,7 +223,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], stateContext: ErgoStateContext): Try[Unit] = { val res: Try[Unit] = Try { - lazy val reemissionSettings = stateContext.ergoSettings.chainSettings.reemission + lazy val reemissionSettings = stateContext.chainSettings.reemission lazy val reemissionRules = reemissionSettings.reemissionRules lazy val reemissionTokenId = ModifierId @@@ reemissionSettings.reemissionTokenId @@ -234,7 +232,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], lazy val emissionNftId = ModifierId @@@ reemissionSettings.emissionNftId lazy val emissionNftIdBytes = reemissionSettings.emissionNftIdBytes - lazy val chainSettings = stateContext.ergoSettings.chainSettings + lazy val chainSettings = stateContext.chainSettings lazy val emissionRules = chainSettings.emissionRules lazy val height = stateContext.currentHeight @@ -328,7 +326,6 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], require(sentToReemission == toBurn, "Burning condition violated") } } else { - Success(()) } } @@ -367,10 +364,10 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], // Cost of transaction initialization: we should read and parse all inputs and data inputs, // and also iterate through all outputs to check rules val initialCost: Long = addExact( - ErgoInterpreter.interpreterInitCost, - multiplyExact(boxesToSpend.size, stateContext.currentParameters.inputCost), - multiplyExact(dataBoxes.size, stateContext.currentParameters.dataInputCost), - multiplyExact(outputCandidates.size, stateContext.currentParameters.outputCost), + ErgoInterpreter.interpreterInitCost.toLong, + multiplyExact(boxesToSpend.size.toLong, stateContext.currentParameters.inputCost.toLong), + multiplyExact(dataBoxes.size.toLong, stateContext.currentParameters.dataInputCost.toLong), + multiplyExact(outputCandidates.size.toLong, stateContext.currentParameters.outputCost.toLong), ) // Cost limit per block @@ -436,7 +433,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], val currentTxCost = validation.result.payload.get verifyInput(validation, boxesToSpend, dataBoxes, box, idx.toShort, stateContext, currentTxCost) } - .validate(txReemission, !stateContext.ergoSettings.chainSettings.reemission.checkReemissionRules || + .validate(txReemission, !stateContext.chainSettings.reemission.checkReemissionRules || verifyReemissionSpending(boxesToSpend, outputCandidates, stateContext).isSuccess, InvalidModifier(id, id, modifierTypeId)) } diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/UnsignedErgoTransaction.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/UnsignedErgoTransaction.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/mempool/UnsignedErgoTransaction.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/mempool/UnsignedErgoTransaction.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/state/StateChanges.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/state/StateChanges.scala similarity index 100% rename from src/main/scala/org/ergoplatform/modifiers/state/StateChanges.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/state/StateChanges.scala diff --git a/src/main/scala/scorex/core/transaction/TooHighCostError.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/TooHighCostError.scala similarity index 87% rename from src/main/scala/scorex/core/transaction/TooHighCostError.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/TooHighCostError.scala index e86d3d6189..26557d7171 100644 --- a/src/main/scala/scorex/core/transaction/TooHighCostError.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/TooHighCostError.scala @@ -1,4 +1,4 @@ -package scorex.core.transaction +package org.ergoplatform.modifiers.transaction import org.ergoplatform.modifiers.mempool.ErgoTransaction diff --git a/src/main/scala/scorex/core/transaction/Transaction.scala b/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/Transaction.scala similarity index 82% rename from src/main/scala/scorex/core/transaction/Transaction.scala rename to ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/Transaction.scala index c170967ad1..1c5263c656 100644 --- a/src/main/scala/scorex/core/transaction/Transaction.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/modifiers/transaction/Transaction.scala @@ -1,7 +1,7 @@ -package scorex.core.transaction +package org.ergoplatform.modifiers.transaction +import org.ergoplatform.EphemerealNodeViewModifier import org.ergoplatform.modifiers.{NetworkObjectTypeId, TransactionTypeId} -import scorex.core.EphemerealNodeViewModifier import scorex.crypto.hash.Blake2b256 import scorex.util.{ModifierId, bytesToId} diff --git a/src/main/scala/scorex/core/network/Handshake.scala b/ergo-core/src/main/scala/org/ergoplatform/network/Handshake.scala similarity index 93% rename from src/main/scala/scorex/core/network/Handshake.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/Handshake.scala index 7588d7671f..bd80167127 100644 --- a/src/main/scala/scorex/core/network/Handshake.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/Handshake.scala @@ -1,4 +1,4 @@ -package scorex.core.network +package org.ergoplatform.network /** * Network message to be send when nodes establish a new connection. diff --git a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala b/ergo-core/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala similarity index 87% rename from src/main/scala/org/ergoplatform/network/ModePeerFeature.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala index b5b47f2556..14052929b3 100644 --- a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala @@ -2,10 +2,8 @@ package org.ergoplatform.network import io.circe.{Encoder, Json} import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.settings.{NodeConfigurationSettings, PeerFeatureDescriptors} -import scorex.core.network.PeerFeature -import scorex.core.network.PeerFeature.Id -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.settings.{ClientCapabilities, PeerFeatureDescriptors} +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} /** @@ -25,7 +23,7 @@ case class ModePeerFeature(stateType: StateType, blocksToKeep: Int) extends PeerFeature { override type M = ModePeerFeature - override val featureId: Id = PeerFeatureDescriptors.ModeFeatureId + override val featureId: PeerFeature.Id = PeerFeatureDescriptors.ModeFeatureId override def serializer: ErgoSerializer[ModePeerFeature] = ModeFeatureSerializer @@ -64,22 +62,22 @@ object ModePeerFeature { */ val UTXOSetBootstrapped = -2 - def apply(nodeSettings: NodeConfigurationSettings): ModePeerFeature = { - val popowBootstrapped = if (nodeSettings.nipopowSettings.nipopowBootstrap) { + def apply(clientCapabilities: ClientCapabilities): ModePeerFeature = { + val popowBootstrapped = if (clientCapabilities.nipopowSettings.nipopowBootstrap) { Some(NiPoPoWDefaultFlag) } else { None } - val blocksKept = if (nodeSettings.utxoSettings.utxoBootstrap) { + val blocksKept = if (clientCapabilities.utxoSettings.utxoBootstrap) { UTXOSetBootstrapped } else { - nodeSettings.blocksToKeep + clientCapabilities.blocksToKeep } new ModePeerFeature( - nodeSettings.stateType, - nodeSettings.verifyTransactions, + clientCapabilities.stateType, + clientCapabilities.verifyTransactions, popowBootstrapped, blocksKept ) diff --git a/src/main/scala/scorex/core/network/PeerFeature.scala b/ergo-core/src/main/scala/org/ergoplatform/network/PeerFeature.scala similarity index 82% rename from src/main/scala/scorex/core/network/PeerFeature.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/PeerFeature.scala index 0275053ca0..64a831130c 100644 --- a/src/main/scala/scorex/core/network/PeerFeature.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/PeerFeature.scala @@ -1,6 +1,7 @@ -package scorex.core.network +package org.ergoplatform.network -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} +import org.ergoplatform.core.BytesSerializable +import org.ergoplatform.serialization.ErgoSerializer /** * An abstract trait to describe peer capabilities. diff --git a/src/main/scala/scorex/core/network/PeerSpec.scala b/ergo-core/src/main/scala/org/ergoplatform/network/PeerSpec.scala similarity index 89% rename from src/main/scala/scorex/core/network/PeerSpec.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/PeerSpec.scala index 99faeb6c2d..1dcd755400 100644 --- a/src/main/scala/scorex/core/network/PeerSpec.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/PeerSpec.scala @@ -1,15 +1,14 @@ -package scorex.core.network +package org.ergoplatform.network +import org.ergoplatform.network.peer.{LocalAddressPeerFeature, RestApiUrlPeerFeature} import org.ergoplatform.settings.PeerFeatureDescriptors - -import java.net.{InetAddress, InetSocketAddress, URL} -import scorex.core.app.{ApplicationVersionSerializer, Version} -import scorex.core.network.peer.{LocalAddressPeerFeature, RestApiUrlPeerFeature} -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.Extensions._ import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} +import java.net.{InetAddress, InetSocketAddress, URL} + /** * Declared information about peer * @@ -58,14 +57,14 @@ object PeerSpecSerializer extends ErgoSerializer[PeerSpec] with ScorexLogging { val addr = isa.getAddress.getAddress writer.put((addr.size + 4).toByteExact) writer.putBytes(addr) - writer.putUInt(isa.getPort) + writer.putUInt(isa.getPort.toLong) } w.put(obj.features.size.toByteExact) obj.features.foreach { f => w.put(f.featureId) val fBytes = f.bytes - w.putUShort(fBytes.length.toShortExact) + w.putUShort(fBytes.length.toShortExact.toInt) w.putBytes(fBytes) } } @@ -87,10 +86,11 @@ object PeerSpecSerializer extends ErgoSerializer[PeerSpec] with ScorexLogging { } val featuresCount = r.getByte() - val feats = (1 to featuresCount).flatMap { _ => + require(featuresCount >= 0) + val feats = (1 to featuresCount.toInt).flatMap { _ => val featId = r.getByte() val featBytesCount = r.getUShort().toShortExact - val featChunk = r.getChunk(featBytesCount) + val featChunk = r.getChunk(featBytesCount.toInt) //we ignore a feature found in the PeersData if we do not know how to parse it or failed to do that val serializer = PeerFeatureDescriptors.FeatureSerializers.get(featId) if (serializer.isEmpty) { diff --git a/src/main/scala/scorex/core/app/Version.scala b/ergo-core/src/main/scala/org/ergoplatform/network/Version.scala similarity index 92% rename from src/main/scala/scorex/core/app/Version.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/Version.scala index 197683bdb6..a91ae9b797 100644 --- a/src/main/scala/scorex/core/app/Version.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/Version.scala @@ -1,6 +1,7 @@ -package scorex.core.app +package org.ergoplatform.network -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} +import org.ergoplatform.core.BytesSerializable +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization._ /** diff --git a/ergo-core/src/main/scala/org/ergoplatform/network/message/InvData.scala b/ergo-core/src/main/scala/org/ergoplatform/network/message/InvData.scala new file mode 100644 index 0000000000..1cad7677e1 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/network/message/InvData.scala @@ -0,0 +1,12 @@ +package org.ergoplatform.network.message + +import org.ergoplatform.modifiers.NetworkObjectTypeId +import scorex.util.ModifierId + +/** + * P2P network message which is encoding "inventory", transactions or block sections the node has + * + * @param typeId + * @param ids + */ +case class InvData(typeId: NetworkObjectTypeId.Value, ids: Seq[ModifierId]) diff --git a/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageConstants.scala b/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageConstants.scala new file mode 100644 index 0000000000..432de21206 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageConstants.scala @@ -0,0 +1,14 @@ +package org.ergoplatform.network.message + +/** + * Type aliases and constants related to P2P network messages formats + */ +object MessageConstants { + type MessageCode = Byte + + val MagicLength: Int = 4 + + val ChecksumLength: Int = 4 + + val HeaderLength: Int = MagicLength + 5 +} diff --git a/src/main/scala/scorex/core/network/message/MessageSpec.scala b/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageSpec.scala similarity index 75% rename from src/main/scala/scorex/core/network/message/MessageSpec.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/message/MessageSpec.scala index 8681364741..f622f484b1 100644 --- a/src/main/scala/scorex/core/network/message/MessageSpec.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/message/MessageSpec.scala @@ -1,7 +1,8 @@ -package scorex.core.network.message +package org.ergoplatform.network.message -import scorex.core.app.Version -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.Version +import org.ergoplatform.network.message.MessageConstants._ +import org.ergoplatform.serialization.ErgoSerializer /** * Base trait for app p2p messages in the network @@ -17,7 +18,7 @@ trait MessageSpec[Content] extends ErgoSerializer[Content] { * Code which identifies what message type is contained in the payload */ - val messageCode: Message.MessageCode + val messageCode: MessageCode /** * Name of this message type. For debug purposes only. diff --git a/ergo-core/src/main/scala/org/ergoplatform/network/message/SyncInfoMessageSpec.scala b/ergo-core/src/main/scala/org/ergoplatform/network/message/SyncInfoMessageSpec.scala new file mode 100644 index 0000000000..824365b62b --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/network/message/SyncInfoMessageSpec.scala @@ -0,0 +1,24 @@ +package org.ergoplatform.network.message + +import org.ergoplatform.consensus.SyncInfo +import org.ergoplatform.network.message.MessageConstants.MessageCode +import org.ergoplatform.serialization.ErgoSerializer +import scorex.util.serialization.{Reader, Writer} + +/** + * The `SyncInfo` message requests an `Inv` message that provides modifier ids + * required be sender to synchronize his blockchain with the recipient. + * It allows a peer which has been disconnected or started for the first + * time to get the data it needs to request the blocks it hasn't seen. + * + * Payload of this message should be determined in underlying applications. + */ +class SyncInfoMessageSpec[SI <: SyncInfo](serializer: ErgoSerializer[SI]) extends MessageSpecV1[SI] { + + override val messageCode: MessageCode = 65: Byte + override val messageName: String = "Sync" + + override def serialize(data: SI, w: Writer): Unit = serializer.serialize(data, w) + + override def parse(r: Reader): SI = serializer.parse(r) +} diff --git a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala b/ergo-core/src/main/scala/org/ergoplatform/network/peer/LocalAddressPeerFeature.scala similarity index 78% rename from src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/peer/LocalAddressPeerFeature.scala index 6ef6a2f5f5..07439ec300 100644 --- a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/peer/LocalAddressPeerFeature.scala @@ -1,10 +1,10 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer + +import org.ergoplatform.network.PeerFeature import java.net.{InetAddress, InetSocketAddress} import org.ergoplatform.settings.PeerFeatureDescriptors -import scorex.core.network.PeerFeature -import scorex.core.network.PeerFeature.Id -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization._ import scorex.util.Extensions._ @@ -14,7 +14,7 @@ import scorex.util.Extensions._ */ case class LocalAddressPeerFeature(address: InetSocketAddress) extends PeerFeature { override type M = LocalAddressPeerFeature - override val featureId: Id = PeerFeatureDescriptors.LocalAddressPeerFeatureId + override val featureId: PeerFeature.Id = PeerFeatureDescriptors.LocalAddressPeerFeatureId override def serializer: LocalAddressPeerFeatureSerializer.type = LocalAddressPeerFeatureSerializer } @@ -25,7 +25,7 @@ object LocalAddressPeerFeatureSerializer extends ErgoSerializer[LocalAddressPeer override def serialize(obj: LocalAddressPeerFeature, w: Writer): Unit = { w.putBytes(obj.address.getAddress.getAddress) - w.putUInt(obj.address.getPort) + w.putUInt(obj.address.getPort.toLong) } override def parse(r: Reader): LocalAddressPeerFeature = { diff --git a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala b/ergo-core/src/main/scala/org/ergoplatform/network/peer/RestApiUrlPeerFeature.scala similarity index 82% rename from src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/peer/RestApiUrlPeerFeature.scala index 7f74b765e9..54d9a911d1 100644 --- a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/peer/RestApiUrlPeerFeature.scala @@ -1,11 +1,10 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer -import scorex.core.network.PeerFeature -import scorex.core.network.PeerFeature.Id -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.PeerFeature +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization._ -import java.net.URL +import java.net.URL import org.ergoplatform.settings.PeerFeatureDescriptors /** @@ -14,7 +13,7 @@ import org.ergoplatform.settings.PeerFeatureDescriptors */ case class RestApiUrlPeerFeature(restApiUrl: URL) extends PeerFeature { override type M = RestApiUrlPeerFeature - override val featureId: Id = PeerFeatureDescriptors.RestApiUrlFeatureId + override val featureId: PeerFeature.Id = PeerFeatureDescriptors.RestApiUrlFeatureId override def serializer: RestApiUrlPeerFeatureSerializer.type = RestApiUrlPeerFeatureSerializer } diff --git a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala b/ergo-core/src/main/scala/org/ergoplatform/network/peer/SessionIdPeerFeature.scala similarity index 74% rename from src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala rename to ergo-core/src/main/scala/org/ergoplatform/network/peer/SessionIdPeerFeature.scala index 386f999a5f..608947e946 100644 --- a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/network/peer/SessionIdPeerFeature.scala @@ -1,10 +1,9 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer +import org.ergoplatform.network.PeerFeature import org.ergoplatform.settings.PeerFeatureDescriptors -import scorex.core.network.PeerFeature -import scorex.core.network.PeerFeature.Id -import scorex.core.network.message.Message -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.message.MessageConstants +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization._ /** @@ -17,7 +16,7 @@ case class SessionIdPeerFeature(networkMagic: Array[Byte], sessionId: Long = scala.util.Random.nextLong()) extends PeerFeature { override type M = SessionIdPeerFeature - override val featureId: Id = PeerFeatureDescriptors.SessionIdPeerFeatureId + override val featureId: PeerFeature.Id = PeerFeatureDescriptors.SessionIdPeerFeatureId override def serializer: SessionIdPeerFeatureSerializer.type = SessionIdPeerFeatureSerializer @@ -32,7 +31,7 @@ object SessionIdPeerFeatureSerializer extends ErgoSerializer[SessionIdPeerFeatur } override def parse(r: Reader): SessionIdPeerFeature = { - val networkMagic = r.getBytes(Message.MagicLength) + val networkMagic = r.getBytes(MessageConstants.MagicLength) val sessionId = r.getLong() SessionIdPeerFeature(networkMagic, sessionId) } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala similarity index 97% rename from src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala index 132501df4b..b197e82e59 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala @@ -20,7 +20,7 @@ class ErgoContext(val stateContext: ErgoStateContext, transactionContext.dataBoxes, transactionContext.boxesToSpend, transactionContext.spendingTransaction, - inputContext.selfIndex, + inputContext.selfIndex.toInt, inputContext.extension, stateContext.validationSettings.sigmaSettings, costLimit, diff --git a/ergo-core/src/main/scala/org/ergoplatform/nodeView/LocallyGeneratedModifier.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/LocallyGeneratedModifier.scala new file mode 100644 index 0000000000..712a185d35 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/LocallyGeneratedModifier.scala @@ -0,0 +1,8 @@ +package org.ergoplatform.nodeView + +import org.ergoplatform.modifiers.BlockSection + +/** + * Wrapper for locally generated block section + */ +case class LocallyGeneratedModifier(pmod: BlockSection) diff --git a/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryUtils.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryUtils.scala new file mode 100644 index 0000000000..3f1f3801fd --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryUtils.scala @@ -0,0 +1,28 @@ +package org.ergoplatform.nodeView.history + +import org.ergoplatform.ErgoLikeContext +import org.ergoplatform.modifiers.history.header.Header + +/** + * Repository for types, constants and functions related to blockchain database (ErgoHistory) + */ +object ErgoHistoryUtils { + /** + * Type for time, represents machine-specific timestamp of a transaction + * or block section, as milliseconds passed since beginning of UNIX + * epoch on the machine + */ + type Time = Long + + type Height = ErgoLikeContext.Height // Int + type Score = BigInt + type Difficulty = BigInt + type NBits = Long + + val CharsetName = "UTF-8" + + val EmptyHistoryHeight: Int = 0 + val GenesisHeight: Int = EmptyHistoryHeight + 1 // first block has height == 1 + + def heightOf(headerOpt: Option[Header]): Int = headerOpt.map(_.height).getOrElse(EmptyHistoryHeight) +} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala similarity index 94% rename from src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala index e67a808b4d..bb5f6ee3ab 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala @@ -1,10 +1,10 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.NodeViewModifier +import org.ergoplatform.consensus.SyncInfo import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import scorex.core.NodeViewModifier -import scorex.core.consensus.SyncInfo -import scorex.core.network.message.SyncInfoMessageSpec -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.message.SyncInfoMessageSpec +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala similarity index 95% rename from src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala index 4f6dd6b642..cfc6905d86 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ExtensionValidator.scala @@ -4,8 +4,8 @@ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandida import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.settings.ValidationRules._ -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{InvalidModifier, ValidationState} +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.{InvalidModifier, ValidationState} import scorex.util.bytesToId /** diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala similarity index 100% rename from src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/mempool/TransactionMembershipProof.scala diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala similarity index 92% rename from src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index ce478458ad..fcd6b6f680 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -1,19 +1,20 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.core.BytesSerializable import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSerializer} import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.history.popow.NipopowAlgos -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils import org.ergoplatform.nodeView.history.storage.modifierprocessors.ExtensionValidator import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationState} +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.serialization.ErgoSerializer +import org.ergoplatform.validation.{InvalidModifier, ModifierValidator, ValidationState} import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} @@ -34,9 +35,9 @@ case class UpcomingStateContext(override val lastHeaders: Seq[Header], override val genesisStateDigest: ADDigest, override val currentParameters: Parameters, override val validationSettings: ErgoValidationSettings, - override val votingData: VotingData)(implicit ergoSettings: ErgoSettings) + override val votingData: VotingData)(implicit chainSettings: ChainSettings) extends ErgoStateContext(lastHeaders, lastExtensionOpt, genesisStateDigest, currentParameters, - validationSettings, votingData)(ergoSettings) { + validationSettings, votingData)(chainSettings) { override def sigmaPreHeader: sigma.PreHeader = PreHeader.toSigma(predictedHeader) @@ -65,7 +66,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], val currentParameters: Parameters, val validationSettings: ErgoValidationSettings, val votingData: VotingData) - (implicit val ergoSettings: ErgoSettings) + (implicit val chainSettings: ChainSettings) extends BlockchainStateContext with BytesSerializable with ScorexEncoding @@ -73,8 +74,8 @@ class ErgoStateContext(val lastHeaders: Seq[Header], override type M = ErgoStateContext - private val votingSettings = ergoSettings.chainSettings.voting - private val popowAlgos = new NipopowAlgos(ergoSettings.chainSettings) + private val votingSettings = chainSettings.voting + private val popowAlgos = new NipopowAlgos(chainSettings) override def sigmaPreHeader: sigma.PreHeader = PreHeader.toSigma(lastHeaders.headOption.getOrElse(PreHeader.fake)) @@ -107,7 +108,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], def lastHeaderOpt: Option[Header] = lastHeaders.headOption - override def serializer: ErgoSerializer[M] = ErgoStateContextSerializer(ergoSettings) + override def serializer: ErgoSerializer[M] = ErgoStateContextSerializer(chainSettings) /** * @return state context corresponding to a block after last known one with fields provided @@ -120,7 +121,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], version: Byte): UpcomingStateContext = { val upcomingHeader = PreHeader(lastHeaderOpt, version, minerPk, timestamp, nBits, votes) val forkVote = votes.contains(Parameters.SoftFork) - val height = ErgoHistory.heightOf(lastHeaderOpt) + 1 + val height = ErgoHistoryUtils.heightOf(lastHeaderOpt) + 1 val (calculatedParams, updated) = currentParameters.update(height, forkVote, votingData.epochVotes, proposedUpdate, votingSettings) val calculatedValidationSettings = validationSettings.updated(updated) UpcomingStateContext(lastHeaders, lastExtensionOpt, upcomingHeader, genesisStateDigest, calculatedParams, @@ -134,12 +135,12 @@ class ErgoStateContext(val lastHeaders: Seq[Header], def simplifiedUpcoming(): UpcomingStateContext = { val minerPk = org.ergoplatform.mining.group.generator val version = lastHeaderOpt.map(_.version).getOrElse(Header.InitialVersion) - val nBits = lastHeaderOpt.map(_.nBits).getOrElse(ergoSettings.chainSettings.initialNBits) + val nBits = lastHeaderOpt.map(_.nBits).getOrElse(chainSettings.initialNBits) val timestamp = lastHeaderOpt.map(_.timestamp + 1).getOrElse(System.currentTimeMillis()) val votes = Array.emptyByteArray val proposedUpdate = ErgoValidationSettingsUpdate.empty val upcomingHeader = PreHeader(lastHeaderOpt, version, minerPk, timestamp, nBits, votes) - val height = ErgoHistory.heightOf(lastHeaderOpt) + 1 + val height = ErgoHistoryUtils.heightOf(lastHeaderOpt) + 1 val (calculatedParams, updated) = currentParameters.update(height, forkVote = false, votingData.epochVotes, proposedUpdate, votingSettings) val calculatedValidationSettings = validationSettings.updated(updated) UpcomingStateContext(lastHeaders, lastExtensionOpt, upcomingHeader, genesisStateDigest, calculatedParams, @@ -227,14 +228,14 @@ class ErgoStateContext(val lastHeaders: Seq[Header], val proposedVotes = votes.map(_ -> 1) val newVoting = VotingData(proposedVotes) new ErgoStateContext(newHeaders, extensionOpt, genesisStateDigest, params, - extractedValidationSettings, newVoting)(ergoSettings) + extractedValidationSettings, newVoting)(chainSettings) } case _ => val newVotes = votes val newVotingResults = newVotes.foldLeft(votingData) { case (v, id) => v.update(id) } state.result.toTry.map { _ => new ErgoStateContext(newHeaders, extensionOpt, genesisStateDigest, currentParameters, validationSettings, - newVotingResults)(ergoSettings) + newVotingResults)(chainSettings) } } }.flatten @@ -332,16 +333,16 @@ object ErgoStateContext { */ val eip27Vote: Byte = 8 - def empty(settings: ErgoSettings, parameters: Parameters): ErgoStateContext = { - empty(settings.chainSettings.genesisStateDigest, settings, parameters) + def empty(chainSettings: ChainSettings, parameters: Parameters): ErgoStateContext = { + empty(chainSettings.genesisStateDigest, chainSettings, parameters) } /** * Initialize empty state context */ - def empty(genesisStateDigest: ADDigest, settings: ErgoSettings, parameters: Parameters): ErgoStateContext = { + def empty(genesisStateDigest: ADDigest, chainSettings: ChainSettings, parameters: Parameters): ErgoStateContext = { new ErgoStateContext(Seq.empty, None, genesisStateDigest, parameters, ErgoValidationSettings.initial, - VotingData.empty)(settings) + VotingData.empty)(chainSettings) } /** @@ -352,14 +353,14 @@ object ErgoStateContext { def recover(genesisStateDigest: ADDigest, extension: Extension, lastHeaders: Seq[Header]) - (settings: ErgoSettings): Try[ErgoStateContext] = { - val vs = settings.chainSettings.voting + (chainSettings: ChainSettings): Try[ErgoStateContext] = { + val vs = chainSettings.voting if (lastHeaders.lastOption.exists(_.height % vs.votingLength == 0)) { val currentHeader = lastHeaders.last Parameters.parseExtension(currentHeader.height, extension).flatMap { params => ErgoValidationSettings.parseExtension(extension).map { validationSettings => new ErgoStateContext(lastHeaders.reverse, Some(extension), genesisStateDigest, params, - validationSettings, VotingData.empty)(settings) + validationSettings, VotingData.empty)(chainSettings) } } } else { @@ -369,7 +370,7 @@ object ErgoStateContext { } -case class ErgoStateContextSerializer(ergoSettings: ErgoSettings) extends ErgoSerializer[ErgoStateContext] { +case class ErgoStateContextSerializer(chainSettings: ChainSettings) extends ErgoSerializer[ErgoStateContext] { private val Eip27SupportValue = 100 // see comment in serialize() @@ -413,7 +414,7 @@ case class ErgoStateContextSerializer(ergoSettings: ErgoSettings) extends ErgoSe } new ErgoStateContext(lastHeaders, lastExtensionOpt, genesisDigest, params, validationSettings, - votingData)(ergoSettings) + votingData)(chainSettings) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala similarity index 73% rename from src/main/scala/org/ergoplatform/nodeView/state/StateType.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala index 8aa5ea8152..ca827e0f18 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/StateType.scala @@ -32,6 +32,8 @@ object StateType { override val requireProofs: Boolean = true } + val values: Seq[StateType] = Seq(Utxo, Digest) + def fromCode(code: StateTypeCode): StateType = if (code == Utxo.stateTypeCode) { Utxo } else if (code == Digest.stateTypeCode) { @@ -40,17 +42,4 @@ object StateType { throw new Exception(s"Unkown state type code $code") } - type UtxoType = Utxo.type - type DigestType = Digest.type - - val values: Seq[StateType] = Seq(Utxo, Digest) - - /** This class allows to check the correspondence between concrete instances of [[StateType]] and [[ErgoState]] - */ - sealed trait Evidence[ST <: StateType, S <: ErgoState[S]] - - implicit final object UtxoEvidence extends Evidence[UtxoType, UtxoState] - - implicit final object DigestEvidence extends Evidence[DigestType, DigestState] - } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala similarity index 93% rename from src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala rename to ergo-core/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala index 3b55bee3ec..d660db7f20 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.state -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -31,7 +31,7 @@ object VotingDataSerializer extends ErgoSerializer[VotingData] { w.putUShort(obj.epochVotes.length) obj.epochVotes.foreach { case (id, cnt) => w.put(id) - w.putUInt(cnt) + w.putUInt(cnt.toLong) } } diff --git a/ergo-core/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala b/ergo-core/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala new file mode 100644 index 0000000000..895434220e --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala @@ -0,0 +1,40 @@ +package org.ergoplatform.reemission + +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.contracts.ReemissionContracts +import org.ergoplatform.mining.emission.EmissionRules +import org.ergoplatform.settings.ReemissionSettings +import sigma.Coll + + +/** + * Contains re-emission contracts (defined in `ReemissionContracts`) and helper functions + * for re-emission. + */ +class ReemissionRules(reemissionSettings: ReemissionSettings) extends ReemissionContracts { + + override val reemissionNftIdBytes: Coll[Byte] = reemissionSettings.reemissionNftIdBytes + override val reemissionStartHeight: Height = reemissionSettings.reemissionStartHeight + + /** + * How many ERG taken from emission to re-emission initially + */ + val basicChargeAmount = 12 // in ERG + + /** + * @return how many re-emission tokens can be unlocked at given height + */ + def reemissionForHeight(height: Height, + emissionRules: EmissionRules): Long = { + val emission = emissionRules.emissionAtHeight(height.toLong) + if (height >= reemissionSettings.activationHeight && + emission >= (basicChargeAmount + 3) * EmissionRules.CoinsInOneErgo) { + basicChargeAmount * EmissionRules.CoinsInOneErgo + } else if (height >= reemissionSettings.activationHeight && + emission > 3 * EmissionRules.CoinsInOneErgo) { + emission - 3 * EmissionRules.CoinsInOneErgo + } else { + 0L + } + } +} diff --git a/src/main/scala/org/ergoplatform/settings/Algos.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/Algos.scala similarity index 90% rename from src/main/scala/org/ergoplatform/settings/Algos.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/Algos.scala index 5239f9d7d0..ac80a7001d 100644 --- a/src/main/scala/org/ergoplatform/settings/Algos.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/Algos.scala @@ -1,18 +1,20 @@ package org.ergoplatform.settings +import org.ergoplatform.utils +import org.ergoplatform.utils.ScorexEncoder import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.MerkleTree import scorex.crypto.hash.Digest32 import scorex.util._ -object Algos extends ErgoAlgos with scorex.core.utils.ScorexEncoding { +object Algos extends ErgoAlgos with utils.ScorexEncoding { // ErgoAlgos in sigmastate extends scorex.util.ScorexEncoding where encoder is BytesEncoder // but here we use scorex.core.utils.ScorexEncoding where encoder is ScorexEncoder // After ScorexEncoder is moved (there is even a todo for that) from scorex.core to scorex.util // we can fix this ugliness. - override implicit val encoder: scorex.core.utils.ScorexEncoder = scorex.core.utils.ScorexEncoder.default + override implicit val encoder: ScorexEncoder = utils.ScorexEncoder.default lazy val emptyMerkleTreeRoot: Digest32 = Algos.hash(LeafData @@ Array[Byte]()) diff --git a/src/main/scala/org/ergoplatform/settings/ChainSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ChainSettings.scala similarity index 100% rename from src/main/scala/org/ergoplatform/settings/ChainSettings.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/ChainSettings.scala diff --git a/ergo-core/src/main/scala/org/ergoplatform/settings/ClientCapabilities.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ClientCapabilities.scala new file mode 100644 index 0000000000..328763da98 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/ClientCapabilities.scala @@ -0,0 +1,14 @@ +package org.ergoplatform.settings + +import org.ergoplatform.nodeView.state.StateType + +/** + * Features client may have enabled, they are reported to other peers + */ +trait ClientCapabilities { + val stateType: StateType + val verifyTransactions: Boolean + val blocksToKeep: Int + val utxoSettings: UtxoSettings + val nipopowSettings: NipopowSettings +} \ No newline at end of file diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/Constants.scala similarity index 64% rename from src/main/scala/org/ergoplatform/settings/Constants.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/Constants.scala index ab9a0e88ba..02090c6afd 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -1,19 +1,11 @@ package org.ergoplatform.settings import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.modifiers.NetworkObjectTypeId -import org.ergoplatform.modifiers.history._ -import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSerializer} -import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} -import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty -import scorex.core.NodeViewModifier -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Difficulty import scorex.crypto.authds.avltree.batch.AvlTreeParameters import sigmastate.Values import sigmastate.Values.ErgoTree - object Constants { /** * Length of hash function output used around the Ergo code @@ -48,18 +40,6 @@ object Constants { // Number of last block headers available is scripts from ErgoStateContext val LastHeadersInContext = 10 - /** - * Serializers for block sections and transactions - * - * // todo: move to NodeViewSynchronizer, used only there - */ - val modifierSerializers: Map[NetworkObjectTypeId.Value, ErgoSerializer[_ <: NodeViewModifier]] = - Map(Header.modifierTypeId -> HeaderSerializer, - Extension.modifierTypeId -> ExtensionSerializer, - BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, - ADProofs.modifierTypeId -> ADProofsSerializer, - ErgoTransaction.modifierTypeId -> ErgoTransactionSerializer) - val SoftForkEpochs = 32 //about 45.5 days def TrueLeaf: ErgoTree = Values.TrueLeaf.toSigmaProp diff --git a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala similarity index 94% rename from src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala index 97bfb0d3f1..3961e5f159 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala @@ -1,10 +1,12 @@ package org.ergoplatform.settings +import org.ergoplatform.core.BytesSerializable import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} +import org.ergoplatform.utils import org.ergoplatform.validation.SigmaValidationSettings -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} -import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationSettings} +import org.ergoplatform.serialization.ErgoSerializer +import org.ergoplatform.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationSettings} import scorex.util.serialization.{Reader, Writer} import scala.util.Try @@ -113,7 +115,7 @@ object ErgoValidationSettings { if (values.isEmpty) { ErgoValidationSettings.initial } else { - val bytes = scorex.core.utils.concatBytes(values) + val bytes = utils.concatBytes(values) ErgoValidationSettingsSerializer.parseBytes(bytes) } } diff --git a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala similarity index 92% rename from src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala index 9dccb5d9fa..a98066a696 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala @@ -1,7 +1,7 @@ package org.ergoplatform.settings import org.ergoplatform.validation.RuleStatusSerializer -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.serialization.ConstantStore import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} @@ -27,12 +27,12 @@ object ErgoValidationSettingsUpdateSerializer extends ErgoSerializer[ErgoValidat override def serialize(obj: ErgoValidationSettingsUpdate, w: Writer): Unit = { val sigmaWriter = new SigmaByteWriter(w, None) - w.putUInt(obj.rulesToDisable.length) + w.putUInt(obj.rulesToDisable.length.toLong) obj.rulesToDisable.foreach { r => - w.putUShort(r) + w.putUShort(r.toInt) } - w.putUInt(obj.statusUpdates.length) + w.putUInt(obj.statusUpdates.length.toLong) obj.statusUpdates.foreach { r => w.putUShort(r._1 - FirstRule) RuleStatusSerializer.serialize(r._2, sigmaWriter) diff --git a/ergo-core/src/main/scala/org/ergoplatform/settings/NipopowSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/NipopowSettings.scala new file mode 100644 index 0000000000..fdcf5acd98 --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/NipopowSettings.scala @@ -0,0 +1,21 @@ +package org.ergoplatform.settings + +import net.ceedubs.ficus.Ficus._ +import net.ceedubs.ficus.readers.ValueReader + +/** + * Settings related to headers-chain bootstrapping with NiPoPoWs. See ergo.node.nipopow section for settings description. + */ +case class NipopowSettings(nipopowBootstrap: Boolean, p2pNipopows: Int) + +/** + * Custom settings reader for `NipopowSettings` + */ +trait NipopowSettingsReader { + implicit val nipopowSettingsReader: ValueReader[NipopowSettings] = { (cfg, path) => + NipopowSettings( + cfg.as[Boolean](s"$path.nipopowBootstrap"), + cfg.as[Int](s"$path.p2pNipopows") + ) + } +} \ No newline at end of file diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/Parameters.scala similarity index 98% rename from src/main/scala/org/ergoplatform/settings/Parameters.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/Parameters.scala index 5a12fa813d..51d76084cf 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -3,8 +3,8 @@ package org.ergoplatform.settings import com.google.common.primitives.Ints import io.circe.Encoder import io.circe.syntax._ -import org.ergoplatform.nodeView.history.ErgoHistory.Height -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -386,9 +386,9 @@ object ParametersSerializer extends ErgoSerializer[Parameters] with ApiCodecs { override def serialize(params: Parameters, w: Writer): Unit = { require(params.parametersTable.nonEmpty, s"$params is empty") - w.putUInt(params.height) + w.putUInt(params.height.toLong) val paramsSize = params.parametersTable.size - w.putUInt(paramsSize) + w.putUInt(paramsSize.toLong) params.parametersTable.foreach { case (k, v) => w.put(k) w.putInt(v) diff --git a/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala similarity index 79% rename from src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala index 43c2d2dfe1..b77a1db5b1 100644 --- a/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/PeerFeatureDescriptors.scala @@ -1,8 +1,7 @@ package org.ergoplatform.settings -import org.ergoplatform.network.ModeFeatureSerializer -import scorex.core.network.PeerFeature -import scorex.core.network.peer.{LocalAddressPeerFeatureSerializer, RestApiUrlPeerFeatureSerializer, SessionIdPeerFeatureSerializer} +import org.ergoplatform.network.{ModeFeatureSerializer, PeerFeature} +import org.ergoplatform.network.peer.{LocalAddressPeerFeatureSerializer, RestApiUrlPeerFeatureSerializer, SessionIdPeerFeatureSerializer} /** * Repository of existing peer feature identifiers, stores their ids along with serializers diff --git a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala similarity index 100% rename from src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala diff --git a/src/main/scala/scorex/core/settings/Settings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/Settings.scala similarity index 96% rename from src/main/scala/scorex/core/settings/Settings.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/Settings.scala index c67890f61b..34f7701bba 100644 --- a/src/main/scala/scorex/core/settings/Settings.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/Settings.scala @@ -1,13 +1,13 @@ -package scorex.core.settings +package org.ergoplatform.settings -import java.io.File -import java.net.{InetSocketAddress, URL} import com.typesafe.config.{Config, ConfigFactory} import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ -import scorex.core.network.message.Message +import org.ergoplatform.network.message.MessageConstants._ import scorex.util.ScorexLogging +import java.io.File +import java.net.{InetSocketAddress, URL} import scala.concurrent.duration._ case class LoggingSettings(level: String) @@ -97,6 +97,6 @@ object ScorexSettings extends ScorexLogging with SettingsReaders { def fromConfig(config: Config): ScorexSettings = { config.as[ScorexSettings](configPath) - .ensuring(_.network.magicBytes.length == Message.MagicLength) + .ensuring(_.network.magicBytes.length == MagicLength) } } diff --git a/src/main/scala/scorex/core/settings/SettingsReaders.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/SettingsReaders.scala similarity index 94% rename from src/main/scala/scorex/core/settings/SettingsReaders.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/SettingsReaders.scala index 8daf6d167c..a41a8e2e2d 100644 --- a/src/main/scala/scorex/core/settings/SettingsReaders.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/SettingsReaders.scala @@ -1,10 +1,11 @@ -package scorex.core.settings +package org.ergoplatform.settings -import java.io.File -import java.net.InetSocketAddress import com.typesafe.config.Config import net.ceedubs.ficus.readers.ValueReader +import java.io.File +import java.net.InetSocketAddress + trait SettingsReaders { implicit val fileReader: ValueReader[File] = (cfg, path) => new File(cfg.getString(path)) implicit val byteValueReader: ValueReader[Byte] = (cfg, path) => cfg.getInt(path).toByte diff --git a/ergo-core/src/main/scala/org/ergoplatform/settings/UtxoSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/UtxoSettings.scala new file mode 100644 index 0000000000..64c14ccadf --- /dev/null +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/UtxoSettings.scala @@ -0,0 +1,22 @@ +package org.ergoplatform.settings + +import net.ceedubs.ficus.Ficus._ +import net.ceedubs.ficus.readers.ValueReader + +/** + * Settings related to state bootstrapping with UTXO set snapshots. See ergo.node.utxo section for settings description. + */ +case class UtxoSettings(utxoBootstrap: Boolean, storingUtxoSnapshots: Int, p2pUtxoSnapshots: Int) + +/** + * Custom settings reader for `UtxoSettings` + */ +trait UtxoSettingsReader { + implicit val utxoSettingsReader: ValueReader[UtxoSettings] = { (cfg, path) => + UtxoSettings( + cfg.as[Boolean](s"$path.utxoBootstrap"), + cfg.as[Int](s"$path.storingUtxoSnapshots"), + cfg.as[Int](s"$path.p2pUtxoSnapshots") + ) + } +} \ No newline at end of file diff --git a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/ValidationRules.scala similarity index 98% rename from src/main/scala/org/ergoplatform/settings/ValidationRules.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/ValidationRules.scala index 49d516c65f..96029c0994 100644 --- a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/settings/ValidationRules.scala @@ -6,10 +6,10 @@ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor -import scorex.core.validation.{InvalidModifier, ModifierValidator} -import scorex.core.validation.ValidationResult.Invalid +import org.ergoplatform.validation.{InvalidModifier, ModifierValidator} +import org.ergoplatform.validation.ValidationResult.Invalid import scorex.util.ModifierId object ValidationRules { @@ -107,7 +107,7 @@ object ValidationRules { hdrGenesisFromConfig -> RuleStatus(im => fatal(s"Genesis header id should be equal to id from the config. ${im.error}", im.modifierId, im.modifierTypeId), Seq(classOf[Header]), mayBeDisabled = false), - hdrGenesisHeight -> RuleStatus(im => fatal(s"Genesis height should be ${ErgoHistory.GenesisHeight}. ${im.error}", im.modifierId, im.modifierTypeId), + hdrGenesisHeight -> RuleStatus(im => fatal(s"Genesis height should be ${GenesisHeight}. ${im.error}", im.modifierId, im.modifierTypeId), Seq(classOf[Header]), mayBeDisabled = false), hdrParent -> RuleStatus(im => recoverable(s"Parent header with id ${im.error} is not defined", im.modifierId, im.modifierTypeId), diff --git a/src/main/scala/org/ergoplatform/settings/VotingSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/settings/VotingSettings.scala similarity index 100% rename from src/main/scala/org/ergoplatform/settings/VotingSettings.scala rename to ergo-core/src/main/scala/org/ergoplatform/settings/VotingSettings.scala diff --git a/src/main/scala/org/ergoplatform/utils/BoxUtils.scala b/ergo-core/src/main/scala/org/ergoplatform/utils/BoxUtils.scala similarity index 93% rename from src/main/scala/org/ergoplatform/utils/BoxUtils.scala rename to ergo-core/src/main/scala/org/ergoplatform/utils/BoxUtils.scala index 37931c9c7f..3802b08077 100644 --- a/src/main/scala/org/ergoplatform/utils/BoxUtils.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/utils/BoxUtils.scala @@ -12,7 +12,7 @@ object BoxUtils { /** Minimal amount for transaction for a box of maximum size*/ @inline - def sufficientAmount(parameters: Parameters): Long = ErgoBox.MaxBoxSize * parameters.minValuePerByte + def sufficientAmount(parameters: Parameters): Long = ErgoBox.MaxBoxSize * parameters.minValuePerByte.toLong /** Used when complete instance of ErgoBox is unavailable. */ @inline @@ -39,5 +39,5 @@ object BoxUtils { ) @inline - def minimalErgoAmount(box: ErgoBox, parameters: Parameters): Long = box.bytes.length * parameters.minValuePerByte + def minimalErgoAmount(box: ErgoBox, parameters: Parameters): Long = box.bytes.length * parameters.minValuePerByte.toLong } diff --git a/src/main/scala/org/ergoplatform/utils/LoggingUtil.scala b/ergo-core/src/main/scala/org/ergoplatform/utils/LoggingUtil.scala similarity index 100% rename from src/main/scala/org/ergoplatform/utils/LoggingUtil.scala rename to ergo-core/src/main/scala/org/ergoplatform/utils/LoggingUtil.scala diff --git a/src/main/scala/scorex/core/utils/ScorexEncoder.scala b/ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoder.scala similarity index 95% rename from src/main/scala/scorex/core/utils/ScorexEncoder.scala rename to ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoder.scala index 1e9bf2b68b..e437be18f6 100644 --- a/src/main/scala/scorex/core/utils/ScorexEncoder.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoder.scala @@ -1,6 +1,6 @@ -package scorex.core.utils +package org.ergoplatform.utils -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.util.ModifierId import scorex.util.encode.{Base16, BytesEncoder} diff --git a/src/main/scala/scorex/core/utils/ScorexEncoding.scala b/ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoding.scala similarity index 85% rename from src/main/scala/scorex/core/utils/ScorexEncoding.scala rename to ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoding.scala index ae25a560dc..089d00b640 100644 --- a/src/main/scala/scorex/core/utils/ScorexEncoding.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/utils/ScorexEncoding.scala @@ -1,4 +1,4 @@ -package scorex.core.utils +package org.ergoplatform.utils /** * Trait with bytes to string encoder diff --git a/src/main/scala/scorex/core/utils/utils.scala b/ergo-core/src/main/scala/org/ergoplatform/utils/utils.scala similarity index 69% rename from src/main/scala/scorex/core/utils/utils.scala rename to ergo-core/src/main/scala/org/ergoplatform/utils/utils.scala index 93eaf37b0f..e27399b445 100644 --- a/src/main/scala/scorex/core/utils/utils.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/utils/utils.scala @@ -1,4 +1,4 @@ -package scorex.core +package org.ergoplatform import java.security.SecureRandom import scala.annotation.tailrec @@ -8,25 +8,6 @@ import scala.util.{Failure, Success, Try} package object utils { - @deprecated("Use scorex.util.ScorexLogging instead.", "scorex-util 0.1.0") - type ScorexLogging = scorex.util.ScorexLogging - - /** - * @param block - function to profile - * @return - execution time in seconds and function result - */ - def profile[R](block: => R): (Float, R) = { - val t0 = System.nanoTime() - val result = block // call-by-name - val t1 = System.nanoTime() - ((t1 - t0).toFloat / 1000000000, result) - } - - def toTry(b: Boolean, msg: String): Try[Unit] = b match { - case true => Success(Unit) - case false => Failure(new Exception(msg)) - } - @tailrec final def untilTimeout[T](timeout: FiniteDuration, delay: FiniteDuration = 100.milliseconds)(fn: => T): T = { @@ -58,22 +39,6 @@ package object utils { result } - def concatFixLengthBytes(seq: Traversable[Array[Byte]]): Array[Byte] = seq.headOption match { - case None => Array[Byte]() - case Some(head) => concatFixLengthBytes(seq, head.length) - } - - - def concatFixLengthBytes(seq: Traversable[Array[Byte]], length: Int): Array[Byte] = { - val result: Array[Byte] = new Array[Byte](seq.toSeq.length * length) - var index = 0 - seq.foreach { s => - Array.copy(s, 0, result, index, length) - index += length - } - result - } - implicit class MapPimp[K, V](underlying: Map[K, V]) { /** * One liner for updating a Map with the possibility to handle case of missing Key diff --git a/src/main/scala/scorex/core/validation/ModifierError.scala b/ergo-core/src/main/scala/org/ergoplatform/validation/ModifierError.scala similarity index 98% rename from src/main/scala/scorex/core/validation/ModifierError.scala rename to ergo-core/src/main/scala/org/ergoplatform/validation/ModifierError.scala index a80444668a..10c1f9729a 100644 --- a/src/main/scala/scorex/core/validation/ModifierError.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/validation/ModifierError.scala @@ -1,4 +1,4 @@ -package scorex.core.validation +package org.ergoplatform.validation import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.util.ModifierId diff --git a/src/main/scala/scorex/core/validation/ModifierValidator.scala b/ergo-core/src/main/scala/org/ergoplatform/validation/ModifierValidator.scala similarity index 97% rename from src/main/scala/scorex/core/validation/ModifierValidator.scala rename to ergo-core/src/main/scala/org/ergoplatform/validation/ModifierValidator.scala index a8c35aea5b..c0a3c3f7f7 100644 --- a/src/main/scala/scorex/core/validation/ModifierValidator.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/validation/ModifierValidator.scala @@ -1,9 +1,9 @@ -package scorex.core.validation +package org.ergoplatform.validation +import org.ergoplatform.consensus.ModifierSemanticValidity import org.ergoplatform.modifiers.NetworkObjectTypeId -import scorex.core.consensus.ModifierSemanticValidity -import scorex.core.utils.ScorexEncoder -import scorex.core.validation.ValidationResult._ +import org.ergoplatform.utils.ScorexEncoder +import org.ergoplatform.validation.ValidationResult._ import scorex.util.{ModifierId, bytesToId} import scala.util.{Failure, Success, Try} @@ -16,7 +16,7 @@ import scala.util.{Failure, Success, Try} * It is designed to perform multiple checks for the same object without any transformation. * See the example of that * kind of validation in Ergo `org.ergoplatform.nodeView.history.storage.modifierprocessors.HeadersProcessor.HeaderValidator`. - * Some other examples could also be found in `scorex.core.validation.ValidationSpec`. + * Some other examples could also be found in `org.ergoplatform.validation.ValidationSpec`. * * The second distinction from cats `Validated` is that we do support both fail-fast and error-accumulating validation * while cats `Validated` supports only accumulative approach. diff --git a/src/main/scala/scorex/core/validation/ValidationResult.scala b/ergo-core/src/main/scala/org/ergoplatform/validation/ValidationResult.scala similarity index 87% rename from src/main/scala/scorex/core/validation/ValidationResult.scala rename to ergo-core/src/main/scala/org/ergoplatform/validation/ValidationResult.scala index 028e08b39c..812871112d 100644 --- a/src/main/scala/scorex/core/validation/ValidationResult.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/validation/ValidationResult.scala @@ -1,9 +1,7 @@ -package scorex.core.validation +package org.ergoplatform.validation -import akka.http.scaladsl.server.Route import io.circe.{ACursor, DecodingFailure} -import scorex.core.api.http.ApiError -import scorex.core.validation.ValidationResult.{Invalid, Valid} +import org.ergoplatform.validation.ValidationResult.{Invalid, Valid} import scala.concurrent.Future import scala.language.implicitConversions @@ -52,13 +50,6 @@ sealed trait ValidationResult[+T] { case Invalid(_) => Left(DecodingFailure(message, cursor.history)) } - /** Convert validation result to akka http route - */ - def toApi(onSuccess: T => Route): Route = this match { - case Valid(value) => onSuccess(value) - case Invalid(_) => ApiError.BadRequest(message) - } - } object ValidationResult { diff --git a/src/main/scala/scorex/core/validation/ValidationSettings.scala b/ergo-core/src/main/scala/org/ergoplatform/validation/ValidationSettings.scala similarity index 88% rename from src/main/scala/scorex/core/validation/ValidationSettings.scala rename to ergo-core/src/main/scala/org/ergoplatform/validation/ValidationSettings.scala index cabd24da9d..d9dd2c889c 100644 --- a/src/main/scala/scorex/core/validation/ValidationSettings.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/validation/ValidationSettings.scala @@ -1,7 +1,7 @@ -package scorex.core.validation +package org.ergoplatform.validation import org.ergoplatform.modifiers.NetworkObjectTypeId -import scorex.core.validation.ValidationResult.Invalid +import org.ergoplatform.validation.ValidationResult.Invalid import scorex.util.ModifierId /** diff --git a/src/it/scala/org/ergoplatform/it/DeepRollBackSpec.scala b/src/it/scala/org/ergoplatform/it/DeepRollBackSpec.scala index 6411ec89a6..b1ea045021 100644 --- a/src/it/scala/org/ergoplatform/it/DeepRollBackSpec.scala +++ b/src/it/scala/org/ergoplatform/it/DeepRollBackSpec.scala @@ -1,10 +1,9 @@ package org.ergoplatform.it import java.io.File - import com.typesafe.config.Config import org.ergoplatform.it.container.{IntegrationSuite, Node} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils import org.scalatest.freespec.AnyFreeSpec import scala.async.Async @@ -51,7 +50,7 @@ class DeepRollBackSpec extends AnyFreeSpec with IntegrationSuite { // 1. Let the first node mine `chainLength + delta` blocks Async.await(minerAIsolated.waitForHeight(chainLength + delta)) - val genesisA = Async.await(minerAIsolated.headerIdsByHeight(ErgoHistory.GenesisHeight)).head + val genesisA = Async.await(minerAIsolated.headerIdsByHeight(ErgoHistoryUtils.GenesisHeight)).head val minerBIsolated: Node = docker.startDevNetNode(minerBConfig, isolatedPeersConfig, specialVolumeOpt = Some((localVolumeB, remoteVolumeB))).get @@ -59,7 +58,7 @@ class DeepRollBackSpec extends AnyFreeSpec with IntegrationSuite { // 2. Let another node mine `chainLength` blocks Async.await(minerBIsolated.waitForHeight(chainLength)) - val genesisB = Async.await(minerBIsolated.headerIdsByHeight(ErgoHistory.GenesisHeight)).head + val genesisB = Async.await(minerBIsolated.headerIdsByHeight(ErgoHistoryUtils.GenesisHeight)).head genesisA should not equal genesisB diff --git a/src/it/scala/org/ergoplatform/it/WalletSpec.scala b/src/it/scala/org/ergoplatform/it/WalletSpec.scala index 3d22140ea0..fd041daaf7 100644 --- a/src/it/scala/org/ergoplatform/it/WalletSpec.scala +++ b/src/it/scala/org/ergoplatform/it/WalletSpec.scala @@ -12,7 +12,7 @@ import org.ergoplatform.it.util.RichEither import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction import org.ergoplatform.nodeView.wallet.requests.{PaymentRequest, PaymentRequestEncoder, RequestsHolder, RequestsHolderEncoder} import org.ergoplatform.nodeView.wallet.{AugWalletTransaction, ErgoWalletServiceImpl} -import org.ergoplatform.settings.{Args, ErgoSettings} +import org.ergoplatform.settings.{Args, ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.{ErgoTestHelpers, WalletTestOps} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.boxes.ErgoBoxSerializer @@ -29,7 +29,7 @@ class WalletSpec extends AsyncWordSpec with IntegrationSuite with WalletTestOps override implicit def executionContext: ExecutionContext = ErgoTestHelpers.defaultExecutionContext - val ergoSettings: ErgoSettings = ErgoSettings.read( + val ergoSettings: ErgoSettings = ErgoSettingsReader.read( Args(userConfigPathOpt = Some("src/test/resources/application.conf"), networkTypeOpt = None)) private val nodeConfig: Config = nonGeneratingPeerConfig.withFallback(nodeSeedConfigs.head) diff --git a/src/it/scala/org/ergoplatform/it/container/Docker.scala b/src/it/scala/org/ergoplatform/it/container/Docker.scala index 88fde59095..5e71e45426 100644 --- a/src/it/scala/org/ergoplatform/it/container/Docker.scala +++ b/src/it/scala/org/ergoplatform/it/container/Docker.scala @@ -5,7 +5,6 @@ import java.net.InetAddress import java.nio.file.{Files, Path, Paths} import java.util.concurrent.atomic.AtomicBoolean import java.util.{Collections, Properties, UUID, List => JList, Map => JMap} - import cats.implicits._ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper @@ -21,7 +20,7 @@ import net.ceedubs.ficus.Ficus._ import org.apache.commons.io.FileUtils import org.asynchttpclient.Dsl.{config, _} import org.ergoplatform.settings.NetworkType.{DevNet, MainNet, TestNet} -import org.ergoplatform.settings.{ErgoSettings, NetworkType} +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader, NetworkType} import scorex.util.ScorexLogging import scala.annotation.tailrec @@ -176,7 +175,7 @@ class Docker(suiteConfig: Config = ConfigFactory.empty, .withFallback(ConfigFactory.defaultApplication()) .withFallback(ConfigFactory.defaultReference()) .resolve() - ErgoSettings.fromConfig(actualConfig) + ErgoSettingsReader.fromConfig(actualConfig) } private def enrichNodeConfig(networkType: NetworkType, diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 5a59717f64..8c07b36cee 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -4479,6 +4479,25 @@ paths: type: integer format: int32 default: -1 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + minimum: 1 + default: 500 + maximum: 2500 + - in: query + name: offset + required: false + description: The number of items in list to skip + schema: + type: integer + format: int32 + minimum: 0 + default: 0 get: security: - ApiKeyAuth: [api_key] @@ -4570,6 +4589,25 @@ paths: type: integer format: int32 default: -1 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + minimum: 1 + default: 500 + maximum: 2500 + - in: query + name: offset + required: false + description: The number of items in list to skip + schema: + type: integer + format: int32 + minimum: 0 + default: 0 get: security: - ApiKeyAuth: [api_key] @@ -5529,6 +5567,25 @@ paths: type: integer format: int32 default: -1 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + minimum: 1 + default: 500 + maximum: 2500 + - in: query + name: offset + required: false + description: The number of items in list to skip + schema: + type: integer + format: int32 + minimum: 0 + default: 0 get: security: - ApiKeyAuth: [api_key] @@ -5596,6 +5653,25 @@ paths: type: integer format: int32 default: -1 + - in: query + name: limit + required: false + description: amount of elements to retrieve + schema: + type: integer + format: int32 + minimum: 1 + default: 500 + maximum: 2500 + - in: query + name: offset + required: false + description: The number of items in list to skip + schema: + type: integer + format: int32 + minimum: 0 + default: 0 get: security: - ApiKeyAuth: [api_key] @@ -6592,4 +6668,4 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ApiError' \ No newline at end of file + $ref: '#/components/schemas/ApiError' diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index d578aeb9ee..1295b7acb6 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -606,4 +606,20 @@ api-dispatcher { # processed per actor before the thread jumps to the next actor. # Set to 1 for as fair as possible. throughput = 4 +} + +indexer-dispatcher { + # Dispatcher is the name of the event-based dispatcher + type = Dispatcher + # What kind of ExecutionService to use + executor = "fork-join-executor" + # Configuration for the fork join pool + fork-join-executor { + # Min number of threads to cap factor-based parallelism number to + parallelism-min = 1 + # Parallelism (threads) ... ceil(available processors * factor) + parallelism-factor = 1.0 + # Max number of threads to cap factor-based parallelism number to + parallelism-max = 4 + } } \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 946b59a392..2929ac6ade 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -20,8 +20,8 @@ ergo-%d{yyyy-MM-dd}.%i.log.gz - - 30 + + 7 500MB 1GB diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 88021c75b2..c483237aa6 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -13,15 +13,14 @@ import org.ergoplatform.network.{ErgoNodeViewSynchronizer, ErgoSyncTracker} import org.ergoplatform.nodeView.history.ErgoSyncInfoMessageSpec import org.ergoplatform.nodeView.history.extra.ExtraIndexer import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} -import org.ergoplatform.settings.{Args, ErgoSettings, NetworkType} +import org.ergoplatform.settings.{Args, ErgoSettings, ErgoSettingsReader, NetworkType, ScorexSettings} import scorex.core.api.http._ import scorex.core.app.ScorexContext import scorex.core.network.NetworkController.ReceivableMessages.ShutdownNetwork import scorex.core.network._ -import scorex.core.network.message.Message.MessageCode -import scorex.core.network.message._ -import scorex.core.network.peer.PeerManagerRef -import scorex.core.settings.ScorexSettings +import org.ergoplatform.network.message.MessageConstants.MessageCode +import org.ergoplatform.network.message._ +import org.ergoplatform.network.peer.PeerManagerRef import scorex.util.ScorexLogging import java.net.InetSocketAddress @@ -36,7 +35,7 @@ class ErgoApp(args: Args) extends ScorexLogging { log.info(s"Running with args: $args") - private val ergoSettings: ErgoSettings = ErgoSettings.read(args) + private val ergoSettings: ErgoSettings = ErgoSettingsReader.read(args) require( ergoSettings.scorexSettings.restApi.apiKeyHash.isDefined, @@ -108,13 +107,13 @@ class ErgoApp(args: Args) extends ScorexLogging { None } - if(ergoSettings.nodeSettings.extraIndex) - require( - ergoSettings.nodeSettings.stateType.holdsUtxoSet && !ergoSettings.nodeSettings.isFullBlocksPruned, - "Node must store full UTXO set and all blocks to run extra indexer." - ) // Create an instance of ExtraIndexer actor (will start if "extraIndex = true" in config) - private val indexer: ActorRef = ExtraIndexer(ergoSettings.chainSettings, ergoSettings.cacheSettings) + private val indexerOpt: Option[ActorRef] = + if (ergoSettings.nodeSettings.extraIndex) { + Some(ExtraIndexer(ergoSettings.chainSettings, ergoSettings.cacheSettings)) + } else { + None + } private val syncTracker = ErgoSyncTracker(scorexSettings.network) @@ -184,7 +183,7 @@ class ErgoApp(args: Args) extends ScorexLogging { private val apiRoutes: Seq[ApiRoute] = Seq( EmissionApiRoute(ergoSettings), ErgoUtilsApiRoute(ergoSettings), - BlockchainApiRoute(readersHolderRef, ergoSettings, indexer), + BlockchainApiRoute(readersHolderRef, ergoSettings, indexerOpt), ErgoPeersApiRoute( peerManagerRef, networkControllerRef, @@ -291,8 +290,7 @@ object ErgoApp extends ScorexLogging { /** Intentional user invoked remote shutdown */ case object RemoteShutdown extends CoordinatedShutdown.Reason - /** Exception that triggers proper system shutdown */ - case class CriticalSystemException(message: String) extends Exception(message) + /** hard application exit in case actor system is not started yet*/ def forceStopApplication(code: Int = 1): Nothing = sys.exit(code) diff --git a/src/main/scala/org/ergoplatform/GlobalConstants.scala b/src/main/scala/org/ergoplatform/GlobalConstants.scala index 0025ba4fc9..5d396394ba 100644 --- a/src/main/scala/org/ergoplatform/GlobalConstants.scala +++ b/src/main/scala/org/ergoplatform/GlobalConstants.scala @@ -10,4 +10,6 @@ object GlobalConstants { * (to avoid clashing between blockchain processing and API actors) */ val ApiDispatcher = "api-dispatcher" + + val IndexerDispatcher = "indexer-dispatcher" } diff --git a/src/main/scala/org/ergoplatform/http/SwaggerRoute.scala b/src/main/scala/org/ergoplatform/http/SwaggerRoute.scala index 65010727b0..b80966ad98 100644 --- a/src/main/scala/org/ergoplatform/http/SwaggerRoute.scala +++ b/src/main/scala/org/ergoplatform/http/SwaggerRoute.scala @@ -2,7 +2,7 @@ package org.ergoplatform.http import akka.http.scaladsl.model.{ContentTypes, HttpEntity} import akka.http.scaladsl.server.{Directives, Route} -import scorex.core.settings.RESTApiSettings +import org.ergoplatform.settings.RESTApiSettings final case class SwaggerRoute(settings: RESTApiSettings, swaggerConfig: String) extends Directives { diff --git a/src/main/scala/scorex/core/api/http/ApiError.scala b/src/main/scala/org/ergoplatform/http/api/ApiError.scala similarity index 97% rename from src/main/scala/scorex/core/api/http/ApiError.scala rename to src/main/scala/org/ergoplatform/http/api/ApiError.scala index 39904c0196..72d36e86b4 100644 --- a/src/main/scala/scorex/core/api/http/ApiError.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiError.scala @@ -1,4 +1,4 @@ -package scorex.core.api.http +package org.ergoplatform.http.api import akka.http.scaladsl.model.{ContentTypes, HttpEntity, StatusCode, StatusCodes} import akka.http.scaladsl.server.{Directives, Route} diff --git a/src/main/scala/org/ergoplatform/http/api/ApiExtraCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiExtraCodecs.scala new file mode 100644 index 0000000000..250d8d88a4 --- /dev/null +++ b/src/main/scala/org/ergoplatform/http/api/ApiExtraCodecs.scala @@ -0,0 +1,116 @@ +package org.ergoplatform.http.api + +import io.circe._ +import io.circe.syntax._ +import org.ergoplatform.ErgoAddressEncoder +import org.ergoplatform.sdk.JsonCodecs +import org.ergoplatform.nodeView.history.extra.ExtraIndexer.getAddress +import org.ergoplatform.nodeView.history.extra.{BalanceInfo, IndexedErgoBox, IndexedErgoTransaction, IndexedToken} +import org.ergoplatform.nodeView.state.SnapshotsInfo +import org.ergoplatform.nodeView.state.UtxoState.ManifestId +import org.ergoplatform.nodeView.wallet.persistence.WalletDigest + +/** + * JSON codecs related to extra indices (available via /blockchain API methods), and also few codecs for wallet + * digest (wallet balances) and UTXO snapshot manifests + */ +trait ApiExtraCodecs extends JsonCodecs { + + def ergoAddressEncoder: ErgoAddressEncoder + + implicit val indexedBoxEncoder: Encoder[IndexedErgoBox] = { iEb => + iEb.box.asJson.deepMerge(Json.obj( + "globalIndex" -> iEb.globalIndex.asJson, + "inclusionHeight" -> iEb.inclusionHeight.asJson, + "address" -> ergoAddressEncoder.toString(getAddress(iEb.box.ergoTree)(ergoAddressEncoder)).asJson, + "spentTransactionId" -> iEb.spendingTxIdOpt.asJson + )) + } + + implicit val indexedBoxSeqEncoder: Encoder[(Seq[IndexedErgoBox], Long)] = { iEbSeq => + Json.obj( + "items" -> iEbSeq._1.asJson, + "total" -> iEbSeq._2.asJson + ) + } + + implicit val indexedTxEncoder: Encoder[IndexedErgoTransaction] = { iEt => + Json.obj( + "id" -> iEt.txid.asJson, + "blockId" -> iEt.blockId.asJson, + "inclusionHeight" -> iEt.inclusionHeight.asJson, + "timestamp" -> iEt.timestamp.asJson, + "index" -> iEt.index.asJson, + "globalIndex" -> iEt.globalIndex.asJson, + "numConfirmations" -> iEt.numConfirmations.asJson, + "inputs" -> iEt.inputs.asJson, + "dataInputs" -> iEt.dataInputs.asJson, + "outputs" -> iEt.outputs.asJson, + "size" -> iEt.size.asJson + ) + } + + implicit val indexedTxSeqEncoder: Encoder[(Seq[IndexedErgoTransaction], Long)] = { iEtSeq => + Json.obj( + "items" -> iEtSeq._1.asJson, + "total" -> iEtSeq._2.asJson + ) + } + + implicit val IndexedTokenEncoder: Encoder[IndexedToken] = { token => + Json.obj( + "id" -> token.tokenId.asJson, + "boxId" -> token.boxId.asJson, + "emissionAmount" -> token.amount.asJson, + "name" -> token.name.asJson, + "description" -> token.description.asJson, + "decimals" -> token.decimals.asJson + ) + } + + implicit val BalanceInfoEncoder: Encoder[BalanceInfo] = { bal => + Json.obj( + "nanoErgs" -> bal.nanoErgs.asJson, + "tokens" -> bal.tokens.map(token => { + Json.obj( + "tokenId" -> token._1.asJson, + "amount" -> token._2.asJson, + "decimals" -> bal.additionalTokenInfo(token._1)._2.asJson, + "name" -> bal.additionalTokenInfo(token._1)._1.asJson + ) + }).asJson + ) + } + + implicit val TotalBalanceInfoEncoder: Encoder[(BalanceInfo, BalanceInfo)] = { tBal => + Json.obj( + "confirmed" -> tBal._1.asJson, + "unconfirmed" -> tBal._2.asJson + ) + } + + + implicit val balancesSnapshotEncoder: Encoder[WalletDigest] = { v => + import v._ + Json.obj( + "height" -> height.asJson, + "balance" -> walletBalance.asJson, + "assets" -> walletAssetBalances.toMap.map(x => (x._1: String, x._2)).asJson //toMap to have assets as JSON map + ) + } + + implicit val SnapshotInfoEncoder: Encoder[SnapshotsInfo] = { si => + Json.obj( + "availableManifests" -> si.availableManifests.map { case (height, manifest) => + height -> manifest + }.asJson + ) + } + + implicit val SnapshotInfoDecoder: Decoder[SnapshotsInfo] = { cursor => + for { + availableManifests <- Decoder.decodeMap[Int, ManifestId].tryDecode(cursor.downField("availableManifests")) + } yield new SnapshotsInfo(availableManifests) + } + +} diff --git a/src/main/scala/org/ergoplatform/http/api/ApiRequestsCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiRequestsCodecs.scala new file mode 100644 index 0000000000..916a660eea --- /dev/null +++ b/src/main/scala/org/ergoplatform/http/api/ApiRequestsCodecs.scala @@ -0,0 +1,107 @@ +package org.ergoplatform.http.api + +import io.circe._ +import io.circe.syntax._ +import org.ergoplatform.ErgoLikeContext +import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest, HintExtractionRequest} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, GenerateCommitmentsRequest, TransactionSigningRequest} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} +import org.ergoplatform.wallet.interpreter.TransactionHintsBag +import sigma.AnyValue +import sigmastate.Values.SigmaBoolean + +/** + * JSON codecs for HTTP API requests related entities + */ +trait ApiRequestsCodecs extends ApiCodecs { + + implicit val hintExtractionRequestEncoder: Encoder[HintExtractionRequest] = { hr => + Map( + "tx" -> hr.tx.asJson, + "real" -> hr.real.asJson, + "simulated" -> hr.simulated.asJson, + "inputsRaw" -> hr.inputs.asJson, + "dataInputsRaw" -> hr.dataInputs.asJson + ).asJson + } + + implicit val hintExtractionRequestDecoder: Decoder[HintExtractionRequest] = { cursor => + for { + tx <- cursor.downField("tx").as[ErgoTransaction] + real <- cursor.downField("real").as[Seq[SigmaBoolean]] + simulated <- cursor.downField("simulated").as[Seq[SigmaBoolean]] + inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] + dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] + } yield HintExtractionRequest(tx, real, simulated, inputs, dataInputs) + } + + implicit val executeRequestDecoder = new Decoder[ExecuteRequest] { + def apply(cursor: HCursor): Decoder.Result[ExecuteRequest] = { + for { + script <- cursor.downField("script").as[String] + env <- cursor.downField("namedConstants").as[Map[String, AnyValue]] + ctx <- cursor.downField("context").as[ErgoLikeContext] + } yield ExecuteRequest(script, env.map({ case (k, v) => k -> v.value }), ctx) + } + } + + implicit val cryptResultEncoder: Encoder[CryptoResult] = { + res => + val fields = Map( + "value" -> res.value.asJson, + "cost" -> res.cost.asJson + ) + fields.asJson + } + + implicit val transactionSigningRequestEncoder: Encoder[TransactionSigningRequest] = { tsr => + Json.obj( + "tx" -> tsr.unsignedTx.asJson, + "secrets" -> Json.obj( + "dlog" -> tsr.dlogs.asJson, + "dht" -> tsr.dhts.asJson + ), + "hints" -> tsr.hints.asJson, + "inputsRaw" -> tsr.inputs.asJson, + "dataInputsRaw" -> tsr.dataInputs.asJson + ) + } + + implicit val transactionSigningRequestDecoder: Decoder[TransactionSigningRequest] = { cursor => + for { + tx <- cursor.downField("tx").as[UnsignedErgoTransaction] + dlogs <- cursor.downField("secrets").downField("dlog").as[Option[Seq[DlogSecretKey]]] + dhts <- cursor.downField("secrets").downField("dht").as[Option[Seq[DhtSecretKey]]] + hints <- cursor.downField("hints").as[Option[TransactionHintsBag]] + inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] + dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] + secrets = (dlogs.getOrElse(Seq.empty) ++ dhts.getOrElse(Seq.empty)).map(ExternalSecret.apply) + } yield TransactionSigningRequest(tx, hints.getOrElse(TransactionHintsBag.empty), secrets, inputs, dataInputs) + } + + implicit val generateCommitmentsRequestEncoder: Encoder[GenerateCommitmentsRequest] = { gcr => + Json.obj( + "tx" -> gcr.unsignedTx.asJson, + "secrets" -> Json.obj( + "dlog" -> gcr.dlogs.asJson, + "dht" -> gcr.dhts.asJson, + "inputsRaw" -> gcr.inputs.asJson, + "dataInputsRaw" -> gcr.dataInputs.asJson + ) + ) + } + + implicit val generateCommitmentsRequestDecoder: Decoder[GenerateCommitmentsRequest] = { cursor => + for { + tx <- cursor.downField("tx").as[UnsignedErgoTransaction] + dlogs <- cursor.downField("secrets").downField("dlog").as[Option[Seq[DlogSecretKey]]] + dhts <- cursor.downField("secrets").downField("dht").as[Option[Seq[DhtSecretKey]]] + secrets = (dlogs.getOrElse(Seq.empty) ++ dhts.getOrElse(Seq.empty)).map(ExternalSecret.apply) + secretsOpt = if (secrets.isEmpty) None else Some(secrets) + inputs <- cursor.downField("inputsRaw").as[Option[Seq[String]]] + dataInputs <- cursor.downField("dataInputsRaw").as[Option[Seq[String]]] + } yield GenerateCommitmentsRequest(tx, secretsOpt, inputs, dataInputs) + } + +} diff --git a/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala index e5cb090761..39c913bdee 100644 --- a/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala @@ -10,16 +10,15 @@ import org.ergoplatform.http.api.SortDirection.{ASC, DESC, Direction, INVALID} import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistoryReader -import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages.GetSegmentTreshold +import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages.GetSegmentThreshold import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{GlobalBoxIndexKey, GlobalTxIndexKey, getIndex} import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hashErgoTree import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.uniqueId import org.ergoplatform.nodeView.history.extra._ import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.settings.ErgoSettings -import scorex.core.api.http.ApiError.{BadRequest, InternalError} +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} +import org.ergoplatform.http.api.ApiError.{BadRequest, InternalError} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.util.{ModifierId, bytesToId} import sigmastate.Values.ErgoTree import spire.implicits.cfor @@ -28,13 +27,14 @@ import scala.concurrent.duration.{Duration, SECONDS} import scala.concurrent.{Await, Future} import scala.util.Success -case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings, indexer: ActorRef) - (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with ApiCodecs { +case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings, indexerOpt: Option[ActorRef]) + (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with ApiCodecs with ApiExtraCodecs { val settings: RESTApiSettings = ergoSettings.scorexSettings.restApi - private implicit val segmentTreshold: Int = - Await.result[Int]((indexer ? GetSegmentTreshold).asInstanceOf[Future[Int]], Duration(3, SECONDS)) + private implicit val segmentTreshold: Int = indexerOpt.map { indexer => + Await.result[Int]((indexer ? GetSegmentThreshold).asInstanceOf[Future[Int]], Duration(3, SECONDS)) + }.getOrElse(0) private val paging: Directive[(Int, Int)] = parameters("offset".as[Int] ? 0, "limit".as[Int] ? 5) @@ -402,13 +402,3 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting } } - -object SortDirection { - - type Direction = Byte - - val ASC: Direction = 1.asInstanceOf[Direction] - val DESC: Direction = 0.asInstanceOf[Direction] - val INVALID: Direction = (-1).asInstanceOf[Direction] - -} diff --git a/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala index ebf82586d8..64a98e6044 100644 --- a/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala @@ -7,14 +7,13 @@ import io.circe.Json import io.circe.syntax._ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} import org.ergoplatform.nodeView.ErgoReadersHolder.GetDataFromHistory import org.ergoplatform.nodeView.history.ErgoHistoryReader -import org.ergoplatform.settings.{Algos, ErgoSettings} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier -import scorex.core.api.http.ApiError.BadRequest +import org.ergoplatform.settings.{Algos, ErgoSettings, RESTApiSettings} +import org.ergoplatform.http.api.ApiError.BadRequest +import org.ergoplatform.nodeView.LocallyGeneratedModifier import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.crypto.authds.merkle.MerkleProof import scorex.crypto.hash.Digest32 import scorex.util.ModifierId diff --git a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala index 0a3f370868..7a6789cc15 100644 --- a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala @@ -5,10 +5,9 @@ import akka.http.scaladsl.server.Route import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.settings.{ErgoSettings, ReemissionSettings} +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings, ReemissionSettings} import org.ergoplatform.{ErgoAddressEncoder, ErgoTreePredef, Pay2SAddress} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings case class EmissionApiRoute(ergoSettings: ErgoSettings) (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute { diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala index bc0ae49191..f12badfb0b 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala @@ -8,13 +8,13 @@ import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} import org.ergoplatform.settings.{Algos, ErgoSettings} -import scorex.core.api.http.{ApiError, ApiRoute} +import scorex.core.api.http.ApiRoute import scorex.util.{bytesToId, ModifierId} import akka.pattern.ask import io.circe.syntax.EncoderOps import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome._ +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.ProcessingOutcome +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.ProcessingOutcome._ import scorex.util.encode.Base16 import sigmastate.Values.ErgoTree import sigmastate.serialization.ErgoTreeSerializer diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala index 6efe66f3cf..88bf56eeea 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoPeersApiRoute.scala @@ -8,12 +8,12 @@ import io.circe.syntax._ import io.circe.{Encoder, Json} import io.circe.generic.semiauto.deriveEncoder import org.ergoplatform.network.ErgoSyncTracker -import scorex.core.api.http.{ApiError, ApiResponse, ApiRoute} +import scorex.core.api.http.{ApiResponse, ApiRoute} import scorex.core.network.{ConnectedPeer, DeliveryTracker} import scorex.core.network.NetworkController.ReceivableMessages.{ConnectTo, GetConnectedPeers, GetPeersStatus} -import scorex.core.network.peer.{PeerInfo, PeersStatus} -import scorex.core.network.peer.PeerManager.ReceivableMessages.{GetAllPeers, GetBlacklistedPeers} -import scorex.core.settings.RESTApiSettings +import org.ergoplatform.network.peer.{PeerInfo, PeersStatus} +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages.{GetAllPeers, GetBlacklistedPeers} +import org.ergoplatform.settings.RESTApiSettings import scala.concurrent.duration._ import scala.concurrent.ExecutionContext diff --git a/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala index 8684526663..87775dba7a 100644 --- a/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ErgoUtilsApiRoute.scala @@ -5,12 +5,11 @@ import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Route import io.circe.Json import io.circe.syntax._ -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.http.api.ApiError.BadRequest +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} -import scorex.core.api.http.ApiError.BadRequest -import scorex.core.api.http.{ApiError, ApiResponse, ApiRoute} -import scorex.core.settings.RESTApiSettings -import scorex.core.utils.ScorexEncoding +import scorex.core.api.http.{ApiResponse, ApiRoute} +import org.ergoplatform.utils.ScorexEncoding import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base16 import sigmastate.crypto.DLogProtocol.ProveDlog diff --git a/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala index e848955fb5..b78f974e61 100644 --- a/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/InfoApiRoute.scala @@ -6,8 +6,8 @@ import akka.http.scaladsl.server.Route import akka.pattern.ask import io.circe.syntax._ import org.ergoplatform.local.ErgoStatsCollector.{GetNodeInfo, NodeInfo} +import org.ergoplatform.settings.RESTApiSettings import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings /** diff --git a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala index c9ca7c1b8c..89a7dbb280 100644 --- a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala @@ -9,10 +9,9 @@ import org.ergoplatform.mining.CandidateGenerator.Candidate import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner} import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.wallet.ErgoAddressJsonEncoder -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import org.ergoplatform.{ErgoAddress, ErgoTreePredef, Pay2SAddress} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import sigmastate.crypto.DLogProtocol.ProveDlog import scala.concurrent.Future @@ -23,7 +22,7 @@ case class MiningApiRoute(miner: ActorRef, val settings: RESTApiSettings = ergoSettings.scorexSettings.restApi - implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings).encoder + implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder override val route: Route = pathPrefix("mining") { candidateR ~ diff --git a/src/main/scala/org/ergoplatform/http/api/NipopowApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/NipopowApiRoute.scala index 5ee2a74720..c8ab4c92e6 100644 --- a/src/main/scala/org/ergoplatform/http/api/NipopowApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/NipopowApiRoute.scala @@ -6,13 +6,12 @@ import akka.actor.{ActorRef, ActorRefFactory} import akka.http.scaladsl.server.Route import io.circe.Encoder import io.circe.syntax._ -import org.ergoplatform.modifiers.history.popow.{PoPowHeader, NipopowProof} +import org.ergoplatform.http.api.ApiError.BadRequest +import org.ergoplatform.modifiers.history.popow.{NipopowProof, PoPowHeader} import org.ergoplatform.nodeView.ErgoReadersHolder.GetDataFromHistory import org.ergoplatform.nodeView.history.ErgoHistoryReader -import org.ergoplatform.settings.ErgoSettings -import scorex.core.api.http.ApiError.BadRequest +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.util.ModifierId import scala.concurrent.Future diff --git a/src/main/scala/org/ergoplatform/http/api/NodeApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/NodeApiRoute.scala index db92275c47..5b0222b53d 100644 --- a/src/main/scala/org/ergoplatform/http/api/NodeApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/NodeApiRoute.scala @@ -4,9 +4,8 @@ import akka.actor.{ActorRefFactory, ActorSystem} import akka.http.scaladsl.server.Route import org.ergoplatform.ErgoApp import org.ergoplatform.ErgoApp.RemoteShutdown -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scala.concurrent.duration._ diff --git a/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala index f99af46995..752b4eb112 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScanApiRoute.scala @@ -6,14 +6,13 @@ import io.circe.Encoder import org.ergoplatform._ import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.wallet.scanning.{EqualsScanningPredicate, ScanRequest, ScanWalletInteraction} -import org.ergoplatform.settings.ErgoSettings -import scorex.core.api.http.ApiError.BadRequest +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scala.util.{Failure, Success} import ScanEntities._ import org.ergoplatform.ErgoBox.R1 +import org.ergoplatform.http.api.ApiError.BadRequest import org.ergoplatform.wallet.Constants.ScanId import sigmastate.Values.ByteArrayConstant import sigmastate.serialization.ErgoTreeSerializer @@ -66,19 +65,21 @@ case class ScanApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings) } def unspentR: Route = (path("unspentBoxes" / IntNumber) & get & boxParams) { - (scanIdInt, minConfNum, maxConfNum, minHeight, maxHeight) => + (scanIdInt, minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) => val scanId = ScanId @@ scanIdInt.toShort val considerUnconfirmed = minConfNum == -1 withWallet(_.scanUnspentBoxes(scanId, considerUnconfirmed, minHeight, maxHeight).map { _.filter(boxConfirmationFilter(_, minConfNum, maxConfNum)) + .slice(offset, offset + limit) }) } def spentR: Route = (path("spentBoxes" / IntNumber) & get & boxParams) { - (scanIdInt, minConfNum, maxConfNum, minHeight, maxHeight) => + (scanIdInt, minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) => val scanId = ScanId @@ scanIdInt.toShort withWallet(_.scanSpentBoxes(scanId).map { _.filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight)) + .slice(offset, offset + limit) }) } diff --git a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala index 908e18c00b..3ab560a3c1 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala @@ -6,14 +6,13 @@ import akka.pattern.ask import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform._ +import org.ergoplatform.http.api.ApiError.BadRequest import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.wallet.ErgoWalletReader import org.ergoplatform.nodeView.wallet.requests.PaymentRequestDecoder -import org.ergoplatform.settings.ErgoSettings -import scorex.core.api.http.ApiError.BadRequest +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.util.encode.Base16 import sigmastate.Values.{ByteArrayConstant, ErgoTree} import sigmastate._ @@ -29,7 +28,7 @@ import scala.util.{Failure, Success, Try} case class ScriptApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings) - (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with ApiCodecs { + (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with ApiCodecs with ApiRequestsCodecs { implicit val paymentRequestDecoder: PaymentRequestDecoder = new PaymentRequestDecoder(ergoSettings) implicit val addressEncoder: ErgoAddressEncoder = ErgoAddressEncoder(ergoSettings.chainSettings.addressPrefix) diff --git a/src/main/scala/org/ergoplatform/http/api/SortDirection.scala b/src/main/scala/org/ergoplatform/http/api/SortDirection.scala new file mode 100644 index 0000000000..ec022abfc6 --- /dev/null +++ b/src/main/scala/org/ergoplatform/http/api/SortDirection.scala @@ -0,0 +1,14 @@ +package org.ergoplatform.http.api + +/** + * Encoded results sorting direction (in ascending or descending order) + */ +object SortDirection { + + type Direction = Byte + + val ASC: Direction = 1.toByte + val DESC: Direction = 0.toByte + val INVALID: Direction = (-1).toByte + +} diff --git a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala index 3f5679c499..0cc95f24a3 100644 --- a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala @@ -7,15 +7,14 @@ import akka.pattern.ask import io.circe.Json import io.circe.syntax._ import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} +import org.ergoplatform.http.api.ApiError.BadRequest import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.mempool.HistogramStats.getFeeHistogram import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} -import org.ergoplatform.settings.{Algos, ErgoSettings} -import scorex.core.api.http.ApiError.BadRequest -import scorex.core.api.http.{ApiError, ApiResponse} -import scorex.core.settings.RESTApiSettings +import org.ergoplatform.settings.{Algos, ErgoSettings, RESTApiSettings} +import scorex.core.api.http.ApiResponse import scorex.crypto.authds.ADKey import scorex.util.encode.Base16 import sigmastate.SType diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index 4dac6ef738..1889d2a579 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -6,14 +6,10 @@ import akka.pattern.ask import org.ergoplatform.ErgoBox import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.nodeView.state.{ - ErgoStateReader, - UtxoSetSnapshotPersistence, - UtxoStateReader -} +import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoSetSnapshotPersistence, UtxoStateReader} +import org.ergoplatform.settings.RESTApiSettings import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.crypto.authds.ADKey import scorex.util.encode.Base16 @@ -22,7 +18,7 @@ import scala.concurrent.Future case class UtxoApiRoute(readersHolder: ActorRef, override val settings: RESTApiSettings)( implicit val context: ActorRefFactory ) extends ErgoBaseApiRoute - with ApiCodecs { + with ApiCodecs with ApiExtraCodecs { private def getState: Future[ErgoStateReader] = (readersHolder ? GetReaders).mapTo[Readers].map(_.s) diff --git a/src/main/scala/org/ergoplatform/http/api/WalletApiOperations.scala b/src/main/scala/org/ergoplatform/http/api/WalletApiOperations.scala index 0dfee640d9..9beb7ecabf 100644 --- a/src/main/scala/org/ergoplatform/http/api/WalletApiOperations.scala +++ b/src/main/scala/org/ergoplatform/http/api/WalletApiOperations.scala @@ -14,18 +14,33 @@ trait WalletApiOperations extends ErgoBaseApiRoute { val readersHolder: ActorRef - private val isLegalBoxParamCombination: ((Int, Int, Int, Int)) => Boolean = { - case (minConfNum, _, _, maxHeight) => + + val MaxLimit = 2500 + + private def isLegalOffset(offset: Int): Boolean = offset >= 0 + + private def isLegalLimit(limit: Int): Boolean = limit >= 1 && limit <= MaxLimit + + private val isLegalBoxParamCombination: ((Int, Int, Int, Int, Int, Int)) => Boolean = { + case (minConfNum, _, _, maxHeight, limit, offset) => + isLegalOffset(offset) && + isLegalLimit(limit) && // maxInclusionHeight cannot be specified when we consider unconfirmed !(minConfNum == -1 && maxHeight != -1) } - val boxParams: Directive[(Int, Int, Int, Int)] = - parameters("minConfirmations".as[Int] ? 0, "maxConfirmations".as[Int] ? -1, "minInclusionHeight".as[Int] ? 0, "maxInclusionHeight".as[Int] ? -1) - .tfilter( - isLegalBoxParamCombination, - ValidationRejection("maxInclusionHeight cannot be specified when we consider unconfirmed") - ) + val boxParams: Directive[(Int, Int, Int, Int, Int, Int)] = + parameters( + "minConfirmations".as[Int] ? 0, + "maxConfirmations".as[Int] ? -1, + "minInclusionHeight".as[Int] ? 0, + "maxInclusionHeight".as[Int] ? -1, + "limit".as[Int] ? 500, + "offset".as[Int] ? 0 + ).tfilter( + isLegalBoxParamCombination, + ValidationRejection("maxInclusionHeight cannot be specified when we consider unconfirmed") + ) /** * Filter function that filters boxes by height or number of confirmations. diff --git a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala index 7669192f19..6a090e42d7 100644 --- a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala @@ -11,14 +11,13 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransacti import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.wallet.requests._ -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.ErgoBoxSerializer -import scorex.core.api.http.ApiError.{BadRequest, NotExists} +import org.ergoplatform.http.api.ApiError.{BadRequest, NotExists} import scorex.core.api.http.ApiResponse -import scorex.core.settings.RESTApiSettings import scorex.util.encode.Base16 import scala.concurrent.Future @@ -29,7 +28,7 @@ import akka.http.scaladsl.server.MissingQueryParamRejection case class WalletApiRoute(readersHolder: ActorRef, nodeViewActorRef: ActorRef, ergoSettings: ErgoSettings) - (implicit val context: ActorRefFactory) extends WalletApiOperations with ApiCodecs { + (implicit val context: ActorRefFactory) extends WalletApiOperations with ApiCodecs with ApiExtraCodecs with ApiRequestsCodecs { implicit val paymentRequestDecoder: PaymentRequestDecoder = new PaymentRequestDecoder(ergoSettings) implicit val assetIssueRequestDecoder: AssetIssueRequestDecoder = new AssetIssueRequestDecoder(ergoSettings) @@ -220,8 +219,8 @@ case class WalletApiRoute(readersHolder: ActorRef, val utx = gcr.unsignedTx val externalSecretsOpt = gcr.externalSecretsOpt - val extInputsOpt = gcr.inputs.map(ErgoWalletService.stringsToBoxes) - val extDataInputsOpt = gcr.dataInputs.map(ErgoWalletService.stringsToBoxes) + val extInputsOpt = gcr.inputs.map(ErgoWalletServiceUtils.stringsToBoxes) + val extDataInputsOpt = gcr.dataInputs.map(ErgoWalletServiceUtils.stringsToBoxes) withWalletOp(_.generateCommitmentsFor(utx, externalSecretsOpt, extInputsOpt, extDataInputsOpt).map(_.response)) { case Failure(e) => BadRequest(s"Bad request $gcr. ${Option(e.getMessage).getOrElse(e.toString)}") @@ -306,23 +305,26 @@ case class WalletApiRoute(readersHolder: ActorRef, } def unspentBoxesR: Route = (path("boxes" / "unspent") & get & boxParams) { - (minConfNum, maxConfNum, minHeight, maxHeight) => + (minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) => val considerUnconfirmed = minConfNum == -1 - withWallet { - _.walletBoxes(unspentOnly = true, considerUnconfirmed) - .map { - _.filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight)) + withWallet { wallet => + wallet.walletBoxes(unspentOnly = true, considerUnconfirmed) + .map { boxes => + boxes + .filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight)) + .slice(offset, offset + limit) } } } def boxesR: Route = (path("boxes") & get & boxParams) { - (minConfNum, maxConfNum, minHeight, maxHeight) => + (minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) => val considerUnconfirmed = minConfNum == -1 withWallet { _.walletBoxes(unspentOnly = false, considerUnconfirmed = considerUnconfirmed) .map { _.filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight)) + .slice(offset, offset + limit) } } } @@ -477,8 +479,8 @@ case class WalletApiRoute(readersHolder: ActorRef, def extractHintsR: Route = (path("extractHints") & post & entity(as[HintExtractionRequest])) { her => withWallet { w => - val extInputsOpt = her.inputs.map(ErgoWalletService.stringsToBoxes) - val extDataInputsOpt = her.dataInputs.map(ErgoWalletService.stringsToBoxes) + val extInputsOpt = her.inputs.map(ErgoWalletServiceUtils.stringsToBoxes) + val extDataInputsOpt = her.dataInputs.map(ErgoWalletServiceUtils.stringsToBoxes) w.extractHints(her.tx, her.real, her.simulated, extInputsOpt, extDataInputsOpt).map(_.transactionHintsBag) } diff --git a/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala b/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala index c171e5ab28..39091b904a 100644 --- a/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala +++ b/src/main/scala/org/ergoplatform/local/ErgoStatsCollector.scala @@ -10,14 +10,15 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.{ErgoStateReader, StateType} import org.ergoplatform.settings.{Algos, ErgoSettings, LaunchParameters, Parameters} import scorex.core.network.ConnectedPeer import scorex.core.network.NetworkController.ReceivableMessages.{GetConnectedPeers, GetPeersStatus} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.network.ErgoSyncTracker import scorex.util.ScorexLogging -import scorex.core.network.peer.PeersStatus +import org.ergoplatform.network.peer.PeersStatus import java.net.URL import scala.concurrent.ExecutionContextExecutor @@ -86,7 +87,7 @@ class ErgoStatsCollector(readersHolder: ActorRef, bestHeaderOpt = h.bestHeaderOpt, headersScore = h.bestHeaderOpt.flatMap(m => h.scoreOf(m.id)), fullBlocksScore = h.bestFullBlockOpt.flatMap(m => h.scoreOf(m.id)), - genesisBlockIdOpt = h.headerIdsAtHeight(ErgoHistory.GenesisHeight).headOption, + genesisBlockIdOpt = h.headerIdsAtHeight(GenesisHeight).headOption, stateRoot = Some(Algos.encode(s.rootDigest)), stateVersion = Some(s.version), parameters = s.stateContext.currentParameters @@ -112,7 +113,7 @@ class ErgoStatsCollector(readersHolder: ActorRef, case ChangedHistory(h: ErgoHistory@unchecked) if h.isInstanceOf[ErgoHistory] => if (nodeInfo.genesisBlockIdOpt.isEmpty) { - nodeInfo = nodeInfo.copy(genesisBlockIdOpt = h.headerIdsAtHeight(ErgoHistory.GenesisHeight).headOption) + nodeInfo = nodeInfo.copy(genesisBlockIdOpt = h.headerIdsAtHeight(GenesisHeight).headOption) } nodeInfo = nodeInfo.copy(bestFullBlockOpt = h.bestFullBlockOpt, diff --git a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala index 6578613c5e..d46670287f 100644 --- a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala +++ b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala @@ -9,9 +9,9 @@ import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.settings.ErgoSettings import scorex.core.network.Broadcast import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.RecheckMempool +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.RecheckMempool import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} -import scorex.core.network.message.{InvData, InvSpec, Message} +import org.ergoplatform.network.message.{InvData, InvSpec, Message} import scorex.util.ScorexLogging import scala.concurrent.duration._ diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 17ef8af2a9..a42b50e6db 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -12,11 +12,12 @@ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, HeaderWithoutPow} import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, LocallyGeneratedModifier} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.EliminateTransactions import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} -import org.ergoplatform.nodeView.history.ErgoHistory.Height -import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} +import org.ergoplatform.nodeView.LocallyGeneratedModifier +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height +import org.ergoplatform.nodeView.history.{ErgoHistoryReader, ErgoHistoryUtils} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoStateReader} import org.ergoplatform.settings.{ErgoSettings, ErgoValidationSettingsUpdate, Parameters} @@ -567,7 +568,7 @@ object CandidateGenerator extends ScorexLogging { ) val ext = deriveWorkMessage(candidate) log.info( - s"Got candidate block at height ${ErgoHistory.heightOf(candidate.parentOpt) + 1}" + + s"Got candidate block at height ${ErgoHistoryUtils.heightOf(candidate.parentOpt) + 1}" + s" with ${candidate.transactions.size} transactions, msg ${Base16.encode(ext.msg)}" ) Success( @@ -656,7 +657,7 @@ object CandidateGenerator extends ScorexLogging { stateContext: ErgoStateContext, assets: Coll[(TokenId, Long)] = Colls.emptyColl ): Seq[ErgoTransaction] = { - val chainSettings = stateContext.ergoSettings.chainSettings + val chainSettings = stateContext.chainSettings val propositionBytes = chainSettings.monetary.feePropositionBytes val emission = chainSettings.emissionRules diff --git a/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala b/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala index 4379ebd26b..82b8630abd 100644 --- a/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala +++ b/src/main/scala/org/ergoplatform/mining/ErgoMiner.scala @@ -5,10 +5,10 @@ import akka.pattern.StatusReply import org.ergoplatform.mining.CandidateGenerator.GenerateCandidate import org.ergoplatform.nodeView.state.DigestState import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.wallet.ErgoWalletActor.{FirstSecretResponse, GetFirstSecret, GetMiningPubKey, MiningPubKeyResponse} +import org.ergoplatform.nodeView.wallet.ErgoWalletActorMessages.{FirstSecretResponse, GetFirstSecret, GetMiningPubKey, MiningPubKeyResponse} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.GetDataFromCurrentView -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.FullBlockApplied import scorex.util.ScorexLogging import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProverWithDbAlgs.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProverWithDbAlgs.scala new file mode 100644 index 0000000000..d4bffa263d --- /dev/null +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProverWithDbAlgs.scala @@ -0,0 +1,120 @@ +package org.ergoplatform.modifiers.history.popow + +import org.ergoplatform.mining.difficulty.DifficultyAdjustment +import org.ergoplatform.nodeView.history.ErgoHistoryReader +import org.ergoplatform.settings.ChainSettings +import scorex.util.ModifierId + +import scala.collection.mutable +import scala.util.Try + +/** + * Container for NiPoPoW methods working with blockchain database instead of in-memory collections, + * for performance's sake. + */ +object NipopowProverWithDbAlgs { + + /** + * Computes NiPoPow proof for the chain stored in `histReader`'s database, + * or a prefix of the chain which contains a specific header (if `headerIdOpt` is specified). + * In the latter case, header will be the first header of the suffix of max length `k` + * (`suffixHead` field of the result). + */ + def prove(histReader: ErgoHistoryReader, + headerIdOpt: Option[ModifierId] = None, chainSettings: ChainSettings)(params: PoPowParams): Try[NipopowProof] = { + val diffAdjustment = new DifficultyAdjustment(chainSettings) + Try { + type Height = Int + + val k = params.k + val m = params.m + + require(params.k >= 1, s"$k < 1") + require(histReader.headersHeight >= k + m, s"Can not prove chain of size < ${k + m}") + + def linksWithIndexes(header: PoPowHeader): Seq[(ModifierId, Int)] = header.interlinks.tail.reverse.zipWithIndex + + def previousHeaderIdAtLevel(level: Int, currentHeader: PoPowHeader): Option[ModifierId] = { + linksWithIndexes(currentHeader).find(_._2 == level).map(_._1) + } + + @scala.annotation.tailrec + def collectLevel(prevHeaderId: ModifierId, + level: Int, + anchoringHeight: Height, + acc: Seq[PoPowHeader] = Seq.empty): Seq[PoPowHeader] = { + val prevHeader = histReader.popowHeader(prevHeaderId).get // to be caught in outer (prove's) Try + if (prevHeader.height < anchoringHeight) { + acc + } else { + val newAcc = prevHeader +: acc + previousHeaderIdAtLevel(level, prevHeader) match { + case Some(newPrevHeaderId) => collectLevel(newPrevHeaderId, level, anchoringHeight, newAcc) + case None => newAcc + } + } + } + + def provePrefix(initAnchoringHeight: Height, + lastHeader: PoPowHeader): Seq[PoPowHeader] = { + + val collected = mutable.TreeMap[ModifierId, PoPowHeader]() + + val levels = linksWithIndexes(lastHeader) + levels.foldRight(initAnchoringHeight) { case ((prevHeaderId, levelIdx), anchoringHeight) => + val levelHeaders = collectLevel(prevHeaderId, levelIdx, anchoringHeight) + levelHeaders.foreach(ph => collected.update(ph.id, ph)) + if (m < levelHeaders.length) { + levelHeaders(levelHeaders.length - m).height + } else { + anchoringHeight + } + } + collected.values.toSeq + } + + val (suffixHead, suffixTail) = headerIdOpt match { + case Some(headerId) => + val suffixHead = histReader.popowHeader(headerId).get // to be caught in outer (prove's) Try + val suffixTail = histReader.bestHeadersAfter(suffixHead.header, k - 1) + suffixHead -> suffixTail + case None => + val suffix = histReader.lastHeaders(k).headers + histReader.popowHeader(suffix.head.id).get -> suffix.tail // .get to be caught in outer (prove's) Try + } + + val storedHeights = mutable.Set[Height]() // cache to filter out duplicate headers + val prefixBuilder = mutable.ArrayBuilder.make[PoPowHeader]() + + val genesisHeight = 1 + prefixBuilder += histReader.popowHeader(genesisHeight).get // to be caught in outer (prove's) Try + storedHeights += genesisHeight + + if (params.continuous) { + // put headers needed to check difficulty of new blocks after suffix into prefix + val epochLength = chainSettings.eip37EpochLength.getOrElse(chainSettings.epochLength) + diffAdjustment.heightsForNextRecalculation(suffixHead.height, epochLength).foreach { height => + // check that header in or after suffix is not included, otherwise, sorting by height would be broken + if (height < suffixHead.height) { + histReader.popowHeader(height).foreach { ph => + prefixBuilder += ph + storedHeights += ph.height + } + } + } + } + + provePrefix(genesisHeight, suffixHead).foreach { ph => + if (!storedHeights.contains(ph.height)) { + prefixBuilder += ph + storedHeights += ph.height + } + } + + val prefix = prefixBuilder.result().sortBy(_.height) + + NipopowProof(new NipopowAlgos(chainSettings), m, k, prefix, suffixHead, suffixTail, params.continuous) + } + } + +} diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 352a41ab70..3ab7e92f54 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -2,43 +2,40 @@ package org.ergoplatform.network import akka.actor.SupervisorStrategy.{Restart, Stop} import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorRef, ActorRefFactory, DeathPactException, OneForOneStrategy, Props} -import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} import org.ergoplatform.modifiers.{BlockSection, ManifestTypeId, NetworkObjectTypeId, SnapshotsInfoTypeId, UtxoSnapshotChunkTypeId} -import org.ergoplatform.modifiers.history.popow.NipopowProof import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} -import org.ergoplatform.nodeView.history._ -import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} -import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader} -import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsHealthy, ChainIsStuck, GetNodeViewChanges, IsChainHealthy, ModifiersFromRemote} +import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.settings.{Algos, ErgoSettings, NetworkSettings} import org.ergoplatform.nodeView.ErgoNodeViewHolder._ -import scorex.core.consensus.{Equal, Fork, Nonsense, Older, Unknown, Younger} +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsHealthy, ChainIsStuck, GetNodeViewChanges, IsChainHealthy, ModifiersFromRemote, TransactionFromRemote} import scorex.core.network.ModifiersStatus.Requested -import scorex.core.{NodeViewModifier, idsToString} +import org.ergoplatform.core.idsToString import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoSetSnapshotPersistence, UtxoStateReader} -import scorex.core.network.message._ -import org.ergoplatform.nodeView.wallet.ErgoWalletReader -import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} +import org.ergoplatform.network.message._ +import org.ergoplatform.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} import scorex.core.network._ import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPeers} -import scorex.core.network.message.{InvData, Message, ModifiersData} -import scorex.core.settings.NetworkSettings -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.MalformedModifierError +import org.ergoplatform.network.message.{InvData, Message, ModifiersData} +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.MalformedModifierError import scorex.util.{ModifierId, ScorexLogging} import scorex.core.network.DeliveryTracker -import scorex.core.network.peer.PenaltyType +import org.ergoplatform.network.peer.PenaltyType import scorex.crypto.hash.Digest32 import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height -import scorex.core.serialization.{ErgoSerializer, ManifestSerializer, SubtreeSerializer} -import scorex.core.transaction.TooHighCostError +import org.ergoplatform.NodeViewModifier +import org.ergoplatform.consensus.{Equal, Fork, Nonsense, Older, Unknown, Younger} +import org.ergoplatform.modifiers.history.{ADProofs, ADProofsSerializer, BlockTransactions, BlockTransactionsSerializer} +import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSerializer} +import org.ergoplatform.modifiers.transaction.TooHighCostError +import org.ergoplatform.serialization.{ErgoSerializer, ManifestSerializer, SubtreeSerializer} import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.splitDigest import scala.annotation.tailrec @@ -59,6 +56,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker: DeliveryTracker)(implicit ex: ExecutionContext) extends Actor with Synchronizer with ScorexLogging with ScorexEncoding { + import org.ergoplatform.network.ErgoNodeViewSynchronizer._ + type EncodedManifestId = ModifierId override val supervisorStrategy: OneForOneStrategy = OneForOneStrategy( @@ -707,7 +706,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, typeId: NetworkObjectTypeId.Value, requestedModifiers: Map[ModifierId, Array[Byte]], remote: ConnectedPeer): Unit = { - Constants.modifierSerializers.get(typeId) match { + modifierSerializers.get(typeId) match { case Some(serializer: ErgoSerializer[BlockSection]@unchecked) => // parse all modifiers and put them to modifiers cache val parsed: Iterable[BlockSection] = parseModifiers(requestedModifiers, typeId, serializer, remote) @@ -1614,127 +1613,13 @@ object ErgoNodeViewSynchronizer { case object CheckModifiersToDownload - object ReceivableMessages { - - // getLocalSyncInfo messages - case object SendLocalSyncInfo - - /** - * Check delivery of modifier with type `modifierTypeId` and id `modifierId`. - * `source` may be defined if we expect modifier from concrete peer or None if - * we just need some modifier, but don't know who have it - * - */ - case class CheckDelivery(source: ConnectedPeer, - modifierTypeId: NetworkObjectTypeId.Value, - modifierId: ModifierId) - - trait PeerManagerEvent - - case class HandshakedPeer(remote: ConnectedPeer) extends PeerManagerEvent - - case class DisconnectedPeer(peer: ConnectedPeer) extends PeerManagerEvent - - trait NodeViewHolderEvent - - trait NodeViewChange extends NodeViewHolderEvent - - case class ChangedHistory(reader: ErgoHistoryReader) extends NodeViewChange - - case class ChangedMempool(mempool: ErgoMemPoolReader) extends NodeViewChange - - case class ChangedVault(reader: ErgoWalletReader) extends NodeViewChange - - case class ChangedState(reader: ErgoStateReader) extends NodeViewChange - - /** - * Event which is published when rollback happened (on finding a better chain) - * @param branchPoint - block id which is last in the chain after rollback (before applying blocks from a fork) - */ - case class Rollback(branchPoint: ModifierId) extends NodeViewHolderEvent - - case object RollbackFailed extends NodeViewHolderEvent - - // hierarchy of events regarding modifiers application outcome - trait ModificationOutcome extends NodeViewHolderEvent - - trait InitialTransactionCheckOutcome extends ModificationOutcome { - val transaction: UnconfirmedTransaction - } - - case class FailedTransaction(transaction: UnconfirmedTransaction, error: Throwable) extends InitialTransactionCheckOutcome - - case class SuccessfulTransaction(transaction: UnconfirmedTransaction) extends InitialTransactionCheckOutcome - - /** - * Transaction declined by the mempool (not permanently invalidated, so pool can accept it in future) - */ - case class DeclinedTransaction(transaction: UnconfirmedTransaction) extends InitialTransactionCheckOutcome - - /** - * Transaction which was failed not immediately but after sitting for some time in the mempool or during block - * candidate generation - */ - case class FailedOnRecheckTransaction(id : ModifierId, error: Throwable) extends ModificationOutcome - - /** - * A signal that block section with id `modifierId` was invalidated due to `error`, but it may be valid in future - */ - case class RecoverableFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome - - /** - * A signal that block section with id `modifierId` was permanently invalidated during stateless checks - */ - case class SyntacticallyFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome - - /** - * Signal associated with stateful validation of a block section - */ - case class SemanticallyFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome - - /** - * Signal associated with stateless validation of a block section - */ - case class SyntacticallySuccessfulModifier(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId) extends ModificationOutcome - - /** - * Signal sent by node view holder when a full block is applied to state - * @param header - full block's header - */ - case class FullBlockApplied(header: Header) extends ModificationOutcome - - /** - * Signal sent after block sections processing (validation and application to state) done - * @param headersCacheSize - headers cache size after processing - * @param blockSectionsCacheSize - block sections cache size after processing - * @param cleared - blocks removed from cache being overfull - */ - case class BlockSectionsProcessingCacheUpdate(headersCacheSize: Int, - blockSectionsCacheSize: Int, - cleared: (NetworkObjectTypeId.Value, Seq[ModifierId])) - - /** - * Command to re-check mempool to clean transactions become invalid while sitting in the mempool up - * @param state - up-to-date state to check transaction against - * @param mempool - mempool to check - */ - case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) - - /** - * Signal for a central node view holder component to initialize UTXO state from UTXO set snapshot - * stored in the local database - * - * @param blockHeight - height of a block corresponding to the UTXO set snapshot - * @param blockId - id of a block corresponding to the UTXO set snapshot - */ - case class InitStateFromSnapshot(blockHeight: Height, blockId: ModifierId) - - /** - * Command for a central node view holder component to process NiPoPoW proof, - * and possibly initialize headers chain from a best NiPoPoW proof known, when enough proofs collected - * @param nipopowProof - proof to initialize history from - */ - case class ProcessNipopow(nipopowProof: NipopowProof) - } - + /** + * Serializers for block sections and transactions + */ + val modifierSerializers: Map[NetworkObjectTypeId.Value, ErgoSerializer[_ <: NodeViewModifier]] = + Map(Header.modifierTypeId -> HeaderSerializer, + Extension.modifierTypeId -> ExtensionSerializer, + BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, + ADProofs.modifierTypeId -> ADProofsSerializer, + ErgoTransaction.modifierTypeId -> ErgoTransactionSerializer) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerMessages.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerMessages.scala new file mode 100644 index 0000000000..e1cc4de78d --- /dev/null +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerMessages.scala @@ -0,0 +1,145 @@ +package org.ergoplatform.network + +import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction +import org.ergoplatform.modifiers.NetworkObjectTypeId +import org.ergoplatform.nodeView.history._ +import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} +import org.ergoplatform.nodeView.wallet.ErgoWalletReader +import scorex.core.network.ConnectedPeer +import scorex.util.ModifierId +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.modifiers.history.popow.NipopowProof + +/** + * Repository of messages processed ErgoNodeViewSynchronizer actor + */ +object ErgoNodeViewSynchronizerMessages { + /** + * Signal which is instructing ErgoNodeViewSynchronizer to send sync message to peers (when it is needed) + */ + case object SendLocalSyncInfo + + /** + * Check delivery of modifier with type `modifierTypeId` and id `modifierId`. + * `source` may be defined if we expect modifier from concrete peer or None if + * we just need some modifier, but don't know who have it + * + */ + case class CheckDelivery(source: ConnectedPeer, + modifierTypeId: NetworkObjectTypeId.Value, + modifierId: ModifierId) + + trait PeerManagerEvent + + case class HandshakedPeer(remote: ConnectedPeer) extends PeerManagerEvent + + case class DisconnectedPeer(peer: ConnectedPeer) extends PeerManagerEvent + + trait NodeViewHolderEvent + + trait NodeViewChange extends NodeViewHolderEvent + + case class ChangedHistory(reader: ErgoHistoryReader) extends NodeViewChange + + case class ChangedMempool(mempool: ErgoMemPoolReader) extends NodeViewChange + + case class ChangedVault(reader: ErgoWalletReader) extends NodeViewChange + + case class ChangedState(reader: ErgoStateReader) extends NodeViewChange + + /** + * Event which is published when rollback happened (on finding a better chain) + * + * @param branchPoint - block id which is last in the chain after rollback (before applying blocks from a fork) + */ + case class Rollback(branchPoint: ModifierId) extends NodeViewHolderEvent + + case object RollbackFailed extends NodeViewHolderEvent + + // hierarchy of events regarding modifiers application outcome + trait ModificationOutcome extends NodeViewHolderEvent + + trait InitialTransactionCheckOutcome extends ModificationOutcome { + val transaction: UnconfirmedTransaction + } + + case class FailedTransaction(transaction: UnconfirmedTransaction, error: Throwable) extends InitialTransactionCheckOutcome + + case class SuccessfulTransaction(transaction: UnconfirmedTransaction) extends InitialTransactionCheckOutcome + + /** + * Transaction declined by the mempool (not permanently invalidated, so pool can accept it in future) + */ + case class DeclinedTransaction(transaction: UnconfirmedTransaction) extends InitialTransactionCheckOutcome + + /** + * Transaction which was failed not immediately but after sitting for some time in the mempool or during block + * candidate generation + */ + case class FailedOnRecheckTransaction(id: ModifierId, error: Throwable) extends ModificationOutcome + + /** + * A signal that block section with id `modifierId` was invalidated due to `error`, but it may be valid in future + */ + case class RecoverableFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + + /** + * A signal that block section with id `modifierId` was permanently invalidated during stateless checks + */ + case class SyntacticallyFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + + /** + * Signal associated with stateful validation of a block section + */ + case class SemanticallyFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + + /** + * Signal associated with stateless validation of a block section + */ + case class SyntacticallySuccessfulModifier(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId) extends ModificationOutcome + + /** + * Signal sent by node view holder when a full block is applied to state + * + * @param header - full block's header + */ + case class FullBlockApplied(header: Header) extends ModificationOutcome + + /** + * Signal sent after block sections processing (validation and application to state) done + * + * @param headersCacheSize - headers cache size after processing + * @param blockSectionsCacheSize - block sections cache size after processing + * @param cleared - blocks removed from cache being overfull + */ + case class BlockSectionsProcessingCacheUpdate(headersCacheSize: Int, + blockSectionsCacheSize: Int, + cleared: (NetworkObjectTypeId.Value, Seq[ModifierId])) + + /** + * Command to re-check mempool to clean transactions become invalid while sitting in the mempool up + * + * @param state - up-to-date state to check transaction against + * @param mempool - mempool to check + */ + case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) + + /** + * Signal for a central node view holder component to initialize UTXO state from UTXO set snapshot + * stored in the local database + * + * @param blockHeight - height of a block corresponding to the UTXO set snapshot + * @param blockId - id of a block corresponding to the UTXO set snapshot + */ + case class InitStateFromSnapshot(blockHeight: Height, blockId: ModifierId) + + /** + * Command for a central node view holder component to process NiPoPoW proof, + * and possibly initialize headers chain from a best NiPoPoW proof known, when enough proofs collected + * + * @param nipopowProof - proof to initialize history from + */ + case class ProcessNipopow(nipopowProof: NipopowProof) +} diff --git a/src/main/scala/org/ergoplatform/network/ErgoPeerStatus.scala b/src/main/scala/org/ergoplatform/network/ErgoPeerStatus.scala index 03f909912b..32b73f31b5 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoPeerStatus.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoPeerStatus.scala @@ -1,11 +1,9 @@ package org.ergoplatform.network import io.circe.{Encoder, Json} -import org.ergoplatform.nodeView.history.ErgoHistory.Height -import scorex.core.app.Version -import scorex.core.consensus.PeerChainStatus +import org.ergoplatform.consensus.PeerChainStatus +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import scorex.core.network.ConnectedPeer -import org.ergoplatform.nodeView.history.ErgoHistory.Time /** * Container for status of another peer diff --git a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala index 3c406f9034..b307202821 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala @@ -1,16 +1,16 @@ package org.ergoplatform.network -import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader, ErgoSyncInfo, ErgoSyncInfoV1, ErgoSyncInfoV2} -import org.ergoplatform.nodeView.history.ErgoHistory.{Height, Time} -import scorex.core.consensus.{Fork, Older, PeerChainStatus, Unknown} +import org.ergoplatform.consensus.{Fork, Older, PeerChainStatus, Unknown} +import org.ergoplatform.nodeView.history.{ErgoHistoryReader, ErgoSyncInfo, ErgoSyncInfoV1, ErgoSyncInfoV2} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ +import org.ergoplatform.settings.NetworkSettings import scorex.core.network.ConnectedPeer -import scorex.core.settings.NetworkSettings import scorex.util.ScorexLogging import scala.collection.mutable import scala.concurrent.duration._ -import scorex.core.utils.MapPimpMutable +import org.ergoplatform.utils.MapPimpMutable /** * Data structures and methods to keep status of peers, find ones with expired status to send sync message etc @@ -86,7 +86,7 @@ final case class ErgoSyncTracker(networkSettings: NetworkSettings) extends Score val seniorsBefore = numOfSeniors() statuses.adjust(peer){ case None => - ErgoPeerStatus(peer, status, height.getOrElse(ErgoHistory.EmptyHistoryHeight), None, None) + ErgoPeerStatus(peer, status, height.getOrElse(EmptyHistoryHeight), None, None) case Some(existingPeer) => existingPeer.copy(status = status, height = height.getOrElse(existingPeer.height)) } diff --git a/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala index ed831e470e..e3b357eeb4 100644 --- a/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/VersionBasedPeerFilteringRule.scala @@ -1,6 +1,5 @@ package org.ergoplatform.network -import scorex.core.app.Version import scorex.core.network.ConnectedPeer /** diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/org/ergoplatform/network/message/BasicMessagesRepo.scala similarity index 91% rename from src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala rename to src/main/scala/org/ergoplatform/network/message/BasicMessagesRepo.scala index 673182b615..41ba8a43a5 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/org/ergoplatform/network/message/BasicMessagesRepo.scala @@ -1,19 +1,18 @@ -package scorex.core.network.message +package org.ergoplatform.network.message +import org.ergoplatform.NodeViewModifier import org.ergoplatform.modifiers.NetworkObjectTypeId +import org.ergoplatform.network.{Handshake, PeerSpec, PeerSpecSerializer} import org.ergoplatform.nodeView.state.SnapshotsInfo import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos -import scorex.core.consensus.SyncInfo -import scorex.core.network._ -import scorex.core.network.message.Message.MessageCode -import scorex.core.serialization.ErgoSerializer -import scorex.core.NodeViewModifier +import org.ergoplatform.network.message.MessageConstants.MessageCode import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} import org.ergoplatform.sdk.wallet.Constants.ModifierIdLength + import scala.collection.immutable /** @@ -21,29 +20,11 @@ import scala.collection.immutable */ case class ModifiersData(typeId: NetworkObjectTypeId.Value, modifiers: Map[ModifierId, Array[Byte]]) -case class InvData(typeId: NetworkObjectTypeId.Value, ids: Seq[ModifierId]) - case class NipopowProofData(m: Int, k: Int, headerId: Option[ModifierId]) { def headerIdBytesOpt: Option[Array[Byte]] = headerId.map(Algos.decode).flatMap(_.toOption) } -/** - * The `SyncInfo` message requests an `Inv` message that provides modifier ids - * required be sender to synchronize his blockchain with the recipient. - * It allows a peer which has been disconnected or started for the first - * time to get the data it needs to request the blocks it hasn't seen. - * - * Payload of this message should be determined in underlying applications. - */ -class SyncInfoMessageSpec[SI <: SyncInfo](serializer: ErgoSerializer[SI]) extends MessageSpecV1[SI] { - - override val messageCode: MessageCode = 65: Byte - override val messageName: String = "Sync" - override def serialize(data: SI, w: Writer): Unit = serializer.serialize(data, w) - - override def parse(r: Reader): SI = serializer.parse(r) -} /** * The `Inv` message (inventory message) transmits one or more inventories of @@ -180,7 +161,7 @@ object ModifiersSpec extends MessageSpecV1[ModifiersData] with ScorexLogging { * messages to arrive over time. */ object GetPeersSpec extends MessageSpecV1[Unit] { - override val messageCode: Message.MessageCode = 1: Byte + override val messageCode: MessageCode = 1: Byte override val messageName: String = "GetPeers message" @@ -194,7 +175,7 @@ object GetPeersSpec extends MessageSpecV1[Unit] { object PeersSpec { - val messageCode: Message.MessageCode = 2: Byte + val messageCode: MessageCode = 2: Byte val messageName: String = "Peers message" @@ -206,7 +187,7 @@ object PeersSpec { */ class PeersSpec(peersLimit: Int) extends MessageSpecV1[Seq[PeerSpec]] { - override val messageCode: Message.MessageCode = PeersSpec.messageCode + override val messageCode: MessageCode = PeersSpec.messageCode override val messageName: String = PeersSpec.messageName diff --git a/src/main/scala/scorex/core/network/message/Message.scala b/src/main/scala/org/ergoplatform/network/message/Message.scala similarity index 86% rename from src/main/scala/scorex/core/network/message/Message.scala rename to src/main/scala/org/ergoplatform/network/message/Message.scala index 94c68e8d83..994e01ddeb 100644 --- a/src/main/scala/scorex/core/network/message/Message.scala +++ b/src/main/scala/org/ergoplatform/network/message/Message.scala @@ -1,10 +1,10 @@ -package scorex.core.network.message +package org.ergoplatform.network.message import akka.actor.DeadLetterSuppression import scorex.core.network.ConnectedPeer import scala.util.{Success, Try} - +import org.ergoplatform.network.message.MessageConstants._ /** * Wrapper for a network message, whether come from external peer or generated locally @@ -20,8 +20,6 @@ case class Message[Content](spec: MessageSpec[Content], source: Option[ConnectedPeer]) extends DeadLetterSuppression { - import Message._ - /** * Message data bytes */ @@ -52,13 +50,3 @@ case class Message[Content](spec: MessageSpec[Content], } } - -object Message { - type MessageCode = Byte - - val MagicLength: Int = 4 - - val ChecksumLength: Int = 4 - - val HeaderLength: Int = MagicLength + 5 -} diff --git a/src/main/scala/scorex/core/network/message/MessageSerializer.scala b/src/main/scala/org/ergoplatform/network/message/MessageSerializer.scala similarity index 95% rename from src/main/scala/scorex/core/network/message/MessageSerializer.scala rename to src/main/scala/org/ergoplatform/network/message/MessageSerializer.scala index 18c28c210e..d98e662e9c 100644 --- a/src/main/scala/scorex/core/network/message/MessageSerializer.scala +++ b/src/main/scala/org/ergoplatform/network/message/MessageSerializer.scala @@ -1,4 +1,4 @@ -package scorex.core.network.message +package org.ergoplatform.network.message import java.nio.ByteOrder @@ -10,7 +10,7 @@ import scala.util.Try class MessageSerializer(specs: Seq[MessageSpec[_]], magicBytes: Array[Byte]) { - import Message.{ChecksumLength, HeaderLength, MagicLength} + import MessageConstants.{ChecksumLength, HeaderLength, MagicLength} import scala.language.existentials diff --git a/src/main/scala/scorex/core/network/peer/PeerDatabase.scala b/src/main/scala/org/ergoplatform/network/peer/PeerDatabase.scala similarity index 97% rename from src/main/scala/scorex/core/network/peer/PeerDatabase.scala rename to src/main/scala/org/ergoplatform/network/peer/PeerDatabase.scala index bf9f518bb5..5040fbe561 100644 --- a/src/main/scala/scorex/core/network/peer/PeerDatabase.scala +++ b/src/main/scala/org/ergoplatform/network/peer/PeerDatabase.scala @@ -1,7 +1,6 @@ -package scorex.core.network.peer - -import org.ergoplatform.nodeView.history.ErgoHistory +package org.ergoplatform.network.peer +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream} import java.net.{InetAddress, InetSocketAddress} import org.ergoplatform.settings.ErgoSettings @@ -30,7 +29,7 @@ final class PeerDatabase(settings: ErgoSettings) extends ScorexLogging { /** * banned peer ip -> ban expiration timestamp */ - private var blacklist = Map.empty[InetAddress, ErgoHistory.Time] + private var blacklist = Map.empty[InetAddress, Time] /** * penalized peer ip -> (accumulated penalty score, last penalty timestamp) diff --git a/src/main/scala/scorex/core/network/peer/PeerInfo.scala b/src/main/scala/org/ergoplatform/network/peer/PeerInfo.scala similarity index 89% rename from src/main/scala/scorex/core/network/peer/PeerInfo.scala rename to src/main/scala/org/ergoplatform/network/peer/PeerInfo.scala index b95bafa938..8b191d5a3d 100644 --- a/src/main/scala/scorex/core/network/peer/PeerInfo.scala +++ b/src/main/scala/org/ergoplatform/network/peer/PeerInfo.scala @@ -1,9 +1,10 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer + +import org.ergoplatform.network.{PeerSpec, PeerSpecSerializer, Version} import java.net.InetSocketAddress -import scorex.core.app.Version -import scorex.core.network.{ConnectionDirection, Incoming, Outgoing, PeerSpec, PeerSpecSerializer} -import scorex.core.serialization.ErgoSerializer +import scorex.core.network.{ConnectionDirection, Incoming, Outgoing} +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} /** @@ -43,7 +44,7 @@ object PeerInfo { } /** - * Serializer of [[scorex.core.network.peer.PeerInfo]] + * Serializer of [[org.ergoplatform.network.peer.PeerInfo]] */ object PeerInfoSerializer extends ErgoSerializer[PeerInfo] { diff --git a/src/main/scala/scorex/core/network/peer/PeerManager.scala b/src/main/scala/org/ergoplatform/network/peer/PeerManager.scala similarity index 99% rename from src/main/scala/scorex/core/network/peer/PeerManager.scala rename to src/main/scala/org/ergoplatform/network/peer/PeerManager.scala index 5e40359bbe..65a6d24c7e 100644 --- a/src/main/scala/scorex/core/network/peer/PeerManager.scala +++ b/src/main/scala/org/ergoplatform/network/peer/PeerManager.scala @@ -1,8 +1,8 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer import java.net.{InetAddress, InetSocketAddress} - import akka.actor.{Actor, ActorRef, ActorSystem, Props} +import org.ergoplatform.network.PeerSpec import org.ergoplatform.settings.ErgoSettings import scorex.core.app.ScorexContext import scorex.core.network._ diff --git a/src/main/scala/scorex/core/network/peer/PenaltyType.scala b/src/main/scala/org/ergoplatform/network/peer/PenaltyType.scala similarity index 95% rename from src/main/scala/scorex/core/network/peer/PenaltyType.scala rename to src/main/scala/org/ergoplatform/network/peer/PenaltyType.scala index ac53def8e9..07409469f6 100644 --- a/src/main/scala/scorex/core/network/peer/PenaltyType.scala +++ b/src/main/scala/org/ergoplatform/network/peer/PenaltyType.scala @@ -1,4 +1,4 @@ -package scorex.core.network.peer +package org.ergoplatform.network.peer /** * A trait describing all possible types of the network participant misbehavior. diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala index b99d81c0eb..70ed8d8f3f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala @@ -4,7 +4,7 @@ import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.ErgoHistory import scorex.core.{LRUCache, ModifiersCache} -import scorex.core.validation.MalformedModifierError +import org.ergoplatform.validation.MalformedModifierError import scorex.util.ScorexLogging import scala.util.Failure diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index fdcc3bb17f..8f81b7f201 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -2,27 +2,25 @@ package org.ergoplatform.nodeView import akka.actor.SupervisorStrategy.Escalate import akka.actor.{Actor, ActorRef, ActorSystem, OneForOneStrategy, Props} -import org.ergoplatform.ErgoApp -import org.ergoplatform.ErgoApp.CriticalSystemException +import org.ergoplatform.{CriticalSystemException, ErgoApp, TransactionsCarryingPersistentNodeViewModifier} +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NetworkObjectTypeId} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.mempool.ErgoMemPool -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.ProcessingOutcome import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.wallet.ErgoWallet import org.ergoplatform.wallet.utils.FileUtils -import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, LaunchParameters, NetworkType} -import scorex.core._ -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, LaunchParameters, NetworkType, ScorexSettings} +import org.ergoplatform.core._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.{BlockAppliedTransactions, CurrentView, DownloadRequest} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ import org.ergoplatform.modifiers.history.{ADProofs, HistoryModifierSerializer} -import scorex.core.consensus.ProgressInfo -import scorex.core.settings.ScorexSettings -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.RecoverableModifierError +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.RecoverableModifierError import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor @@ -618,7 +616,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val recoveredStateTry = firstExtensionOpt .fold[Try[ErgoStateContext]](Failure(new Exception("Could not find extension to recover from")) - )(ext => ErgoStateContext.recover(settings.chainSettings.genesisStateDigest, ext, lastHeaders)(settings)) + )(ext => ErgoStateContext.recover(settings.chainSettings.genesisStateDigest, ext, lastHeaders)(settings.chainSettings)) .flatMap { ctx => val recoverVersion = idToVersion(lastHeaders.last.id) val recoverRoot = bestFullBlock.header.stateRoot @@ -737,8 +735,6 @@ object ErgoNodeViewHolder { */ case class RecheckedTransactions(unconfirmedTxs: Iterable[UnconfirmedTransaction]) - case class LocallyGeneratedModifier(pmod: BlockSection) - case class EliminateTransactions(ids: Seq[ModifierId]) case object IsChainHealthy diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoReadersHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoReadersHolder.scala index 339ae47b80..a7df32ee3f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoReadersHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoReadersHolder.scala @@ -8,7 +8,7 @@ import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.ErgoStateReader import org.ergoplatform.nodeView.wallet.ErgoWalletReader import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import scorex.util.ScorexLogging import scala.concurrent.duration._ diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index dc63c806d3..c001dd8e64 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.history import akka.actor.ActorContext +import org.ergoplatform.consensus.ProgressInfo import java.io.File -import org.ergoplatform.ErgoLikeContext import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} @@ -12,10 +12,9 @@ import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages.S import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{IndexedHeightKey, NewestVersion, NewestVersionBytes, SchemaVersionKey, getIndex} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors._ -import org.ergoplatform.settings._ +import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.LoggingUtil -import scorex.core.consensus.ProgressInfo -import scorex.core.validation.RecoverableModifierError +import org.ergoplatform.validation.RecoverableModifierError import scorex.util.{ModifierId, ScorexLogging, idToBytes} import scala.util.{Failure, Success, Try} @@ -234,25 +233,6 @@ trait ErgoHistory object ErgoHistory extends ScorexLogging { - /** - * Type for time, represents machine-specific timestamp of a transaction - * or block section, as miliseconds passed since beginning of UNIX - * epoch on the machine - */ - type Time = Long - - type Height = ErgoLikeContext.Height // Int - type Score = BigInt - type Difficulty = BigInt - type NBits = Long - - val CharsetName = "UTF-8" - - val EmptyHistoryHeight: Int = 0 - val GenesisHeight: Int = EmptyHistoryHeight + 1 // first block has height == 1 - - def heightOf(headerOpt: Option[Header]): Int = headerOpt.map(_.height).getOrElse(EmptyHistoryHeight) - def historyDir(settings: ErgoSettings): File = { val dir = new File(s"${settings.directory}/history") dir.mkdirs() @@ -314,26 +294,6 @@ object ErgoHistory extends ScorexLogging { repairIfNeeded(history) - // temporary hack which is injecting nipopow proof to the database to make it possible to bootstrap with - // nipopows + utxo set snapshot soon after 5.0.13 release - // todo: remove after height 1,096,693 on the mainnet - val bestHeaderHeight = history.headersHeight - if (bestHeaderHeight > 1054000 && bestHeaderHeight < 1096693 && history.readPopowProofBytesFromDb().isEmpty) { - // we store nipopow proof for height 1,044,469 corresponding to UTXO set snapshot - // @ # 1,044,479 already taken by 5.0.12 nodes - val block1044469Id = "25a11667e38e62412522c062d90b073afd9ed9551080ff4e0a67d1757ce18b98" - history.popowProofBytes( - history.P2PNipopowProofM, - history.P2PNipopowProofK, - Some(ModifierId @@ block1044469Id)) match { - case Success(proofBytes) => - log.info("Writing nipopow proof bytes for height 1,044,469") - db.insert(Array(history.NipopowSnapshotHeightKey -> proofBytes), Array.empty[BlockSection]) - case Failure(e) => - log.warn("Can't dump NiPoPoW proof bytes for height 1,044,469", e) - } - } - log.info("History database read") if(ergoSettings.nodeSettings.extraIndex) // start extra indexer, if enabled context.system.eventStream.publish(StartExtraIndexer(history)) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index 7dfd9c9ba4..3fde451134 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -1,18 +1,18 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.NodeViewComponent +import org.ergoplatform.consensus.{ContainsModifiers, Equal, Fork, ModifierSemanticValidity, Older, PeerChainStatus, Unknown, Younger} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NetworkObjectTypeId, NonHeaderBlockSection} -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.{EmptyHistoryHeight, GenesisHeight, Height} import org.ergoplatform.nodeView.history.extra.ExtraIndex import org.ergoplatform.nodeView.history.storage._ -import org.ergoplatform.nodeView.history.storage.modifierprocessors._ +import org.ergoplatform.nodeView.history.storage.modifierprocessors.{BlockSectionProcessor, HeadersProcessor} import org.ergoplatform.settings.{ErgoSettings, NipopowSettings} -import scorex.core.NodeViewComponent -import scorex.core.consensus.{ContainsModifiers, Equal, Fork, ModifierSemanticValidity, Older, PeerChainStatus, Unknown, Younger} -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.MalformedModifierError +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.MalformedModifierError import scorex.util.{ModifierId, ScorexLogging} import scala.annotation.tailrec @@ -247,8 +247,8 @@ trait ErgoHistoryReader syncInfo.lastHeaderIds.map(b => Header.modifierTypeId -> b) } else if (syncInfo.lastHeaderIds.isEmpty) { // if other node has no headers yet, send up to `size` headers from genesis - val heightTo = Math.min(headersHeight, size + ErgoHistory.EmptyHistoryHeight) - (ErgoHistory.GenesisHeight to heightTo).flatMap { height => + val heightTo = Math.min(headersHeight, size + EmptyHistoryHeight) + (GenesisHeight to heightTo).flatMap { height => bestHeaderIdAtHeight(height).map(id => Header.modifierTypeId -> id) } } else { @@ -258,7 +258,7 @@ trait ErgoHistoryReader .find(m => isInBestChain(m)) .orElse(if (ids.contains(PreGenesisHeader.id)) Some(PreGenesisHeader.id) else None) branchingPointOpt.toSeq.flatMap { branchingPoint => - val otherNodeHeight = heightOf(branchingPoint).getOrElse(ErgoHistory.GenesisHeight) + val otherNodeHeight = heightOf(branchingPoint).getOrElse(GenesisHeight) val heightTo = Math.min(headersHeight, otherNodeHeight + size - 1) (otherNodeHeight to heightTo).flatMap { height => bestHeaderIdAtHeight(height).map(id => Header.modifierTypeId -> id) @@ -274,8 +274,8 @@ trait ErgoHistoryReader def continuationIdsV2(syncV2: ErgoSyncInfoV2, size: Int): ModifierIds = { if (syncV2.lastHeaders.isEmpty) { // if other node has no headers yet, send up to `size` headers from genesis - val heightTo = Math.min(headersHeight, size + ErgoHistory.EmptyHistoryHeight) - (ErgoHistory.GenesisHeight to heightTo) + val heightTo = Math.min(headersHeight, size + EmptyHistoryHeight) + (GenesisHeight to heightTo) .flatMap(height => bestHeaderIdAtHeight(height)) .map(h => Header.modifierTypeId -> h) //todo: remove modifierTypeId ? } else { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala index adbd35eecc..c15c5ebf09 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala @@ -4,7 +4,7 @@ import org.ergoplatform.{ErgoAddressEncoder, ErgoBox} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.history.extra.ExtraIndexer.fastIdToBytes import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.uniqueId -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import spire.implicits.cfor diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndex.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndex.scala new file mode 100644 index 0000000000..3d5bc02fe5 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndex.scala @@ -0,0 +1,11 @@ +package org.ergoplatform.nodeView.history.extra + +import scorex.util.{ModifierId, bytesToId} + +/** + * Base trait for all additional indexes made by ExtraIndexer + */ +trait ExtraIndex { + lazy val id: ModifierId = bytesToId(serializedId) + def serializedId: Array[Byte] +} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala index e8d1c37a33..45a703fad9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.extra -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} object ExtraIndexSerializer extends ErgoSerializer[ExtraIndex]{ diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala index f0b0e4e34c..dbda566f34 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala @@ -1,15 +1,15 @@ package org.ergoplatform.nodeView.history.extra -import akka.actor.{Actor, ActorRef, ActorSystem, Props} +import akka.actor.{Actor, ActorRef, ActorSystem, Props, Stash} import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder, Pay2SAddress} +import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder, GlobalConstants, Pay2SAddress} import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{FullBlockApplied, Rollback} -import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{GlobalBoxIndexKey, GlobalTxIndexKey, IndexedHeightKey, getIndex} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{FullBlockApplied, Rollback} +import org.ergoplatform.nodeView.history.extra.ExtraIndexer._ import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} -import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages.{GetSegmentTreshold, StartExtraIndexer} +import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages._ import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hashErgoTree import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.uniqueId import org.ergoplatform.nodeView.history.storage.HistoryStorage @@ -22,79 +22,87 @@ import java.nio.ByteBuffer import scala.collection.mutable.ArrayBuffer import spire.syntax.all.cfor +import java.util.concurrent.ConcurrentHashMap import scala.collection.mutable +import scala.collection.concurrent +import scala.concurrent.{ExecutionContextExecutor, Future} +import scala.jdk.CollectionConverters._ /** * Base trait for extra indexer actor and its test. */ -trait ExtraIndexerBase extends ScorexLogging { +trait ExtraIndexerBase extends Actor with Stash with ScorexLogging { - /** - * Indexed block height - */ - protected var indexedHeight: Int = 0 - - /** - * Indexed transaction count - */ - protected var globalTxIndex: Long = 0L - - /** - * Indexed box count - */ - protected var globalBoxIndex: Long = 0L - - /** - * Last block height when buffer contents were saved to database - */ - protected var lastWroteToDB: Int = 0 + private implicit val ec: ExecutionContextExecutor = context.dispatcher /** - * Max buffer size (determined by config) - */ + * Max buffer size (determined by config) + */ protected val saveLimit: Int /** - * Number of transaction/box numberic indexes object segments contain - */ - protected implicit val segmentTreshold: Int + * Number of transaction/box numeric indexes object segments contain + */ + protected implicit val segmentThreshold: Int /** - * Address encoder instance - */ + * Address encoder instance + */ protected implicit val addressEncoder: ErgoAddressEncoder /** - * Flag to signal when indexer has reached current block height - */ - protected var caughtUp: Boolean = false - - /** - * Flag to signal a rollback - */ - protected var rollback: Boolean = false - - /** - * Database handle - */ + * Database handle + */ protected var _history: ErgoHistory = _ protected def chainHeight: Int = _history.fullBlockHeight + protected def history: ErgoHistoryReader = _history.getReader + protected def historyStorage: HistoryStorage = _history.historyStorage // fast access buffers protected val general: ArrayBuffer[ExtraIndex] = ArrayBuffer.empty[ExtraIndex] - protected val boxes: mutable.HashMap[ModifierId,IndexedErgoBox] = mutable.HashMap.empty[ModifierId,IndexedErgoBox] - protected val trees: mutable.HashMap[ModifierId,IndexedErgoAddress] = mutable.HashMap.empty[ModifierId,IndexedErgoAddress] - protected val tokens: mutable.HashMap[ModifierId,IndexedToken] = mutable.HashMap.empty[ModifierId,IndexedToken] - protected val segments: mutable.HashMap[ModifierId,Segment[_]] = mutable.HashMap.empty[ModifierId,Segment[_]] + protected val boxes: mutable.HashMap[ModifierId, IndexedErgoBox] = mutable.HashMap.empty[ModifierId, IndexedErgoBox] + protected val trees: mutable.HashMap[ModifierId, IndexedErgoAddress] = mutable.HashMap.empty[ModifierId, IndexedErgoAddress] + protected val tokens: mutable.HashMap[ModifierId, IndexedToken] = mutable.HashMap.empty[ModifierId, IndexedToken] + protected val segments: mutable.HashMap[ModifierId, Segment[_]] = mutable.HashMap.empty[ModifierId, Segment[_]] /** - * Input tokens in a transaction - */ + * Input tokens in a transaction, cleared after every transaction + */ private val inputTokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] + /** + * Holds upcoming blocks to be indexed, and when empty, it is filled back from multiple threads + */ + private val blockCache: concurrent.Map[Int, BlockTransactions] = new ConcurrentHashMap[Int, BlockTransactions]().asScala + private var readingUpTo: Int = 0 + + /** + * Get transactions for specified height, preferably from cache, or from database. + * If indexer is getting close to emptying cache, asynchronously reads 1000 blocks into it + * + * @param height - blockheight to get transations from + * @return transactions at height + */ + private def getBlockTransactionsAt(height: Int): BlockTransactions = { + val txs = blockCache.remove(height).getOrElse(history.bestBlockTransactionsAt(height).get) + if (height % 1000 == 0) blockCache.keySet.filter(_ < height).map(blockCache.remove) + if (readingUpTo - height < 300 && chainHeight - height > 1000) { + readingUpTo = math.min(height + 1001, chainHeight) + val blockNums = height + 1 to readingUpTo by 50 + blockNums.zip(blockNums.tail).map { range => // ranges of 50 blocks for each thread to read + Future { + (range._1 until range._2).foreach { blockNum => + blockCache.put(blockNum, history.bestBlockTransactionsAt(blockNum).get) + } + } + } + } + txs + } + /** * Spend an IndexedErgoBox from buffer or database. Also record tokens for later use in balance tracking logic. * @@ -125,34 +133,34 @@ trait ExtraIndexerBase extends ScorexLogging { * @param id - hash of the (ergotree) address * @param spendOrReceive - IndexedErgoBox to receive (Right) or spend (Left) */ - private def findAndUpdateTree(id: ModifierId, spendOrReceive: Either[IndexedErgoBox, IndexedErgoBox]): Unit = { + private def findAndUpdateTree(id: ModifierId, spendOrReceive: Either[IndexedErgoBox, IndexedErgoBox])(implicit state: IndexerState): Unit = { trees.get(id).map { tree => spendOrReceive match { - case Left(iEb) => tree.addTx(globalTxIndex).spendBox(iEb, Some(history)) // spend box - case Right(iEb) => tree.addTx(globalTxIndex).addBox(iEb) // receive box + case Left(iEb) => tree.addTx(state.globalTxIndex).spendBox(iEb, Some(history)) // spend box + case Right(iEb) => tree.addTx(state.globalTxIndex).addBox(iEb) // receive box } return } history.typedExtraIndexById[IndexedErgoAddress](id) match { // address not found in last saveLimit modifiers case Some(x) => spendOrReceive match { - case Left(iEb) => trees.put(id, x.addTx(globalTxIndex).spendBox(iEb, Some(history))) // spend box - case Right(iEb) => trees.put(id, x.addTx(globalTxIndex).addBox(iEb)) // receive box + case Left(iEb) => trees.put(id, x.addTx(state.globalTxIndex).spendBox(iEb, Some(history))) // spend box + case Right(iEb) => trees.put(id, x.addTx(state.globalTxIndex).addBox(iEb)) // receive box } case None => // address not found at all spendOrReceive match { case Left(iEb) => log.warn(s"Unknown address spent box ${bytesToId(iEb.box.id)}") // spend box should never happen by an unknown address - case Right(iEb) => trees.put(id, IndexedErgoAddress(id).initBalance.addTx(globalTxIndex).addBox(iEb)) // receive box, new address + case Right(iEb) => trees.put(id, IndexedErgoAddress(id).initBalance.addTx(state.globalTxIndex).addBox(iEb)) // receive box, new address } } } /** - * Add or subtract a box from a token in the buffer or in database. - * - * @param id - token id - * @param spendOrReceive - IndexedErgoBox to receive (Right) or spend (Left) - */ + * Add or subtract a box from a token in the buffer or in database. + * + * @param id - token id + * @param spendOrReceive - IndexedErgoBox to receive (Right) or spend (Left) + */ private def findAndUpdateToken(id: ModifierId, spendOrReceive: Either[IndexedErgoBox, IndexedErgoBox]): Unit = { tokens.get(id).map { token => spendOrReceive match { @@ -180,38 +188,36 @@ trait ExtraIndexerBase extends ScorexLogging { /** * Write buffered indexes to database and clear buffers. */ - private def saveProgress(writeLog: Boolean = true): Unit = { + private def saveProgress(state: IndexerState, writeLog: Boolean = true): Unit = { val start: Long = System.currentTimeMillis // perform segmentation on big addresses and save their internal segment buffer trees.values.foreach { tree => - if(tree.buffer.nonEmpty) { - tree.buffer.values.foreach(seg => segments.put(seg.id, seg)) - tree.buffer.clear() - } - if(tree.txs.length > segmentTreshold || tree.boxes.length > segmentTreshold) - tree.splitToSegments.foreach(seg => segments.put(seg.id, seg)) + tree.buffer.values.foreach(seg => segments.put(seg.id, seg)) + tree.splitToSegments.foreach(seg => segments.put(seg.id, seg)) } // perform segmentation on big tokens and save their internal segment buffer tokens.values.foreach { token => - if(token.buffer.nonEmpty) { - token.buffer.values.foreach(seg => segments.put(seg.id, seg)) - token.buffer.clear() - } - if(token.boxes.length > segmentTreshold) - token.splitToSegments.foreach(seg => segments.put(seg.id, seg)) + token.buffer.values.foreach(seg => segments.put(seg.id, seg)) + token.splitToSegments.foreach(seg => segments.put(seg.id, seg)) } // insert modifiers and progress info to db - historyStorage.insertExtra(Array((IndexedHeightKey, ByteBuffer.allocate(4).putInt(indexedHeight).array), - (GlobalTxIndexKey, ByteBuffer.allocate(8).putLong(globalTxIndex).array), - (GlobalBoxIndexKey,ByteBuffer.allocate(8).putLong(globalBoxIndex).array)), - ((((general ++= boxes.values) ++= trees.values) ++= tokens.values) ++= segments.values).toArray) - - if (writeLog) + historyStorage.insertExtra( + Array( + (IndexedHeightKey, ByteBuffer.allocate(4).putInt(state.indexedHeight).array), + (GlobalTxIndexKey, ByteBuffer.allocate(8).putLong(state.globalTxIndex).array), + (GlobalBoxIndexKey, ByteBuffer.allocate(8).putLong(state.globalBoxIndex).array), + (RollbackToKey, ByteBuffer.allocate(4).putInt(state.rollbackTo).array) + ), + ((((general ++= boxes.values) ++= trees.values) ++= tokens.values) ++= segments.values).toArray + ) + + if (writeLog) { log.info(s"Processed ${trees.size} ErgoTrees with ${boxes.size} boxes and inserted them to database in ${System.currentTimeMillis - start}ms") + } // clear buffers for next batch general.clear() @@ -219,29 +225,29 @@ trait ExtraIndexerBase extends ScorexLogging { trees.clear() tokens.clear() segments.clear() - - lastWroteToDB = indexedHeight } /** * Process a batch of BlockTransactions into memory and occasionally write them to database. * - * @param bt - BlockTransaction to process - * @param height - height of the block containing the transactions + * @param state - current indexer state + * @param headerOpt - header to index blocktransactions of (used after caught up with chain) */ - protected def index(bt: BlockTransactions, height: Int): Unit = { - - if (rollback || // rollback in progress - (caughtUp && height <= indexedHeight)) // do not process older blocks again after caught up (due to actor message queue) - return + protected def index(state: IndexerState, headerOpt: Option[Header] = None): IndexerState = { + val bt = headerOpt.flatMap { header => + history.typedModifierById[BlockTransactions](header.transactionsId) + }.getOrElse(getBlockTransactionsAt(state.indexedHeight)) + val height = headerOpt.map(_.height).getOrElse(state.indexedHeight) var boxCount: Int = 0 + implicit var newState: IndexerState = state // record transactions and boxes cfor(0)(_ < bt.txs.length, _ + 1) { n => val tx: ErgoTransaction = bt.txs(n) val inputs: Array[Long] = Array.ofDim[Long](tx.inputs.length) + val outputs: Array[Long] = Array.ofDim[Long](tx.outputs.length) inputTokens.clear() @@ -262,156 +268,182 @@ trait ExtraIndexerBase extends ScorexLogging { //process transaction outputs cfor(0)(_ < tx.outputs.size, _ + 1) { i => - val iEb: IndexedErgoBox = new IndexedErgoBox(height, None, None, tx.outputs(i), globalBoxIndex) + val iEb: IndexedErgoBox = new IndexedErgoBox(height, None, None, tx.outputs(i), newState.globalBoxIndex) boxes.put(iEb.id, iEb) // box by id - general += NumericBoxIndex(globalBoxIndex, iEb.id) // box id by global box number + general += NumericBoxIndex(newState.globalBoxIndex, iEb.id) // box id by global box number + outputs(i) = iEb.globalIndex // box by address findAndUpdateTree(hashErgoTree(iEb.box.ergoTree), Right(boxes(iEb.id))) // check if box is creating new tokens, if yes record them cfor(0)(_ < iEb.box.additionalTokens.length, _ + 1) { j => - if (!inputTokens.exists(x => x._1 == iEb.box.additionalTokens(j)._1)) { + if (!inputTokens.exists(x => java.util.Arrays.equals(x._1.toArray, iEb.box.additionalTokens(j)._1.toArray))) { val token = IndexedToken.fromBox(iEb, j) tokens.get(token.tokenId) match { case Some(t) => // same new token created in multiple boxes -> add amounts - val newToken = IndexedToken(t.tokenId, t.boxId, t.amount + token.amount, t.name, t.description, t.decimals, t.boxes) - newToken.buffer ++= t.buffer - tokens.put(token.tokenId, newToken) + tokens.put(token.tokenId, t.addEmissionAmount(token.amount)) case None => tokens.put(token.tokenId, token) // new token } } findAndUpdateToken(iEb.box.additionalTokens(j)._1.toModifierId, Right(iEb)) } - globalBoxIndex += 1 + newState = newState.incrementBoxIndex boxCount += 1 } //process transaction - general += IndexedErgoTransaction(tx.id, height, globalTxIndex, inputs) - general += NumericTxIndex(globalTxIndex, tx.id) + general += IndexedErgoTransaction.fromTx(tx, n, height, newState.globalTxIndex, inputs, outputs) + general += NumericTxIndex(newState.globalTxIndex, tx.id) - globalTxIndex += 1 + newState = newState.incrementTxIndex } log.info(s"Buffered block $height / $chainHeight [txs: ${bt.txs.length}, boxes: $boxCount] (buffer: $modCount / $saveLimit)") - if (caughtUp) { - - indexedHeight = height // update height here after caught up with chain - - if (modCount >= saveLimit || // modifier limit reached to write to db - history.fullBlockHeight == history.headersHeight) // write to db every block after chain synced - saveProgress() - - } else if (modCount >= saveLimit) - saveProgress() // active syncing, write to db after modifier limit - - } - - /** - * Main indexer loop that tries to catch up with the already present blocks in database. - */ - protected def run(): Unit = { - - indexedHeight = getIndex(IndexedHeightKey, history).getInt - globalTxIndex = getIndex(GlobalTxIndexKey, history).getLong - globalBoxIndex = getIndex(GlobalBoxIndexKey, history).getLong - - log.info(s"Started extra indexer at height $indexedHeight") - - while (indexedHeight < chainHeight && !rollback) { - indexedHeight += 1 - index(history.bestBlockTransactionsAt(indexedHeight).get, indexedHeight) - } - - saveProgress(false) // flush any remaining data - - if (rollback) - log.info("Stopping indexer to perform rollback") - else { - caughtUp = true - log.info("Indexer caught up with chain") - } - + val maxHeight = headerOpt.map(_.height).getOrElse(chainHeight) + newState.copy(caughtUp = newState.indexedHeight == maxHeight) } /** * Remove all indexes after a given height and revert address balances. * + * @param state - current state of indexer * @param height - starting height */ - protected def removeAfter(height: Int): Unit = { + protected def removeAfter(state: IndexerState, height: Int): IndexerState = { - saveProgress(false) - log.info(s"Rolling back indexes from $indexedHeight to $height") + var newState: IndexerState = state - val lastTxToKeep: ErgoTransaction = history.bestBlockTransactionsAt(height).get.txs.last - val txTarget: Long = history.typedExtraIndexById[IndexedErgoTransaction](lastTxToKeep.id).get.globalIndex - val boxTarget: Long = history.typedExtraIndexById[IndexedErgoBox](bytesToId(lastTxToKeep.outputs.last.id)).get.globalIndex - val toRemove: ArrayBuffer[ModifierId] = ArrayBuffer.empty[ModifierId] + saveProgress(newState, writeLog = false) + log.info(s"Rolling back indexes from ${state.indexedHeight} to $height") - // remove all tx indexes - globalTxIndex -= 1 - while(globalTxIndex > txTarget) { - val tx: IndexedErgoTransaction = NumericTxIndex.getTxByNumber(history, globalTxIndex).get - tx.inputNums.map(NumericBoxIndex.getBoxByNumber(history, _).get).foreach { iEb => // undo all spendings + try { + val lastTxToKeep: ErgoTransaction = history.bestBlockTransactionsAt(height).get.txs.last + val txTarget: Long = history.typedExtraIndexById[IndexedErgoTransaction](lastTxToKeep.id).get.globalIndex + val boxTarget: Long = history.typedExtraIndexById[IndexedErgoBox](bytesToId(lastTxToKeep.outputs.last.id)).get.globalIndex + val toRemove: ArrayBuffer[ModifierId] = ArrayBuffer.empty[ModifierId] - iEb.spendingHeightOpt = None - iEb.spendingTxIdOpt = None + // remove all tx indexes + newState = newState.decrementTxIndex + while (newState.globalTxIndex > txTarget) { + val tx: IndexedErgoTransaction = NumericTxIndex.getTxByNumber(history, newState.globalTxIndex).get + tx.inputNums.map(NumericBoxIndex.getBoxByNumber(history, _).get).foreach { iEb => // undo all spendings - val address = history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(iEb.box.ergoTree)).get.addBox(iEb, record = false) - address.findAndModBox(iEb.globalIndex, history) - historyStorage.insertExtra(Array.empty, Array[ExtraIndex](iEb, address) ++ address.buffer.values) + iEb.spendingHeightOpt = None + iEb.spendingTxIdOpt = None + + val address = history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(iEb.box.ergoTree)).get.addBox(iEb, record = false) + address.findAndModBox(iEb.globalIndex, history) + historyStorage.insertExtra(Array.empty, Array[ExtraIndex](iEb, address) ++ address.buffer.values) + + cfor(0)(_ < iEb.box.additionalTokens.length, _ + 1) { i => + history.typedExtraIndexById[IndexedToken](IndexedToken.fromBox(iEb, i).id).map { token => + token.findAndModBox(iEb.globalIndex, history) + historyStorage.insertExtra(Array.empty, Array[ExtraIndex](token) ++ token.buffer.values) + } + } + } + toRemove += tx.id // tx by id + toRemove += bytesToId(NumericTxIndex.indexToBytes(newState.globalTxIndex)) // tx id by number + newState = newState.decrementTxIndex + } + newState = newState.incrementTxIndex + // remove all box indexes, tokens and address balances + newState = newState.decrementBoxIndex + while (newState.globalBoxIndex > boxTarget) { + val iEb: IndexedErgoBox = NumericBoxIndex.getBoxByNumber(history, newState.globalBoxIndex).get cfor(0)(_ < iEb.box.additionalTokens.length, _ + 1) { i => history.typedExtraIndexById[IndexedToken](IndexedToken.fromBox(iEb, i).id).map { token => - token.findAndModBox(iEb.globalIndex, history) - historyStorage.insertExtra(Array.empty, Array[ExtraIndex](token) ++ token.buffer.values) + if (token.boxId == iEb.id) { // token created, delete + toRemove += token.id + log.info(s"Removing token ${token.tokenId} created in box ${iEb.id} at height ${iEb.inclusionHeight}") + } else // no token created, update + toRemove ++= token.rollback(txTarget, boxTarget, _history) } } + history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(iEb.box.ergoTree)).map { address => + address.spendBox(iEb) + toRemove ++= address.rollback(txTarget, boxTarget, _history) + } + toRemove += iEb.id // box by id + toRemove += bytesToId(NumericBoxIndex.indexToBytes(newState.globalBoxIndex)) // box id by number + newState = newState.decrementBoxIndex } - toRemove += tx.id // tx by id - toRemove += bytesToId(NumericTxIndex.indexToBytes(globalTxIndex)) // tx id by number - globalTxIndex -= 1 + newState = newState.incrementBoxIndex + + // Save changes + newState = newState.copy(indexedHeight = height, rollbackTo = 0, caughtUp = true) + historyStorage.removeExtra(toRemove.toArray) + saveProgress(newState, writeLog = false) + } catch { + case t: Throwable => log.error(s"removeAfter during rollback failed due to: ${t.getMessage}", t) } - globalTxIndex += 1 - - // remove all box indexes, tokens and address balances - globalBoxIndex -= 1 - while(globalBoxIndex > boxTarget) { - val iEb: IndexedErgoBox = NumericBoxIndex.getBoxByNumber(history, globalBoxIndex).get - cfor(0)(_ < iEb.box.additionalTokens.length, _ + 1) { i => - history.typedExtraIndexById[IndexedToken](IndexedToken.fromBox(iEb, i).id).map { token => - if(token.boxId == iEb.id) // token created, delete - toRemove += token.id - else // no token created, update - toRemove ++= token.rollback(txTarget, boxTarget, _history) + + newState + } + + protected def loaded(state: IndexerState): Receive = { + + case Index() if !state.caughtUp && !state.rollbackInProgress => + val newState = index(state.incrementIndexedHeight) + if (modCount >= saveLimit) saveProgress(newState) + context.become(loaded(newState)) + self ! Index() + + case Index() if state.caughtUp => + if (modCount > 0) saveProgress(state) + blockCache.clear() + log.info("Indexer caught up with chain") + + // after the indexer caught up with the chain, stay up to date + case FullBlockApplied(header: Header) if state.caughtUp && !state.rollbackInProgress => + if (header.height == state.indexedHeight + 1) { // applied block is next in line + val newState: IndexerState = index(state.incrementIndexedHeight, Some(header)) + saveProgress(newState) + context.become(loaded(newState)) + } else if(header.height > state.indexedHeight + 1) { // applied block is ahead of indexer + context.become(loaded(state.copy(caughtUp = false))) + self ! Index() + } else // applied block has already been indexed, skipping duplicate + log.warn(s"Skipping block ${header.id} applied at height ${header.height}, indexed height is ${state.indexedHeight}") + + case Rollback(branchPoint: ModifierId) => + if (state.rollbackInProgress) { + log.warn(s"Rollback already in progress") + stash() + } else { + history.heightOf(branchPoint) match { + case Some(branchHeight) => + if (branchHeight < state.indexedHeight) { + context.become (loaded (state.copy (rollbackTo = branchHeight) ) ) + self ! RemoveAfter (branchHeight) + } + case None => + log.error(s"No rollback height found for $branchPoint") + val newState = state.copy(rollbackTo = 0) + context.become(loaded(newState)) + unstashAll() } } - history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(iEb.box.ergoTree)).map { address => - address.spendBox(iEb) - toRemove ++= address.rollback(txTarget, boxTarget, _history) - } - toRemove += iEb.id // box by id - toRemove += bytesToId(NumericBoxIndex.indexToBytes(globalBoxIndex)) // box id by number - globalBoxIndex -= 1 - } - globalBoxIndex += 1 - // Reset indexer flags - indexedHeight = height - caughtUp = false - rollback = false + case RemoveAfter(branchHeight: Int) if state.rollbackInProgress => + blockCache.clear() + readingUpTo = 0 + val newState = removeAfter(state, branchHeight) + context.become(loaded(newState)) + log.info(s"Successfully rolled back indexes to $branchHeight") + unstashAll() + + case GetSegmentThreshold => + sender ! segmentThreshold - // Save changes - saveProgress(false) - historyStorage.removeExtra(toRemove.toArray) + case _ => - log.info(s"Successfully rolled back indexes to $height") } } @@ -419,16 +451,17 @@ trait ExtraIndexerBase extends ScorexLogging { /** * Actor that constructs an index of database elements. + * * @param cacheSettings - cacheSettings to use for saveLimit size - * @param ae - ergo address encoder to use for handling addresses + * @param ae - ergo address encoder to use for handling addresses */ class ExtraIndexer(cacheSettings: CacheSettings, ae: ErgoAddressEncoder) - extends Actor with ExtraIndexerBase { + extends ExtraIndexerBase { override val saveLimit: Int = cacheSettings.history.extraCacheSize * 20 - override implicit val segmentTreshold: Int = 512 + override implicit val segmentThreshold: Int = 512 override implicit val addressEncoder: ErgoAddressEncoder = ae @@ -438,28 +471,28 @@ class ExtraIndexer(cacheSettings: CacheSettings, context.system.eventStream.subscribe(self, classOf[StartExtraIndexer]) } - override def postStop(): Unit = - log.info(s"Stopped extra indexer at height ${if(lastWroteToDB > 0) lastWroteToDB else indexedHeight}") - - override def receive: Receive = { + override def postStop(): Unit = { + log.error(s"Stopped extra indexer") + super.postStop() + } - case FullBlockApplied(header: Header) if caughtUp => - index(history.typedModifierById[BlockTransactions](header.transactionsId).get, header.height) // after the indexer caught up with the chain, stay up to date + override def preRestart(reason: Throwable, message: Option[Any]): Unit = { + log.error(s"Attempted extra indexer restart due to ${reason.getMessage} ", reason) + super.preRestart(reason, message) + } - case Rollback(branchPoint: ModifierId) if _history != null => // only rollback if indexing is enabled - val branchHeight: Int = history.heightOf(branchPoint).get - rollback = branchHeight < indexedHeight - if(rollback) { - removeAfter(branchHeight) - run() // restart indexer - } + override def receive: Receive = { case StartExtraIndexer(history: ErgoHistory) => _history = history - run() + val state = IndexerState.fromHistory(history) + context.become(loaded(state)) + log.info(s"Started extra indexer at height ${state.indexedHeight}") + self ! Index() + unstashAll() + + case _ => stash() - case GetSegmentTreshold => - sender ! segmentTreshold } } @@ -470,6 +503,7 @@ object ExtraIndexer { object ReceivableMessages { /** * Initialize ExtraIndexer and start indexing. + * * @param history - handle to database */ case class StartExtraIndexer(history: ErgoHistory) @@ -477,7 +511,19 @@ object ExtraIndexer { /** * Retreive the currently used segment treshold */ - case class GetSegmentTreshold() + case class GetSegmentThreshold() + + /** + * Index block at current indexer height + */ + case class Index() + + /** + * Remove and roll back all indexes after branchHeight + * + * @param branchHeight - height of last block to keep + */ + case class RemoveAfter(branchHeight: Int) } /** @@ -494,7 +540,7 @@ object ExtraIndexer { "0123456789abcdef".toCharArray.zipWithIndex.foreach { case (c, i) => index(c) = i.toByte } - "abcdef".toCharArray.foreach{ c => + "abcdef".toCharArray.foreach { c => index(c.toUpper) = index(c) } index @@ -502,39 +548,37 @@ object ExtraIndexer { /** * Faster id to bytes - no safety checks + * * @param id - ModifierId to convert to byte representation * @return an array of bytes */ private[extra] def fastIdToBytes(id: ModifierId): Array[Byte] = { val x: Array[Byte] = new Array[Byte](id.length / 2) - cfor(0)(_ < id.length, _ + 2) {i => x(i / 2) = ((hexIndex(id(i)) << 4) | hexIndex(id(i + 1))).toByte} + cfor(0)(_ < id.length, _ + 2) { i => x(i / 2) = ((hexIndex(id(i)) << 4) | hexIndex(id(i + 1))).toByte } x } /** - * Current newest database schema version. Used to force extra database resync. - */ - val NewestVersion: Int = 4 + * Current newest database schema version. Used to force extra database resync. + */ + val NewestVersion: Int = 5 val NewestVersionBytes: Array[Byte] = ByteBuffer.allocate(4).putInt(NewestVersion).array val IndexedHeightKey: Array[Byte] = Algos.hash("indexed height") val GlobalTxIndexKey: Array[Byte] = Algos.hash("txns height") val GlobalBoxIndexKey: Array[Byte] = Algos.hash("boxes height") + val RollbackToKey: Array[Byte] = Algos.hash("rollback to") val SchemaVersionKey: Array[Byte] = Algos.hash("schema version") def getIndex(key: Array[Byte], history: HistoryStorage): ByteBuffer = - ByteBuffer.wrap(history.modifierBytesById(bytesToId(key)).getOrElse(Array.fill[Byte](8){0})) + ByteBuffer.wrap(history.modifierBytesById(bytesToId(key)).getOrElse(Array.fill[Byte](8) { + 0 + })) def getIndex(key: Array[Byte], history: ErgoHistoryReader): ByteBuffer = getIndex(key, history.historyStorage) - def apply(chainSettings: ChainSettings, cacheSettings: CacheSettings)(implicit system: ActorSystem): ActorRef = - system.actorOf(Props.create(classOf[ExtraIndexer], cacheSettings, chainSettings.addressEncoder)) -} - -/** - * Base trait for all additional indexes made by ExtraIndexer - */ -trait ExtraIndex { - lazy val id: ModifierId = bytesToId(serializedId) - def serializedId: Array[Byte] + def apply(chainSettings: ChainSettings, cacheSettings: CacheSettings)(implicit system: ActorSystem): ActorRef = { + val props = Props.create(classOf[ExtraIndexer], cacheSettings, chainSettings.addressEncoder) + system.actorOf(props.withDispatcher(GlobalConstants.IndexerDispatcher)) + } } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala index f7408a497a..c95acc381d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala @@ -5,7 +5,7 @@ import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hashErgoTree import org.ergoplatform.settings.Algos -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.ErgoTree diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala index 1e1ad79c44..abb4318d2d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.ErgoBox import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.wallet.boxes.ErgoBoxSerializer -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala index 8b35587686..963365a03c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala @@ -3,9 +3,10 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.DataInput -import org.ergoplatform.modifiers.history.BlockTransactions +import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer +import scorex.crypto.authds.ADKey import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, bytesToId} import spire.implicits.cfor @@ -13,14 +14,21 @@ import spire.implicits.cfor /** * Minimum general information for transaction. Not storing the whole transation is done to save space. * @param txid - id of this transaction + * @param index - index of transaction in parent block * @param height - height of the block which includes this transaction + * @param size - size of this transaction in bytes * @param globalIndex - serial number of this transaction counting from block 1 - * @param inputNums - list of transaction inputs (needed for rollback) + * @param inputNums - list of transaction inputs + * @param outputNums - list of transaction outputs */ case class IndexedErgoTransaction(txid: ModifierId, + index: Int, height: Int, + size: Int, globalIndex: Long, - inputNums: Array[Long]) extends ExtraIndex { + inputNums: Array[Long], + outputNums: Array[Long], + dataInputs: Array[DataInput]) extends ExtraIndex { override lazy val id: ModifierId = txid override def serializedId: Array[Byte] = fastIdToBytes(id) @@ -28,22 +36,16 @@ case class IndexedErgoTransaction(txid: ModifierId, private var _blockId: ModifierId = ModifierId @@ "" private var _inclusionHeight: Int = 0 private var _timestamp: Header.Timestamp = 0L - private var _index: Int = 0 private var _numConfirmations: Int = 0 private var _inputs: IndexedSeq[IndexedErgoBox] = IndexedSeq.empty[IndexedErgoBox] - private var _dataInputs: IndexedSeq[DataInput] = IndexedSeq.empty[DataInput] private var _outputs: IndexedSeq[IndexedErgoBox] = IndexedSeq.empty[IndexedErgoBox] - private var _txSize: Int = 0 def blockId: ModifierId = _blockId def inclusionHeight: Int = _inclusionHeight def timestamp: Header.Timestamp = _timestamp - def index: Int = _index def numConfirmations: Int = _numConfirmations def inputs: IndexedSeq[IndexedErgoBox] = _inputs - def dataInputs: IndexedSeq[DataInput] = _dataInputs def outputs: IndexedSeq[IndexedErgoBox] = _outputs - def txSize: Int = _txSize /** * Get all information related to this transaction from database. @@ -53,17 +55,13 @@ case class IndexedErgoTransaction(txid: ModifierId, def retrieveBody(history: ErgoHistoryReader): IndexedErgoTransaction = { val header: Header = history.typedModifierById[Header](history.bestHeaderIdAtHeight(height).get).get - val blockTxs: BlockTransactions = history.typedModifierById[BlockTransactions](header.transactionsId).get _blockId = header.id _inclusionHeight = height _timestamp = header.timestamp - _index = blockTxs.txs.indices.find(blockTxs.txs(_).id == txid).get - _numConfirmations = history.bestFullBlockOpt.get.height - height - _inputs = blockTxs.txs(_index).inputs.map(input => history.typedExtraIndexById[IndexedErgoBox](bytesToId(input.boxId)).get) - _dataInputs = blockTxs.txs(_index).dataInputs - _outputs = blockTxs.txs(_index).outputs.map(output => history.typedExtraIndexById[IndexedErgoBox](bytesToId(output.id)).get) - _txSize = blockTxs.txs(_index).size + _numConfirmations = history.fullBlockHeight - height + _inputs = inputNums.flatMap(NumericBoxIndex.getBoxByNumber(history, _)) + _outputs = outputNums.flatMap(NumericBoxIndex.getBoxByNumber(history, _)) this } @@ -74,24 +72,41 @@ object IndexedErgoTransactionSerializer extends ErgoSerializer[IndexedErgoTransa override def serialize(iTx: IndexedErgoTransaction, w: Writer): Unit = { w.putUByte(iTx.serializedId.length) w.putBytes(iTx.serializedId) + w.putInt(iTx.index) w.putInt(iTx.height) + w.putInt(iTx.size) w.putLong(iTx.globalIndex) w.putUShort(iTx.inputNums.length) cfor(0)(_ < iTx.inputNums.length, _ + 1) { i => w.putLong(iTx.inputNums(i)) } + w.putUShort(iTx.outputNums.length) + cfor(0)(_ < iTx.outputNums.length, _ + 1) { i => w.putLong(iTx.outputNums(i)) } + w.putUShort(iTx.dataInputs.length) + cfor(0)(_ < iTx.dataInputs.length, _ + 1) { i => w.putBytes(iTx.dataInputs(i).boxId) } } override def parse(r: Reader): IndexedErgoTransaction = { val idLen = r.getUByte() val id = bytesToId(r.getBytes(idLen)) + val index = r.getInt() val height = r.getInt() + val size = r.getInt() val globalIndex = r.getLong() val inputCount: Int = r.getUShort() val inputNums: Array[Long] = Array.ofDim[Long](inputCount) cfor(0)(_ < inputCount, _ + 1) { i => inputNums(i) = r.getLong() } - IndexedErgoTransaction(id, height, globalIndex, inputNums) + val outputCount: Int = r.getUShort() + val outputNums: Array[Long] = Array.ofDim[Long](outputCount) + cfor(0)(_ < outputCount, _ + 1) { i => outputNums(i) = r.getLong() } + val dataInputsCount = r.getUShort() + val dataInputs: Array[DataInput] = Array.ofDim[DataInput](dataInputsCount) + cfor(0)(_ < dataInputsCount, _ + 1) { i => dataInputs(i) = DataInput(ADKey @@ r.getBytes(32)) } + IndexedErgoTransaction(id, index, height, size, globalIndex, inputNums, outputNums, dataInputs) } } object IndexedErgoTransaction { val extraIndexTypeId: ExtraIndexTypeId = 10.toByte + + def fromTx(tx: ErgoTransaction, index: Int, height: Int, globalIndex: Long, inputs: Array[Long], outputs: Array[Long]): IndexedErgoTransaction = + IndexedErgoTransaction(tx.id, index, height, tx.size, globalIndex, inputs, outputs, tx.dataInputs.toArray) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala index ec00019729..e29b0fc04d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala @@ -6,7 +6,7 @@ import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.{ByteColl, uniqueId} import org.ergoplatform.settings.Algos -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.CollectionConstant @@ -49,9 +49,10 @@ case class IndexedToken(tokenId: ModifierId, val toRemove: ArrayBuffer[ModifierId] = rollbackState(txTarget, boxTarget, history.getReader) - if (boxCount == 0) + if (boxCount == 0) { toRemove += id // all segments empty after rollback, delete parent - else + log.info(s"Removing token $tokenId because no more boxes are associated with it") + } else history.historyStorage.insertExtra(Array.empty, Array(this)) // save the changes made to this address toRemove.toArray @@ -100,6 +101,17 @@ case class IndexedToken(tokenId: ModifierId, override private[extra] def filterMempool(boxes: Seq[ErgoBox]): Seq[ErgoBox] = boxes.filter(_.additionalTokens.exists(_._1.toModifierId == tokenId)) + /** + * Increase emission amount of this token. Sometimes tokens get created in multiple boxes. + * @param plus - emission amount to add + * @return updated token + */ + private[extra] def addEmissionAmount(plus: Long): IndexedToken = { + val updated = IndexedToken(tokenId, boxId, amount + plus, name, description, decimals, boxes) + updated.buffer ++= buffer + updated + } + } object IndexedTokenSerializer extends ErgoSerializer[IndexedToken] { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexerState.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexerState.scala new file mode 100644 index 0000000000..05dec8d177 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexerState.scala @@ -0,0 +1,48 @@ +package org.ergoplatform.nodeView.history.extra + +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.extra.ExtraIndexer._ + +/** + * An immutable state for extra indexer + * @param indexedHeight - Indexed block height + * @param globalTxIndex - Indexed transaction count + * @param globalBoxIndex - Indexed box count + * @param rollbackTo - blockheight to rollback to, 0 if no rollback is in progress + * @param caughtUp - flag to indicate if the indexer is caught up with the chain and is listening for updates + */ +case class IndexerState(indexedHeight: Int, + globalTxIndex: Long, + globalBoxIndex: Long, + rollbackTo: Int, + caughtUp: Boolean) { + + def rollbackInProgress: Boolean = rollbackTo > 0 + + def incrementIndexedHeight: IndexerState = copy(indexedHeight = indexedHeight + 1) + + def incrementTxIndex: IndexerState = copy(globalTxIndex = globalTxIndex + 1) + def incrementBoxIndex: IndexerState = copy(globalBoxIndex = globalBoxIndex + 1) + + def decrementTxIndex: IndexerState = copy(globalTxIndex = globalTxIndex - 1) + def decrementBoxIndex: IndexerState = copy(globalBoxIndex = globalBoxIndex - 1) + +} + +object IndexerState { + + def fromHistory(history: ErgoHistory): IndexerState = { + val indexedHeight = getIndex(IndexedHeightKey, history).getInt + val globalTxIndex = getIndex(GlobalTxIndexKey, history).getLong + val globalBoxIndex = getIndex(GlobalBoxIndexKey, history).getLong + val rollbackTo = getIndex(RollbackToKey, history).getInt + IndexerState( + indexedHeight, + globalTxIndex, + globalBoxIndex, + rollbackTo, + caughtUp = indexedHeight == history.fullBlockHeight + ) + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/NumericIndex.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/NumericIndex.scala index bfdcdbd9ad..24b171668b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/NumericIndex.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/NumericIndex.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.settings.Algos -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/extra/Segment.scala b/src/main/scala/org/ergoplatform/nodeView/history/extra/Segment.scala index e1f82c7d8b..c45a5c9595 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/Segment.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/Segment.scala @@ -18,7 +18,7 @@ import scala.reflect.ClassTag /** * Class to manage the tracking of transactions/boxes in relation to some other object (ErgoTree/token). - * When [[ExtraIndexerBase.segmentTreshold]] number of transaction/box indexes are accumulated, new instances of the parent object are created to contain them. + * When [[ExtraIndexerBase.segmentThreshold]] number of transaction/box indexes are accumulated, new instances of the parent object are created to contain them. * This mechanism is used to prevent excessive serialization/deserialization delays caused by objects with a lot of transaction/box indexes. * @param parentId - identifier of parent object * @param factory - parent object factory @@ -96,15 +96,19 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId, buffer.get(segmentId) match { case Some(segment) => val i: Int = binarySearch(segment.boxes, boxNumAbs) - segment.boxes(i) = -segment.boxes(i) + if (i >= 0) { + segment.boxes(i) = -segment.boxes(i) + } else { + log.error(s"Box $boxNum not found in predicted segment of parent: ${segment.boxes.mkString("[", ",", "]")}") + } case None => - log.warn(s"Box $boxNum not found in any segment of parent when trying to spend") + log.error(s"Box $boxNum not found in any segment of parent") } } } /** - * Create an array of parent objects each containing [[ExtraIndexerBase.segmentTreshold]] number of transaction/box indexes. + * Create an array of parent objects each containing [[ExtraIndexerBase.segmentThreshold]] number of transaction/box indexes. * These objects have their ids calculated by "txSegmentId" and "boxSegmentId" respectively. * * @return array of parent objects @@ -307,8 +311,8 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId, txs.clear() txs ++= tmp if (txs.isEmpty && txSegmentCount > 0) { // entire current tx set removed, retrieving more from database if possible - val segmentId = txSegmentId(parentId, txSegmentCount - 1) - history.typedExtraIndexById[T](idMod(segmentId)).get.txs ++=: txs + val segmentId = idMod(txSegmentId(parentId, txSegmentCount - 1)) + txs ++= history.typedExtraIndexById[T](segmentId).get.txs toRemove += segmentId txSegmentCount -= 1 } @@ -320,8 +324,8 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId, boxes.clear() boxes ++= tmp if (boxes.isEmpty && boxSegmentCount > 0) { // entire current box set removed, retrieving more from database if possible - val segmentId = boxSegmentId(parentId, boxSegmentCount - 1) - history.typedExtraIndexById[T](idMod(segmentId)).get.boxes ++=: boxes + val segmentId = idMod(boxSegmentId(parentId, boxSegmentCount - 1)) + boxes ++= history.typedExtraIndexById[T](segmentId).get.boxes toRemove += segmentId boxSegmentCount -= 1 } @@ -418,19 +422,21 @@ object SegmentSerializer { } def serialize(s: Segment[_], w: Writer): Unit = { - w.putUInt(s.txs.length) + w.putInt(s.txs.length) cfor(0)(_ < s.txs.length, _ + 1) { i => w.putLong(s.txs(i)) } - w.putUInt(s.boxes.length) + w.putInt(s.boxes.length) cfor(0)(_ < s.boxes.length, _ + 1) { i => w.putLong(s.boxes(i)) } w.putInt(s.boxSegmentCount) w.putInt(s.txSegmentCount) } def parse(r: Reader, s: Segment[_]): Unit = { - val txnsLen: Long = r.getUInt() - cfor(0)(_ < txnsLen, _ + 1) { _ => s.txs.+=(r.getLong()) } - val boxesLen: Long = r.getUInt() - cfor(0)(_ < boxesLen, _ + 1) { _ => s.boxes.+=(r.getLong()) } + val txnsLen: Int = r.getInt() + s.txs.sizeHint(txnsLen) + cfor(0)(_ < txnsLen, _ + 1) { _ => s.txs += r.getLong() } + val boxesLen: Int = r.getInt() + s.boxes.sizeHint(boxesLen) + cfor(0)(_ < boxesLen, _ + 1) { _ => s.boxes += r.getLong() } s.boxSegmentCount = r.getInt() s.txSegmentCount = r.getInt() } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala index 2b4559af84..53e06b3738 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -6,7 +6,7 @@ import org.ergoplatform.modifiers.history.HistoryModifierSerializer import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.extra.{ExtraIndex, ExtraIndexSerializer, Segment} import org.ergoplatform.settings.{Algos, CacheSettings, ErgoSettings} -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.utils.ScorexEncoding import scorex.db.{ByteArrayWrapper, LDBFactory, LDBKVStore} import scorex.util.{ModifierId, ScorexLogging, idToBytes} @@ -154,7 +154,6 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, e objectsToInsert.map(mod => mod.serializedId), objectsToInsert.map(mod => ExtraIndexSerializer.toBytes(mod)) ) - cfor(0)(_ < objectsToInsert.length, _ + 1) { i => val ei = objectsToInsert(i); extraCache.put(ei.id, ei)} cfor(0)(_ < indexesToInsert.length, _ + 1) { i => extraStore.insert(indexesToInsert(i)._1, indexesToInsert(i)._2)} } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BlockSectionProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BlockSectionProcessor.scala index eec47cb0bc..653e592452 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BlockSectionProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BlockSectionProcessor.scala @@ -1,8 +1,8 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} -import scorex.core.consensus.ProgressInfo -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.consensus.ProgressInfo +import org.ergoplatform.modifiers.{BlockSection, NonHeaderBlockSection} +import org.ergoplatform.utils.ScorexEncoding import scala.util.Try diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/EmptyBlockSectionProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/EmptyBlockSectionProcessor.scala index 8f602453f5..f7d35e3ea8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/EmptyBlockSectionProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/EmptyBlockSectionProcessor.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} -import scorex.core.consensus.ProgressInfo +import org.ergoplatform.consensus.ProgressInfo +import org.ergoplatform.modifiers.{BlockSection, NonHeaderBlockSection} import scala.util.{Failure, Success, Try} diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockProcessor.scala index 714dbabce4..8c84852f09 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockProcessor.scala @@ -1,11 +1,12 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.Algos -import scorex.core.consensus.ProgressInfo import scorex.db.ByteArrayWrapper import scorex.util.{ModifierId, bytesToId, idToBytes} @@ -222,7 +223,7 @@ trait FullBlockProcessor extends HeadersProcessor { s"New best block is ${toApply.last.header.encodedId} " + s"with height ${toApply.last.header.height} " + s"updates block ${prevBest.map(_.encodedId).getOrElse("None")} " + - s"with height ${ErgoHistory.heightOf(prevBest.map(_.header))}" + s"with height ${ErgoHistoryUtils.heightOf(prevBest.map(_.header))}" } log.info(s"Full block ${appliedBlock.encodedId} appended, " + s"going to apply ${toApply.length}$toRemoveStr modifiers. $newStatusStr") @@ -285,6 +286,6 @@ object FullBlockProcessor { def emptyCache: IncompleteFullChainCache = IncompleteFullChainCache(TreeMap.empty(ord)) def chainStatusKey(id: ModifierId): ByteArrayWrapper = - ByteArrayWrapper(Algos.hash("main_chain".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + ByteArrayWrapper(Algos.hash("main_chain".getBytes(CharsetName) ++ idToBytes(id))) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockPruningProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockPruningProcessor.scala index d77b9e0def..95de235c49 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockPruningProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockPruningProcessor.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.ErgoSettings /** @@ -51,7 +51,7 @@ trait FullBlockPruningProcessor extends MinimalFullBlockHeightFunctions { // we have constant min full block height corresponding to first block after utxo set snapshot readMinimalFullBlockHeight() } else { - ErgoHistory.GenesisHeight // keep all blocks in history as no pruning set + GenesisHeight // keep all blocks in history as no pruning set } } else { // Start from config.blocksToKeep blocks back diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockSectionProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockSectionProcessor.scala index a702b88d5d..b9c36f987d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockSectionProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/FullBlockSectionProcessor.scala @@ -1,14 +1,14 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings.{Algos, ErgoValidationSettings} -import scorex.core.consensus.ProgressInfo -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{ModifierValidator, _} +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.{ModifierValidator, _} import scorex.db.ByteArrayWrapper import scorex.util.ModifierId diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/HeadersProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/HeadersProcessor.scala index 1d6371f430..0a801ccd5e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/HeadersProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/HeadersProcessor.scala @@ -1,23 +1,21 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints -import org.ergoplatform.ErgoApp.CriticalSystemException +import org.ergoplatform.CriticalSystemException import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.consensus.{ModifierSemanticValidity, ProgressInfo} import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.mining.difficulty.DifficultyAdjustment import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.ErgoHistory.{Difficulty, GenesisHeight, Height} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ -import scorex.core.consensus.ProgressInfo -import scorex.core.consensus.ModifierSemanticValidity -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} import scorex.db.ByteArrayWrapper import scorex.util._ import scorex.util.encode.Base16 @@ -65,13 +63,13 @@ trait HeadersProcessor extends ToDownloadProcessor with PopowProcessor with Scor // todo for performance reasons we may just use key like s"score$id" but this will require to resync the blockchain protected def headerScoreKey(id: ModifierId): ByteArrayWrapper = - ByteArrayWrapper(Algos.hash("score".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + ByteArrayWrapper(Algos.hash("score".getBytes(CharsetName) ++ idToBytes(id))) protected def headerHeightKey(id: ModifierId): ByteArrayWrapper = - ByteArrayWrapper(Algos.hash("height".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + ByteArrayWrapper(Algos.hash("height".getBytes(CharsetName) ++ idToBytes(id))) protected[history] def validityKey(id: ModifierId): ByteArrayWrapper = - ByteArrayWrapper(Algos.hash("validity".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + ByteArrayWrapper(Algos.hash("validity".getBytes(CharsetName) ++ idToBytes(id))) override def writeMinimalFullBlockHeight(height: Height): Unit = { historyStorage.insert( @@ -80,7 +78,7 @@ trait HeadersProcessor extends ToDownloadProcessor with PopowProcessor with Scor } override def readMinimalFullBlockHeight(): Height = { - historyStorage.getIndex(MinFullBlockHeightKey).map(Ints.fromByteArray).getOrElse(ErgoHistory.GenesisHeight) + historyStorage.getIndex(MinFullBlockHeightKey).map(Ints.fromByteArray).getOrElse(GenesisHeight) } def bestHeaderIdOpt: Option[ModifierId] = historyStorage.getIndex(BestHeaderKey).map(bytesToId) @@ -93,12 +91,12 @@ trait HeadersProcessor extends ToDownloadProcessor with PopowProcessor with Scor /** * @return height of best header */ - def headersHeight: Height = bestHeaderIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) + def headersHeight: Height = bestHeaderIdOpt.flatMap(id => heightOf(id)).getOrElse(EmptyHistoryHeight) /** * @return height of best header with all block sections */ - def fullBlockHeight: Height = bestFullBlockIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) + def fullBlockHeight: Height = bestFullBlockIdOpt.flatMap(id => heightOf(id)).getOrElse(EmptyHistoryHeight) /** * @param id - id of ErgoPersistentModifier @@ -386,7 +384,7 @@ trait HeadersProcessor extends ToDownloadProcessor with PopowProcessor with Scor private def validationState: ValidationState[Unit] = ModifierValidator(ErgoValidationSettings.initial) - private def time(): ErgoHistory.Time = System.currentTimeMillis() + private def time(): Time = System.currentTimeMillis() def validate(header: Header): ValidationResult[Unit] = { if (header.isGenesis) { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala index fc53aca3c0..08bcc1c4c5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height /** * Interface to methods providing updating and reading height of first full block stored in local database diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/PopowProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/PopowProcessor.scala index 97ee72f9c9..8baa167e40 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/PopowProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/PopowProcessor.scala @@ -1,15 +1,15 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.local.{CorrectNipopowProofVerificationResult, NipopowProofVerificationResult, NipopowVerifier} import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, NipopowProofSerializer, PoPowHeader, PoPowParams} -import org.ergoplatform.nodeView.history.ErgoHistory.GenesisHeight +import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProverWithDbAlgs, NipopowProof, NipopowProofSerializer, PoPowHeader, PoPowParams} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.GenesisHeight import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.settings.{ChainSettings, NipopowSettings} import org.ergoplatform.settings.Constants.HashLength -import scorex.core.consensus.ProgressInfo import scorex.db.ByteArrayWrapper import scorex.util.{ModifierId, ScorexLogging} @@ -108,7 +108,7 @@ trait PopowProcessor extends BasicReaders with ScorexLogging { */ def popowProof(m: Int, k: Int, headerIdOpt: Option[ModifierId]): Try[NipopowProof] = { val proofParams = PoPowParams(m, k, continuous = true) - nipopowAlgos.prove(historyReader, headerIdOpt = headerIdOpt)(proofParams) + NipopowProverWithDbAlgs.prove(historyReader, headerIdOpt = headerIdOpt, chainSettings)(proofParams) } /** diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ToDownloadProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ToDownloadProcessor.scala index a2851553c5..090610c3c2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ToDownloadProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/ToDownloadProcessor.scala @@ -15,7 +15,7 @@ import scala.annotation.tailrec trait ToDownloadProcessor extends FullBlockPruningProcessor with UtxoSetSnapshotProcessor with BasicReaders with ScorexLogging { - import scorex.core.utils.MapPimp + import org.ergoplatform.utils.MapPimp protected val settings: ErgoSettings diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala index 66f614da64..1d2972cfe9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala @@ -2,14 +2,15 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} +import org.ergoplatform.nodeView.history.ErgoHistoryReader +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings} -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.core.network.ConnectedPeer -import scorex.core.serialization.SubtreeSerializer +import org.ergoplatform.serialization.SubtreeSerializer import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore @@ -46,7 +47,7 @@ trait UtxoSetSnapshotProcessor extends MinimalFullBlockHeightFunctions with Scor * After first full-block block application not needed anymore. */ def isUtxoSnapshotApplied: Boolean = { - readMinimalFullBlockHeight() > ErgoHistory.GenesisHeight + readMinimalFullBlockHeight() > GenesisHeight } /** diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala index 75f626db88..df08cd4858 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPool.scala @@ -8,12 +8,12 @@ import org.ergoplatform.nodeView.state.{ErgoState, UtxoState} import org.ergoplatform.settings.{ErgoSettings, MonetarySettings, NodeConfigurationSettings} import scorex.util.{ModifierId, ScorexLogging, bytesToId} import OrderedTxPool.weighted -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils._ import spire.syntax.all.cfor import scala.annotation.tailrec import scala.collection.mutable -import scala.util.{Failure, Random, Success, Try} +import scala.util.{Failure, Success, Try} /** @@ -31,7 +31,6 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, (implicit settings: ErgoSettings) extends ErgoMemPoolReader with ScorexLogging { - import ErgoMemPool._ import EmissionRules.CoinsInOneErgo /** @@ -324,111 +323,12 @@ class ErgoMemPool private[mempool](private[mempool] val pool: OrderedTxPool, } object ErgoMemPool extends ScorexLogging { - /** - * Hierarchy of sorting strategies for mempool transactions + * Create empty mempool + * + * @param settings - node settings (to get mempool settings from) + * @return empty mempool */ - sealed trait SortingOption - - object SortingOption { - /** - * Sort transactions by fee paid for transaction size, so fee/byte - */ - case object FeePerByte extends SortingOption - - /** - * Sort transactions by fee paid for transaction contracts validation cost, so fee/execution unit - */ - case object FeePerCycle extends SortingOption - - /** - * @return randomly chosen mempool sorting strategy - */ - def random(): SortingOption = { - if (Random.nextBoolean()) { - FeePerByte - } else { - FeePerCycle - } - } - } - - /** - * Root of possible mempool transaction validation result family - */ - sealed trait ProcessingOutcome { - /** - * Time when transaction validation was started - */ - protected val validationStartTime: Long - - /** - * We assume that validation ends when this processing result class is constructed - */ - private val validationEndTime: Long = System.currentTimeMillis() - - /** - * 5.0 JIT costing was designed in a way that 1000 cost units are roughly corresponding to 1 ms of 1 CPU core - * on commodity hardware (of 2021). So if we do not know the exact cost of transaction, we can estimate it by - * tracking validation time and then getting estimated validation cost by multiplying the time (in ms) by 1000 - */ - val costPerMs = 1000 - - /** - * Estimated validation cost, see comment for `costPerMs` - */ - def cost: Int = { - val timeDiff = validationEndTime - validationStartTime - if (timeDiff == 0) { - costPerMs - } else if (timeDiff > 1000000) { - Int.MaxValue // shouldn't be here, so this branch is mostly to have safe .toInt below - } else { - (timeDiff * costPerMs).toInt - } - } - } - - object ProcessingOutcome { - - /** - * Object signalling that a transaction is accepted to the memory pool - */ - class Accepted(val tx: UnconfirmedTransaction, - override protected val validationStartTime: Long) extends ProcessingOutcome { - override val cost: Int = tx.lastCost.getOrElse(super.cost) - } - - /** - * Class signalling that a valid transaction was rejected as it is double-spending inputs of mempool transactions - * and has no bigger weight (fee/byte) than them on average. - * - * @param winnerTxIds - identifiers of transactions won in replace-by-fee auction - */ - class DoubleSpendingLoser(val winnerTxIds: Set[ModifierId], - override protected val validationStartTime: Long) extends ProcessingOutcome - - /** - * Class signalling that a transaction declined from being accepted into the memory pool - */ - class Declined(val e: Throwable, - override protected val validationStartTime: Long) extends ProcessingOutcome - - - /** - * Class signalling that a transaction turned out to be invalid when checked in the mempool - */ - class Invalidated(val e: Throwable, - override protected val validationStartTime: Long) extends ProcessingOutcome - - } - - /** - * Create empty mempool - * - * @param settings - node settings (to get mempool settings from) - * @return empty mempool - */ def empty(settings: ErgoSettings): ErgoMemPool = { val sortingOption = settings.nodeSettings.mempoolSorting sortingOption match { @@ -441,5 +341,4 @@ object ErgoMemPool extends ScorexLogging { sortingOption )(settings) } - } diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala index 884e7249e2..a3f31a1c61 100644 --- a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolReader.scala @@ -1,10 +1,10 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoBox.BoxId +import org.ergoplatform.NodeViewComponent +import org.ergoplatform.consensus.ContainsModifiers import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.OrderedTxPool.WeightedTxId -import scorex.core.NodeViewComponent -import scorex.core.consensus.ContainsModifiers import scorex.util.ModifierId trait ErgoMemPoolReader extends NodeViewComponent with ContainsModifiers[ErgoTransaction] { diff --git a/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolUtils.scala b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolUtils.scala new file mode 100644 index 0000000000..a672fa5a26 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolUtils.scala @@ -0,0 +1,110 @@ +package org.ergoplatform.nodeView.mempool + +import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction +import scorex.util.{ModifierId, ScorexLogging} +import scala.util.Random + +/** + * Additional types and functions used in ErgoMemPool + */ +object ErgoMemPoolUtils extends ScorexLogging { + + /** + * Hierarchy of sorting strategies for mempool transactions + */ + sealed trait SortingOption + + object SortingOption { + /** + * Sort transactions by fee paid for transaction size, so fee/byte + */ + case object FeePerByte extends SortingOption + + /** + * Sort transactions by fee paid for transaction contracts validation cost, so fee/execution unit + */ + case object FeePerCycle extends SortingOption + + /** + * @return randomly chosen mempool sorting strategy + */ + def random(): SortingOption = { + if (Random.nextBoolean()) { + FeePerByte + } else { + FeePerCycle + } + } + } + + /** + * Root of possible mempool transaction validation result family + */ + sealed trait ProcessingOutcome { + /** + * Time when transaction validation was started + */ + protected val validationStartTime: Long + + /** + * We assume that validation ends when this processing result class is constructed + */ + private val validationEndTime: Long = System.currentTimeMillis() + + /** + * 5.0 JIT costing was designed in a way that 1000 cost units are roughly corresponding to 1 ms of 1 CPU core + * on commodity hardware (of 2021). So if we do not know the exact cost of transaction, we can estimate it by + * tracking validation time and then getting estimated validation cost by multiplying the time (in ms) by 1000 + */ + val costPerMs = 1000 + + /** + * Estimated validation cost, see comment for `costPerMs` + */ + def cost: Int = { + val timeDiff = validationEndTime - validationStartTime + if (timeDiff == 0) { + costPerMs + } else if (timeDiff > 1000000) { + Int.MaxValue // shouldn't be here, so this branch is mostly to have safe .toInt below + } else { + (timeDiff * costPerMs).toInt + } + } + } + + object ProcessingOutcome { + + /** + * Object signalling that a transaction is accepted to the memory pool + */ + class Accepted(val tx: UnconfirmedTransaction, + override protected val validationStartTime: Long) extends ProcessingOutcome { + override val cost: Int = tx.lastCost.getOrElse(super.cost) + } + + /** + * Class signalling that a valid transaction was rejected as it is double-spending inputs of mempool transactions + * and has no bigger weight (fee/byte) than them on average. + * + * @param winnerTxIds - identifiers of transactions won in replace-by-fee auction + */ + class DoubleSpendingLoser(val winnerTxIds: Set[ModifierId], + override protected val validationStartTime: Long) extends ProcessingOutcome + + /** + * Class signalling that a transaction declined from being accepted into the memory pool + */ + class Declined(val e: Throwable, + override protected val validationStartTime: Long) extends ProcessingOutcome + + + /** + * Class signalling that a transaction turned out to be invalid when checked in the mempool + */ + class Invalidated(val e: Throwable, + override protected val validationStartTime: Long) extends ProcessingOutcome + + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/BoxHolder.scala b/src/main/scala/org/ergoplatform/nodeView/state/BoxHolder.scala index 3fe970cd77..a28814973f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/BoxHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/BoxHolder.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.db.ByteArrayWrapper import scala.collection.immutable.SortedMap diff --git a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala index 0711bc1a3c..3c5a3a4721 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala @@ -1,21 +1,20 @@ package org.ergoplatform.nodeView.state import java.io.File - import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.nodeView.state.ErgoState.ModifierProcessing import org.ergoplatform.settings._ import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.db.{ByteArrayWrapper, LDBVersionedStore} -import scorex.core._ -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.core._ +import org.ergoplatform.nodeView.LocallyGeneratedModifier +import org.ergoplatform.utils.ScorexEncoding import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging @@ -57,7 +56,7 @@ class DigestState protected(override val version: VersionTag, .fold[Try[ErgoBox]](Failure(new Exception(s"Box with id ${Algos.encode(id)} not found")))(Success(_)) } - ErgoState.execTransactions(transactions, currentStateContext)(checkBoxExistence) + ErgoState.execTransactions(transactions, currentStateContext, nodeSettings)(checkBoxExistence) .toTry .map(_ => ()) } @@ -89,7 +88,7 @@ class DigestState protected(override val version: VersionTag, @SuppressWarnings(Array("OptionGet")) override def rollbackTo(version: VersionTag): Try[DigestState] = { log.info(s"Rollback Digest State to version ${Algos.encoder.encode(version)}") - val versionBytes = scorex.core.versionToBytes(version) + val versionBytes = org.ergoplatform.core.versionToBytes(version) Try(store.rollbackTo(versionBytes)).map { _ => store.clean(nodeSettings.keepVersions) val rootHash = ADDigest @@ store.get(versionBytes).get @@ -145,7 +144,7 @@ class DigestState protected(override val version: VersionTag, val toUpdate = DigestState.metadata(newVersion, newRootHash, newStateContext) - store.update(scorex.core.versionToBytes(newVersion), Seq.empty, toUpdate).map { _ => + store.update(org.ergoplatform.core.versionToBytes(newVersion), Seq.empty, toUpdate).map { _ => new DigestState(newVersion, newRootHash, store, ergoSettings) } } @@ -165,7 +164,7 @@ object DigestState extends ScorexLogging with ScorexEncoding { val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) val toUpdate = DigestState.metadata(version, rootHash, stateContext) - store.update(scorex.core.versionToBytes(version), Seq.empty, toUpdate).map { _ => + store.update(org.ergoplatform.core.versionToBytes(version), Seq.empty, toUpdate).map { _ => new DigestState(version, rootHash, store, settings) } } @@ -209,7 +208,7 @@ object DigestState extends ScorexLogging with ScorexEncoding { protected def metadata(newVersion: VersionTag, newRootHash: ADDigest, newStateContext: ErgoStateContext): Seq[(Array[Byte], Array[Byte])] = Seq( - scorex.core.versionToBytes(newVersion) -> newRootHash, + org.ergoplatform.core.versionToBytes(newVersion) -> newRootHash, ErgoStateReader.ContextKey -> newStateContext.bytes ) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 4290d96ecc..31362aa4d2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -10,14 +10,14 @@ import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.state.StateChanges -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.ValidationRules._ -import org.ergoplatform.settings.{ChainSettings, Constants, ErgoSettings, LaunchParameters} +import org.ergoplatform.settings.{ChainSettings, Constants, ErgoSettings, LaunchParameters, NodeConfigurationSettings} import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import scorex.core.validation.ValidationResult.Valid -import scorex.core.validation.{ModifierValidator, ValidationResult} -import scorex.core.{VersionTag, idToVersion} +import org.ergoplatform.validation.ValidationResult.Valid +import org.ergoplatform.validation.{ModifierValidator, ValidationResult} +import org.ergoplatform.core.{VersionTag, idToVersion} +import org.ergoplatform.nodeView.LocallyGeneratedModifier import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Remove} import scorex.crypto.authds.{ADDigest, ADValue} import scorex.util.encode.Base16 @@ -103,7 +103,8 @@ object ErgoState extends ScorexLogging { * @return Result of transactions execution with total cost inside */ def execTransactions(transactions: Seq[ErgoTransaction], - currentStateContext: ErgoStateContext) + currentStateContext: ErgoStateContext, + nodeSettings: NodeConfigurationSettings) (checkBoxExistence: ErgoBox.BoxId => Try[ErgoBox]): ValidationResult[Long] = { val verifier: ErgoInterpreter = ErgoInterpreter(currentStateContext.currentParameters) @@ -130,7 +131,7 @@ object ErgoState extends ScorexLogging { } } - val checkpointHeight = currentStateContext.ergoSettings.nodeSettings.checkpoint.map(_.height).getOrElse(0) + val checkpointHeight = nodeSettings.checkpoint.map(_.height).getOrElse(0) if (currentStateContext.currentHeight <= checkpointHeight) { Valid(0L) } else { @@ -206,7 +207,7 @@ object ErgoState extends ScorexLogging { additionalRegisters: AdditionalRegisters = Map.empty): ErgoBox = { import sigmastate.eval._ - val creationHeight: Int = ErgoHistory.EmptyHistoryHeight + val creationHeight: Int = EmptyHistoryHeight val transactionId: ModifierId = ErgoBox.allZerosModifierId val boxIndex: Short = 0: Short diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 18947880c5..9b57c853cf 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,10 +1,10 @@ package org.ergoplatform.nodeView.state -import org.ergoplatform.ErgoBox -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.{ErgoBox, NodeViewComponent} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, LaunchParameters, Parameters} -import scorex.core.{NodeViewComponent, VersionTag} +import org.ergoplatform.core.VersionTag import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.LDBVersionedStore @@ -66,10 +66,10 @@ object ErgoStateReader extends ScorexLogging { */ def storageStateContext(store: LDBVersionedStore, settings: ErgoSettings): ErgoStateContext = { store.get(ErgoStateReader.ContextKey) - .flatMap(b => ErgoStateContextSerializer(settings).parseBytesTry(b).toOption) + .flatMap(b => ErgoStateContextSerializer(settings.chainSettings).parseBytesTry(b).toOption) .getOrElse { log.warn("Can't read blockchain parameters from database") - ErgoStateContext.empty(settings, LaunchParameters) + ErgoStateContext.empty(settings.chainSettings, LaunchParameters) } } @@ -94,13 +94,13 @@ object ErgoStateReader extends ScorexLogging { if (lastHeaders.size != Constants.LastHeadersInContext) { Failure(new Exception(s"Only ${lastHeaders.size} headers found in reconstructStateContextBeforeEpoch")) } else { - val empty = ErgoStateContext.empty(settings, LaunchParameters) + val empty = ErgoStateContext.empty(settings.chainSettings, LaunchParameters) val esc = new ErgoStateContext( lastHeaders, None, empty.genesisStateDigest, empty.currentParameters, empty.validationSettings, - empty.votingData)(settings) + empty.votingData)(settings.chainSettings) Success(esc) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 5170247a7c..5889c9458f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.{Algos, ErgoSettings} -import scorex.core.serialization.ManifestSerializer +import org.ergoplatform.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 575e5bdf7a..990359091c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.settings.Constants -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index ad6f9193bf..1416bc11d7 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -7,7 +7,7 @@ import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLD import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import org.ergoplatform.settings.ErgoSettings -import scorex.core.serialization.ManifestSerializer +import org.ergoplatform.serialization.ManifestSerializer import scala.concurrent.Future import scala.util.Try diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index a4254f07b0..01b98a59ef 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -3,6 +3,7 @@ package org.ergoplatform.nodeView.state import java.io.File import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.core.VersionTag import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction @@ -11,10 +12,10 @@ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} import org.ergoplatform.settings.{Algos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier -import scorex.core._ -import scorex.core.utils.ScorexEncoding -import scorex.core.validation.ModifierValidator +import org.ergoplatform.utils.ScorexEncoding +import org.ergoplatform.core._ +import org.ergoplatform.nodeView.LocallyGeneratedModifier +import org.ergoplatform.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} @@ -49,7 +50,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 override def rollbackTo(version: VersionTag): Try[UtxoState] = persistentProver.synchronized { val p = persistentProver log.info(s"Rollback UtxoState to version ${Algos.encoder.encode(version)}") - store.get(scorex.core.versionToBytes(version)) match { + store.get(versionToBytes(version)) match { case Some(hash) => val rootHash: ADDigest = ADDigest @@ hash val rollbackResult = p.rollback(rootHash).map { _ => @@ -80,7 +81,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 .orElse(boxById(id)) .fold[Try[ErgoBox]](Failure(new Exception(s"Box with id ${Algos.encode(id)} not found")))(Success(_)) - val txProcessing = ErgoState.execTransactions(transactions, currentStateContext)(checkBoxExistence) + val txProcessing = ErgoState.execTransactions(transactions, currentStateContext, ergoSettings.nodeSettings)(checkBoxExistence) if (txProcessing.isValid) { log.debug(s"Cost of block $headerId (${currentStateContext.currentHeight}): ${txProcessing.payload.getOrElse(0)}") val blockOpsTry = ErgoState.stateChanges(transactions).flatMap { stateChanges => @@ -143,7 +144,8 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 var proofBytes = persistentProver.generateProofAndUpdateStorage(meta) - if (!store.get(scorex.core.idToBytes(fb.id)).exists(w => java.util.Arrays.equals(w, fb.header.stateRoot))) { + if (!store.get(org.ergoplatform.core.idToBytes(fb.id)) + .exists(w => java.util.Arrays.equals(w, fb.header.stateRoot))) { throw new Exception("Storage kept roothash is not equal to the declared one") } @@ -192,7 +194,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } if (fb.adProofs.isEmpty) { - if (fb.height >= estimatedTip.getOrElse(Int.MaxValue) - stateContext.ergoSettings.nodeSettings.adProofsSuffixLength) { + if (fb.height >= estimatedTip.getOrElse(Int.MaxValue) - ergoSettings.nodeSettings.adProofsSuffixLength) { val adProofs = ADProofs(fb.header.id, proofBytes) generate(LocallyGeneratedModifier(adProofs)) } @@ -309,7 +311,7 @@ object UtxoState { val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) - val defaultStateContext = ErgoStateContext.empty(settings, parameters) + val defaultStateContext = ErgoStateContext.empty(settings.chainSettings, parameters) val storage = new VersionedLDBAVLStorage(store) val persistentProver = PersistentBatchAVLProver.create( p, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index f717464185..a2c10bd248 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -4,13 +4,13 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.transaction.TooHighCostError import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import scorex.core.transaction.TooHighCostError -import scorex.core.validation.MalformedModifierError +import org.ergoplatform.validation.MalformedModifierError import scorex.crypto.authds.avltree.batch.{Lookup, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala index 96a6814898..888a1eed4e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.wallet -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height import org.ergoplatform.sdk.wallet.TokensMap final case class BalancesSnapshot(height: Height, balance: Long, assetBalances: TokensMap) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoAddressJsonEncoder.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoAddressJsonEncoder.scala index acd5920bfe..2cc994fcef 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoAddressJsonEncoder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoAddressJsonEncoder.scala @@ -2,20 +2,20 @@ package org.ergoplatform.nodeView.wallet import io.circe.syntax._ import io.circe.{Decoder, DecodingFailure, Encoder} -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.ChainSettings import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder} import scala.util.{Failure, Success} -case class ErgoAddressJsonEncoder(settings: ErgoSettings) { +case class ErgoAddressJsonEncoder(chainSettings: ChainSettings) { implicit val encoder: Encoder[ErgoAddress] = { address => - ErgoAddressEncoder(settings.chainSettings.addressPrefix).toString(address).asJson + ErgoAddressEncoder(chainSettings.addressPrefix).toString(address).asJson } implicit val decoder: Decoder[ErgoAddress] = { cursor => def decodeString(addrStr: String) = { - ErgoAddressEncoder(settings.chainSettings.addressPrefix).fromString(addrStr) match { + ErgoAddressEncoder(chainSettings.addressPrefix).fromString(addrStr) match { case Success(address) => Right(address) case Failure(exception) => Left(DecodingFailure(exception.toString, cursor.history)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala index 5971ff24ff..54c0808eb0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala @@ -5,10 +5,10 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state.ErgoState -import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ +import org.ergoplatform.nodeView.wallet.ErgoWalletActorMessages._ import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.wallet.boxes.{ReemissionData, ReplaceCompactCollectBoxSelector} -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.util.ScorexLogging import scala.util.{Failure, Success, Try} @@ -64,7 +64,7 @@ class ErgoWallet(historyReader: ErgoHistoryReader, settings: ErgoSettings, param } def rollback(to: VersionTag): Try[ErgoWallet] = - historyReader.heightOf(scorex.core.versionToId(to)) match { + historyReader.heightOf(org.ergoplatform.core.versionToId(to)) match { case Some(_) => walletActor ! Rollback(to) Success(this) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index 3c1594e091..c65599f212 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -4,32 +4,23 @@ import akka.actor.SupervisorStrategy.{Restart, Stop} import akka.actor._ import akka.pattern.StatusReply import org.ergoplatform.ErgoBox._ -import org.ergoplatform.modifiers.ErgoFullBlock -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedMempool, ChangedState} -import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{ChangedMempool, ChangedState} +import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.ErgoStateReader -import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult -import org.ergoplatform.nodeView.wallet.models.CollectedBoxes -import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGenerationRequest} -import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest} +import org.ergoplatform.nodeView.wallet.ErgoWalletServiceUtils.DeriveNextKeyResult import org.ergoplatform.sdk.wallet.secrets.DerivationPath import org.ergoplatform.settings._ import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.boxes.{BoxSelector, ChainStatus} +import org.ergoplatform.wallet.boxes.BoxSelector import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.interpreter.TransactionHintsBag +import org.ergoplatform.nodeView.wallet.ErgoWalletActorMessages._ import org.ergoplatform._ -import scorex.core.VersionTag -import scorex.core.utils.ScorexEncoding -import scorex.util.{ModifierId, ScorexLogging} -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} - +import org.ergoplatform.core.VersionTag +import org.ergoplatform.utils.ScorexEncoding +import scorex.util.ScorexLogging import scala.concurrent.duration._ -import scala.util.{Failure, Success, Try} - +import scala.util.{Failure, Success} class ErgoWalletActor(settings: ErgoSettings, parameters: Parameters, @@ -38,8 +29,6 @@ class ErgoWalletActor(settings: ErgoSettings, historyReader: ErgoHistoryReader) extends Actor with Stash with ScorexLogging with ScorexEncoding { - import ErgoWalletActor._ - private val ergoAddressEncoder: ErgoAddressEncoder = settings.addressEncoder override val supervisorStrategy: OneForOneStrategy = @@ -530,392 +519,4 @@ object ErgoWalletActor extends ScorexLogging { ) walletActorRef } - - // Private signals the wallet actor sends to itself - /** - * A signal the wallet actor sends to itself to scan a block in the past - * - * @param blockHeight - height of a block to scan - * @param rescan - scan a block even if height is out of order, to serve rescan requests from arbitrary height - */ - private final case class ScanInThePast(blockHeight: ErgoHistory.Height, rescan: Boolean) - - - // Publicly available signals for the wallet actor - - /** - * Command to scan offchain transaction - * - * @param tx - offchain transaction - */ - final case class ScanOffChain(tx: ErgoTransaction) - - /** - * Command to scan a block - * - * @param block - block to scan - */ - final case class ScanOnChain(block: ErgoFullBlock) - - /** - * Rollback to previous version of the wallet, by throwing away effects of blocks after the version - * - * @param version - */ - final case class Rollback(version: VersionTag) - - /** - * Generate new transaction fulfilling given requests - * - * @param requests - * @param inputsRaw - * @param dataInputsRaw - * @param sign - */ - final case class GenerateTransaction(requests: Seq[TransactionGenerationRequest], - inputsRaw: Seq[String], - dataInputsRaw: Seq[String], - sign: Boolean) - - /** - * Request to generate commitments for an unsigned transaction - * - * @param utx - unsigned transaction - * @param secrets - optionally, externally provided secrets - * @param inputsOpt - optionally, externally provided inputs - * @param dataInputsOpt - optionally, externally provided inputs - */ - case class GenerateCommitmentsFor(utx: UnsignedErgoTransaction, - secrets: Option[Seq[ExternalSecret]], - inputsOpt: Option[Seq[ErgoBox]], - dataInputsOpt: Option[Seq[ErgoBox]]) - - /** - * Response for commitments generation request - * - * @param response - hints to sign a transaction - */ - case class GenerateCommitmentsResponse(response: Try[TransactionHintsBag]) - - /** - * A request to sign a transaction - * - * @param utx - unsigned transaction - * @param secrets - additional secrets given to the prover - * @param hints - hints used for transaction signing (commitments and partial proofs) - * @param boxesToSpend - boxes the transaction is spending - * @param dataBoxes - read-only inputs of the transaction - */ - case class SignTransaction(utx: UnsignedErgoTransaction, - secrets: Seq[ExternalSecret], - hints: TransactionHintsBag, - boxesToSpend: Option[Seq[ErgoBox]], - dataBoxes: Option[Seq[ErgoBox]]) - - /** - * - * @param chainStatus - */ - final case class ReadBalances(chainStatus: ChainStatus) - - /** - * Read a slice of wallet public keys - * - * @param from - * @param until - */ - final case class ReadPublicKeys(from: Int, until: Int) - - /** - * Read all wallet public keys - */ - final case class ReadExtendedPublicKeys() - - /** - * Get the private key from seed based on a given derivation path - */ - final case class GetPrivateKeyFromPath(path: DerivationPath) - - /** - * Read wallet either from mnemonic or from secret storage - */ - final case class ReadWallet(state: ErgoWalletState) - - /** - * Initialize wallet with given wallet pass and optional mnemonic pass (according to BIP-32) - * - * @param walletPass - * @param mnemonicPassOpt - */ - final case class InitWallet(walletPass: SecretString, mnemonicPassOpt: Option[SecretString]) - - /** - * Restore wallet with mnemonic, optional mnemonic password and (mandatory) wallet encryption password - * - * @param mnemonic - * @param mnemonicPassOpt - * @param walletPass - */ - final case class RestoreWallet(mnemonic: SecretString, mnemonicPassOpt: Option[SecretString], walletPass: SecretString, usePre1627KeyDerivation: Boolean) - - /** - * Unlock wallet with wallet password - * - * @param walletPass - */ - final case class UnlockWallet(walletPass: SecretString) - - /** - * Derive key with given path according to BIP-32 - * - * @param path - */ - final case class DeriveKey(path: String) - - /** - * Get boxes related to P2PK payments - * - * @param unspentOnly - return only unspent boxes - * @param considerUnconfirmed - consider mempool (filter our unspent boxes spent in the pool if unspent = true, add - * boxes created in the pool for both values of unspentOnly). - */ - final case class GetWalletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean) - - /** - * Get boxes by requested params - * - * @param targetBalance - Balance requested by user - * @param targetAssets - IDs and amounts of other tokens - */ - final case class CollectWalletBoxes(targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, Long]) - - /** - * Wallet's response for requested boxes - * - * @param result - */ - final case class ReqBoxesResponse(result: Try[CollectedBoxes]) - - /** - * Get scan related transactions - * - * @param scanId - Scan identifier - * @param includeUnconfirmed - whether to include transactions from mempool that match given scanId - */ - final case class GetScanTransactions(scanId: ScanId, includeUnconfirmed: Boolean) - - /** - * Response for requested scan related transactions - * - * @param result - */ - final case class ScanRelatedTxsResponse(result: Seq[AugWalletTransaction]) - - /** - * Get unspent boxes related to a scan - * - * @param scanId - scan identifier - * @param considerUnconfirmed - consider boxes from mempool - * @param minHeight - min inclusion height of unspent boxes - * @param maxHeight - max inclusion height of unspent boxes - */ - final case class GetScanUnspentBoxes(scanId: ScanId, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int) - - /** - * Get spent boxes related to a scan - * - * @param scanId - scan identifier - */ - final case class GetScanSpentBoxes(scanId: ScanId) - - /** - * Set or update address for change outputs. Initially the address is set to root key address - * - * @param address - */ - final case class UpdateChangeAddress(address: P2PKAddress) - - /** - * Command to register new scan - * - * @param appRequest - */ - final case class AddScan(appRequest: ScanRequest) - - /** - * Wallet's response for scan registration request - * - * @param response - */ - final case class AddScanResponse(response: Try[Scan]) - - /** - * Command to deregister a scan - * - * @param scanId - */ - final case class RemoveScan(scanId: ScanId) - - /** - * Wallet's response for scan removal request - * - * @param response - */ - final case class RemoveScanResponse(response: Try[Unit]) - - /** - * Get wallet-related transaction - * - * @param id - */ - final case class GetTransaction(id: ModifierId) - - final case class CheckSeed(mnemonic: SecretString, passOpt: Option[SecretString]) - - /** - * Get wallet-related transaction - */ - case object GetTransactions - - /** - * Get filtered scan-related txs - * @param scanIds - scan identifiers - * @param minHeight - minimal tx inclusion height - * @param maxHeight - maximal tx inclusion height - * @param minConfNum - minimal confirmations number - * @param maxConfNum - maximal confirmations number - * @param includeUnconfirmed - whether to include transactions from mempool that match given scanId - */ - case class GetFilteredScanTxs(scanIds: List[ScanId], - minHeight: Int, - maxHeight: Int, - minConfNum: Int, - maxConfNum: Int, - includeUnconfirmed: Boolean) - - /** - * Derive next key-pair according to BIP-32 - * //todo: describe procedure or provide a link - */ - case object DeriveNextKey - - /** - * Lock wallet - */ - case object LockWallet - - /** - * Close wallet - */ - case object CloseWallet - - /** - * Rescan wallet - */ - case class RescanWallet(fromHeight: Int) - - /** - * Get wallet status - */ - case object GetWalletStatus - - /** - * Wallet status. To be sent in response to GetWalletStatus - * - * @param initialized - whether wallet is initialized or not - * @param unlocked - whether wallet is unlocked or not - * @param changeAddress - address used for change (optional) - * @param height - last height scanned - */ - case class WalletStatus(initialized: Boolean, - unlocked: Boolean, - changeAddress: Option[P2PKAddress], - height: ErgoHistory.Height, - error: Option[String]) - - /** - * Get root secret key (used in miner) - */ - case object GetFirstSecret - - /** - * Response with root secret key (used in miner) - */ - case class FirstSecretResponse(secret: Try[DLogProverInput]) - - /** - * Get mining public key - */ - case object GetMiningPubKey - - /** - * Response with mining public key - */ - case class MiningPubKeyResponse(miningPubKeyOpt: Option[ProveDlog]) - - /** - * Get registered scans list - */ - case object ReadScans - - /** - * Get registered scans list - */ - case class ReadScansResponse(apps: Seq[Scan]) - - /** - * Remove association between a scan and a box (remove a box if its the only one which belongs to the - * scan) - * - * @param scanId - * @param boxId - */ - case class StopTracking(scanId: ScanId, boxId: BoxId) - - /** - * Wrapper for a result of StopTracking processing - * - * @param status - */ - case class StopTrackingResponse(status: Try[Unit]) - - /** - * A request to extract hints from given transaction - * - * @param tx - transaction to extract hints from - * @param real - public keys corresponing to the secrets known - * @param simulated - public keys to simulate - * @param inputsOpt - optionally, externally provided inputs - * @param dataInputsOpt - optionally, externally provided inputs - */ - case class ExtractHints(tx: ErgoTransaction, - real: Seq[SigmaBoolean], - simulated: Seq[SigmaBoolean], - inputsOpt: Option[Seq[ErgoBox]], - dataInputsOpt: Option[Seq[ErgoBox]]) - - /** - * Result of hints generation operation - * - * @param transactionHintsBag - hints for transaction - */ - case class ExtractHintsResult(transactionHintsBag: TransactionHintsBag) - - - /** - * Add association between a scan and a box (and add the box to the database if it is not there) - * - * @param box - * @param scanIds - * - */ - case class AddBox(box: ErgoBox, scanIds: Set[ScanId]) - - /** - * Wrapper for a result of AddBox processing - * - * @param status - */ - case class AddBoxResponse(status: Try[Unit]) - } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActorMessages.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActorMessages.scala new file mode 100644 index 0000000000..c1f12a4941 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActorMessages.scala @@ -0,0 +1,414 @@ +package org.ergoplatform.nodeView.wallet + +import org.ergoplatform.ErgoBox._ +import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ +import org.ergoplatform.nodeView.wallet.models.CollectedBoxes +import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGenerationRequest} +import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest} +import org.ergoplatform.sdk.wallet.secrets.DerivationPath +import org.ergoplatform.wallet.Constants.ScanId +import org.ergoplatform.wallet.boxes.ChainStatus +import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.wallet.interpreter.TransactionHintsBag +import org.ergoplatform._ +import org.ergoplatform.core.VersionTag +import scorex.util.ModifierId +import sigmastate.Values.SigmaBoolean +import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import scala.util.Try + +/** + * Repository for messages processed by ErgoWalletActors + */ +object ErgoWalletActorMessages { + + // Private signals the wallet actor sends to itself + /** + * A signal the wallet actor sends to itself to scan a block in the past + * + * @param blockHeight - height of a block to scan + * @param rescan - scan a block even if height is out of order, to serve rescan requests from arbitrary height + */ + final case class ScanInThePast(blockHeight: Height, rescan: Boolean) + + + // Publicly available signals for the wallet actor + + /** + * Command to scan offchain transaction + * + * @param tx - offchain transaction + */ + final case class ScanOffChain(tx: ErgoTransaction) + + /** + * Command to scan a block + * + * @param block - block to scan + */ + final case class ScanOnChain(block: ErgoFullBlock) + + /** + * Rollback to previous version of the wallet, by throwing away effects of blocks after the version + * + * @param version + */ + final case class Rollback(version: VersionTag) + + /** + * Generate new transaction fulfilling given requests + * + * @param requests + * @param inputsRaw + * @param dataInputsRaw + * @param sign + */ + final case class GenerateTransaction(requests: Seq[TransactionGenerationRequest], + inputsRaw: Seq[String], + dataInputsRaw: Seq[String], + sign: Boolean) + + /** + * Request to generate commitments for an unsigned transaction + * + * @param utx - unsigned transaction + * @param secrets - optionally, externally provided secrets + * @param inputsOpt - optionally, externally provided inputs + * @param dataInputsOpt - optionally, externally provided inputs + */ + case class GenerateCommitmentsFor(utx: UnsignedErgoTransaction, + secrets: Option[Seq[ExternalSecret]], + inputsOpt: Option[Seq[ErgoBox]], + dataInputsOpt: Option[Seq[ErgoBox]]) + + /** + * Response for commitments generation request + * + * @param response - hints to sign a transaction + */ + case class GenerateCommitmentsResponse(response: Try[TransactionHintsBag]) + + /** + * A request to sign a transaction + * + * @param utx - unsigned transaction + * @param secrets - additional secrets given to the prover + * @param hints - hints used for transaction signing (commitments and partial proofs) + * @param boxesToSpend - boxes the transaction is spending + * @param dataBoxes - read-only inputs of the transaction + */ + case class SignTransaction(utx: UnsignedErgoTransaction, + secrets: Seq[ExternalSecret], + hints: TransactionHintsBag, + boxesToSpend: Option[Seq[ErgoBox]], + dataBoxes: Option[Seq[ErgoBox]]) + + /** + * + * @param chainStatus + */ + final case class ReadBalances(chainStatus: ChainStatus) + + /** + * Read a slice of wallet public keys + * + * @param from + * @param until + */ + final case class ReadPublicKeys(from: Int, until: Int) + + /** + * Read all wallet public keys + */ + final case class ReadExtendedPublicKeys() + + /** + * Get the private key from seed based on a given derivation path + */ + final case class GetPrivateKeyFromPath(path: DerivationPath) + + /** + * Read wallet either from mnemonic or from secret storage + */ + final case class ReadWallet(state: ErgoWalletState) + + /** + * Initialize wallet with given wallet pass and optional mnemonic pass (according to BIP-32) + * + * @param walletPass + * @param mnemonicPassOpt + */ + final case class InitWallet(walletPass: SecretString, mnemonicPassOpt: Option[SecretString]) + + /** + * Restore wallet with mnemonic, optional mnemonic password and (mandatory) wallet encryption password + * + * @param mnemonic + * @param mnemonicPassOpt + * @param walletPass + */ + final case class RestoreWallet(mnemonic: SecretString, mnemonicPassOpt: Option[SecretString], walletPass: SecretString, usePre1627KeyDerivation: Boolean) + + /** + * Unlock wallet with wallet password + * + * @param walletPass + */ + final case class UnlockWallet(walletPass: SecretString) + + /** + * Derive key with given path according to BIP-32 + * + * @param path + */ + final case class DeriveKey(path: String) + + /** + * Get boxes related to P2PK payments + * + * @param unspentOnly - return only unspent boxes + * @param considerUnconfirmed - consider mempool (filter our unspent boxes spent in the pool if unspent = true, add + * boxes created in the pool for both values of unspentOnly). + */ + final case class GetWalletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean) + + /** + * Get boxes by requested params + * + * @param targetBalance - Balance requested by user + * @param targetAssets - IDs and amounts of other tokens + */ + final case class CollectWalletBoxes(targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, Long]) + + /** + * Wallet's response for requested boxes + * + * @param result + */ + final case class ReqBoxesResponse(result: Try[CollectedBoxes]) + + /** + * Get scan related transactions + * + * @param scanId - Scan identifier + * @param includeUnconfirmed - whether to include transactions from mempool that match given scanId + */ + final case class GetScanTransactions(scanId: ScanId, includeUnconfirmed: Boolean) + + /** + * Response for requested scan related transactions + * + * @param result + */ + final case class ScanRelatedTxsResponse(result: Seq[AugWalletTransaction]) + + /** + * Get unspent boxes related to a scan + * + * @param scanId - scan identifier + * @param considerUnconfirmed - consider boxes from mempool + * @param minHeight - min inclusion height of unspent boxes + * @param maxHeight - max inclusion height of unspent boxes + */ + final case class GetScanUnspentBoxes(scanId: ScanId, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int) + + /** + * Get spent boxes related to a scan + * + * @param scanId - scan identifier + */ + final case class GetScanSpentBoxes(scanId: ScanId) + + /** + * Set or update address for change outputs. Initially the address is set to root key address + * + * @param address + */ + final case class UpdateChangeAddress(address: P2PKAddress) + + /** + * Command to register new scan + * + * @param appRequest + */ + final case class AddScan(appRequest: ScanRequest) + + /** + * Wallet's response for scan registration request + * + * @param response + */ + final case class AddScanResponse(response: Try[Scan]) + + /** + * Command to deregister a scan + * + * @param scanId + */ + final case class RemoveScan(scanId: ScanId) + + /** + * Wallet's response for scan removal request + * + * @param response + */ + final case class RemoveScanResponse(response: Try[Unit]) + + /** + * Get wallet-related transaction + * + * @param id + */ + final case class GetTransaction(id: ModifierId) + + final case class CheckSeed(mnemonic: SecretString, passOpt: Option[SecretString]) + + /** + * Get wallet-related transaction + */ + case object GetTransactions + + /** + * Get filtered scan-related txs + * @param scanIds - scan identifiers + * @param minHeight - minimal tx inclusion height + * @param maxHeight - maximal tx inclusion height + * @param minConfNum - minimal confirmations number + * @param maxConfNum - maximal confirmations number + * @param includeUnconfirmed - whether to include transactions from mempool that match given scanId + */ + case class GetFilteredScanTxs(scanIds: List[ScanId], + minHeight: Int, + maxHeight: Int, + minConfNum: Int, + maxConfNum: Int, + includeUnconfirmed: Boolean) + + /** + * Derive next key-pair according to BIP-32 + * //todo: describe procedure or provide a link + */ + case object DeriveNextKey + + /** + * Lock wallet + */ + case object LockWallet + + /** + * Close wallet + */ + case object CloseWallet + + /** + * Rescan wallet + */ + case class RescanWallet(fromHeight: Int) + + /** + * Get wallet status + */ + case object GetWalletStatus + + /** + * Wallet status. To be sent in response to GetWalletStatus + * + * @param initialized - whether wallet is initialized or not + * @param unlocked - whether wallet is unlocked or not + * @param changeAddress - address used for change (optional) + * @param height - last height scanned + */ + case class WalletStatus(initialized: Boolean, + unlocked: Boolean, + changeAddress: Option[P2PKAddress], + height: Height, + error: Option[String]) + + /** + * Get root secret key (used in miner) + */ + case object GetFirstSecret + + /** + * Response with root secret key (used in miner) + */ + case class FirstSecretResponse(secret: Try[DLogProverInput]) + + /** + * Get mining public key + */ + case object GetMiningPubKey + + /** + * Response with mining public key + */ + case class MiningPubKeyResponse(miningPubKeyOpt: Option[ProveDlog]) + + /** + * Get registered scans list + */ + case object ReadScans + + /** + * Get registered scans list + */ + case class ReadScansResponse(apps: Seq[Scan]) + + /** + * Remove association between a scan and a box (remove a box if its the only one which belongs to the + * scan) + * + * @param scanId + * @param boxId + */ + case class StopTracking(scanId: ScanId, boxId: BoxId) + + /** + * Wrapper for a result of StopTracking processing + * + * @param status + */ + case class StopTrackingResponse(status: Try[Unit]) + + /** + * A request to extract hints from given transaction + * + * @param tx - transaction to extract hints from + * @param real - public keys corresponing to the secrets known + * @param simulated - public keys to simulate + * @param inputsOpt - optionally, externally provided inputs + * @param dataInputsOpt - optionally, externally provided inputs + */ + case class ExtractHints(tx: ErgoTransaction, + real: Seq[SigmaBoolean], + simulated: Seq[SigmaBoolean], + inputsOpt: Option[Seq[ErgoBox]], + dataInputsOpt: Option[Seq[ErgoBox]]) + + /** + * Result of hints generation operation + * + * @param transactionHintsBag - hints for transaction + */ + case class ExtractHintsResult(transactionHintsBag: TransactionHintsBag) + + + /** + * Add association between a scan and a box (and add the box to the database if it is not there) + * + * @param box + * @param scanIds + * + */ + case class AddBox(box: ErgoBox, scanIds: Set[ScanId]) + + /** + * Wrapper for a result of AddBox processing + * + * @param status + */ + case class AddBoxResponse(status: Try[Unit]) + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala index fd2d38ba76..ca8fa867d0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala @@ -5,8 +5,8 @@ import akka.pattern.ask import akka.util.Timeout import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ -import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult +import org.ergoplatform.nodeView.wallet.ErgoWalletActorMessages._ +import org.ergoplatform.nodeView.wallet.ErgoWalletServiceUtils.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.persistence.WalletDigest import org.ergoplatform.nodeView.wallet.requests.{BoxesRequest, ExternalSecret, TransactionGenerationRequest} import org.ergoplatform.nodeView.wallet.scanning.ScanRequest @@ -16,8 +16,7 @@ import org.ergoplatform.wallet.boxes.ChainStatus import org.ergoplatform.wallet.boxes.ChainStatus.{OffChain, OnChain} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.{ErgoBox, P2PKAddress} -import scorex.core.NodeViewComponent +import org.ergoplatform.{ErgoBox, NodeViewComponent, P2PKAddress} import scorex.util.ModifierId import sigmastate.Values.SigmaBoolean import sigmastate.crypto.DLogProtocol.DLogProverInput diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala index 7ff6ab2d72..b84402e288 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala @@ -6,23 +6,22 @@ import org.ergoplatform._ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.state.{ErgoStateContext, UtxoStateReader} -import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult +import org.ergoplatform.nodeView.wallet.ErgoWalletServiceUtils.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.models.{ChangeBox, CollectedBoxes} import org.ergoplatform.nodeView.wallet.persistence.{WalletRegistry, WalletStorage} import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGenerationRequest} import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest} -import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} +import org.ergoplatform.sdk.wallet.secrets.DerivationPath import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.boxes.{BoxSelector, ErgoBoxSerializer, TrackedBox} +import org.ergoplatform.wallet.boxes.{BoxSelector, TrackedBox} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.{ErgoProvingInterpreter, TransactionHintsBag} import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.secrets.JsonSecretStorage import org.ergoplatform.wallet.settings.SecretStorageSettings import org.ergoplatform.wallet.utils.FileUtils -import scorex.util.encode.Base16 -import scorex.util.{ModifierId} +import scorex.util.ModifierId import sigmastate.Values.SigmaBoolean import sigmastate.crypto.DLogProtocol.DLogProverInput import sigma.Extensions.CollBytesOps @@ -678,16 +677,4 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg .map(ErgoTransaction.apply) } -} - -object ErgoWalletService { - /** - * Result of "deriveNextKey" operation - */ - case class DeriveNextKeyResult(result: Try[(DerivationPath, P2PKAddress, ExtendedSecretKey)]) - - // A helper which is deserializing Base16-encoded boxes to ErgoBox instances - def stringsToBoxes(strings: Seq[String]): Seq[ErgoBox] = - strings.map(in => Base16.decode(in).flatMap(ErgoBoxSerializer.parseBytesTry)).map(_.get) - -} +} \ No newline at end of file diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceUtils.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceUtils.scala new file mode 100644 index 0000000000..c5bf8b97f0 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceUtils.scala @@ -0,0 +1,23 @@ +package org.ergoplatform.nodeView.wallet + +import org.ergoplatform._ +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} +import org.ergoplatform.wallet.boxes.ErgoBoxSerializer +import scorex.util.encode.Base16 +import scala.util.Try + +/** + * Additional types and functions used in ErgoWalletService + */ +object ErgoWalletServiceUtils { + /** + * Result of "deriveNextKey" operation + */ + case class DeriveNextKeyResult(result: Try[(DerivationPath, P2PKAddress, ExtendedSecretKey)]) + + // A helper which is deserializing Base16-encoded boxes to ErgoBox instances + def stringsToBoxes(strings: Seq[String]): Seq[ErgoBox] = + strings.map(in => Base16.decode(in).flatMap(ErgoBoxSerializer.parseBytesTry)).map(_.get) + +} + diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletState.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletState.scala index 049893830f..03fe2cc98d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletState.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.wallet import com.google.common.hash.BloomFilter import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform._ -import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Height import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.{ErgoStateContext, ErgoStateReader, UtxoStateReader} import org.ergoplatform.nodeView.wallet.ErgoWalletState.FilterFn diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index 247c6cd116..2854306ddd 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -5,7 +5,7 @@ import cats.implicits._ import org.ergoplatform.ErgoBox.{BoxId, R4, R5, R6} import org.ergoplatform._ import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction -import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult +import org.ergoplatform.nodeView.wallet.ErgoWalletServiceUtils.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.persistence.WalletStorage import org.ergoplatform.nodeView.wallet.requests._ import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedPublicKey, ExtendedSecretKey} @@ -283,7 +283,7 @@ trait ErgoWalletSupport extends ScorexLogging { dataInputsRaw: Seq[String]): Try[(UnsignedErgoTransaction, IndexedSeq[ErgoBox], IndexedSeq[ErgoBox])] = Try { require(requests.count(_.isInstanceOf[AssetIssueRequest]) <= 1, "Too many asset issuance requests") - val userInputs = ErgoWalletService.stringsToBoxes(inputsRaw) + val userInputs = ErgoWalletServiceUtils.stringsToBoxes(inputsRaw) val inputBoxes = if (userInputs.nonEmpty) { // make TrackedBox sequence out of boxes provided @@ -336,7 +336,7 @@ trait ErgoWalletSupport extends ScorexLogging { val targetAssetsWithBurn = AssetUtils.mergeAssets(targetAssets, burnTokensMap) val selectionOpt = boxSelector.select(inputBoxes.iterator, targetBalance, targetAssetsWithBurn) - val dataInputs = ErgoWalletService.stringsToBoxes(dataInputsRaw).toIndexedSeq + val dataInputs = ErgoWalletServiceUtils.stringsToBoxes(dataInputsRaw).toIndexedSeq selectionOpt.map { selectionResult => val changeAddressOpt: Option[ProveDlog] = state.getChangeAddress(addressEncoder).map(_.pubkey) prepareUnsignedTransaction(outputs, state.getWalletHeight, selectionResult, dataInputs, changeAddressOpt) -> selectionResult.inputBoxes diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala index c93660c0a6..edd19a8fcc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.ScanId -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.ModifierId import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistry.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistry.scala index 220a2b5afa..50f10904b9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistry.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistry.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.wallet.persistence -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.wallet.scanning.Scan import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.PaymentsScanId @@ -92,7 +92,7 @@ case class OffChainRegistry(height: Int, object OffChainRegistry { def empty: OffChainRegistry = - OffChainRegistry(ErgoHistory.EmptyHistoryHeight, ArraySeq.empty, ArraySeq.empty) + OffChainRegistry(EmptyHistoryHeight, ArraySeq.empty, ArraySeq.empty) def init(walletRegistry: WalletRegistry):OffChainRegistry = { val unspent = walletRegistry.unspentBoxes(PaymentsScanId) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala index 9223a4b91e..1b96025f1f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.wallet.persistence -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.settings.Constants -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} import sigmastate.eval.Extensions.ArrayByteOps @@ -24,7 +24,7 @@ final case class WalletDigest(height: Int, object WalletDigest { val empty: WalletDigest = - WalletDigest(ErgoHistory.EmptyHistoryHeight, 0, mutable.WrappedArray.empty) + WalletDigest(EmptyHistoryHeight, 0, mutable.WrappedArray.empty) } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala index 1dc3753b5d..f3e7deba48 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala @@ -13,7 +13,7 @@ import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import org.ergoplatform.wallet.boxes.{TrackedBox, TrackedBoxSerializer} import org.ergoplatform.wallet.transactions.TransactionBuilder -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.crypto.authds.ADKey import scorex.db.LDBVersionedStore import scorex.util.encode.Base16 @@ -314,7 +314,7 @@ class WalletRegistry(private val store: LDBVersionedStore)(ws: WalletSettings) e def rollback(version: VersionTag): Try[Unit] = { cache.clear() - store.rollbackTo(scorex.core.versionToBytes(version)) + store.rollbackTo(org.ergoplatform.core.versionToBytes(version)) } /** diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala index 617755f474..2f52c218f0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala @@ -112,8 +112,8 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco def readStateContext(parameters: Parameters): ErgoStateContext = { cachedStateContext = Some(store .get(StateContextKey) - .flatMap(r => ErgoStateContextSerializer(settings).parseBytesTry(r).toOption) - .getOrElse(ErgoStateContext.empty(settings, parameters)) + .flatMap(r => ErgoStateContextSerializer(settings.chainSettings).parseBytesTry(r).toOption) + .getOrElse(ErgoStateContext.empty(settings.chainSettings, parameters)) ) cachedStateContext.get } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/AssetIssueRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/AssetIssueRequest.scala index 714e8cb61f..0dd6fd72d0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/AssetIssueRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/AssetIssueRequest.scala @@ -42,9 +42,9 @@ object AssetIssueRequest { new AssetIssueRequest(Some(address), valueOpt, amount, name, description, decimals, registers) } -class AssetIssueRequestEncoder(settings: ErgoSettings) extends Encoder[AssetIssueRequest] with ApiCodecs { +class AssetIssueRequestEncoder(ergoSettings: ErgoSettings) extends Encoder[AssetIssueRequest] with ApiCodecs { - implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(settings).encoder + implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder def apply(request: AssetIssueRequest): Json = { Json.obj( @@ -60,9 +60,9 @@ class AssetIssueRequestEncoder(settings: ErgoSettings) extends Encoder[AssetIssu } -class AssetIssueRequestDecoder(settings: ErgoSettings) extends Decoder[AssetIssueRequest] with ApiCodecs { +class AssetIssueRequestDecoder(ergoSettings: ErgoSettings) extends Decoder[AssetIssueRequest] with ApiCodecs { - val addressEncoders: ErgoAddressJsonEncoder = ErgoAddressJsonEncoder(settings) + val addressEncoders: ErgoAddressJsonEncoder = ErgoAddressJsonEncoder(ergoSettings.chainSettings) implicit def addressDecoder: Decoder[ErgoAddress] = addressEncoders.decoder diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/PaymentRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/PaymentRequest.scala index 3537677ed7..9b44cdccb8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/PaymentRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/PaymentRequest.scala @@ -19,9 +19,9 @@ case class PaymentRequest(address: ErgoAddress, registers: Map[NonMandatoryRegisterId, EvaluatedValue[_ <: SType]]) extends TransactionGenerationRequest -class PaymentRequestEncoder(settings: ErgoSettings) extends Encoder[PaymentRequest] { +class PaymentRequestEncoder(ergoSettings: ErgoSettings) extends Encoder[PaymentRequest] { - implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(settings).encoder + implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder def apply(request: PaymentRequest): Json = { Json.obj( @@ -34,9 +34,9 @@ class PaymentRequestEncoder(settings: ErgoSettings) extends Encoder[PaymentReque } -class PaymentRequestDecoder(settings: ErgoSettings) extends Decoder[PaymentRequest] { +class PaymentRequestDecoder(ergoSettings: ErgoSettings) extends Decoder[PaymentRequest] { - val addressEncoders: ErgoAddressJsonEncoder = ErgoAddressJsonEncoder(settings) + val addressEncoders: ErgoAddressJsonEncoder = ErgoAddressJsonEncoder(ergoSettings.chainSettings) implicit def addressDecoder: Decoder[ErgoAddress] = addressEncoders.decoder diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala index 39890f4562..4ff6d38fbb 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala @@ -26,10 +26,10 @@ case class RequestsHolder(requests: Seq[TransactionGenerationRequest], } -class RequestsHolderEncoder(settings: ErgoSettings) extends Encoder[RequestsHolder] with ApiCodecs { +class RequestsHolderEncoder(ergoSettings: ErgoSettings) extends Encoder[RequestsHolder] with ApiCodecs { - implicit val transactionRequestEncoder: TransactionRequestEncoder = new TransactionRequestEncoder(settings) - implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(settings).encoder + implicit val transactionRequestEncoder: TransactionRequestEncoder = new TransactionRequestEncoder(ergoSettings) + implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder def apply(holder: RequestsHolder): Json = { Json.obj( diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionGenerationRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionGenerationRequest.scala index 545fa2f013..22047ebbee 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionGenerationRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionGenerationRequest.scala @@ -8,13 +8,13 @@ import org.ergoplatform.settings.ErgoSettings trait TransactionGenerationRequest -class TransactionRequestEncoder(settings: ErgoSettings) extends Encoder[TransactionGenerationRequest] with ApiCodecs { +class TransactionRequestEncoder(ergoSettings: ErgoSettings) extends Encoder[TransactionGenerationRequest] with ApiCodecs { - implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(settings).encoder + implicit val addressEncoder: Encoder[ErgoAddress] = ErgoAddressJsonEncoder(ergoSettings.chainSettings).encoder def apply(request: TransactionGenerationRequest): Json = request match { - case pr: PaymentRequest => new PaymentRequestEncoder(settings)(pr) - case ar: AssetIssueRequest => new AssetIssueRequestEncoder(settings)(ar) + case pr: PaymentRequest => new PaymentRequestEncoder(ergoSettings)(pr) + case ar: AssetIssueRequest => new AssetIssueRequestEncoder(ergoSettings)(ar) case br: BurnTokensRequest => new BurnTokensRequestEncoder()(br) case other => throw new Exception(s"Unknown TransactionRequest type: $other") } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/Scan.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/Scan.scala index 76aa9c3085..a13d2f3f4c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/Scan.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/Scan.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.nodeView.wallet.scanning.ScanWalletInteraction.ScanWalletInteraction import org.ergoplatform.wallet.Constants.ScanId -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scala.util.{Failure, Success, Try} diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala index 2c70437053..330c071373 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} import sigmastate.SType diff --git a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala b/src/main/scala/org/ergoplatform/reemission/ReemissionRulesUtils.scala similarity index 65% rename from src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala rename to src/main/scala/org/ergoplatform/reemission/ReemissionRulesUtils.scala index 2660693fac..f149097489 100644 --- a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala +++ b/src/main/scala/org/ergoplatform/reemission/ReemissionRulesUtils.scala @@ -1,57 +1,22 @@ package org.ergoplatform.reemission -import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.contracts.ReemissionContracts import org.ergoplatform.mining.emission.EmissionRules +import org.ergoplatform.settings.ErgoSettingsReader import org.ergoplatform.{ErgoAddressEncoder, Pay2SAddress} -import org.ergoplatform.settings.{ErgoSettings, ReemissionSettings} import sigmastate.SBoolean import sigmastate.Values.Value import sigmastate.eval.CompiletimeIRContext import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} -import sigma.Coll import scala.util.Try -/** - * Contains re-emission contracts (defined in `ReemissionContracts`) and helper functions - * for re-emission. - */ -class ReemissionRules(reemissionSettings: ReemissionSettings) extends ReemissionContracts { - - override val reemissionNftIdBytes: Coll[Byte] = reemissionSettings.reemissionNftIdBytes - override val reemissionStartHeight: Height = reemissionSettings.reemissionStartHeight - - /** - * How many ERG taken from emission to re-emission initially - */ - val basicChargeAmount = 12 // in ERG - - /** - * @return how many re-emission tokens can be unlocked at given height - */ - def reemissionForHeight(height: Height, - emissionRules: EmissionRules): Long = { - val emission = emissionRules.emissionAtHeight(height) - if (height >= reemissionSettings.activationHeight && - emission >= (basicChargeAmount + 3) * EmissionRules.CoinsInOneErgo) { - basicChargeAmount * EmissionRules.CoinsInOneErgo - } else if (height >= reemissionSettings.activationHeight && - emission > 3 * EmissionRules.CoinsInOneErgo) { - emission - 3 * EmissionRules.CoinsInOneErgo - } else { - 0L - } - } -} - -object ReemissionRules { +object ReemissionRulesUtils { /** - * @param mainnet - whether to create address for mainnet or testnet - * @return - P2S address for a box used to carry emission NFT and re-emission tokens - * to inject them into the emission box on activation height - */ + * @param mainnet - whether to create address for mainnet or testnet + * @return - P2S address for a box used to carry emission NFT and re-emission tokens + * to inject them into the emission box on activation height + */ def injectionBoxP2SAddress(mainnet: Boolean): Pay2SAddress = { val networkPrefix = if (mainnet) { ErgoAddressEncoder.MainnetNetworkPrefix @@ -79,7 +44,7 @@ object ReemissionRules { println(new ErgoAddressEncoder(ErgoAddressEncoder.TestnetNetworkPrefix).fromString(p2sAddress.toString())) println("injectioon box p2s: " + p2sAddress) - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() println("Monetary settings: " + settings.chainSettings.monetary) println("Reemission settings: " + settings.chainSettings.reemission) diff --git a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala index 41bdd67a43..8ada7c5c16 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala @@ -1,24 +1,12 @@ package org.ergoplatform.settings -import java.io.{File, FileOutputStream} -import java.nio.channels.Channels -import ch.qos.logback.classic.{LoggerContext, Level} -import org.slf4j.{Logger, LoggerFactory} -import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory} -import net.ceedubs.ficus.Ficus._ -import net.ceedubs.ficus.readers.ArbitraryTypeReader._ import org.ergoplatform.mining.groupElemFromBytes -import org.ergoplatform.nodeView.state.StateType.Digest -import org.ergoplatform.{ErgoAddressEncoder, ErgoApp, P2PKAddress} -import scorex.core.settings.{ScorexSettings, SettingsReaders} -import scorex.util.ScorexLogging +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import scorex.util.encode.Base16 import sigmastate.crypto.DLogProtocol.ProveDlog -import java.net.{InetAddress, URL} import scala.util.Try - case class ErgoSettings(directory: String, networkType: NetworkType, chainSettings: ChainSettings, @@ -43,204 +31,7 @@ case class ErgoSettings(directory: String, } -object ErgoSettings extends ScorexLogging - with PowSchemeReaders - with NodeConfigurationReaders - with SettingsReaders { - +object ErgoSettings { val configPath: String = "ergo" val scorexConfigPath: String = "scorex" - - def read(args: Args = Args.empty): ErgoSettings = { - fromConfig(readConfig(args), args.networkTypeOpt) - } - - def fromConfig(config: Config, desiredNetworkTypeOpt: Option[NetworkType] = None): ErgoSettings = { - val directory = config.as[String](s"$configPath.directory") - val networkTypeName = config.as[String](s"$configPath.networkType") - val networkType = NetworkType.fromString(networkTypeName) - .getOrElse(throw new Error(s"Unknown `networkType = $networkTypeName`")) - val nodeSettings = config.as[NodeConfigurationSettings](s"$configPath.node") - val chainSettings = config.as[ChainSettings](s"$configPath.chain") - val walletSettings = config.as[WalletSettings](s"$configPath.wallet") - val cacheSettings = config.as[CacheSettings](s"$configPath.cache") - val scorexSettings = config.as[ScorexSettings](scorexConfigPath) - val votingTargets = VotingTargets.fromConfig(config) - - overrideLogLevel(scorexSettings.logging.level) - - if (nodeSettings.stateType == Digest && nodeSettings.mining) { - log.error("Malformed configuration file was provided! Mining is not possible with digest state. Aborting!") - ErgoApp.forceStopApplication() - } - - consistentSettings( - ErgoSettings( - directory, - networkType, - chainSettings, - nodeSettings, - scorexSettings, - walletSettings, - cacheSettings, - votingTargets - ), - desiredNetworkTypeOpt - ) - } - - // Helper method to read user-provided `configFile` with network-specific `fallbackConfig` - // to be used for default fallback values before reference.conf (which is the last resort) - private def configWithOverrides(configFile: File, fallbackConfig: Option[File]) = { - val firstFallBack = fallbackConfig.map(ConfigFactory.parseFile).getOrElse(ConfigFactory.empty()) - - val cfg = ConfigFactory.parseFile(configFile) - - val keystorePath = "ergo.wallet.secretStorage.secretDir" - - // Check that user-provided Ergo directory exists and has write access (if provided at all) - val userDirOpt = Try(cfg.getString("ergo.directory")).toOption - userDirOpt.foreach { ergoDirName => - require(new File(s"$ergoDirName").canWrite, s"Folder $ergoDirName does not exist or not writable") - } - - // Check that user-provided wallet secret directory exists and has read access (if provided at all) - val walletKeystoreDirOpt = Try(cfg.getString(keystorePath)).toOption - walletKeystoreDirOpt.foreach { secretDirName => - require(new File(s"$secretDirName").canRead, s"Folder $secretDirName does not exist or not readable") - } - - val fullConfig = ConfigFactory - .defaultOverrides() - .withFallback(cfg) - .withFallback(firstFallBack) - .withFallback(ConfigFactory.defaultApplication()) - .withFallback(ConfigFactory.defaultReference()) - .resolve() - - // If user provided only ergo.directory but not ergo.wallet.secretStorage.secretDir in his config, - // set ergo.wallet.secretStorage.secretDir like in reference.conf (so ergo.directory + "/wallet/keystore") - // Otherwise, a user may have an issue, especially with Powershell it seems from reports. - userDirOpt.map { userDir => - if(walletKeystoreDirOpt.isEmpty) { - fullConfig.withValue(keystorePath, ConfigValueFactory.fromAnyRef(userDir + "/wallet/keystore")) - } else { - fullConfig - } - }.getOrElse(fullConfig) - } - - private def readConfig(args: Args): Config = { - - val networkConfigFileOpt = args.networkTypeOpt - .flatMap { networkType => - val confName = s"${networkType.verboseName}.conf" - val classLoader = ClassLoader.getSystemClassLoader - val destDir = System.getProperty("java.io.tmpdir") + "/" - - Option(classLoader.getResourceAsStream(confName)) - .map { stream => - val source = Channels.newChannel(stream) - val fileOut = new File(destDir, confName) - val dest = new FileOutputStream(fileOut) - dest.getChannel.transferFrom(source, 0, Long.MaxValue) - - source.close() - dest.close() - - sys.addShutdownHook { - new File(destDir, confName).delete - } - - fileOut - } - } - - val userConfigFileOpt = for { - filePathOpt <- args.userConfigPathOpt - file = new File(filePathOpt) - if file.exists - } yield file - - networkConfigFileOpt.flatMap(_ => args.networkTypeOpt).fold(log.warn("Running without network config"))( - x => log.info(s"Running in ${x.verboseName} network mode")) - - (networkConfigFileOpt, userConfigFileOpt) match { - // if no user config is supplied, the library will handle overrides/application/reference automatically - case (Some(networkConfigFile), None) => - log.warn("NO CONFIGURATION FILE WAS PROVIDED. STARTING WITH DEFAULT SETTINGS!") - ConfigFactory - .defaultOverrides() - .withFallback(ConfigFactory.parseFile(networkConfigFile)) - .withFallback(ConfigFactory.defaultReference()) - .resolve() - // application config needs to be resolved wrt both system properties *and* user-supplied config. - case (Some(networkConfigFile), Some(file)) => - configWithOverrides(file, Some(networkConfigFile)) - case (None, Some(file)) => - configWithOverrides(file, None) - case (None, None) => - ConfigFactory.load() - } - } - - protected[settings] def invalidRestApiUrl(url: URL): Boolean = - Try(url.toURI).map { uri => - val inetAddress = InetAddress.getByName(url.getHost) - Option(uri.getQuery).exists(_.nonEmpty) || - Option(uri.getPath).exists(_.nonEmpty) || - Option(uri.getFragment).exists(_.nonEmpty) || - inetAddress.isAnyLocalAddress || - inetAddress.isLoopbackAddress || - inetAddress.isSiteLocalAddress - }.getOrElse(false) - - private def consistentSettings(settings: ErgoSettings, - desiredNetworkTypeOpt: Option[NetworkType]): ErgoSettings = { - val nodeSettings = settings.nodeSettings - if (nodeSettings.keepVersions < 0) { - failWithError("nodeSettings.keepVersions should not be negative") - } else if (!nodeSettings.verifyTransactions && !nodeSettings.stateType.requireProofs) { - failWithError("Can not use UTXO state when nodeSettings.verifyTransactions is false") - } else if (desiredNetworkTypeOpt.exists(_ != settings.networkType)) { - failWithError(s"Malformed network config. Desired networkType is `${desiredNetworkTypeOpt.get}`, " + - s"but one declared in config is `${settings.networkType}`") - } else if(settings.networkType.isMainNet && - nodeSettings.mining && - !settings.chainSettings.reemission.checkReemissionRules) { - failWithError(s"Mining is enabled, but ergo.chain.reemission.checkReemissionRules = false , set it to true") - } else if (settings.scorexSettings.restApi.publicUrl.exists(invalidRestApiUrl)) { - failWithError(s"scorex.restApi.publicUrl should not contain query, path or fragment and should not " + - s"be local or loopback address : ${settings.scorexSettings.restApi.publicUrl.get}") - } else if (settings.nodeSettings.utxoSettings.p2pUtxoSnapshots <= 0) { - failWithError(s"p2pUtxoSnapshots <= 0, must be 1 at least") - } else if (settings.nodeSettings.extraIndex && settings.nodeSettings.isFullBlocksPruned) { - failWithError(s"Extra indexes could be enabled only if there is no blockchain pruning") - } else if (nodeSettings.nipopowSettings.nipopowBootstrap && - !(nodeSettings.utxoSettings.utxoBootstrap || nodeSettings.blocksToKeep >= 0)) { - failWithError("nodeSettings.popowBootstrap can be set only if " + - "nodeSettings.utxoBootstrap is set or nodeSettings.blocksToKeep >=0") - } else if (nodeSettings.nipopowSettings.nipopowBootstrap && settings.chainSettings.genesisId.isEmpty) { - failWithError("nodeSettings.popowBootstrap is set but genesisId is not") - } else { - settings - } - } - - private def failWithError(msg: String): Nothing = { - log.error(s"Stop application due to malformed configuration file: $msg") - ErgoApp.forceStopApplication() - } - - /** - * Override the log level at runtime with values provided in config/user provided config. - */ - private def overrideLogLevel(level: String): Unit = level match { - case "TRACE" | "ERROR" | "INFO" | "WARN" | "DEBUG" => - log.info(s"Log level set to $level") - val loggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] - val root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) - root.setLevel(Level.toLevel(level)) - case _ => log.warn("No log level configuration provided") - } } diff --git a/src/main/scala/org/ergoplatform/settings/ErgoSettingsReader.scala b/src/main/scala/org/ergoplatform/settings/ErgoSettingsReader.scala new file mode 100644 index 0000000000..4cacd4be02 --- /dev/null +++ b/src/main/scala/org/ergoplatform/settings/ErgoSettingsReader.scala @@ -0,0 +1,219 @@ +package org.ergoplatform.settings + +import java.io.{File, FileOutputStream} +import java.nio.channels.Channels +import ch.qos.logback.classic.{Level, LoggerContext} +import org.slf4j.{Logger, LoggerFactory} +import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory} +import net.ceedubs.ficus.Ficus._ +import net.ceedubs.ficus.readers.ArbitraryTypeReader._ +import org.ergoplatform.nodeView.state.StateType.Digest +import org.ergoplatform.ErgoApp +import scorex.util.ScorexLogging +import org.ergoplatform.settings.ErgoSettings.{configPath, scorexConfigPath} + +import java.net.{InetAddress, URL} +import scala.util.Try + +/** + * Functions to read configs (ErgoSettings instances) + */ +object ErgoSettingsReader extends ScorexLogging + with PowSchemeReaders + with NodeConfigurationReaders + with SettingsReaders { + + def read(args: Args = Args.empty): ErgoSettings = { + fromConfig(readConfig(args), args.networkTypeOpt) + } + + def fromConfig(config: Config, desiredNetworkTypeOpt: Option[NetworkType] = None): ErgoSettings = { + val directory = config.as[String](s"$configPath.directory") + val networkTypeName = config.as[String](s"$configPath.networkType") + val networkType = NetworkType.fromString(networkTypeName) + .getOrElse(throw new Error(s"Unknown `networkType = $networkTypeName`")) + val nodeSettings = config.as[NodeConfigurationSettings](s"$configPath.node") + val chainSettings = config.as[ChainSettings](s"$configPath.chain") + val walletSettings = config.as[WalletSettings](s"$configPath.wallet") + val cacheSettings = config.as[CacheSettings](s"$configPath.cache") + val scorexSettings = config.as[ScorexSettings](scorexConfigPath) + val votingTargets = VotingTargets.fromConfig(config) + + overrideLogLevel(scorexSettings.logging.level) + + if (nodeSettings.stateType == Digest && nodeSettings.mining) { + log.error("Malformed configuration file was provided! Mining is not possible with digest state. Aborting!") + ErgoApp.forceStopApplication() + } + + consistentSettings( + ErgoSettings( + directory, + networkType, + chainSettings, + nodeSettings, + scorexSettings, + walletSettings, + cacheSettings, + votingTargets + ), + desiredNetworkTypeOpt + ) + } + + // Helper method to read user-provided `configFile` with network-specific `fallbackConfig` + // to be used for default fallback values before reference.conf (which is the last resort) + private def configWithOverrides(configFile: File, fallbackConfig: Option[File]) = { + val firstFallBack = fallbackConfig.map(ConfigFactory.parseFile).getOrElse(ConfigFactory.empty()) + + val cfg = ConfigFactory.parseFile(configFile) + + val keystorePath = "ergo.wallet.secretStorage.secretDir" + + // Check that user-provided Ergo directory exists and has write access (if provided at all) + val userDirOpt = Try(cfg.getString("ergo.directory")).toOption + userDirOpt.foreach { ergoDirName => + require(new File(s"$ergoDirName").canWrite, s"Folder $ergoDirName does not exist or not writable") + } + + // Check that user-provided wallet secret directory exists and has read access (if provided at all) + val walletKeystoreDirOpt = Try(cfg.getString(keystorePath)).toOption + walletKeystoreDirOpt.foreach { secretDirName => + require(new File(s"$secretDirName").canRead, s"Folder $secretDirName does not exist or not readable") + } + + val fullConfig = ConfigFactory + .defaultOverrides() + .withFallback(cfg) + .withFallback(firstFallBack) + .withFallback(ConfigFactory.defaultApplication()) + .withFallback(ConfigFactory.defaultReference()) + .resolve() + + // If user provided only ergo.directory but not ergo.wallet.secretStorage.secretDir in his config, + // set ergo.wallet.secretStorage.secretDir like in reference.conf (so ergo.directory + "/wallet/keystore") + // Otherwise, a user may have an issue, especially with Powershell it seems from reports. + userDirOpt.map { userDir => + if(walletKeystoreDirOpt.isEmpty) { + fullConfig.withValue(keystorePath, ConfigValueFactory.fromAnyRef(userDir + "/wallet/keystore")) + } else { + fullConfig + } + }.getOrElse(fullConfig) + } + + private def readConfig(args: Args): Config = { + + val networkConfigFileOpt = args.networkTypeOpt + .flatMap { networkType => + val confName = s"${networkType.verboseName}.conf" + val classLoader = ClassLoader.getSystemClassLoader + val destDir = System.getProperty("java.io.tmpdir") + "/" + + Option(classLoader.getResourceAsStream(confName)) + .map { stream => + val source = Channels.newChannel(stream) + val fileOut = new File(destDir, confName) + val dest = new FileOutputStream(fileOut) + dest.getChannel.transferFrom(source, 0, Long.MaxValue) + + source.close() + dest.close() + + sys.addShutdownHook { + new File(destDir, confName).delete + } + + fileOut + } + } + + val userConfigFileOpt = for { + filePathOpt <- args.userConfigPathOpt + file = new File(filePathOpt) + if file.exists + } yield file + + networkConfigFileOpt.flatMap(_ => args.networkTypeOpt).fold(log.warn("Running without network config"))( + x => log.info(s"Running in ${x.verboseName} network mode")) + + (networkConfigFileOpt, userConfigFileOpt) match { + // if no user config is supplied, the library will handle overrides/application/reference automatically + case (Some(networkConfigFile), None) => + log.warn("NO CONFIGURATION FILE WAS PROVIDED. STARTING WITH DEFAULT SETTINGS!") + ConfigFactory + .defaultOverrides() + .withFallback(ConfigFactory.parseFile(networkConfigFile)) + .withFallback(ConfigFactory.defaultReference()) + .resolve() + // application config needs to be resolved wrt both system properties *and* user-supplied config. + case (Some(networkConfigFile), Some(file)) => + configWithOverrides(file, Some(networkConfigFile)) + case (None, Some(file)) => + configWithOverrides(file, None) + case (None, None) => + ConfigFactory.load() + } + } + + protected[settings] def invalidRestApiUrl(url: URL): Boolean = + Try(url.toURI).map { uri => + val inetAddress = InetAddress.getByName(url.getHost) + Option(uri.getQuery).exists(_.nonEmpty) || + Option(uri.getPath).exists(_.nonEmpty) || + Option(uri.getFragment).exists(_.nonEmpty) || + inetAddress.isAnyLocalAddress || + inetAddress.isLoopbackAddress || + inetAddress.isSiteLocalAddress + }.getOrElse(false) + + private def consistentSettings(settings: ErgoSettings, + desiredNetworkTypeOpt: Option[NetworkType]): ErgoSettings = { + val nodeSettings = settings.nodeSettings + if (nodeSettings.keepVersions < 0) { + failWithError("nodeSettings.keepVersions should not be negative") + } else if (!nodeSettings.verifyTransactions && !nodeSettings.stateType.requireProofs) { + failWithError("Can not use UTXO state when nodeSettings.verifyTransactions is false") + } else if (desiredNetworkTypeOpt.exists(_ != settings.networkType)) { + failWithError(s"Malformed network config. Desired networkType is `${desiredNetworkTypeOpt.get}`, " + + s"but one declared in config is `${settings.networkType}`") + } else if(settings.networkType.isMainNet && + nodeSettings.mining && + !settings.chainSettings.reemission.checkReemissionRules) { + failWithError(s"Mining is enabled, but ergo.chain.reemission.checkReemissionRules = false , set it to true") + } else if (settings.scorexSettings.restApi.publicUrl.exists(invalidRestApiUrl)) { + failWithError(s"scorex.restApi.publicUrl should not contain query, path or fragment and should not " + + s"be local or loopback address : ${settings.scorexSettings.restApi.publicUrl.get}") + } else if (settings.nodeSettings.utxoSettings.p2pUtxoSnapshots <= 0) { + failWithError(s"p2pUtxoSnapshots <= 0, must be 1 at least") + } else if (settings.nodeSettings.extraIndex && settings.nodeSettings.isFullBlocksPruned) { + failWithError(s"Extra indexes could be enabled only if there is no blockchain pruning") + } else if (nodeSettings.nipopowSettings.nipopowBootstrap && + !(nodeSettings.utxoSettings.utxoBootstrap || nodeSettings.blocksToKeep >= 0)) { + failWithError("nodeSettings.popowBootstrap can be set only if " + + "nodeSettings.utxoBootstrap is set or nodeSettings.blocksToKeep >=0") + } else if (nodeSettings.nipopowSettings.nipopowBootstrap && settings.chainSettings.genesisId.isEmpty) { + failWithError("nodeSettings.popowBootstrap is set but genesisId is not") + } else { + settings + } + } + + private def failWithError(msg: String): Nothing = { + log.error(s"Stop application due to malformed configuration file: $msg") + ErgoApp.forceStopApplication() + } + + /** + * Override the log level at runtime with values provided in config/user provided config. + */ + private def overrideLogLevel(level: String): Unit = level match { + case "TRACE" | "ERROR" | "INFO" | "WARN" | "DEBUG" => + log.info(s"Log level set to $level") + val loggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + val root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) + root.setLevel(Level.toLevel(level)) + case _ => log.warn("No log level configuration provided") + } +} + diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index d1b60b5518..fdfbbc962f 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -3,7 +3,7 @@ package org.ergoplatform.settings import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ValueReader import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.SortingOption import org.ergoplatform.nodeView.state.StateType import scorex.util.ModifierId @@ -20,52 +20,16 @@ trait CheckpointingSettingsReader extends ModifierIdReader { } } -/** - * Settings related to state bootstrapping with UTXO set snapshots. See ergo.node.utxo section for settings description. - */ -case class UtxoSettings(utxoBootstrap: Boolean, storingUtxoSnapshots: Int, p2pUtxoSnapshots: Int) - -/** - * Custom settings reader for `UtxoSettings` - */ -trait UtxoSettingsReader { - implicit val utxoSettingsReader: ValueReader[UtxoSettings] = { (cfg, path) => - UtxoSettings( - cfg.as[Boolean](s"$path.utxoBootstrap"), - cfg.as[Int](s"$path.storingUtxoSnapshots"), - cfg.as[Int](s"$path.p2pUtxoSnapshots") - ) - } -} - - -/** - * Settings related to headers-chain bootstrapping with NiPoPoWs. See ergo.node.nipopow section for settings description. - */ -case class NipopowSettings(nipopowBootstrap: Boolean, p2pNipopows: Int) - -/** - * Custom settings reader for `NipopowSettings` - */ -trait NipopowSettingsReader { - implicit val nipopowSettingsReader: ValueReader[NipopowSettings] = { (cfg, path) => - NipopowSettings( - cfg.as[Boolean](s"$path.nipopowBootstrap"), - cfg.as[Int](s"$path.p2pNipopows") - ) - } -} - /** * Configuration file for Ergo node regime * * @see src/main/resources/application.conf for parameters description */ -case class NodeConfigurationSettings(stateType: StateType, - verifyTransactions: Boolean, - blocksToKeep: Int, - utxoSettings: UtxoSettings, - nipopowSettings: NipopowSettings, +case class NodeConfigurationSettings(override val stateType: StateType, + override val verifyTransactions: Boolean, + override val blocksToKeep: Int, + override val utxoSettings: UtxoSettings, + override val nipopowSettings: NipopowSettings, mining: Boolean, maxTransactionCost: Int, maxTransactionSize: Int, @@ -85,7 +49,7 @@ case class NodeConfigurationSettings(stateType: StateType, adProofsSuffixLength: Int, extraIndex: Boolean, blacklistedTransactions: Seq[String] = Seq.empty, - checkpoint: Option[CheckpointSettings] = None) { + checkpoint: Option[CheckpointSettings] = None) extends ClientCapabilities { /** * Whether the node keeping all the full blocks of the blockchain or not. * @return true if the blockchain is pruned, false if not diff --git a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala index ed668f4904..86239767b3 100644 --- a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala +++ b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala @@ -2,7 +2,7 @@ package org.ergoplatform.tools import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.settings.ValidationRules -import scorex.core.validation.InvalidModifier +import org.ergoplatform.validation.InvalidModifier import scorex.util.{ModifierId, ScorexLogging, bytesToId} object ValidationRulesPrinter extends App with ScorexLogging { diff --git a/src/main/scala/scorex/core/ModifiersCache.scala b/src/main/scala/scorex/core/ModifiersCache.scala index e87a917cf1..71dc659fd3 100644 --- a/src/main/scala/scorex/core/ModifiersCache.scala +++ b/src/main/scala/scorex/core/ModifiersCache.scala @@ -1,8 +1,8 @@ package scorex.core +import org.ergoplatform.consensus.ContainsModifiers import org.ergoplatform.modifiers.{BlockSection, ErgoNodeViewModifier} import org.ergoplatform.nodeView.history.ErgoHistory -import scorex.core.consensus.ContainsModifiers import scala.annotation.tailrec import scala.collection.mutable diff --git a/src/main/scala/scorex/core/api/http/ApiDirectives.scala b/src/main/scala/scorex/core/api/http/ApiDirectives.scala index bebba07910..bb78146dda 100644 --- a/src/main/scala/scorex/core/api/http/ApiDirectives.scala +++ b/src/main/scala/scorex/core/api/http/ApiDirectives.scala @@ -1,8 +1,8 @@ package scorex.core.api.http import akka.http.scaladsl.server.{AuthorizationFailedRejection, Directive0} -import scorex.core.settings.RESTApiSettings -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.settings.RESTApiSettings +import org.ergoplatform.utils.ScorexEncoding import scorex.crypto.hash.Blake2b256 trait ApiDirectives extends CorsHandler with ScorexEncoding { diff --git a/src/main/scala/scorex/core/api/http/ApiErrorHandler.scala b/src/main/scala/scorex/core/api/http/ApiErrorHandler.scala index e1ff032731..a8711e02c0 100644 --- a/src/main/scala/scorex/core/api/http/ApiErrorHandler.scala +++ b/src/main/scala/scorex/core/api/http/ApiErrorHandler.scala @@ -1,6 +1,7 @@ package scorex.core.api.http import akka.http.scaladsl.server.ExceptionHandler +import org.ergoplatform.http.api.ApiError import scala.util.control.NonFatal diff --git a/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala b/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala index 628316b301..77d2cee772 100644 --- a/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala +++ b/src/main/scala/scorex/core/api/http/ApiRejectionHandler.scala @@ -1,6 +1,7 @@ package scorex.core.api.http import akka.http.scaladsl.server._ +import org.ergoplatform.http.api.ApiError object ApiRejectionHandler { diff --git a/src/main/scala/scorex/core/api/http/ApiResponse.scala b/src/main/scala/scorex/core/api/http/ApiResponse.scala index 19bd1a84d3..78690db8aa 100644 --- a/src/main/scala/scorex/core/api/http/ApiResponse.scala +++ b/src/main/scala/scorex/core/api/http/ApiResponse.scala @@ -4,6 +4,7 @@ import akka.http.scaladsl.model.{ContentTypes, HttpEntity, StatusCode, StatusCod import akka.http.scaladsl.server.{Directives, Route} import io.circe.syntax._ import io.circe.{Encoder, Json} +import org.ergoplatform.http.api.ApiError import scala.concurrent.Future import scala.language.implicitConversions diff --git a/src/main/scala/scorex/core/api/http/CompositeHttpService.scala b/src/main/scala/scorex/core/api/http/CompositeHttpService.scala index fcba826590..2adcd13e09 100644 --- a/src/main/scala/scorex/core/api/http/CompositeHttpService.scala +++ b/src/main/scala/scorex/core/api/http/CompositeHttpService.scala @@ -4,8 +4,8 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.directives.RouteDirectives +import org.ergoplatform.settings.RESTApiSettings import scorex.core.api.http.swagger.SwaggerConfigRoute -import scorex.core.settings.RESTApiSettings case class CompositeHttpService(system: ActorSystem, routes: Seq[ApiRoute], settings: RESTApiSettings, swaggerConf: String) extends CorsHandler { diff --git a/src/main/scala/scorex/core/api/http/swagger/SwaggerConfigRoute.scala b/src/main/scala/scorex/core/api/http/swagger/SwaggerConfigRoute.scala index f5b104ed1d..79801be49a 100644 --- a/src/main/scala/scorex/core/api/http/swagger/SwaggerConfigRoute.scala +++ b/src/main/scala/scorex/core/api/http/swagger/SwaggerConfigRoute.scala @@ -3,8 +3,8 @@ package scorex.core.api.http.swagger import akka.actor.ActorRefFactory import akka.http.scaladsl.model.{ContentTypes, HttpEntity} import akka.http.scaladsl.server.Route +import org.ergoplatform.settings.RESTApiSettings import scorex.core.api.http.ApiRoute -import scorex.core.settings.RESTApiSettings class SwaggerConfigRoute(swaggerConf: String, override val settings: RESTApiSettings)(implicit val context: ActorRefFactory) extends ApiRoute { diff --git a/src/main/scala/scorex/core/app/ScorexContext.scala b/src/main/scala/scorex/core/app/ScorexContext.scala index 6af361ab9a..1ee96fa461 100644 --- a/src/main/scala/scorex/core/app/ScorexContext.scala +++ b/src/main/scala/scorex/core/app/ScorexContext.scala @@ -3,7 +3,7 @@ package scorex.core.app import java.net.InetSocketAddress import scorex.core.network.UPnPGateway -import scorex.core.network.message.MessageSpec +import org.ergoplatform.network.message.MessageSpec case class ScorexContext(messageSpecs: Seq[MessageSpec[_]], upnpGateway: Option[UPnPGateway], diff --git a/src/main/scala/scorex/core/network/ConnectedPeer.scala b/src/main/scala/scorex/core/network/ConnectedPeer.scala index b8e28ccd86..38461c3ddf 100644 --- a/src/main/scala/scorex/core/network/ConnectedPeer.scala +++ b/src/main/scala/scorex/core/network/ConnectedPeer.scala @@ -3,7 +3,7 @@ package scorex.core.network import akka.actor.ActorRef import io.circe.{Encoder, Json} import org.ergoplatform.network.ModePeerFeature -import scorex.core.network.peer.PeerInfo +import org.ergoplatform.network.peer.PeerInfo /** * Peer connected to our node diff --git a/src/main/scala/scorex/core/network/ConnectionDescription.scala b/src/main/scala/scorex/core/network/ConnectionDescription.scala index 2f12775f01..024f6113b5 100644 --- a/src/main/scala/scorex/core/network/ConnectionDescription.scala +++ b/src/main/scala/scorex/core/network/ConnectionDescription.scala @@ -1,8 +1,8 @@ package scorex.core.network import java.net.InetSocketAddress - import akka.actor.ActorRef +import org.ergoplatform.network.PeerFeature case class ConnectionDescription(connection: ActorRef, connectionId: ConnectionId, diff --git a/src/main/scala/scorex/core/network/DeliveryTracker.scala b/src/main/scala/scorex/core/network/DeliveryTracker.scala index 3e8d37a578..158c1b5d11 100644 --- a/src/main/scala/scorex/core/network/DeliveryTracker.scala +++ b/src/main/scala/scorex/core/network/DeliveryTracker.scala @@ -2,15 +2,15 @@ package scorex.core.network import akka.actor.Cancellable import io.circe.{Encoder, Json} +import org.ergoplatform.consensus.ContainsModifiers import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.CheckDelivery +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.CheckDelivery import org.ergoplatform.nodeView.mempool.ExpiringApproximateCache import org.ergoplatform.settings.{ErgoSettings, NetworkCacheSettings} -import scorex.core.consensus.ContainsModifiers import scorex.core.network.DeliveryTracker._ import scorex.core.network.ModifiersStatus._ -import scorex.core.utils._ +import org.ergoplatform.utils._ import scorex.util.{ModifierId, ScorexLogging} import scala.collection.mutable diff --git a/src/main/scala/scorex/core/network/NetworkController.scala b/src/main/scala/scorex/core/network/NetworkController.scala index 50a13552a4..60001e7154 100644 --- a/src/main/scala/scorex/core/network/NetworkController.scala +++ b/src/main/scala/scorex/core/network/NetworkController.scala @@ -6,16 +6,15 @@ import akka.io.Tcp._ import akka.io.{IO, Tcp} import akka.pattern.ask import akka.util.Timeout -import scorex.core.app.{ScorexContext, Version} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{DisconnectedPeer, HandshakedPeer} -import org.ergoplatform.network.ModePeerFeature -import org.ergoplatform.nodeView.history.ErgoHistory +import scorex.core.app.ScorexContext +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{DisconnectedPeer, HandshakedPeer} +import org.ergoplatform.network.{ModePeerFeature, Version} import org.ergoplatform.settings.ErgoSettings -import scorex.core.network.message.Message.MessageCode -import scorex.core.network.message.Message -import scorex.core.network.peer.PeerManager.ReceivableMessages._ -import scorex.core.network.peer.{LocalAddressPeerFeature, PeerInfo, PeerManager, PeersStatus, PenaltyType, RestApiUrlPeerFeature, SessionIdPeerFeature} -import org.ergoplatform.nodeView.history.ErgoHistory.Time +import org.ergoplatform.network.message.MessageConstants.MessageCode +import org.ergoplatform.network.message.Message +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages._ +import org.ergoplatform.network.peer.{LocalAddressPeerFeature, PeerInfo, PeerManager, PeersStatus, PenaltyType, RestApiUrlPeerFeature, SessionIdPeerFeature} +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Time import scorex.core.utils.NetworkUtils import scorex.util.ScorexLogging @@ -69,7 +68,7 @@ class NetworkController(ergoSettings: ErgoSettings, * Storing timestamp of a last message got via p2p network. * Used to check whether connectivity is lost. */ - private var lastIncomingMessageTime: ErgoHistory.Time = 0L + private var lastIncomingMessageTime: Time = 0L private val activityDelta: Long = 60 * 1000 // 1 min //check own declared address for validity diff --git a/src/main/scala/scorex/core/network/PeerConnectionHandler.scala b/src/main/scala/scorex/core/network/PeerConnectionHandler.scala index 84bb3ac2f4..32dc1e25a9 100644 --- a/src/main/scala/scorex/core/network/PeerConnectionHandler.scala +++ b/src/main/scala/scorex/core/network/PeerConnectionHandler.scala @@ -4,13 +4,14 @@ import akka.actor.{Actor, ActorRef, Cancellable, Props, SupervisorStrategy} import akka.io.Tcp import akka.io.Tcp._ import akka.util.{ByteString, CompactByteString} -import scorex.core.app.Version.Eip37ForkVersion -import scorex.core.app.{ScorexContext, Version} +import org.ergoplatform.network.{Handshake, PeerSpec, Version} +import org.ergoplatform.network.Version.Eip37ForkVersion +import scorex.core.app.ScorexContext import scorex.core.network.NetworkController.ReceivableMessages.{Handshaked, PenalizePeer} import scorex.core.network.PeerConnectionHandler.ReceivableMessages -import scorex.core.network.message.{HandshakeSerializer, MessageSerializer} -import scorex.core.network.peer.{PeerInfo, PenaltyType} -import scorex.core.settings.ScorexSettings +import org.ergoplatform.network.message.{HandshakeSerializer, MessageSerializer} +import org.ergoplatform.network.peer.{PeerInfo, PenaltyType} +import org.ergoplatform.settings.ScorexSettings import scorex.util.ScorexLogging import scala.annotation.tailrec @@ -146,7 +147,7 @@ class PeerConnectionHandler(scorexSettings: ScorexSettings, } def localInterfaceWriting: Receive = { - case msg: message.Message[_] => + case msg: org.ergoplatform.network.message.Message[_] => log.info("Send message " + msg.spec + " to " + connectionId) outMessagesCounter += 1 connection ! Write(messageSerializer.serialize(msg), ReceivableMessages.Ack(outMessagesCounter)) @@ -164,7 +165,7 @@ class PeerConnectionHandler(scorexSettings: ScorexSettings, // operate in ACK mode until all buffered messages are transmitted def localInterfaceBuffering: Receive = { - case msg: message.Message[_] => + case msg: org.ergoplatform.network.message.Message[_] => outMessagesCounter += 1 buffer(outMessagesCounter, messageSerializer.serialize(msg)) diff --git a/src/main/scala/scorex/core/network/PeerSynchronizer.scala b/src/main/scala/scorex/core/network/PeerSynchronizer.scala index fddb446e12..6843b5b151 100644 --- a/src/main/scala/scorex/core/network/PeerSynchronizer.scala +++ b/src/main/scala/scorex/core/network/PeerSynchronizer.scala @@ -4,11 +4,12 @@ import akka.actor.SupervisorStrategy.{Restart, Stop} import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorRef, ActorSystem, DeathPactException, OneForOneStrategy, Props} import akka.pattern.ask import akka.util.Timeout +import org.ergoplatform.network.PeerSpec import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} -import scorex.core.network.message.{GetPeersSpec, Message, MessageSpec, PeersSpec} -import scorex.core.network.peer.{PeerInfo, PenaltyType} -import scorex.core.network.peer.PeerManager.ReceivableMessages.{AddPeerIfEmpty, SeenPeers} -import scorex.core.settings.NetworkSettings +import org.ergoplatform.network.message.{GetPeersSpec, Message, MessageSpec, PeersSpec} +import org.ergoplatform.network.peer.{PeerInfo, PenaltyType} +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages.{AddPeerIfEmpty, SeenPeers} +import org.ergoplatform.settings.NetworkSettings import scorex.util.ScorexLogging import shapeless.syntax.typeable._ diff --git a/src/main/scala/scorex/core/network/Synchronizer.scala b/src/main/scala/scorex/core/network/Synchronizer.scala index 27833d78c9..8e13836bf9 100644 --- a/src/main/scala/scorex/core/network/Synchronizer.scala +++ b/src/main/scala/scorex/core/network/Synchronizer.scala @@ -1,6 +1,6 @@ package scorex.core.network -import scorex.core.network.message.MessageSpec +import org.ergoplatform.network.message.MessageSpec import scorex.util.ScorexLogging import scala.util.{Failure, Success, Try} diff --git a/src/main/scala/scorex/core/network/UPnP.scala b/src/main/scala/scorex/core/network/UPnP.scala index 0d0d2a9123..2f82e4dcaa 100644 --- a/src/main/scala/scorex/core/network/UPnP.scala +++ b/src/main/scala/scorex/core/network/UPnP.scala @@ -1,9 +1,8 @@ package scorex.core.network import java.net.{InetAddress, InetSocketAddress} - import org.bitlet.weupnp.{GatewayDevice, GatewayDiscover, PortMappingEntry} -import scorex.core.settings.NetworkSettings +import org.ergoplatform.settings.NetworkSettings import scorex.util.ScorexLogging import scala.collection.JavaConverters._ diff --git a/src/test/scala/org/ergoplatform/db/DBSpec.scala b/src/test/scala/org/ergoplatform/db/DBSpec.scala index f2617ee32a..58f908be48 100644 --- a/src/test/scala/org/ergoplatform/db/DBSpec.scala +++ b/src/test/scala/org/ergoplatform/db/DBSpec.scala @@ -21,7 +21,7 @@ trait DBSpec extends TestFileUtils { protected def byteString32(s: String): Array[Byte] = Algos.hash(byteString(s)) - protected def withDb(body: DB => Unit): Unit = { + protected def withDb[T](body: DB => T): T = { val options = new Options() options.createIfMissing(true) options.verifyChecksums(true) @@ -32,10 +32,10 @@ trait DBSpec extends TestFileUtils { protected def versionId(s: String): Array[Byte] = byteString32(s) - protected def withStore(body: LDBKVStore => Unit): Unit = + protected def withStore[T](body: LDBKVStore => T): T = withDb { db: DB => body(new LDBKVStore(db)) } - protected def withVersionedStore(keepVersions: Int)(body: LDBVersionedStore => Unit): Unit = { + protected def withVersionedStore[T](keepVersions: Int)(body: LDBVersionedStore => T): T = { val db = new LDBVersionedStore(createTempDir, keepVersions) try body(db) finally db.close() } diff --git a/src/test/scala/org/ergoplatform/http/routes/EmissionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/EmissionApiRouteSpec.scala index 7d89b239a5..c48d4a6dbe 100644 --- a/src/test/scala/org/ergoplatform/http/routes/EmissionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/EmissionApiRouteSpec.scala @@ -9,7 +9,7 @@ import io.circe.Json import io.circe.syntax._ import org.ergoplatform.http.api.EmissionApiRoute import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.settings.{ErgoSettings, ReemissionSettings} +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader, ReemissionSettings} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -24,7 +24,7 @@ class EmissionApiRouteSpec extends AnyFlatSpec implicit val timeout: RouteTestTimeout = RouteTestTimeout(15.seconds.dilated) - val ergoSettings: ErgoSettings = ErgoSettings.read() + val ergoSettings: ErgoSettings = ErgoSettingsReader.read() val coinEmission: EmissionRules = ergoSettings.chainSettings.emissionRules val reemissionSettings: ReemissionSettings = ergoSettings.chainSettings.reemission diff --git a/src/test/scala/org/ergoplatform/http/routes/ErgoPeersApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/ErgoPeersApiRouteSpec.scala index de1a6edc5d..371dc088be 100644 --- a/src/test/scala/org/ergoplatform/http/routes/ErgoPeersApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/ErgoPeersApiRouteSpec.scala @@ -14,8 +14,8 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.core.network.NetworkController.ReceivableMessages.GetConnectedPeers -import scorex.core.network.peer.PeerManager.ReceivableMessages.GetAllPeers -import scorex.core.settings.RESTApiSettings +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages.GetAllPeers +import org.ergoplatform.settings.RESTApiSettings import java.net.InetSocketAddress import scala.concurrent.Future diff --git a/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala b/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala index 706ff37ddd..8b5c67a621 100644 --- a/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala @@ -16,8 +16,8 @@ import org.ergoplatform.local.ErgoStatsCollector.{GetNodeInfo, NodeInfo} import org.ergoplatform.local.ErgoStatsCollectorRef import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.ChangedHistory -import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.ChangedHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Difficulty import org.ergoplatform.utils.Stubs import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala index fb4956190f..c3a1c33851 100644 --- a/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/ScanApiRouteSpec.scala @@ -10,7 +10,7 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.http.api.ScanEntities.{ScanIdBoxId, ScanIdWrapper} import org.ergoplatform.http.api.{ApiCodecs, ScanApiRoute} import org.ergoplatform.nodeView.wallet.scanning._ -import org.ergoplatform.settings.{Args, ErgoSettings} +import org.ergoplatform.settings.{Args, ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.Stubs import org.ergoplatform.wallet.Constants.ScanId import org.scalatest.flatspec.AnyFlatSpec @@ -36,7 +36,7 @@ class ScanApiRouteSpec extends AnyFlatSpec val prefix = "/scan" - val ergoSettings: ErgoSettings = ErgoSettings.read( + val ergoSettings: ErgoSettings = ErgoSettingsReader.read( Args(userConfigPathOpt = Some("src/test/resources/application.conf"), networkTypeOpt = None)) val route: Route = ScanApiRoute(utxoReadersRef, ergoSettings).route diff --git a/src/test/scala/org/ergoplatform/http/routes/ScriptApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/ScriptApiRouteSpec.scala index acd2fd8de5..ff4b372c56 100644 --- a/src/test/scala/org/ergoplatform/http/routes/ScriptApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/ScriptApiRouteSpec.scala @@ -6,7 +6,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport import io.circe.Json import org.ergoplatform.{Pay2SAddress, Pay2SHAddress} -import org.ergoplatform.settings.{Args, ErgoSettings} +import org.ergoplatform.settings.{Args, ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.Stubs import io.circe.syntax._ import org.ergoplatform.http.api.ScriptApiRoute @@ -26,7 +26,7 @@ class ScriptApiRouteSpec extends AnyFlatSpec val prefix = "/script" - val ergoSettings: ErgoSettings = ErgoSettings.read( + val ergoSettings: ErgoSettings = ErgoSettingsReader.read( Args(userConfigPathOpt = Some("src/test/resources/application.conf"), networkTypeOpt = None)) val route: Route = ScriptApiRoute(digestReadersRef, settings).route diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index 1e7860907f..2708b93390 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -11,12 +11,11 @@ import org.ergoplatform.ErgoBox.{AdditionalRegisters, NonMandatoryRegisterId, To import org.ergoplatform.http.api.{ApiCodecs, TransactionsApiRoute} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} -import org.ergoplatform.settings.Constants +import org.ergoplatform.settings.{Constants, RESTApiSettings} import org.ergoplatform.utils.Stubs import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, Input} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import scorex.core.settings.RESTApiSettings import scorex.util.encode.Base16 import sigmastate.SType import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} diff --git a/src/test/scala/org/ergoplatform/http/routes/UtilsApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/UtilsApiRouteSpec.scala index ea4f0e01ba..57503fb23a 100644 --- a/src/test/scala/org/ergoplatform/http/routes/UtilsApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/UtilsApiRouteSpec.scala @@ -1,7 +1,6 @@ package org.ergoplatform.http.routes import java.net.InetSocketAddress - import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Route import akka.http.scaladsl.testkit.ScalatestRouteTest @@ -10,9 +9,9 @@ import io.circe.Json import org.ergoplatform.utils.Stubs import org.ergoplatform.{P2PKAddress, Pay2SAddress, Pay2SHAddress} import org.ergoplatform.http.api.ErgoUtilsApiRoute +import org.ergoplatform.settings.RESTApiSettings import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import scorex.core.settings.RESTApiSettings import scorex.util.encode.Base16 import sigmastate.serialization.ErgoTreeSerializer diff --git a/src/test/scala/org/ergoplatform/http/routes/WalletApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/WalletApiRouteSpec.scala index 3aa73d9dc1..843c99b69f 100644 --- a/src/test/scala/org/ergoplatform/http/routes/WalletApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/WalletApiRouteSpec.scala @@ -6,11 +6,11 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport import io.circe.syntax._ import io.circe.{Decoder, Json} -import org.ergoplatform.http.api.{ApiCodecs, WalletApiRoute} +import org.ergoplatform.http.api.{ApiCodecs, ApiExtraCodecs, ApiRequestsCodecs, WalletApiRoute} import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequestEncoder, PaymentRequest, PaymentRequestEncoder, _} import org.ergoplatform.nodeView.wallet.{AugWalletTransaction, ErgoAddressJsonEncoder} -import org.ergoplatform.settings.{Args, Constants, ErgoSettings} +import org.ergoplatform.settings.{Args, Constants, ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.Stubs import org.ergoplatform.utils.generators.ErgoTransactionGenerators import org.ergoplatform.{ErgoAddress, Pay2SAddress} @@ -27,13 +27,15 @@ class WalletApiRouteSpec extends AnyFlatSpec with ScalatestRouteTest with Stubs with FailFastCirceSupport - with ApiCodecs { + with ApiCodecs + with ApiRequestsCodecs + with ApiExtraCodecs { implicit val timeout: RouteTestTimeout = RouteTestTimeout(145.seconds) val prefix = "/wallet" - val ergoSettings: ErgoSettings = ErgoSettings.read( + val ergoSettings: ErgoSettings = ErgoSettingsReader.read( Args(userConfigPathOpt = Some("src/test/resources/application.conf"), networkTypeOpt = None)) val route: Route = WalletApiRoute(digestReadersRef, nodeViewRef, settings).route val failingNodeViewRef = system.actorOf(NodeViewStub.failingProps()) @@ -44,7 +46,7 @@ class WalletApiRouteSpec extends AnyFlatSpec implicit val paymentRequestEncoder: PaymentRequestEncoder = new PaymentRequestEncoder(ergoSettings) implicit val assetIssueRequestEncoder: AssetIssueRequestEncoder = new AssetIssueRequestEncoder(ergoSettings) implicit val requestsHolderEncoder: RequestsHolderEncoder = new RequestsHolderEncoder(ergoSettings) - implicit val addressJsonDecoder: Decoder[ErgoAddress] = ErgoAddressJsonEncoder(settings).decoder + implicit val addressJsonDecoder: Decoder[ErgoAddress] = ErgoAddressJsonEncoder(settings.chainSettings).decoder val paymentRequest = PaymentRequest(Pay2SAddress(Constants.FalseLeaf)(addressEncoder), 100L, Seq.empty, Map.empty) val assetIssueRequest = AssetIssueRequest(Pay2SAddress(Constants.FalseLeaf)(addressEncoder), None, 100L, "TEST", "Test", 8) diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index f018569c5e..749f9f41de 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -4,9 +4,9 @@ import akka.actor.{ActorRef, ActorSystem} import akka.testkit.{TestActorRef, TestProbe} import org.ergoplatform.ErgoAddressEncoder import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{FailedTransaction, RecheckMempool, SuccessfulTransaction} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{FailedTransaction, RecheckMempool, SuccessfulTransaction} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{LocallyGeneratedTransaction, RecheckedTransactions} -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.ProcessingOutcome import org.ergoplatform.nodeView.state.ErgoState import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} @@ -86,7 +86,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH applyBlock(block) shouldBe 'success - scorex.core.utils.untilTimeout(cleanupDuration * 4, 100.millis) { + org.ergoplatform.utils.untilTimeout(cleanupDuration * 4, 100.millis) { // first tx removed from pool during node view update // another tx invalidated by `MempoolAuditor` getPoolSize shouldBe 0 diff --git a/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala b/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala index 173623f510..415120f93b 100644 --- a/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala @@ -3,10 +3,10 @@ package org.ergoplatform.mining import com.google.common.primitives.Ints import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} +import org.ergoplatform.testkit.utils.NoShrink import org.ergoplatform.utils.ErgoPropertyTest import org.scalacheck.Gen import scorex.crypto.hash.Blake2b256 -import scorex.testkit.utils.NoShrink import scorex.util.encode.Base16 class AutolykosPowSchemeSpec extends ErgoPropertyTest with NoShrink { diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index 1c666e18e4..4917d4d104 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -1,7 +1,7 @@ package org.ergoplatform.mining import org.ergoplatform.ErgoTreePredef -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.ErgoStateContext import org.ergoplatform.settings.MonetarySettings import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} @@ -199,11 +199,11 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { val bh = boxesHolderGen.sample.get var us = createUtxoState(bh, parameters) - val height = ErgoHistory.EmptyHistoryHeight + val height = EmptyHistoryHeight val ms = MonetarySettings(minerRewardDelay = delta) val st = settings.copy(chainSettings = settings.chainSettings.copy(monetary = ms)) - val sc = ErgoStateContext.empty(genesisStateDigest, st, parameters) + val sc = ErgoStateContext.empty(genesisStateDigest, st.chainSettings, parameters) val txBoxes = bh.boxes.grouped(inputsNum).map(_.values.toIndexedSeq).toSeq val blockTx = @@ -248,7 +248,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { Gen.nonEmptyListOf(validErgoTransactionGenTemplate(minAssets = 0, propositionGen = feeProp)) ) { btxs => val blockTxs = btxs.map(_._2) - val height = ErgoHistory.EmptyHistoryHeight + val height = EmptyHistoryHeight val txs = CandidateGenerator.collectRewards( us.emissionBoxOpt, height, diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala index 148d5ebffb..d94938f591 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala @@ -9,12 +9,12 @@ import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.FullBlockApplied import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import org.scalatest.concurrent.Eventually @@ -34,7 +34,7 @@ class CandidateGeneratorSpec extends AnyFlatSpec with ErgoTestHelpers with Event private val blockValidationDelay: FiniteDuration = 2.seconds val defaultSettings: ErgoSettings = { - val empty = ErgoSettings.read() + val empty = ErgoSettingsReader.read() val nodeSettings = empty.nodeSettings.copy( mining = true, stateType = StateType.Utxo, diff --git a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala index 25258277e3..c86eea5209 100644 --- a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala @@ -10,14 +10,14 @@ import org.ergoplatform.mining.ErgoMiner.StartMining import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction, UnsignedErgoTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.FullBlockApplied import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.utils.generators.ValidBlocksGenerators import org.ergoplatform.wallet.interpreter.ErgoInterpreter @@ -47,7 +47,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen await(minerRef.askWithStatus(GenerateCandidate(mandatoryTransactions, reply = true)).mapTo[Candidate].map(_.externalVersion)) val defaultSettings: ErgoSettings = { - val empty = ErgoSettings.read() + val empty = ErgoSettingsReader.read() val nodeSettings = empty.nodeSettings.copy(mining = true, stateType = StateType.Utxo, @@ -206,7 +206,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen } // wait for mempool to be cleaned - scorex.core.utils.untilTimeout(5.minute, 500.millis) { + org.ergoplatform.utils.untilTimeout(5.minute, 500.millis) { log.debug(s"Wait until transactions in mempool will be included into blocks. Currents size: ${requestReaders.m.size}") requestReaders.m.size shouldBe 0 system.terminate() @@ -376,7 +376,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen system.eventStream.subscribe(testProbe.ref, newBlockSignal) val forkSettings: ErgoSettings = { - val empty = ErgoSettings.read() + val empty = ErgoSettingsReader.read() val nodeSettings = empty.nodeSettings.copy(mining = true, stateType = StateType.Utxo, diff --git a/src/test/scala/org/ergoplatform/modifiers/history/PoPowAlgosSpec.scala b/src/test/scala/org/ergoplatform/modifiers/history/PoPowAlgosSpec.scala index ea2a976724..a13fb887cf 100644 --- a/src/test/scala/org/ergoplatform/modifiers/history/PoPowAlgosSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/history/PoPowAlgosSpec.scala @@ -1,6 +1,6 @@ package org.ergoplatform.modifiers.history -import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, PoPowHeader, PoPowParams} +import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProverWithDbAlgs, NipopowProof, PoPowHeader, PoPowParams} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.utils.generators.ChainGenerator @@ -127,7 +127,7 @@ class PoPowAlgosSpec extends AnyPropSpec with Matchers with HistoryTestHelpers w val h = generateHistory(true, StateType.Digest, false, 10000, 10000, 10, None) val hr = applyChain(h, blocksChain) - val proof1 = nipopowAlgos.prove(hr)(poPowParams).get + val proof1 = NipopowProverWithDbAlgs.prove(hr, chainSettings = settings.chainSettings)(poPowParams).get proof0.suffixHead.id shouldBe proof1.suffixHead.id proof0.suffixTail.map(_.id) shouldBe proof1.suffixTail.map(_.id) @@ -145,12 +145,12 @@ class PoPowAlgosSpec extends AnyPropSpec with Matchers with HistoryTestHelpers w val h = generateHistory(true, StateType.Digest, false, 10000, 10000, 10, None) val hr = applyChain(h, blocksChain.take(at)) - val proof0 = nipopowAlgos.prove(hr, None)(poPowParams).get + val proof0 = NipopowProverWithDbAlgs.prove(hr, None, chainSettings = settings.chainSettings)(poPowParams).get val id = proof0.suffixHead.header.id val hrf = applyChain(hr, blocksChain.drop(at)) - val proof1 = nipopowAlgos.prove(hrf, Some(id))(poPowParams).get + val proof1 = NipopowProverWithDbAlgs.prove(hrf, Some(id), chainSettings = settings.chainSettings)(poPowParams).get proof0.suffixHead.id shouldBe proof1.suffixHead.id proof0.suffixTail.map(_.id) shouldBe proof1.suffixTail.map(_.id) diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index 4901e7526b..371b356384 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -356,7 +356,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { val maxCost = (Int.MaxValue - 10) / 10 // cannot use Int.MaxValue directly due to overflow when it is converted to block cost val ps = Parameters(0, DefaultParameters.updated(MaxBlockCostIncrease, maxCost), emptyVSUpdate) val sc = new ErgoStateContext(Seq.empty, None, genesisStateDigest, ps, ErgoValidationSettings.initial, - VotingData.empty)(settings) + VotingData.empty)(settings.chainSettings) .upcoming(org.ergoplatform.mining.group.generator, 0L, settings.chainSettings.initialNBits, @@ -478,7 +478,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { LaunchParameters.parametersTable.updated(Parameters.BlockVersion, blockVersion), LaunchParameters.proposedUpdate) new ErgoStateContext(Seq(header), None, genesisStateDigest, params, ErgoValidationSettings.initial, - VotingData.empty)(settings) + VotingData.empty)(settings.chainSettings) } def stateContextForTx(tx: ErgoTransaction, blockVersion: Byte): ErgoStateContext = { diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala index 7398a9ce5f..38c67cad88 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ExpirationSpecification.scala @@ -6,11 +6,13 @@ import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} import org.scalatest.Assertion +import scorex.util.encode.Base16 import sigma.Colls -import sigmastate.Values.ShortConstant +import sigmastate.Values.{ErgoTree, ShortConstant} import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ +import sigmastate.serialization.ErgoTreeSerializer class ExpirationSpecification extends ErgoPropertyTest { @@ -18,9 +20,9 @@ class ExpirationSpecification extends ErgoPropertyTest { private implicit val verifier: ErgoInterpreter = ErgoInterpreter(parameters) - def falsify(box: ErgoBox): ErgoBox = { + def injectScript(box: ErgoBox, script: ErgoTree): ErgoBox = { testBox(box.value, - Constants.FalseLeaf, + script, box.creationHeight, box.additionalTokens.toArray.toSeq, box.additionalRegisters, @@ -49,7 +51,7 @@ class ExpirationSpecification extends ErgoPropertyTest { val updContext = { val inContext = new ErgoStateContext(Seq(fakeHeader), None, genesisStateDigest, parameters, validationSettingsNoIl, - VotingData.empty)(settings) + VotingData.empty)(settings.chainSettings) inContext.appendFullBlock(fb).get } @@ -68,6 +70,16 @@ class ExpirationSpecification extends ErgoPropertyTest { } } + property("successful spending w. invalid ergotree") { + forAll(unspendableErgoBoxGen()) { from => + // invalid (unparseable) ergo tree + val etString = "0e1631393039303063646462363930366462363530336665" + val et = ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(Base16.decode(etString).get) + val modified = injectScript(from, et) + constructTest(modified, 0, _ => IndexedSeq(modified), expectedValidity = true) + } + } + property("successful spending w. max spending") { forAll(unspendableErgoBoxGen()) { from => constructTest(from, 0, h => { diff --git a/src/test/scala/org/ergoplatform/network/ActivePeerFilteringSpecification.scala b/src/test/scala/org/ergoplatform/network/ActivePeerFilteringSpecification.scala index 8271050c45..3bd2e3d823 100644 --- a/src/test/scala/org/ergoplatform/network/ActivePeerFilteringSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ActivePeerFilteringSpecification.scala @@ -1,8 +1,8 @@ package org.ergoplatform.network import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.network.peer.PeerInfo -import scorex.core.network.peer.PeerManager.ReceivableMessages.SeenPeers +import org.ergoplatform.network.peer.PeerInfo +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages.SeenPeers import java.net.{InetAddress, InetSocketAddress} diff --git a/src/test/scala/org/ergoplatform/network/DeliveryTrackerSpec.scala b/src/test/scala/org/ergoplatform/network/DeliveryTrackerSpec.scala new file mode 100644 index 0000000000..2372e5c085 --- /dev/null +++ b/src/test/scala/org/ergoplatform/network/DeliveryTrackerSpec.scala @@ -0,0 +1,73 @@ +package org.ergoplatform.network + +import akka.actor.{ActorRef, Cancellable} +import io.circe._ +import io.circe.syntax._ +import org.ergoplatform.modifiers.NetworkObjectTypeId +import org.ergoplatform.testkit.generators.ObjectGenerators +import org.ergoplatform.utils.ErgoPropertyTest +import scorex.util.ModifierId +import scorex.core.network.DeliveryTracker +import scorex.core.network.ModifiersStatus._ + + +class DeliveryTrackerSpec extends ErgoPropertyTest with ObjectGenerators { + + property("tracker should accept requested modifiers, turn them into received and clear them") { + forAll(connectedPeerGen(ActorRef.noSender)) { peer => + val tracker = DeliveryTracker.empty(settings) + val mid: ModifierId = ModifierId @@ "foo" + val mTypeId: NetworkObjectTypeId.Value = NetworkObjectTypeId.fromByte(104) + tracker.setRequested(mTypeId, mid, peer) { _ => Cancellable.alreadyCancelled} + val infoFields = + Seq( + "address" -> peer.connectionId.remoteAddress.toString.asJson, + "checks" -> 0.asJson + ) ++ peer.peerInfo.map(_.peerSpec.protocolVersion.toString.asJson).map("version" -> _) + tracker.fullInfo.asJson shouldBe Json.obj( + "invalidModifierApproxSize" -> 0.asJson, + "requested" -> Json.obj( + "104" -> Json.obj( + "foo" -> Json.obj(infoFields:_*) + ) + ), + "received" -> Json.obj() + ) + + tracker.setReceived(mid, mTypeId, peer) + val infoFields2 = + Seq( + "address" -> peer.connectionId.remoteAddress.toString.asJson + ) ++ peer.peerInfo.map(_.peerSpec.protocolVersion.toString.asJson).map("version" -> _) + + tracker.fullInfo.asJson shouldBe Json.obj( + "invalidModifierApproxSize" -> 0.asJson, + "requested" -> Json.obj( + "104" -> Json.obj(), + ), + "received" -> Json.obj( + "104" -> Json.obj( + "foo" -> Json.obj(infoFields2:_*) + ), + ) + ) + tracker.clearStatusForModifier(mid, mTypeId, Received) + tracker.fullInfo.asJson shouldBe Json.obj( + "invalidModifierApproxSize" -> 0.asJson, + "requested" -> Json.obj( + "104" -> Json.obj(), + ), + "received" -> Json.obj( + "104" -> Json.obj() + ) + ) + + tracker.reset() + val fullInfoAfterReset = tracker.fullInfo + fullInfoAfterReset.invalidModifierApproxSize shouldBe 0 + fullInfoAfterReset.requested.size shouldBe 0 + fullInfoAfterReset.received.size shouldBe 0 + } + } + +} diff --git a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala index 2430aa7728..7b2c64cf4c 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala @@ -2,29 +2,29 @@ package org.ergoplatform.network import akka.actor.{ActorRef, ActorSystem, Cancellable, Props} import akka.testkit.TestProbe +import org.ergoplatform.PersistentNodeViewModifier import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader, ErgoSyncInfoMessageSpec, ErgoSyncInfoV2} import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{StateType, UtxoState} import org.ergoplatform.sanity.ErgoSanity._ -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader} import org.ergoplatform.utils.HistoryTestHelpers import org.ergoplatform.wallet.utils.FileUtils import org.scalacheck.Gen import org.scalatest.concurrent.Eventually import org.scalatest.matchers.should.Matchers -import scorex.core.PersistentNodeViewModifier import scorex.core.network.ModifiersStatus.{Received, Unknown} import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork -import scorex.core.network.message._ -import scorex.core.network.peer.PeerInfo +import org.ergoplatform.network.message._ +import org.ergoplatform.network.peer.PeerInfo import scorex.core.network.{ConnectedPeer, DeliveryTracker} -import scorex.core.serialization.ErgoSerializer -import scorex.testkit.utils.AkkaFixture +import org.ergoplatform.serialization.ErgoSerializer +import org.ergoplatform.testkit.utils.AkkaFixture import scala.concurrent.duration.{Duration, _} import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor} @@ -126,7 +126,7 @@ class ErgoNodeViewSynchronizerSpecification extends HistoryTestHelpers with Matc val h = localHistoryGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val s = localStateGen.sample.get - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() val pool = ErgoMemPool.empty(settings) implicit val ec: ExecutionContextExecutor = system.dispatcher val ncProbe = TestProbe("NetworkControllerProbe") diff --git a/src/test/scala/org/ergoplatform/network/ErgoSyncInfoSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoSyncInfoSpecification.scala index 3768260d27..b331e3e51a 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoSyncInfoSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoSyncInfoSpecification.scala @@ -5,7 +5,7 @@ import java.nio.ByteBuffer import com.google.common.primitives.Ints import org.ergoplatform.nodeView.history.{ErgoSyncInfoMessageSpec, ErgoSyncInfoV1} import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.network.message.{Message, MessageSerializer} +import org.ergoplatform.network.message.{Message, MessageSerializer} import scorex.crypto.hash import scorex.util.ModifierId import scorex.util.encode.Base16 diff --git a/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala index b96081117e..0da8eeefda 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoSyncTrackerSpecification.scala @@ -1,9 +1,9 @@ package org.ergoplatform.network +import org.ergoplatform.consensus.{Older, Younger} import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.consensus.{Older, Younger} import scorex.core.network.{ConnectedPeer, ConnectionId, Incoming} -import scorex.core.network.peer.PeerInfo +import org.ergoplatform.network.peer.PeerInfo class ErgoSyncTrackerSpecification extends ErgoPropertyTest { property("getters test") { diff --git a/src/test/scala/org/ergoplatform/network/HandshakeSpecification.scala b/src/test/scala/org/ergoplatform/network/HandshakeSpecification.scala index b00ec2867d..44f71edebb 100644 --- a/src/test/scala/org/ergoplatform/network/HandshakeSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/HandshakeSpecification.scala @@ -1,11 +1,9 @@ package org.ergoplatform.network import java.nio.ByteBuffer - import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.app.Version -import scorex.core.network.message.HandshakeSerializer +import org.ergoplatform.network.message.HandshakeSerializer import scorex.util.encode.Base16 class HandshakeSpecification extends ErgoPropertyTest with DecodingUtils { diff --git a/src/test/scala/org/ergoplatform/network/InvSpecification.scala b/src/test/scala/org/ergoplatform/network/InvSpecification.scala index 7c756ec079..e3d0bb41cb 100644 --- a/src/test/scala/org/ergoplatform/network/InvSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/InvSpecification.scala @@ -5,7 +5,7 @@ import java.nio.ByteBuffer import com.google.common.primitives.Ints import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.network.message.{InvData, InvSpec, Message, MessageSerializer} +import org.ergoplatform.network.message.{InvData, InvSpec, Message, MessageSerializer} import scorex.crypto.hash import scorex.util.ModifierId import scorex.util.encode.Base16 diff --git a/src/test/scala/org/ergoplatform/network/ModifiersSpecification.scala b/src/test/scala/org/ergoplatform/network/ModifiersSpecification.scala index bc8e8d876f..d8dc4cd7d5 100644 --- a/src/test/scala/org/ergoplatform/network/ModifiersSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ModifiersSpecification.scala @@ -5,7 +5,7 @@ import java.nio.ByteBuffer import com.google.common.primitives.Ints import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.network.message.{Message, MessageSerializer, ModifiersData, ModifiersSpec} +import org.ergoplatform.network.message.{Message, MessageSerializer, ModifiersData, ModifiersSpec} import scorex.util.encode.Base16 /** diff --git a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala index cd7ee1e71e..c2352f58ff 100644 --- a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala @@ -2,9 +2,7 @@ package org.ergoplatform.network import akka.actor.ActorRef import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.app.Version -import scorex.core.network.PeerSpec -import scorex.core.network.peer.PeerInfo +import org.ergoplatform.network.peer.PeerInfo import scorex.core.network.{ConnectedPeer, ConnectionId} class PeerFilteringRuleSpecification extends ErgoPropertyTest { diff --git a/src/test/scala/org/ergoplatform/network/PeerSpecSerializerSpec.scala b/src/test/scala/org/ergoplatform/network/PeerSpecSerializerSpec.scala new file mode 100644 index 0000000000..f343fd2ec0 --- /dev/null +++ b/src/test/scala/org/ergoplatform/network/PeerSpecSerializerSpec.scala @@ -0,0 +1,21 @@ +package org.ergoplatform.network + +import org.ergoplatform.testkit.generators.ObjectGenerators +import org.ergoplatform.utils.ErgoPropertyTest +import scorex.util.ByteArrayBuilder +import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} + +import java.nio.ByteBuffer + +class PeerSpecSerializerSpec extends ErgoPropertyTest with ObjectGenerators { + + property("All variants of peer spec should be serialized and deserialized successfully") { + forAll(peerSpecGen) { peerSpec => + val writer = new VLQByteBufferWriter(new ByteArrayBuilder()) + PeerSpecSerializer.serialize(peerSpec, writer) + val reader = new VLQByteBufferReader(ByteBuffer.wrap(writer.result().toBytes)) + val actualPeerSpec = PeerSpecSerializer.parse(reader) + peerSpec shouldBe actualPeerSpec + } + } +} diff --git a/src/test/scala/org/ergoplatform/network/RequestModifiersSpecification.scala b/src/test/scala/org/ergoplatform/network/RequestModifiersSpecification.scala index 7489c43871..03b2a842aa 100644 --- a/src/test/scala/org/ergoplatform/network/RequestModifiersSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/RequestModifiersSpecification.scala @@ -5,7 +5,7 @@ import java.nio.ByteBuffer import com.google.common.primitives.Ints import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.network.message.{InvData, Message, MessageSerializer, RequestModifierSpec} +import org.ergoplatform.network.message.{InvData, Message, MessageSerializer, RequestModifierSpec} import scorex.crypto.hash import scorex.util.ModifierId import scorex.util.encode.Base16 diff --git a/src/test/scala/org/ergoplatform/nodeView/ErgoModifiersCacheSpec.scala b/src/test/scala/org/ergoplatform/nodeView/ErgoModifiersCacheSpec.scala index 29e67176bd..d84ac8be04 100644 --- a/src/test/scala/org/ergoplatform/nodeView/ErgoModifiersCacheSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/ErgoModifiersCacheSpec.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.utils.{ErgoPropertyTest, HistoryTestHelpers} import scorex.crypto.hash.Blake2b256 @@ -64,7 +64,7 @@ class ErgoModifiersCacheSpec extends ErgoPropertyTest with HistoryTestHelpers { val c1 = modifiersCache.popCandidate(history0).value c1.isInstanceOf[Header] shouldBe true val h1 = c1.asInstanceOf[Header] - h1.height shouldBe ErgoHistory.GenesisHeight + h1.height shouldBe GenesisHeight val history1 = history0.append(c1).get._1 diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index 9311d13c78..1c383c2dd5 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -2,10 +2,12 @@ package org.ergoplatform.nodeView import akka.actor.{ActorRef, ActorSystem} import akka.testkit.TestProbe +import org.ergoplatform.consensus.SyncInfo +import org.ergoplatform.core.BytesSerializable import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeViewChanges, ModifiersFromRemote} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.ErgoMemPool @@ -16,15 +18,14 @@ import org.ergoplatform.wallet.utils.TestFileUtils import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec -import scorex.core.consensus.SyncInfo import scorex.core.network.ConnectedPeer import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} -import scorex.core.network.message._ -import scorex.core.network.peer.PenaltyType -import scorex.core.serialization.{BytesSerializable, ErgoSerializer, ManifestSerializer} +import org.ergoplatform.network.message._ +import org.ergoplatform.network.peer.PenaltyType +import org.ergoplatform.serialization.{ErgoSerializer, ManifestSerializer} +import org.ergoplatform.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} +import org.ergoplatform.testkit.utils.AkkaFixture import scorex.crypto.hash.Digest32 -import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} -import scorex.testkit.utils.AkkaFixture import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} import org.ergoplatform.utils.generators.ChainGenerator diff --git a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala index 40feb58cef..fca3d13d93 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala @@ -1,12 +1,13 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.consensus.ModifierSemanticValidity import org.ergoplatform.modifiers.{BlockSection, NonHeaderBlockSection} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.state.StateType +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.utils.HistoryTestHelpers -import scorex.core.consensus.ModifierSemanticValidity import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId import scorex.util.encode.Base16 @@ -75,7 +76,7 @@ class BlockSectionValidationSpecification extends HistoryTestHelpers { history.writeMinimalFullBlockHeight(history.bestHeaderOpt.get.height + 1) history.isHeadersChainSyncedVar = true history.applicableTry(section) shouldBe 'failure - history.writeMinimalFullBlockHeight(ErgoHistory.GenesisHeight) + history.writeMinimalFullBlockHeight(GenesisHeight) // should not be able to apply if corresponding header is marked as invalid history.applicableTry(section) shouldBe 'success diff --git a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala index b2cbb964ca..5001a9ddc1 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala @@ -1,15 +1,16 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.consensus.{Equal, Fork, Older, Younger} import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.modifiers.history.HeaderChain import org.ergoplatform.nodeView.state.StateType +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.Algos import org.ergoplatform.utils.HistoryTestHelpers import scorex.crypto.hash.Digest32 -import scorex.core.consensus.{Older, Younger, Fork, Equal} import scala.util.Random @@ -268,7 +269,7 @@ class NonVerifyADHistorySpecification extends HistoryTestHelpers { val chain = genHeaderChain(BlocksInChain, history, diffBitsOpt = None, useRealTs = false) chain.headers.foreach { header => - val inHeight = history.heightOf(header.parentId).getOrElse(ErgoHistory.EmptyHistoryHeight) + val inHeight = history.heightOf(header.parentId).getOrElse(EmptyHistoryHeight) history.contains(header) shouldBe false history.applicable(header) shouldBe true diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 3903251054..def8600a21 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -1,12 +1,13 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor import org.ergoplatform.nodeView.state.{StateType, UtxoState} import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers -import scorex.core.VersionTag -import scorex.core.serialization.{ManifestSerializer, SubtreeSerializer} +import org.ergoplatform.core.VersionTag +import org.ergoplatform.serialization.{ManifestSerializer, SubtreeSerializer} import scorex.db.LDBVersionedStore import scorex.util.ModifierId @@ -19,7 +20,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val epochLength = 20 val utxoSetSnapshotProcessor = new UtxoSetSnapshotProcessor { - var minimalFullBlockHeightVar = ErgoHistory.GenesisHeight + var minimalFullBlockHeightVar = GenesisHeight override protected val settings: ErgoSettings = s.copy(chainSettings = s.chainSettings.copy(voting = s.chainSettings.voting.copy(votingLength = epochLength))) override protected val historyStorage: HistoryStorage = HistoryStorage(settings) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala index 0240d00afb..c5c8240ec7 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala @@ -1,16 +1,16 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.HeaderChain import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.ErgoModifiersCache import org.ergoplatform.nodeView.state.StateType +import org.ergoplatform.testkit.utils.NoShrink import org.ergoplatform.utils.HistoryTestHelpers -import scorex.core.consensus.ProgressInfo -import scorex.core.consensus.ModifierSemanticValidity.{Absent, Invalid, Unknown, Valid} -import scorex.testkit.utils.NoShrink - +import org.ergoplatform.consensus.ModifierSemanticValidity._ import scala.collection.mutable.ArrayBuffer import scala.util.Random @@ -19,7 +19,7 @@ class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { type PM = BlockSection private def genHistory(blocksNum: Int = 0, - minFullHeight: Option[Int] = Some(ErgoHistory.GenesisHeight)): (ErgoHistory, Seq[ErgoFullBlock]) = { + minFullHeight: Option[Int] = Some(GenesisHeight)): (ErgoHistory, Seq[ErgoFullBlock]) = { val inHistory = generateHistory(verifyTransactions = true, StateType.Digest, PoPoWBootstrap = false, BlocksToKeep) minFullHeight.foreach { h => inHistory.writeMinimalFullBlockHeight(h) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index cb024dd9bb..99e8786850 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -1,5 +1,6 @@ package org.ergoplatform.nodeView.history +import org.ergoplatform.consensus.ProgressInfo import org.ergoplatform.modifiers.{ErgoFullBlock, NetworkObjectTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension @@ -8,10 +9,9 @@ import org.ergoplatform.nodeView.history.storage.modifierprocessors.FullBlockPro import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.settings.Algos import org.ergoplatform.utils.HistoryTestHelpers -import scorex.core.consensus.ProgressInfo class VerifyNonADHistorySpecification extends HistoryTestHelpers { - import scorex.core.utils.MapPimp + import org.ergoplatform.utils.MapPimp private def genHistory() = generateHistory(verifyTransactions = true, StateType.Utxo, PoPoWBootstrap = false, BlocksToKeep) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/extra/ChainGenerator.scala b/src/test/scala/org/ergoplatform/nodeView/history/extra/ChainGenerator.scala new file mode 100644 index 0000000000..144305ab33 --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ChainGenerator.scala @@ -0,0 +1,208 @@ +package org.ergoplatform.nodeView.history.extra + +import org.ergoplatform.ErgoBox.TokenId +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.mining.difficulty.DifficultySerializer +import org.ergoplatform.mining.{AutolykosPowScheme, CandidateBlock, CandidateGenerator} +import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} +import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.modifiers.history.popow.NipopowAlgos +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.GenesisHeight +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, UtxoState, UtxoStateReader} +import org.ergoplatform.utils.ErgoTestHelpers +import org.ergoplatform._ +import scorex.util.ModifierId +import sigma.{Coll, Colls} +import sigmastate.Values +import sigmastate.crypto.DLogProtocol.ProveDlog +import sigmastate.eval.Extensions._ +import sigmastate.eval._ + +import java.io.File +import scala.annotation.tailrec +import scala.collection.mutable.ArrayBuffer +import scala.concurrent.duration.{DurationInt, FiniteDuration} +import scala.util.{Random, Try} + +object ChainGenerator extends ErgoTestHelpers { + + val pow: AutolykosPowScheme = new AutolykosPowScheme(powScheme.k, powScheme.n) + val blockInterval: FiniteDuration = 2.minute + val EmissionTxCost: Long = 20000 + val MinTxAmount: Long = 2000000 + val RewardDelay: Int = initSettings.chainSettings.monetary.minerRewardDelay + val MaxTxsPerBlock: Int = 10 + val minerPk: ProveDlog = defaultProver.hdKeys.head.publicImage + val selfAddressScript: Values.ErgoTree = P2PKAddress(minerPk).script + val minerProp: Values.ErgoTree = ErgoTreePredef.rewardOutputScript(RewardDelay, minerPk) + val votingEpochLength: Height = votingSettings.votingLength + val protocolVersion: Byte = initSettings.chainSettings.protocolVersion + val minimalSuffix = 2 + val txCostLimit: Height = initSettings.nodeSettings.maxTransactionCost + val txSizeLimit: Height = initSettings.nodeSettings.maxTransactionSize + + var startTime: Long = 0 + + def generate(length: Int, dir: File)(history: ErgoHistory): Unit = { + val stateDir = new File(s"${dir.getAbsolutePath}/state") + stateDir.mkdirs() + val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, initSettings) + System.out.println(s"Going to generate a chain at ${dir.getAbsolutePath} starting from ${history.bestFullBlockOpt}") + startTime = System.currentTimeMillis() - (blockInterval * (length - 1)).toMillis + val chain = loop(state, None, None, Seq())(history) + System.out.println(s"Chain of length ${chain.length} generated") + history.bestHeaderOpt shouldBe history.bestFullBlockOpt.map(_.header) + history.bestFullBlockOpt.get.id shouldBe chain.last + System.out.println("History was generated successfully") + } + + @tailrec + private def loop(state: UtxoState, + initBox: Option[ErgoBox], + last: Option[Header], + acc: Seq[ModifierId])(history: ErgoHistory): Seq[ModifierId] = { + val time: Long = last.map(_.timestamp + blockInterval.toMillis).getOrElse(startTime) + if (time < System.currentTimeMillis()) { + val (txs, lastOut) = genTransactions(last.map(_.height).getOrElse(GenesisHeight), + initBox, state.stateContext) + + val candidate = genCandidate(defaultProver.hdPubKeys.head.key, last, time, txs, state)(history) + val block = proveCandidate(candidate.get) + + history.append(block.header).get + block.blockSections.foreach(s => if (!history.contains(s)) history.append(s).get) + + val outToPassNext = if (last.isEmpty) { + block.transactions.flatMap(_.outputs).find(_.ergoTree == minerProp) + } else { + lastOut + } + + assert(outToPassNext.isDefined) + + log.info( + s"Block ${block.id} with ${block.transactions.size} transactions at height ${block.header.height} generated") + + loop(state.applyModifier(block, None)(_ => ()).get, outToPassNext, Some(block.header), acc :+ block.id)(history) + } else { + acc + } + } + + private def moveTokens(inOpt: Option[ErgoBox], cond: Boolean): Coll[(TokenId, Long)] = { + val tokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] + inOpt match { + case Some(input) if cond => + tokens += Tuple2(input.id.toTokenId, math.abs(Random.nextInt())) + case Some(tokenBox) if !cond => + tokenBox.additionalTokens.toArray.foreach(tokens += _) + case _ => + } + Colls.fromArray(tokens.toArray) + } + + private def genTransactions(height: Height, + inOpt: Option[ErgoBox], + ctx: ErgoStateContext): (Seq[ErgoTransaction], Option[ErgoBox]) = { + inOpt + .find { bx => + val canUnlock = (bx.creationHeight + RewardDelay <= height) || (bx.ergoTree != minerProp) + canUnlock && bx.ergoTree != initSettings.chainSettings.monetary.emissionBoxProposition && bx.value >= MinTxAmount + } + .map { input => + val qty = MaxTxsPerBlock + val amount = input.value + val outs = (0 until qty).map(i => new ErgoBoxCandidate(amount, selfAddressScript, height, moveTokens(inOpt, i == 0))) + var i = 0 + val x = outs + .foldLeft((Seq.empty[ErgoTransaction], input)) { case ((acc, in), out) => + val inputs = IndexedSeq(in) + val newOut = + if (i > 0) + new ErgoBoxCandidate(amount, selfAddressScript, height, moveTokens(acc.lastOption.map(_.outputs.head), cond = false)) + else + out + val unsignedTx = UnsignedErgoTransaction(inputs.map(box => new UnsignedInput(box.id)), IndexedSeq(newOut)) + i += 1 + defaultProver.sign(unsignedTx, inputs, emptyDataBoxes, ctx) + .fold(_ => acc -> in, tx => (acc :+ ErgoTransaction(tx)) -> unsignedTx.outputs.head) + } + ._1 + (x, Some(x.last.outputs.head)) + } + .getOrElse(Seq.empty -> inOpt) + } + + private def genCandidate(minerPk: ProveDlog, + lastHeaderOpt: Option[Header], + ts: Long, + txsFromPool: Seq[ErgoTransaction], + state: UtxoStateReader)(history: ErgoHistory): Try[CandidateBlock] = Try { + val stateContext = state.stateContext + val nBits: Long = lastHeaderOpt + .map(parent => history.requiredDifficultyAfter(parent)) + .map(d => DifficultySerializer.encodeCompactBits(d)) + .getOrElse(settings.chainSettings.initialNBits) + + val interlinks = lastHeaderOpt + .flatMap { h => + history.typedModifierById[Extension](h.extensionId) + .flatMap(ext => NipopowAlgos.unpackInterlinks(ext.fields).toOption) + .map(nipopowAlgos.updateInterlinks(h, _)) + } + .getOrElse(Seq.empty) + val interlinksExtension = nipopowAlgos.interlinksToExtension(interlinks) + + val (extensionCandidate, votes: Array[Byte], version: Byte) = lastHeaderOpt.map { header => + val newHeight = header.height + 1 + val currentParams = stateContext.currentParameters + val betterVersion = protocolVersion > header.version + val votingFinishHeight: Option[Height] = currentParams.softForkStartingHeight + .map(_ + votingSettings.votingLength * votingSettings.softForkEpochs) + val forkVotingAllowed = votingFinishHeight.forall(fh => newHeight < fh) + val forkOrdered = settings.votingTargets.softFork != 0 + val voteForFork = betterVersion && forkOrdered && forkVotingAllowed + + if (newHeight % votingEpochLength == 0 && newHeight > 0) { + val (newParams, _) = currentParams.update(newHeight, voteForFork, stateContext.votingData.epochVotes, emptyVSUpdate, votingSettings) + (newParams.toExtensionCandidate ++ interlinksExtension, + newParams.suggestVotes(settings.votingTargets.targets, voteForFork), + newParams.blockVersion) + } else { + (nipopowAlgos.interlinksToExtension(interlinks), + currentParams.vote(settings.votingTargets.targets, stateContext.votingData.epochVotes, voteForFork), + currentParams.blockVersion) + } + }.getOrElse((interlinksExtension, Array(0: Byte, 0: Byte, 0: Byte), Header.InitialVersion)) + + val emissionTxOpt = CandidateGenerator.collectEmission(state, minerPk, emptyStateContext) + val txs = emissionTxOpt.toSeq ++ txsFromPool + + state.proofsForTransactions(txs).map { case (adProof, adDigest) => + CandidateBlock(lastHeaderOpt, version, nBits, adDigest, adProof, txs, ts, extensionCandidate, votes) + } + }.flatten + + @tailrec + private def proveCandidate(candidate: CandidateBlock): ErgoFullBlock = { + log.info(s"Trying to prove block with parent ${candidate.parentOpt.map(_.encodedId)} and timestamp ${candidate.timestamp}") + + pow.proveCandidate(candidate, defaultProver.hdKeys.head.privateInput.w) match { + case Some(fb) => fb + case _ => + val interlinks = candidate.parentOpt + .map(nipopowAlgos.updateInterlinks(_, NipopowAlgos.unpackInterlinks(candidate.extension.fields).get)) + .getOrElse(Seq.empty) + val minerTag = scorex.utils.Random.randomBytes(Extension.FieldKeySize) + proveCandidate { + candidate.copy( + extension = ExtensionCandidate(Seq(Array(0: Byte, 2: Byte) -> minerTag)) ++ nipopowAlgos.interlinksToExtension(interlinks) + ) + } + } + } + +} diff --git a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala index a377e8a2e7..b18e2e7536 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -1,80 +1,33 @@ package org.ergoplatform.nodeView.history.extra -import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform._ -import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{AutolykosPowScheme, CandidateBlock, CandidateGenerator} -import org.ergoplatform.modifiers.ErgoFullBlock -import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} -import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.history.popow.NipopowAlgos -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.nodeView.history.ErgoHistory +import akka.actor.{ActorRef, ActorSystem, Props} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{FullBlockApplied, Rollback} +import org.ergoplatform.nodeView.history.extra.ExtraIndexer.ReceivableMessages.Index import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hashErgoTree import org.ergoplatform.nodeView.history.extra.SegmentSerializer.{boxSegmentId, txSegmentId} -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.nodeView.state._ -import org.ergoplatform.settings.{ErgoSettings, NetworkType, NipopowSettings, NodeConfigurationSettings, UtxoSettings} -import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} +import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} +import org.ergoplatform.utils.HistoryTestHelpers import scorex.util.{ModifierId, bytesToId} -import sigmastate.Values -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.eval.Extensions._ -import sigmastate.eval._ -import sigma.{Coll, Colls} import spire.implicits.cfor -import java.io.File -import scala.annotation.tailrec import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.reflect.ClassTag -import scala.util.{Random, Try} -class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase with HistoryTestHelpers { +class ExtraIndexerSpecification extends HistoryTestHelpers { - type ID_LL = mutable.HashMap[ModifierId,(Long,Long)] - - override protected val saveLimit: Int = 1 // save every block - override protected implicit val segmentTreshold: Int = 8 // split to smaller segments - override protected implicit val addressEncoder: ErgoAddressEncoder = initSettings.chainSettings.addressEncoder + case class CreateDB() - val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, UtxoSettings(utxoBootstrap = false, 0, 2), NipopowSettings(nipopowBootstrap = false, 1), mining = false, - ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, internalMinersCount = 1, - internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, - 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, - 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) + type ID_LL = mutable.HashMap[ModifierId,(Long,Long)] val HEIGHT: Int = 50 val BRANCHPOINT: Int = HEIGHT / 2 + implicit val segmentThreshold: Int = 8 - def createDB(): Unit = { - val dir: File = createTempDir - dir.mkdirs() - - val fullHistorySettings: ErgoSettings = ErgoSettings(dir.getAbsolutePath, NetworkType.TestNet, initSettings.chainSettings, - nodeSettings, settings.scorexSettings, settings.walletSettings, settings.cacheSettings) + val system: ActorSystem = ActorSystem.create("indexer-test") + val indexer: ActorRef = system.actorOf(Props.create(classOf[ExtraIndexerTestActor], this)) - _history = ErgoHistory.readOrGenerate(fullHistorySettings)(null) - - ChainGenerator.generate(HEIGHT, dir)(_history) - - // reset all variables - indexedHeight = 0 - globalTxIndex = 0L - globalBoxIndex = 0L - lastWroteToDB = 0 - caughtUp = false - rollback = false - general.clear() - boxes.clear() - trees.clear() - tokens.clear() - segments.clear() - } + var _history: ErgoHistory = _ + def history: ErgoHistoryReader = _history.getReader def manualIndex(limit: Int): (ID_LL, // address -> (erg,tokenSum) ID_LL, // tokenId -> (boxesCount,_) @@ -82,8 +35,8 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w Int) = { // boxes indexed var txsIndexed = 0 var boxesIndexed = 0 - val addresses: ID_LL = mutable.HashMap[ModifierId,(Long,Long)]() - val indexedTokens: ID_LL = mutable.HashMap[ModifierId,(Long,Long)]() + val addresses: ID_LL = mutable.HashMap[ModifierId, (Long, Long)]() + val indexedTokens: ID_LL = mutable.HashMap[ModifierId, (Long, Long)]() cfor(1)(_ <= limit, _ + 1) { i => _history.getReader.bestBlockTransactionsAt(i).get.txs.foreach { tx => txsIndexed += 1 @@ -119,18 +72,18 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w segmentables.foreach { segmentable => history.typedExtraIndexById[T](segmentable._1) match { case Some(obj: T) => - if(isChild) { // this is a segment + if (isChild) { // this is a segment // check tx segments - val txSegments: ID_LL = mutable.HashMap.empty[ModifierId,(Long,Long)] + val txSegments: ID_LL = mutable.HashMap.empty[ModifierId, (Long, Long)] txSegments ++= (0 until obj.txSegmentCount).map(n => obj.idMod(txSegmentId(obj.parentId, n))).map(Tuple2(_, (0L, 0L))) checkSegmentables(txSegments, isChild = true, check) shouldBe 0 // check box segments - val boxSegments: ID_LL = mutable.HashMap.empty[ModifierId,(Long,Long)] + val boxSegments: ID_LL = mutable.HashMap.empty[ModifierId, (Long, Long)] boxSegments ++= (0 until obj.boxSegmentCount).map(n => obj.idMod(boxSegmentId(obj.parentId, n))).map(Tuple2(_, (0L, 0L))) checkSegmentables(boxSegments, isChild = true, check) shouldBe 0 - }else { // this is the parent object + } else { // this is the parent object // check properties of object - if(!check((obj, segmentable._2))) + if (!check((obj, segmentable._2))) errors += 1 // check boxes in memory obj.boxes.foreach { boxNum => @@ -169,9 +122,11 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w }) property("extra indexer transactions") { - createDB() - run() - cfor(0)(_ < globalTxIndex, _ + 1) {n => + indexer ! CreateDB() + indexer ! Index() + Thread.sleep(5000) + val state = IndexerState.fromHistory(_history) + cfor(0)(_ < state.globalTxIndex, _ + 1) { n => val id = history.typedExtraIndexById[NumericTxIndex](bytesToId(NumericTxIndex.indexToBytes(n))) id shouldNot be(empty) history.typedExtraIndexById[IndexedErgoTransaction](id.get.m) shouldNot be(empty) @@ -179,9 +134,11 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w } property("extra indexer boxes") { - createDB() - run() - cfor(0)(_ < globalBoxIndex, _ + 1) { n => + indexer ! CreateDB() + indexer ! Index() + Thread.sleep(5000) + val state = IndexerState.fromHistory(_history) + cfor(0)(_ < state.globalBoxIndex, _ + 1) { n => val id = history.typedExtraIndexById[NumericBoxIndex](bytesToId(NumericBoxIndex.indexToBytes(n))) id shouldNot be(empty) history.typedExtraIndexById[IndexedErgoBox](id.get.m) shouldNot be(empty) @@ -189,32 +146,37 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w } property("extra indexer addresses") { - createDB() - run() + indexer ! CreateDB() + indexer ! Index() + Thread.sleep(5000) val (addresses, _, _, _) = manualIndex(HEIGHT) checkAddresses(addresses) shouldBe 0 } property("extra indexer tokens") { - createDB() - run() + indexer ! CreateDB() + indexer ! Index() + Thread.sleep(5000) val (_, indexedTokens, _, _) = manualIndex(HEIGHT) checkTokens(indexedTokens) shouldBe 0 } property("extra indexer rollback") { - createDB() + indexer ! CreateDB() + indexer ! Index() + Thread.sleep(5000) + var state = IndexerState.fromHistory(_history) - run() - - val txIndexBefore = globalTxIndex - val boxIndexBefore = globalBoxIndex + val txIndexBefore = state.globalTxIndex + val boxIndexBefore = state.globalBoxIndex // manually count balances val (addresses, indexedTokens, txsIndexed, boxesIndexed) = manualIndex(BRANCHPOINT) // perform rollback - removeAfter(BRANCHPOINT) + indexer ! Rollback(history.bestHeaderIdAtHeight(BRANCHPOINT).get) + Thread.sleep(5000) + state = IndexerState.fromHistory(_history) // address balances checkAddresses(addresses) shouldBe 0 @@ -223,13 +185,13 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w checkTokens(indexedTokens) shouldBe 0 // check indexnumbers - globalTxIndex shouldBe txsIndexed - globalBoxIndex shouldBe boxesIndexed + state.globalTxIndex shouldBe txsIndexed + state.globalBoxIndex shouldBe boxesIndexed // check txs - cfor(0)(_ < txIndexBefore, _ + 1) {txNum => + cfor(0)(_ < txIndexBefore, _ + 1) { txNum => val txOpt = history.typedExtraIndexById[NumericTxIndex](bytesToId(NumericTxIndex.indexToBytes(txNum))) - if(txNum < globalTxIndex) + if (txNum < state.globalTxIndex) txOpt shouldNot be(empty) else txOpt shouldBe None @@ -238,7 +200,7 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w // check boxes cfor(0)(_ < boxIndexBefore, _ + 1) { boxNum => val boxOpt = history.typedExtraIndexById[NumericBoxIndex](bytesToId(NumericBoxIndex.indexToBytes(boxNum))) - if (boxNum < globalBoxIndex) + if (boxNum < state.globalBoxIndex) boxOpt shouldNot be(empty) else boxOpt shouldBe None @@ -246,7 +208,11 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w // ------------------------------------------------------------------- // restart indexer to catch up - run() + cfor(BRANCHPOINT)(_ <= HEIGHT, _ + 1) { i => + indexer ! FullBlockApplied(history.bestHeaderAtHeight(i).get) + } + Thread.sleep(5000) + state = IndexerState.fromHistory(_history) // Check addresses again val (addresses2, indexedTokens2, _, _) = manualIndex(HEIGHT) @@ -254,8 +220,8 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w checkTokens(indexedTokens2) shouldBe 0 // check indexnumbers again - globalTxIndex shouldBe txIndexBefore - globalBoxIndex shouldBe boxIndexBefore + state.globalTxIndex shouldBe txIndexBefore + state.globalBoxIndex shouldBe boxIndexBefore // check txs after caught up cfor(0)(_ < txIndexBefore, _ + 1) { txNum => @@ -266,187 +232,5 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w cfor(0)(_ < boxIndexBefore, _ + 1) { boxNum => history.typedExtraIndexById[NumericBoxIndex](bytesToId(NumericBoxIndex.indexToBytes(boxNum))) shouldNot be(empty) } - - } - -} - -object ChainGenerator extends ErgoTestHelpers { - - val pow: AutolykosPowScheme = new AutolykosPowScheme(powScheme.k, powScheme.n) - val blockInterval: FiniteDuration = 2.minute - val EmissionTxCost: Long = 20000 - val MinTxAmount: Long = 2000000 - val RewardDelay: Int = initSettings.chainSettings.monetary.minerRewardDelay - val MaxTxsPerBlock: Int = 10 - val minerPk: ProveDlog = defaultProver.hdKeys.head.publicImage - val selfAddressScript: Values.ErgoTree = P2PKAddress(minerPk).script - val minerProp: Values.ErgoTree = ErgoTreePredef.rewardOutputScript(RewardDelay, minerPk) - val votingEpochLength: Height = votingSettings.votingLength - val protocolVersion: Byte = initSettings.chainSettings.protocolVersion - val minimalSuffix = 2 - val txCostLimit: Height = initSettings.nodeSettings.maxTransactionCost - val txSizeLimit: Height = initSettings.nodeSettings.maxTransactionSize - - var startTime: Long = 0 - - def generate(length: Int, dir: File)(history: ErgoHistory): Unit = { - val stateDir = new File(s"${dir.getAbsolutePath}/state") - stateDir.mkdirs() - val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, initSettings) - System.out.println(s"Going to generate a chain at ${dir.getAbsolutePath} starting from ${history.bestFullBlockOpt}") - startTime = System.currentTimeMillis() - (blockInterval * (length - 1)).toMillis - val chain = loop(state, None, None, Seq())(history) - System.out.println(s"Chain of length ${chain.length} generated") - history.bestHeaderOpt shouldBe history.bestFullBlockOpt.map(_.header) - history.bestFullBlockOpt.get.id shouldBe chain.last - System.out.println("History was generated successfully") } - - @tailrec - private def loop(state: UtxoState, - initBox: Option[ErgoBox], - last: Option[Header], - acc: Seq[ModifierId])(history: ErgoHistory): Seq[ModifierId] = { - val time: Long = last.map(_.timestamp + blockInterval.toMillis).getOrElse(startTime) - if (time < System.currentTimeMillis()) { - val (txs, lastOut) = genTransactions(last.map(_.height).getOrElse(ErgoHistory.GenesisHeight), - initBox, state.stateContext) - - val candidate = genCandidate(defaultProver.hdPubKeys.head.key, last, time, txs, state)(history) - val block = proveCandidate(candidate.get) - - history.append(block.header).get - block.blockSections.foreach(s => if (!history.contains(s)) history.append(s).get) - - val outToPassNext = if (last.isEmpty) { - block.transactions.flatMap(_.outputs).find(_.ergoTree == minerProp) - } else { - lastOut - } - - assert(outToPassNext.isDefined) - - log.info( - s"Block ${block.id} with ${block.transactions.size} transactions at height ${block.header.height} generated") - - loop(state.applyModifier(block, None)(_ => ()).get, outToPassNext, Some(block.header), acc :+ block.id)(history) - } else { - acc - } - } - - private def moveTokens(inOpt: Option[ErgoBox], cond: Boolean): Coll[(TokenId, Long)] = { - val tokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] - inOpt match { - case Some(input) if cond => - tokens += Tuple2(input.id.toTokenId, math.abs(Random.nextInt())) - case Some(tokenBox) if !cond => - tokenBox.additionalTokens.toArray.foreach(tokens += _) - case _ => - } - Colls.fromArray(tokens.toArray) - } - - private def genTransactions(height: Height, - inOpt: Option[ErgoBox], - ctx: ErgoStateContext): (Seq[ErgoTransaction], Option[ErgoBox]) = { - inOpt - .find { bx => - val canUnlock = (bx.creationHeight + RewardDelay <= height) || (bx.ergoTree != minerProp) - canUnlock && bx.ergoTree != initSettings.chainSettings.monetary.emissionBoxProposition && bx.value >= MinTxAmount - } - .map { input => - val qty = MaxTxsPerBlock - val amount = input.value - val outs = (0 until qty).map(i => new ErgoBoxCandidate(amount, selfAddressScript, height, moveTokens(inOpt, i == 0))) - var i = 0 - val x = outs - .foldLeft((Seq.empty[ErgoTransaction], input)) { case ((acc, in), out) => - val inputs = IndexedSeq(in) - val newOut = - if (i > 0) - new ErgoBoxCandidate(amount, selfAddressScript, height, moveTokens(acc.lastOption.map(_.outputs.head), cond = false)) - else - out - val unsignedTx = UnsignedErgoTransaction(inputs.map(box => new UnsignedInput(box.id)), IndexedSeq(newOut)) - i += 1 - defaultProver.sign(unsignedTx, inputs, emptyDataBoxes, ctx) - .fold(_ => acc -> in, tx => (acc :+ ErgoTransaction(tx)) -> unsignedTx.outputs.head) - } - ._1 - (x, Some(x.last.outputs.head)) - } - .getOrElse(Seq.empty -> inOpt) - } - - private def genCandidate(minerPk: ProveDlog, - lastHeaderOpt: Option[Header], - ts: Long, - txsFromPool: Seq[ErgoTransaction], - state: UtxoStateReader)(history: ErgoHistory): Try[CandidateBlock] = Try { - val stateContext = state.stateContext - val nBits: Long = lastHeaderOpt - .map(parent => history.requiredDifficultyAfter(parent)) - .map(d => DifficultySerializer.encodeCompactBits(d)) - .getOrElse(settings.chainSettings.initialNBits) - - val interlinks = lastHeaderOpt - .flatMap { h => - history.typedModifierById[Extension](h.extensionId) - .flatMap(ext => NipopowAlgos.unpackInterlinks(ext.fields).toOption) - .map(nipopowAlgos.updateInterlinks(h, _)) - } - .getOrElse(Seq.empty) - val interlinksExtension = nipopowAlgos.interlinksToExtension(interlinks) - - val (extensionCandidate, votes: Array[Byte], version: Byte) = lastHeaderOpt.map { header => - val newHeight = header.height + 1 - val currentParams = stateContext.currentParameters - val betterVersion = protocolVersion > header.version - val votingFinishHeight: Option[Height] = currentParams.softForkStartingHeight - .map(_ + votingSettings.votingLength * votingSettings.softForkEpochs) - val forkVotingAllowed = votingFinishHeight.forall(fh => newHeight < fh) - val forkOrdered = settings.votingTargets.softFork != 0 - val voteForFork = betterVersion && forkOrdered && forkVotingAllowed - - if (newHeight % votingEpochLength == 0 && newHeight > 0) { - val (newParams, _) = currentParams.update(newHeight, voteForFork, stateContext.votingData.epochVotes, emptyVSUpdate, votingSettings) - (newParams.toExtensionCandidate ++ interlinksExtension, - newParams.suggestVotes(settings.votingTargets.targets, voteForFork), - newParams.blockVersion) - } else { - (nipopowAlgos.interlinksToExtension(interlinks), - currentParams.vote(settings.votingTargets.targets, stateContext.votingData.epochVotes, voteForFork), - currentParams.blockVersion) - } - }.getOrElse((interlinksExtension, Array(0: Byte, 0: Byte, 0: Byte), Header.InitialVersion)) - - val emissionTxOpt = CandidateGenerator.collectEmission(state, minerPk, emptyStateContext) - val txs = emissionTxOpt.toSeq ++ txsFromPool - - state.proofsForTransactions(txs).map { case (adProof, adDigest) => - CandidateBlock(lastHeaderOpt, version, nBits, adDigest, adProof, txs, ts, extensionCandidate, votes) - } - }.flatten - - @tailrec - private def proveCandidate(candidate: CandidateBlock): ErgoFullBlock = { - log.info(s"Trying to prove block with parent ${candidate.parentOpt.map(_.encodedId)} and timestamp ${candidate.timestamp}") - - pow.proveCandidate(candidate, defaultProver.hdKeys.head.privateInput.w) match { - case Some(fb) => fb - case _ => - val interlinks = candidate.parentOpt - .map(nipopowAlgos.updateInterlinks(_, NipopowAlgos.unpackInterlinks(candidate.extension.fields).get)) - .getOrElse(Seq.empty) - val minerTag = scorex.utils.Random.randomBytes(Extension.FieldKeySize) - proveCandidate { - candidate.copy( - extension = ExtensionCandidate(Seq(Array(0: Byte, 2: Byte) -> minerTag)) ++ nipopowAlgos.interlinksToExtension(interlinks) - ) - } - } - } - } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerTestActor.scala b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerTestActor.scala new file mode 100644 index 0000000000..e32c3c647f --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerTestActor.scala @@ -0,0 +1,55 @@ +package org.ergoplatform.nodeView.history.extra + +import org.ergoplatform._ +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.SortingOption +import org.ergoplatform.nodeView.state._ +import org.ergoplatform.settings._ +import org.ergoplatform.wallet.utils.FileUtils +import scorex.util.ModifierId + +import java.io.File +import scala.collection.mutable +import scala.concurrent.duration.DurationInt + +class ExtraIndexerTestActor(test: ExtraIndexerSpecification) extends ExtraIndexerBase with FileUtils { + + override def receive: Receive = { + case test.CreateDB() => createDB() + } + + type ID_LL = mutable.HashMap[ModifierId,(Long,Long)] + + override protected val saveLimit: Int = 1 // save every block + override protected implicit val segmentThreshold: Int = 8 // split to smaller segments + override protected implicit val addressEncoder: ErgoAddressEncoder = test.initSettings.chainSettings.addressEncoder + + val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, + -1, UtxoSettings(utxoBootstrap = false, 0, 2), NipopowSettings(nipopowBootstrap = false, 1), mining = false, + ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, internalMinersCount = 1, + internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, + 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, + 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) + + def createDB(): Unit = { + val dir: File = createTempDir + dir.mkdirs() + + val fullHistorySettings: ErgoSettings = ErgoSettings(dir.getAbsolutePath, NetworkType.TestNet, test.initSettings.chainSettings, + nodeSettings, test.initSettings.scorexSettings, test.initSettings.walletSettings, test.initSettings.cacheSettings) + + _history = ErgoHistory.readOrGenerate(fullHistorySettings)(null) + + ChainGenerator.generate(test.HEIGHT, dir)(_history) + test._history = _history + + // reset all variables + general.clear() + boxes.clear() + trees.clear() + tokens.clear() + segments.clear() + context.become(receive.orElse(loaded(IndexerState(0, 0, 0, 0, caughtUp = false)))) + } + +} diff --git a/src/test/scala/org/ergoplatform/nodeView/history/storage/HistoryStorageSpec.scala b/src/test/scala/org/ergoplatform/nodeView/history/storage/HistoryStorageSpec.scala index 7020d1fee7..fd50355f3f 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/storage/HistoryStorageSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/storage/HistoryStorageSpec.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.storage import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.settings.Algos import org.ergoplatform.utils.HistoryTestHelpers import org.scalacheck.Gen @@ -17,7 +17,7 @@ class HistoryStorageSpec extends HistoryTestHelpers { property("Write Read Remove") { val headers: Array[Header] = Gen.listOfN(20, defaultHeaderGen).sample.get.toArray val modifiers: Array[ADProofs] = Gen.listOfN(20, randomADProofsGen).sample.get.toArray - def validityKey(id: ModifierId) = ByteArrayWrapper(Algos.hash("validity".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + def validityKey(id: ModifierId) = ByteArrayWrapper(Algos.hash("validity".getBytes(CharsetName) ++ idToBytes(id))) val indexes = headers.flatMap(h => Array(validityKey(h.id) -> Array(1.toByte))) db.insert(indexes, (headers ++ modifiers).asInstanceOf[Array[BlockSection]]) shouldBe 'success diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index 38aaa799d3..b705ec11ad 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -1,9 +1,8 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.{ErgoBoxCandidate, Input} -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.{SortingOption, ProcessingOutcome} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.ErgoTestHelpers diff --git a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala index 7cf71def1a..e9db1e9e72 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala @@ -5,7 +5,7 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} -import scorex.core._ +import org.ergoplatform.core._ import scorex.crypto.authds.ADDigest import sigmastate.interpreter.ProverResult diff --git a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala index 059b4e0d1c..2260b25314 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala @@ -4,12 +4,12 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.settings.{Args, ErgoSettings} +import org.ergoplatform.settings.{Args, ErgoSettingsReader} import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.scalacheck.Gen -import scorex.core.bytesToVersion -import scorex.core.validation.ValidationResult.Valid +import org.ergoplatform.core.bytesToVersion +import org.ergoplatform.validation.ValidationResult.Valid import scorex.db.ByteArrayWrapper import scala.collection.mutable @@ -66,7 +66,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { } property("generateGenesisUtxoState & generateGenesisDigestState are compliant") { - val settings = ErgoSettings.read(Args.empty) + val settings = ErgoSettingsReader.read(Args.empty) val dir = createTempDir val rootHash = createUtxoState(settings)._1.rootDigest val expectedRootHash = ErgoState.generateGenesisDigestState(dir, settings).rootDigest @@ -163,28 +163,28 @@ class ErgoStateSpecification extends ErgoPropertyTest { val expectedCost = 185160 // successful validation - ErgoState.execTransactions(txs, stateContext)(id => Try(boxes(ByteArrayWrapper(id)))) shouldBe Valid(expectedCost) + ErgoState.execTransactions(txs, stateContext, settings.nodeSettings)(id => Try(boxes(ByteArrayWrapper(id)))) shouldBe Valid(expectedCost) // cost limit exception expected when crossing MaxBlockCost val tooManyTxs = (1 to 10).flatMap(_ => generateTxs) assert( - ErgoState.execTransactions(tooManyTxs, stateContext)(id => Try(boxes(ByteArrayWrapper(id)))).errors.head.message.contains( + ErgoState.execTransactions(tooManyTxs, stateContext, settings.nodeSettings)(id => Try(boxes(ByteArrayWrapper(id)))).errors.head.message.contains( "Accumulated cost of block transactions should not exceed " ) ) // missing box in state - ErgoState.execTransactions(txs, stateContext)(_ => Failure(new RuntimeException)).errors.head.message shouldBe + ErgoState.execTransactions(txs, stateContext, settings.nodeSettings)(_ => Failure(new RuntimeException)).errors.head.message shouldBe "Every input of the transaction should be in UTXO. null" // tx validation should kick in and detect block height violation val invalidTx = invalidErgoTransactionGen.sample.get assert( - ErgoState.execTransactions(txs :+ invalidTx, stateContext)(id => Try(boxes.getOrElse(ByteArrayWrapper(id), invalidTx.outputs.head))) + ErgoState.execTransactions(txs :+ invalidTx, stateContext, settings.nodeSettings)(id => Try(boxes.getOrElse(ByteArrayWrapper(id), invalidTx.outputs.head))) .errors.head.message.startsWith("Transaction outputs should have creationHeight not exceeding block height.") ) // no transactions are valid - assert(ErgoState.execTransactions(Seq.empty, stateContext)(id => Try(boxes(ByteArrayWrapper(id)))).isValid) + assert(ErgoState.execTransactions(Seq.empty, stateContext, settings.nodeSettings)(id => Try(boxes(ByteArrayWrapper(id)))).isValid) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala index e3dc146a8c..4ce52bc67c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala @@ -9,13 +9,13 @@ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ +import org.ergoplatform.modifiers.transaction.TooHighCostError +import org.ergoplatform.core.idToVersion import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.Constants import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import scorex.core._ -import scorex.core.transaction.TooHighCostError import scorex.crypto.authds.ADKey import scorex.db.ByteArrayWrapper import scorex.util.{ModifierId, bytesToId} @@ -62,7 +62,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("Founders should be able to spend genesis founders box") { var (us, bh) = createUtxoState(settings) val foundersBox = genesisBoxes.last - var height: Int = ErgoHistory.GenesisHeight + var height: Int = GenesisHeight val settingsPks = settings.chainSettings.foundersPubkeys .map(str => groupElemFromBytes(Base16.decode(str).get)) @@ -160,7 +160,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("proofsForTransactions") { var (us: UtxoState, bh) = createUtxoState(settings) - var height: Int = ErgoHistory.GenesisHeight + var height: Int = GenesisHeight forAll(defaultHeaderGen) { header => val t = validTransactionsFromBoxHolder(bh, new RandomWrapper(Some(height))) val txs = t._1 @@ -184,7 +184,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera var bh = BoxHolder(Seq(genesisEmissionBox)) var us = createUtxoState(bh, parameters) - var height: Int = ErgoHistory.GenesisHeight + var height: Int = GenesisHeight // generate chain of correct full blocks val chain = (0 until 10) map { _ => val header = defaultHeaderGen.sample.value diff --git a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala index fe1933fad9..256b4c9492 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala @@ -1,11 +1,11 @@ package org.ergoplatform.nodeView.state.wrapped -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.DigestState import org.ergoplatform.settings.ErgoSettings -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag +import org.ergoplatform.nodeView.LocallyGeneratedModifier import scala.util.Try diff --git a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala index d747a4b5be..e0ee4aceed 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala @@ -1,17 +1,16 @@ package org.ergoplatform.nodeView.state.wrapped import java.io.File - import akka.actor.ActorRef -import org.ergoplatform.ErgoBox -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier +import org.ergoplatform.{ErgoBox, TransactionsCarryingPersistentNodeViewModifier} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer -import scorex.core.{TransactionsCarryingPersistentNodeViewModifier, VersionTag, idToVersion} +import org.ergoplatform.core.{VersionTag, idToVersion} +import org.ergoplatform.nodeView.LocallyGeneratedModifier import scorex.crypto.authds.avltree.batch._ import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala index 83e9243ad9..a07eadc8fb 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala @@ -4,22 +4,21 @@ import java.io.File import org.ergoplatform.ErgoBoxCandidate import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.UnconfirmedTransaction -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.StateType.Utxo import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} import org.ergoplatform.utils.{ErgoPropertyTest, HistoryTestHelpers, NodeViewTestConfig, NodeViewTestOps, TestCase} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.ErgoNodeViewHolder +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ +import org.ergoplatform.nodeView.{ErgoNodeViewHolder, LocallyGeneratedModifier} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.ChainProgress -import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome.Accepted +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.ProcessingOutcome.Accepted import scorex.crypto.authds.{ADKey, SerializedAdProof} -import scorex.testkit.utils.NoShrink import scorex.util.{ModifierId, bytesToId} -class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers with NodeViewTestOps with NoShrink { +class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers with NodeViewTestOps { private val t0 = TestCase("check chain is healthy") { fixture => val (us, bh) = createUtxoState(settings) @@ -55,7 +54,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -63,8 +62,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi nodeViewHolderRef ! LocallyGeneratedModifier(block.header) expectMsgType[SyntacticallySuccessfulModifier] - getHistoryHeight shouldBe ErgoHistory.GenesisHeight - getHeightOf(block.header.id) shouldBe Some(ErgoHistory.GenesisHeight) + getHistoryHeight shouldBe GenesisHeight + getHeightOf(block.header.id) shouldBe Some(GenesisHeight) getLastHeadersLength(10) shouldBe 1 getBestHeaderOpt shouldBe Some(block.header) } @@ -76,7 +75,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val block = validFullBlock(Some(parentBlock), us, bh) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -317,7 +316,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[RecoverableFailedModification]) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -327,8 +326,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi nodeViewHolderRef ! LocallyGeneratedModifier(block.header) expectMsgType[SyntacticallySuccessfulModifier] val currentHeight = getHistoryHeight - currentHeight shouldBe ErgoHistory.GenesisHeight - getHeightOf(block.header.id) shouldBe Some(ErgoHistory.GenesisHeight) + currentHeight shouldBe GenesisHeight + getHeightOf(block.header.id) shouldBe Some(GenesisHeight) val randomId = modifierIdGen.sample.value val recoverableTxs = block.blockTransactions.copy(headerId = randomId) @@ -371,7 +370,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[RecoverableFailedModification]) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -404,7 +403,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[RecoverableFailedModification]) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -414,7 +413,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi nodeViewHolderRef ! LocallyGeneratedModifier(block.header) expectMsgType[SyntacticallyFailedModification] getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight } private val t15 = TestCase("apply genesis block header if it's equal to genesisId from config") { fixture => @@ -424,7 +423,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi updateConfig(genesisIdConfig(Some(block.header.id))) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[RecoverableFailedModification]) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -432,8 +431,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi nodeViewHolderRef ! LocallyGeneratedModifier(block.header) expectMsgType[SyntacticallySuccessfulModifier] - getHistoryHeight shouldBe ErgoHistory.GenesisHeight - getHeightOf(block.header.id) shouldBe Some(ErgoHistory.GenesisHeight) + getHistoryHeight shouldBe GenesisHeight + getHeightOf(block.header.id) shouldBe Some(GenesisHeight) } private val t16 = TestCase("apply forks that include genesis block") { fixture => @@ -473,7 +472,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi val header = validFullBlock(None, us, bh).header.copy(parentId = bytesToId(Array.fill(32)(9: Byte))) getBestHeaderOpt shouldBe None - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight subscribeEvents(classOf[RecoverableFailedModification]) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -481,7 +480,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi nodeViewHolderRef ! LocallyGeneratedModifier(header) expectMsgType[SyntacticallyFailedModification] - getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight + getHistoryHeight shouldBe EmptyHistoryHeight getHeightOf(header.id) shouldBe None } diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala index 621a2119dd..e1f69d6eec 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala @@ -3,13 +3,13 @@ package org.ergoplatform.nodeView.viewholder import akka.actor.ActorRef import org.ergoplatform.mining.DefaultFakePowScheme import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.nodeView.LocallyGeneratedModifier import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{DigestState, StateType} -import org.ergoplatform.settings.{ErgoSettings, VotingSettings} +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader, VotingSettings} import org.ergoplatform.utils.fixtures.NodeViewFixture import org.ergoplatform.utils.{ErgoPropertyTest, NodeViewTestOps} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier -import scorex.testkit.utils.NoShrink +import org.ergoplatform.testkit.utils.NoShrink import scala.concurrent.duration._ @@ -20,7 +20,7 @@ class PrunedNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps wit private val BlockInterval = 2.minutes def prunedSettings(blocksToKeep: Int): ErgoSettings = { - val defaultSettings = ErgoSettings.read() + val defaultSettings = ErgoSettingsReader.read() defaultSettings.copy( chainSettings = defaultSettings.chainSettings.copy( powScheme = new DefaultFakePowScheme(defaultSettings.chainSettings.powScheme.k, defaultSettings.chainSettings.powScheme.n), diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala index 5f5967d4d3..73ef39d8ee 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala @@ -57,7 +57,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually log.info(s"Payment request $req") val tx = await(wallet.generateTransaction(req)).get log.info(s"Generated transaction $tx") - val context = new ErgoStateContext(Seq(genesisBlock.header), Some(genesisBlock.extension), startDigest, parameters, validationSettingsNoIl, VotingData.empty) + val context = new ErgoStateContext(Seq(genesisBlock.header), Some(genesisBlock.extension), startDigest, parameters, validationSettingsNoIl, VotingData.empty)(settings.chainSettings) val boxesToSpend = tx.inputs.map(i => genesisTx.outputs.find(o => java.util.Arrays.equals(o.id, i.boxId)).get) tx.statefulValidity(boxesToSpend, emptyDataBoxes, context) shouldBe 'success val block = makeNextBlock(getUtxoState, Seq(tx)) @@ -112,7 +112,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually startDigest, parameters, validationSettingsNoIl, - VotingData.empty) + VotingData.empty)(settings.chainSettings) val boxesToSpend = tx.inputs.map(i => genesisTx.outputs.find(o => java.util.Arrays.equals(o.id, i.boxId)).get) tx.statefulValidity(boxesToSpend, emptyDataBoxes, context) shouldBe 'success } @@ -371,7 +371,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually startDigest, parameters, validationSettingsNoIl, - VotingData.empty) + VotingData.empty)(settings.chainSettings) val boxesToSpend = tx.inputs.map(i => genesisTx.outputs.find(o => java.util.Arrays.equals(o.id, i.boxId)).get) tx.statefulValidity(boxesToSpend, emptyDataBoxes, context) shouldBe 'success @@ -388,7 +388,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually log.info(s"Payment requests 2 $req2") val tx2 = await(wallet.generateTransaction(req2)).get log.info(s"Generated transaction $tx2") - val context2 = new ErgoStateContext(Seq(block.header), Some(block.extension), startDigest, parameters, validationSettingsNoIl, VotingData.empty) + val context2 = new ErgoStateContext(Seq(block.header), Some(block.extension), startDigest, parameters, validationSettingsNoIl, VotingData.empty)(settings.chainSettings) val knownBoxes = tx.outputs ++ genesisTx.outputs val boxesToSpend2 = tx2.inputs.map(i => knownBoxes.find(o => java.util.Arrays.equals(o.id, i.boxId)).get) tx2.statefulValidity(boxesToSpend2, emptyDataBoxes, context2) shouldBe 'success diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistrySpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistrySpec.scala index 6b5dfb4977..b3f5581981 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistrySpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistrySpec.scala @@ -6,11 +6,11 @@ import org.ergoplatform.db.DBSpec import org.ergoplatform.nodeView.wallet.WalletScanLogic.{ScanResults, SpentInputData} import org.ergoplatform.utils.generators.WalletGenerators import org.ergoplatform.wallet.boxes.TrackedBox +import org.ergoplatform.core.VersionTag import org.scalacheck.Gen import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.core.VersionTag import scorex.util.encode.Base16 import scala.collection.compat.immutable.ArraySeq diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala index f40b10c22a..59f54536df 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala @@ -1,7 +1,7 @@ package org.ergoplatform.sanity import akka.actor.ActorRef -import org.ergoplatform.ErgoBox +import org.ergoplatform.{ErgoBox, PersistentNodeViewModifier} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} @@ -14,16 +14,16 @@ import org.ergoplatform.nodeView.state.{DigestState, ErgoState, UtxoState} import org.ergoplatform.sanity.ErgoSanity._ import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.settings.Constants.HashLength +import org.ergoplatform.testkit.generators.{ModifierProducerTemplateItem, SynInvalid, Valid} +import org.ergoplatform.testkit.properties.HistoryTests +import org.ergoplatform.testkit.properties.mempool.{MempoolRemovalTest, MempoolTransactionsTest} +import org.ergoplatform.testkit.properties.state.StateApplicationTest import org.ergoplatform.utils.{ErgoTestHelpers, HistoryTestHelpers} +import org.ergoplatform.core.bytesToId import org.scalacheck.Gen import scorex.core.network.DeliveryTracker -import scorex.core.{PersistentNodeViewModifier, bytesToId} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.testkit.generators.{ModifierProducerTemplateItem, SynInvalid, Valid} -import scorex.testkit.properties._ -import scorex.testkit.properties.mempool.{MempoolRemovalTest, MempoolTransactionsTest} -import scorex.testkit.properties.state.StateApplicationTest import scorex.utils.Random import scala.concurrent.ExecutionContext diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala index d9b07a50e7..0b7d947f44 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala @@ -5,19 +5,19 @@ import akka.testkit.TestProbe import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.history.header.HeaderSerializer -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedHistory, ChangedMempool} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{ChangedHistory, ChangedMempool} import org.ergoplatform.network.ErgoSyncTracker import org.ergoplatform.nodeView.history.ErgoSyncInfoMessageSpec import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.wrapped.{WrappedDigestState, WrappedUtxoState} import org.ergoplatform.nodeView.state.{DigestState, StateType} import org.ergoplatform.sanity.ErgoSanity._ -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.ErgoSettingsReader import org.scalacheck.Gen -import scorex.core.idToBytes +import org.ergoplatform.core.idToBytes import scorex.core.network.{ConnectedPeer, DeliveryTracker} -import scorex.core.network.peer.PeerInfo -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.peer.PeerInfo +import org.ergoplatform.serialization.ErgoSerializer import scala.concurrent.ExecutionContextExecutor @@ -58,7 +58,7 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { val h = historyGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val s = stateGen.sample.get - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() val pool = ErgoMemPool.empty(settings) val v = h.bestFullBlockIdOpt.orElse(h.bestHeaderIdOpt) v.foreach(id => s.store.update(idToBytes(id), Seq(), Seq()).get) diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala index ef6107e35b..a7e55e929e 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala @@ -5,19 +5,19 @@ import akka.testkit.TestProbe import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedHistory, ChangedMempool} +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages.{ChangedHistory, ChangedMempool} import org.ergoplatform.network.ErgoSyncTracker import org.ergoplatform.nodeView.history.ErgoSyncInfoMessageSpec import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.sanity.ErgoSanity._ -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.ErgoSettingsReader import org.ergoplatform.utils.ErgoTestHelpers import org.scalacheck.Gen import scorex.core.network.{ConnectedPeer, DeliveryTracker} -import scorex.core.network.peer.PeerInfo -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.network.peer.PeerInfo +import org.ergoplatform.serialization.ErgoSerializer import scala.concurrent.ExecutionContextExecutor @@ -55,7 +55,7 @@ class ErgoSanityUTXO extends ErgoSanity[UTXO_ST] with ErgoTestHelpers { val h = historyGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val s = stateGen.sample.get - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() val pool = ErgoMemPool.empty(settings) implicit val ec: ExecutionContextExecutor = system.dispatcher val ncProbe = TestProbe("NetworkControllerProbe") diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 016696d708..09ff24da54 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -4,7 +4,7 @@ import io.circe.syntax._ import io.circe.{ACursor, Decoder, Encoder, Json} import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.{AdditionalRegisters, NonMandatoryRegisterId} -import org.ergoplatform.http.api.ApiCodecs +import org.ergoplatform.http.api.{ApiCodecs, ApiExtraCodecs, ApiRequestsCodecs} import org.ergoplatform.http.api.ApiEncoderOption.HideDetails.implicitValue import org.ergoplatform.http.api.ApiEncoderOption.{Detalization, ShowDetails} import org.ergoplatform.modifiers.ErgoFullBlock @@ -12,7 +12,7 @@ import org.ergoplatform.modifiers.history.popow.NipopowProof import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction import org.ergoplatform.nodeView.wallet.requests._ import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} -import org.ergoplatform.settings.{Algos, ErgoSettings} +import org.ergoplatform.settings.{Algos, ErgoSettingsReader} import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators import org.ergoplatform.wallet.Constants.ScanId @@ -24,7 +24,11 @@ import sigmastate.Values.{ErgoTree, EvaluatedValue} import scala.util.Random -class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with ApiCodecs { +class JsonSerializationSpec extends ErgoPropertyTest + with WalletGenerators + with ApiCodecs + with ApiRequestsCodecs + with ApiExtraCodecs { property("ErgoFullBlock should be encoded into JSON and decoded back correctly") { @@ -52,7 +56,7 @@ class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with } property("PaymentRequest should be serialized to json") { - val ergoSettings = ErgoSettings.read() + val ergoSettings = ErgoSettingsReader.read() implicit val requestEncoder: Encoder[PaymentRequest] = new PaymentRequestEncoder(ergoSettings) implicit val requestDecoder: Decoder[PaymentRequest] = new PaymentRequestDecoder(ergoSettings) forAll(paymentRequestGen) { request => @@ -88,7 +92,7 @@ class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with } property("AssetIssueRequest should be serialized to json") { - val ergoSettings = ErgoSettings.read() + val ergoSettings = ErgoSettingsReader.read() implicit val requestEncoder: Encoder[AssetIssueRequest] = new AssetIssueRequestEncoder(ergoSettings) implicit val requestDecoder: Decoder[AssetIssueRequest] = new AssetIssueRequestDecoder(ergoSettings) forAll(assetIssueRequestGen) { request => diff --git a/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala b/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala index 65ac78905d..c8a2c35dbd 100644 --- a/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala +++ b/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala @@ -6,15 +6,15 @@ import org.ergoplatform.modifiers.history.extension.ExtensionSerializer import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.history.popow.NipopowProofSerializer import org.ergoplatform.modifiers.mempool.ErgoTransactionSerializer +import org.ergoplatform.network.ErgoNodeViewSynchronizer import org.ergoplatform.nodeView.history.ErgoSyncInfoSerializer import org.ergoplatform.nodeView.wallet.persistence.WalletDigestSerializer import org.ergoplatform.nodeView.state.ErgoStateContextSerializer -import org.ergoplatform.settings.{Constants, ErgoValidationSettings, ErgoValidationSettingsSerializer, ErgoValidationSettingsUpdateSerializer} +import org.ergoplatform.settings.{ErgoValidationSettings, ErgoValidationSettingsSerializer, ErgoValidationSettingsUpdateSerializer} import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators import org.scalacheck.Gen import org.scalatest.Assertion -import scorex.core.serialization.ErgoSerializer class SerializationTests extends ErgoPropertyTest with WalletGenerators with scorex.testkit.SerializationTests { @@ -30,7 +30,7 @@ class SerializationTests extends ErgoPropertyTest with WalletGenerators with sco property("Serializers should be defined for all block sections") { val block = invalidErgoFullBlockGen.sample.get block.toSeq.foreach { s => - Constants.modifierSerializers.get(s.modifierTypeId) should not be None + ErgoNodeViewSynchronizer.modifierSerializers.get(s.modifierTypeId) should not be None } } @@ -48,7 +48,7 @@ class SerializationTests extends ErgoPropertyTest with WalletGenerators with sco } property("ErgoStateContext serialization") { - val serializer = ErgoStateContextSerializer(settings) + val serializer = ErgoStateContextSerializer(settings.chainSettings) val b = ergoStateContextGen.sample.get val recovered = serializer.parseBytes(serializer.toBytes(b)) serializer.toBytes(b) shouldEqual serializer.toBytes(recovered) diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index e222ec2d88..7d52cc5f71 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -1,9 +1,8 @@ package org.ergoplatform.settings -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.SortingOption import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.settings.RESTApiSettings import java.net.{InetSocketAddress, URL} import scala.concurrent.duration._ @@ -14,12 +13,12 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { private val txSizeLimit = initSettings.nodeSettings.maxTransactionSize property("should keep data user home by default") { - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() settings.directory shouldBe System.getProperty("user.dir") + "/.ergo_test/data" } property("should read default settings") { - val settings = ErgoSettings.read() + val settings = ErgoSettingsReader.read() settings.nodeSettings shouldBe NodeConfigurationSettings( StateType.Utxo, verifyTransactions = true, @@ -68,7 +67,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { } property("should read user settings from json file") { - val settings = ErgoSettings.read(Args(Some("src/test/resources/settings.json"), None)) + val settings = ErgoSettingsReader.read(Args(Some("src/test/resources/settings.json"), None)) settings.nodeSettings shouldBe NodeConfigurationSettings( StateType.Utxo, verifyTransactions = true, @@ -110,7 +109,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { } property("should read user settings from HOCON file") { - val settings = ErgoSettings.read(Args(Some("src/test/resources/settings.conf"), None)) + val settings = ErgoSettingsReader.read(Args(Some("src/test/resources/settings.conf"), None)) settings.nodeSettings shouldBe NodeConfigurationSettings( StateType.Utxo, verifyTransactions = true, @@ -162,7 +161,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { "http://example.com?foo=bar" ).map(new URL(_)) - invalidUrls.forall(ErgoSettings.invalidRestApiUrl) shouldBe true + invalidUrls.forall(ErgoSettingsReader.invalidRestApiUrl) shouldBe true val validUrls = List( @@ -172,7 +171,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { "http://82.90.21.31:80" ).map(new URL(_)) - validUrls.forall(url => !ErgoSettings.invalidRestApiUrl(url)) shouldBe true + validUrls.forall(url => !ErgoSettingsReader.invalidRestApiUrl(url)) shouldBe true } } diff --git a/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala b/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala index 2923aa7b31..1d444969e5 100644 --- a/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala @@ -38,7 +38,7 @@ class VotingSpecification extends ErgoPropertyTest { Seq(VR.CheckDeserializedScriptType.id -> DisabledRule, VR.CheckValidOpCode.id -> ReplacedRule((VR.FirstRuleId + 11).toShort))) private val proposedUpdate2 = ErgoValidationSettingsUpdate(Seq(ValidationRules.fbOperationFailed), Seq()) val ctx: ErgoStateContext = { - new ErgoStateContext(Seq.empty, None, genesisStateDigest, parameters, validationSettingsNoIl, VotingData.empty)(updSettings) + new ErgoStateContext(Seq.empty, None, genesisStateDigest, parameters, validationSettingsNoIl, VotingData.empty)(updSettings.chainSettings) .upcoming(org.ergoplatform.mining.group.generator, 0L, settings.chainSettings.initialNBits, Array.fill(3)(0.toByte), emptyVSUpdate, 0.toByte) } val initialVs: ErgoValidationSettings = ctx.validationSettings @@ -100,7 +100,7 @@ class VotingSpecification extends ErgoPropertyTest { property("voting for non-existing parameter") { val p: Parameters = Parameters(2, Map(BlockVersion -> 0), proposedUpdate) val vr: VotingData = VotingData.empty - val esc = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings) + val esc = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings.chainSettings) val invalidVote = 100: Byte val votes = Array(invalidVote , NoParameter, NoParameter) @@ -119,7 +119,7 @@ class VotingSpecification extends ErgoPropertyTest { val p: Parameters = Parameters(2, Map(StorageFeeFactorIncrease -> kInit, BlockVersion -> 0), proposedUpdate) val vr: VotingData = VotingData.empty - val esc = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings) + val esc = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings.chainSettings) val votes = Array(StorageFeeFactorIncrease, NoParameter, NoParameter) val h = defaultHeaderGen.sample.get.copy(height = 2, votes = votes, version = 0: Byte) val esc2 = process(esc, p, h).get @@ -152,7 +152,7 @@ class VotingSpecification extends ErgoPropertyTest { val p: Parameters = Parameters(1, Map(BlockVersion -> 0), proposedUpdate) val vr: VotingData = VotingData.empty - val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettings, vr)(updSettings) + val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettings, vr)(updSettings.chainSettings) checkValidationSettings(esc0.validationSettings, emptyVSUpdate) val forkVote = Array(SoftFork, NoParameter, NoParameter) val emptyVotes = Array(NoParameter, NoParameter, NoParameter) @@ -259,7 +259,7 @@ class VotingSpecification extends ErgoPropertyTest { val forkVote = Array(SoftFork, NoParameter, NoParameter) val emptyVotes = Array(NoParameter, NoParameter, NoParameter) - val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings) + val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), p, validationSettingsNoIl, vr)(updSettings.chainSettings) val h1 = defaultHeaderGen.sample.get.copy(votes = forkVote, version = 0: Byte, height = 1) val esc1 = process(esc0, p, h1).get @@ -317,7 +317,7 @@ class VotingSpecification extends ErgoPropertyTest { property("hardfork - v2 - activation") { val vr: VotingData = VotingData.empty - val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), parameters, ErgoValidationSettings.initial, vr)(updSettings) + val esc0 = new ErgoStateContext(Seq(), None, ADDigest @@ Array.fill(33)(0: Byte), parameters, ErgoValidationSettings.initial, vr)(updSettings.chainSettings) val h1 = defaultHeaderGen.sample.get.copy(votes = Array.empty, version = 1: Byte, height = hfActivationHeight - 1) val expectedParameters1 = Parameters(hfActivationHeight - 1, DefaultParameters, ErgoValidationSettingsUpdate.empty) val esc1 = process(esc0, expectedParameters1, h1).get diff --git a/src/test/scala/org/ergoplatform/testkit/SerializationTests.scala b/src/test/scala/org/ergoplatform/testkit/SerializationTests.scala new file mode 100644 index 0000000000..f7a21004cc --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/SerializationTests.scala @@ -0,0 +1,16 @@ +package org.ergoplatform.testkit + +import org.scalacheck.Gen +import org.scalatest.Assertion +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.ergoplatform.serialization.ErgoSerializer + +trait SerializationTests extends ScalaCheckPropertyChecks with Matchers { + def checkSerializationRoundtrip[A](generator: Gen[A], serializer: ErgoSerializer[A]): Assertion = { + forAll(generator) { b: A => + val recovered = serializer.parseBytes(serializer.toBytes(b)) + serializer.toBytes(b) shouldEqual serializer.toBytes(recovered) + } + } +} diff --git a/src/test/scala/org/ergoplatform/testkit/TestkitHelpers.scala b/src/test/scala/org/ergoplatform/testkit/TestkitHelpers.scala new file mode 100644 index 0000000000..d38259c0d8 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/TestkitHelpers.scala @@ -0,0 +1,10 @@ +package org.ergoplatform.testkit + +trait TestkitHelpers { + + val MinTestsOk = 100 + + def check(minTestsOk:Int)(f: Int => Unit): Unit = (0 until minTestsOk) foreach (i => f(i)) + + def check(f: Int => Unit): Unit = check(MinTestsOk)(f) +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/AllModifierProducers.scala b/src/test/scala/org/ergoplatform/testkit/generators/AllModifierProducers.scala new file mode 100644 index 0000000000..ba9d379b59 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/AllModifierProducers.scala @@ -0,0 +1,11 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.nodeView.state.ErgoState + + +trait AllModifierProducers[ST <: ErgoState[ST]] + extends SemanticallyValidModifierProducer[ST] + with SyntacticallyTargetedModifierProducer + with ArbitraryTransactionsCarryingModifierProducer + with TotallyValidModifierProducer[ST] + with SemanticallyValidTransactionsCarryingModifier[ST] diff --git a/src/test/scala/org/ergoplatform/testkit/generators/ArbitraryTransactionsCarryingModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/ArbitraryTransactionsCarryingModifierProducer.scala new file mode 100644 index 0000000000..4fc2d9244c --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/ArbitraryTransactionsCarryingModifierProducer.scala @@ -0,0 +1,12 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.history.BlockTransactions +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.mempool.ErgoMemPool +/** + * Produces a modifier with transactions, not necessary syntatically or semantically valid + */ +trait ArbitraryTransactionsCarryingModifierProducer{ + +def modifierWithTransactions(memoryPoolOpt: Option[ErgoMemPool], customTransactionsOpt: Option[Seq[ErgoTransaction]]): BlockTransactions +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/CoreGenerators.scala b/src/test/scala/org/ergoplatform/testkit/generators/CoreGenerators.scala new file mode 100644 index 0000000000..d7ba97731b --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/CoreGenerators.scala @@ -0,0 +1,9 @@ +package org.ergoplatform.testkit.generators + +import org.scalacheck.Gen +import org.ergoplatform.core.{VersionTag, idToVersion} + +//Generators of objects from scorex-core +trait CoreGenerators extends ObjectGenerators { + lazy val versionTagGen: Gen[VersionTag] = modifierIdGen.map(id => idToVersion(id)) +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/CustomModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/CustomModifierProducer.scala new file mode 100644 index 0000000000..ab4e38e421 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/CustomModifierProducer.scala @@ -0,0 +1,17 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.state.ErgoState + +sealed trait ModifierProducerTemplateItem + +case object SynInvalid extends ModifierProducerTemplateItem +case object Valid extends ModifierProducerTemplateItem + +trait CustomModifierProducer[ST <: ErgoState[ST]] { + + def customModifiers(history: ErgoHistory, + state: ST, + template: Seq[ModifierProducerTemplateItem]): Seq[BlockSection] +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/ObjectGenerators.scala b/src/test/scala/org/ergoplatform/testkit/generators/ObjectGenerators.scala new file mode 100644 index 0000000000..a676b243e3 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/ObjectGenerators.scala @@ -0,0 +1,114 @@ +package org.ergoplatform.testkit.generators + +import java.net.{InetAddress, InetSocketAddress, URL} +import akka.actor.ActorRef +import akka.util.ByteString +import org.ergoplatform.NodeViewModifier +import org.ergoplatform.modifiers.NetworkObjectTypeId +import org.ergoplatform.network.{ModePeerFeature, PeerFeature, PeerSpec, Version} +import org.ergoplatform.nodeView.state.StateType +import org.scalacheck.Gen.{const, some} +import org.scalacheck.{Arbitrary, Gen} +import org.ergoplatform.network.message.{InvData, ModifiersData} +import org.ergoplatform.network.peer.{PeerInfo, RestApiUrlPeerFeature} +import scorex.core.network.{ConnectedPeer, ConnectionDirection, ConnectionId, Incoming, Outgoing} +import scorex.util.{ModifierId, bytesToId} + +trait ObjectGenerators { + + val MaxVersion = 999 + val MaxIp = 255 + val MaxPort = 65535 + + val modePeerFeatureGen = for { + utxo <- Gen.oneOf(true, false) + stateType <- if(utxo) StateType.Utxo else StateType.Digest + verifyingTransactions <- Gen.oneOf(true, false) + popowSuffix <- Gen.option(smallInt) + blocksToKeep <- smallInt + } yield ModePeerFeature(stateType, verifyingTransactions, popowSuffix, blocksToKeep) + + lazy val smallInt: Gen[Int] = Gen.choose(0, 20) + + lazy val nonEmptyBytesGen: Gen[Array[Byte]] = Gen.nonEmptyListOf(Arbitrary.arbitrary[Byte]) + .map(_.toArray).suchThat(_.length > 0) + + lazy val nonEmptyByteStringGen: Gen[ByteString] = nonEmptyBytesGen.map(ByteString(_)) + + def genBoundedBytes(minSize: Int, maxSize: Int): Gen[Array[Byte]] = { + Gen.choose(minSize, maxSize) flatMap { sz => Gen.listOfN(sz, Arbitrary.arbitrary[Byte]).map(_.toArray) } + } + + def genBytes(size: Int): Gen[Array[Byte]] = genBoundedBytes(size, size) + + lazy val positiveLongGen: Gen[Long] = Gen.choose(1, Long.MaxValue) + + lazy val positiveByteGen: Gen[Byte] = Gen.choose(1, Byte.MaxValue) + + + lazy val modifierIdGen: Gen[ModifierId] = Gen.listOfN(NodeViewModifier.ModifierIdSize, Arbitrary.arbitrary[Byte]) + .map(id => bytesToId(id.toArray)) + + lazy val modifierTypeIdGen: Gen[NetworkObjectTypeId.Value] = Arbitrary.arbitrary[Byte].map(t => NetworkObjectTypeId.fromByte(t)) + + lazy val invDataGen: Gen[InvData] = for { + modifierTypeId: NetworkObjectTypeId.Value <- modifierTypeIdGen + modifierIds: Seq[ModifierId] <- Gen.nonEmptyListOf(modifierIdGen) if modifierIds.nonEmpty + } yield InvData(modifierTypeId, modifierIds) + + lazy val modifierWithIdGen: Gen[(ModifierId, Array[Byte])] = for { + id <- modifierIdGen + mod <- nonEmptyBytesGen + } yield id -> mod + + lazy val modifiersGen: Gen[ModifiersData] = for { + modifierTypeId: NetworkObjectTypeId.Value <- modifierTypeIdGen + modifiers: Map[ModifierId, Array[Byte]] <- Gen.nonEmptyMap(modifierWithIdGen).suchThat(_.nonEmpty) + } yield ModifiersData(modifierTypeId, modifiers) + + lazy val appVersionGen: Gen[Version] = for { + fd <- Gen.choose(0: Byte, Byte.MaxValue) + sd <- Gen.choose(0: Byte, Byte.MaxValue) + td <- Gen.choose(0: Byte, Byte.MaxValue) + } yield Version(fd, sd, td) + + lazy val inetSocketAddressGen: Gen[InetSocketAddress] = for { + ip1 <- Gen.choose(0, MaxIp) + ip2 <- Gen.choose(0, MaxIp) + ip3 <- Gen.choose(0, MaxIp) + ip4 <- Gen.choose(0, MaxIp) + port <- Gen.choose(0, MaxPort) + } yield new InetSocketAddress(InetAddress.getByName(s"$ip1.$ip2.$ip3.$ip4"), port) + + lazy val urlGen: Gen[URL] = for { + protocol <- Gen.frequency(5 -> const("http://"), 5 -> const("https://")) + ip1 <- Gen.choose(0, MaxIp) + ip2 <- Gen.choose(0, MaxIp) + ip3 <- Gen.choose(0, MaxIp) + ip4 <- Gen.choose(0, MaxIp) + host <- Gen.frequency(5 -> const(s"$ip1.$ip2.$ip3.$ip4"), 5 -> const("example.com")) + port <- Gen.choose(0, MaxPort) + suffix <- Gen.frequency(5 -> const("/"), 5 -> const("")) + } yield new URL(s"$protocol$host:$port$suffix") + + lazy val connectionIdGen: Gen[ConnectionId] = for { + ip1 <- inetSocketAddressGen + ip2 <- inetSocketAddressGen + direction <- Gen.oneOf[ConnectionDirection](Seq[ConnectionDirection](Incoming, Outgoing)) + } yield ConnectionId(ip1, ip2, direction) + + def peerInfoGen: Gen[PeerInfo] = for { + peerSpec <- peerSpecGen + } yield PeerInfo(peerSpec, 0L, Some(Incoming), 0L) + + def connectedPeerGen(peerRef: ActorRef): Gen[ConnectedPeer] = for { + connectionId <- connectionIdGen + peerInfo <- peerInfoGen + } yield ConnectedPeer(connectionId, peerRef, Some(peerInfo)) + + def peerSpecGen: Gen[PeerSpec] = for { + declaredAddress <- Gen.frequency(5 -> const(None), 5 -> some(inetSocketAddressGen)) + features: Seq[PeerFeature] <- Gen.someOf(modePeerFeatureGen, urlGen.flatMap(url => RestApiUrlPeerFeature(url))) + version <- appVersionGen + } yield PeerSpec("ergoref", version, "ergo-node", declaredAddress, features) +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyInvalidModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyInvalidModifierProducer.scala new file mode 100644 index 0000000000..47cb9085aa --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyInvalidModifierProducer.scala @@ -0,0 +1,8 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.state.ErgoState + +trait SemanticallyInvalidModifierProducer[ST <: ErgoState[ST]] { + def semanticallyInvalidModifier(state: ST): BlockSection +} \ No newline at end of file diff --git a/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidModifierProducer.scala new file mode 100644 index 0000000000..1dc87fead4 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidModifierProducer.scala @@ -0,0 +1,10 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.state.ErgoState + +trait SemanticallyValidModifierProducer[ST <: ErgoState[ST]] { + def semanticallyValidModifier(state: ST): BlockSection +} + + diff --git a/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidTransactionsCarryingModifier.scala b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidTransactionsCarryingModifier.scala new file mode 100644 index 0000000000..b57fb2d6ca --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/SemanticallyValidTransactionsCarryingModifier.scala @@ -0,0 +1,12 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.history.BlockTransactions +import org.ergoplatform.modifiers.mempool.ErgoTransaction +import org.ergoplatform.nodeView.state.ErgoState + +trait SemanticallyValidTransactionsCarryingModifier[ST <: ErgoState[ST]] { + + def semanticallyValidModifier(state: ST): BlockTransactions + def genValidTransactionPair(state: ST): Seq[ErgoTransaction] + def semanticallyValidModifierWithCustomTransactions(state: ST, transactions: Seq[ErgoTransaction]): BlockTransactions +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/SyntacticallyTargetedModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/SyntacticallyTargetedModifierProducer.scala new file mode 100644 index 0000000000..172196f383 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/SyntacticallyTargetedModifierProducer.scala @@ -0,0 +1,11 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.history.ErgoHistory + + +trait SyntacticallyTargetedModifierProducer { + def syntacticallyValidModifier(history: ErgoHistory): BlockSection + + def syntacticallyInvalidModifier(history: ErgoHistory): BlockSection +} diff --git a/src/test/scala/org/ergoplatform/testkit/generators/TotallyValidModifierProducer.scala b/src/test/scala/org/ergoplatform/testkit/generators/TotallyValidModifierProducer.scala new file mode 100644 index 0000000000..26b14b13d6 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/generators/TotallyValidModifierProducer.scala @@ -0,0 +1,13 @@ +package org.ergoplatform.testkit.generators + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.state.ErgoState + + +trait TotallyValidModifierProducer[ST <: ErgoState[ST]] { + + def totallyValidModifier(history: ErgoHistory, state: ST): BlockSection + + def totallyValidModifiers(history: ErgoHistory, state: ST, count: Int): Seq[BlockSection] +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/HistoryTests.scala b/src/test/scala/org/ergoplatform/testkit/properties/HistoryTests.scala new file mode 100644 index 0000000000..93db38e540 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/HistoryTests.scala @@ -0,0 +1,86 @@ +package org.ergoplatform.testkit.properties + +import org.ergoplatform.consensus.ModifierSemanticValidity.Valid +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.testkit.TestkitHelpers +import org.ergoplatform.testkit.generators.SyntacticallyTargetedModifierProducer +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import scorex.util.ScorexLogging + + +trait HistoryTests + extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with ScorexLogging + with TestkitHelpers + with SyntacticallyTargetedModifierProducer { + + val historyGen: Gen[ErgoHistory] + + lazy val generatorWithValidModifier: Gen[(ErgoHistory, BlockSection)] = { + historyGen.map { h => (h, syntacticallyValidModifier(h))} + } + + lazy val generatorWithInvalidModifier: Gen[(ErgoHistory, BlockSection)] = { + historyGen.map { h => (h, syntacticallyInvalidModifier(h))} + } + + private def propertyNameGenerator(propName: String): String = s"HistoryTests: $propName" + + property(propertyNameGenerator("applicable with valid modifier")) { + forAll(generatorWithValidModifier) { case (h, m) => h.applicableTry(m) shouldBe 'success} + } + + property(propertyNameGenerator("append valid modifier")) { + forAll(generatorWithValidModifier) { case (h, m) => h.append(m).isSuccess shouldBe true } + } + + property(propertyNameGenerator("contain valid modifier after appending")) { + forAll(generatorWithValidModifier) { case (h, m) => + h.append(m) + h.contains(m) shouldBe true + } + } + + property(propertyNameGenerator("find valid modifier after appending by modifierId")) { + forAll(generatorWithValidModifier) { case (h, m) => + h.append(m) + h.modifierById(m.id) shouldBe defined + } + } + + property(propertyNameGenerator("report semantically validation after appending valid modifier")) { + forAll(generatorWithValidModifier) { case (h, m) => + h.append(m) + h.reportModifierIsValid(m).get + h.isSemanticallyValid(m.id) shouldBe Valid + } + } + + property(propertyNameGenerator("not applicable with invalid modifier")) { + forAll(generatorWithInvalidModifier) { case (h, m) => h.applicableTry(m) shouldBe 'failure} + } + + property(propertyNameGenerator("not append invalid modifier")) { + forAll(generatorWithInvalidModifier) { case (h, m) => h.append(m).isSuccess shouldBe false } + } + + property(propertyNameGenerator("not contain invalid modifier after appending")) { + forAll(generatorWithInvalidModifier) { case (h, m) => + h.append(m) + h.contains(m) shouldBe false + } + } + + property(propertyNameGenerator("not finds valid modifier after appending by modifierId")) { + forAll(generatorWithInvalidModifier) { case (h, m) => + h.append(m) + h.modifierById(m.id) shouldBe None + } + } +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/NodeViewHolderTests.scala b/src/test/scala/org/ergoplatform/testkit/properties/NodeViewHolderTests.scala new file mode 100644 index 0000000000..a810831554 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/NodeViewHolderTests.scala @@ -0,0 +1,339 @@ +package org.ergoplatform.testkit.properties + +import akka.actor._ +import akka.testkit.TestProbe +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.history.ErgoHistory +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.ergoplatform.nodeView.ErgoNodeViewHolder.CurrentView +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.GetDataFromCurrentView +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ +import org.ergoplatform.nodeView.LocallyGeneratedModifier +import org.ergoplatform.nodeView.state.ErgoState +import org.ergoplatform.testkit.generators.{CustomModifierProducer, ModifierProducerTemplateItem, ObjectGenerators, SemanticallyInvalidModifierProducer, SynInvalid, SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer, Valid} +import org.ergoplatform.testkit.utils.AkkaFixture +import scorex.testkit.generators._ +import scorex.util.ScorexLogging + +import scala.concurrent.Await +import scala.concurrent.duration._ + +@SuppressWarnings(Array("org.wartremover.warts.TraversableOps")) +trait NodeViewHolderTests[ST <: ErgoState[ST]] + extends AnyPropSpec + with Matchers + with ScorexLogging + with SyntacticallyTargetedModifierProducer + with TotallyValidModifierProducer[ST] + with SemanticallyInvalidModifierProducer[ST] + with CustomModifierProducer[ST] + with ObjectGenerators { + + def nodeViewHolder(implicit system: ActorSystem): (ActorRef, TestProbe, BlockSection, ST, ErgoHistory) + + class HolderFixture extends AkkaFixture { + @SuppressWarnings(Array("org.wartremover.warts.PublicInference")) + val (node, eventListener, mod, s, h) = nodeViewHolder + } + + private def withFixture(testCode: HolderFixture => Any): Unit = { + val fixture = new HolderFixture + try { + testCode(fixture) + } finally { + Await.result(fixture.system.terminate(), Duration.Inf) + } + } + + private type CurrentViewType = CurrentView[ST] + + private def withView[T](node: ActorRef)(f: CurrentViewType => T) + (implicit system: ActorSystem): T = { + val probe = TestProbe() + probe.send(node, + GetDataFromCurrentView[ST, CurrentViewType] { view => view }) + val view = probe.expectMsgClass(10.seconds, classOf[CurrentViewType]) + f(view) + } +/* todo: fix + property("NodeViewHolder: modifiers from remote") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[ModifiersRemovedFromCache]) + p.send(node, ModifiersFromRemote(Seq(mod))) + eventListener.expectMsgType[ModifiersRemovedFromCache] + } + }*/ + + property("NodeViewHolder syntactically valid modifier subscription") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => totallyValidModifiers(v.history, v.state, 2).head }) + val mod = p.expectMsgClass(classOf[BlockSection]) + p.send(node, LocallyGeneratedModifier(mod)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + } + } + + property("NodeViewHolder: syntactically failed modifier subscription") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) + val invalid = syntacticallyInvalidModifier(h) + p.send(node, LocallyGeneratedModifier(invalid)) + eventListener.expectMsgType[SyntacticallyFailedModification] + } + } + + property("NodeViewHolder: semantically valid modifier subscription") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[FullBlockApplied]) + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => totallyValidModifiers(v.history, v.state, 2).head }) + val mod = p.expectMsgClass(classOf[BlockSection]) + p.send(node, LocallyGeneratedModifier(mod)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + eventListener.expectMsgType[FullBlockApplied] + } + } + + property("NodeViewHolder: semantically failed modifier subscription") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[SemanticallyFailedModification]) + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => semanticallyInvalidModifier(v.state) }) + val invalid = p.expectMsgClass(classOf[BlockSection]) + p.send(node, LocallyGeneratedModifier(invalid)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + eventListener.expectMsgType[SemanticallyFailedModification] + } + } + + property("NodeViewHolder: syntactically/semantically valid modifier subscription") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[FullBlockApplied]) + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => totallyValidModifiers(v.history, v.state, 2).head }) + val mod = p.expectMsgClass(classOf[BlockSection]) + p.send(node, LocallyGeneratedModifier(mod)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + eventListener.expectMsgType[FullBlockApplied] + } + } + + property("NodeViewHolder: check state after creation") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + p.send(node, GetDataFromCurrentView[ST, Boolean] { v => + v.state.version == s.version + }) + p.expectMsg(true) + } + } + + property("NodeViewHolder: check that a valid modifier is applicable") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + p.send(node, GetDataFromCurrentView[ST, Boolean] { v => + v.history.applicableTry(mod).isSuccess + }) + p.expectMsg(true) + } + } + + property("NodeViewHolder: check that valid modifiers are applicable") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => + totallyValidModifiers(v.history, v.state, 10) //todo: fix magic number + }) + val mods = p.expectMsgClass(classOf[Seq[BlockSection]]) + + mods.foreach { mod => + p.send(node, LocallyGeneratedModifier(mod)) + } + + (1 to mods.size).foreach(_ => eventListener.expectMsgType[SyntacticallySuccessfulModifier]) + } + } + + property("NodeViewHolder: apply locally generated mod") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) + + val invalid = syntacticallyInvalidModifier(h) + + p.send(node, LocallyGeneratedModifier(invalid)) + + eventListener.expectMsgType[SyntacticallyFailedModification] + + p.send(node, LocallyGeneratedModifier(mod)) + + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + + p.send(node, GetDataFromCurrentView[ST, Boolean] { v => + v.state.version == s.version && v.history.contains(mod.id) + }) + + p.expectMsg(true) + } + } + + property("NodeViewHolder: simple forking") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + val waitDuration = 5.seconds + + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) + system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) + + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => totallyValidModifiers(v.history, v.state, 2) }) + val initMods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) + initMods.foreach { mod => + p.send(node, LocallyGeneratedModifier(mod)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + } + + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => + totallyValidModifiers(v.history, v.state, 2).head + }) + val fork1Mod = p.expectMsgClass(waitDuration, classOf[BlockSection]) + + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => + totallyValidModifiers(v.history, v.state, 2).head + }) + val fork2Mod = p.expectMsgClass(waitDuration, classOf[BlockSection]) + + p.send(node, LocallyGeneratedModifier(fork1Mod)) + p.send(node, LocallyGeneratedModifier(fork2Mod)) + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + eventListener.expectMsgType[SyntacticallySuccessfulModifier] + + p.send(node, GetDataFromCurrentView[ST, Boolean] { v => + v.history.contains(fork1Mod.id) || v.history.contains(fork2Mod.id) + }) + + p.expectMsg(10.seconds, true) + } + } + + /** + * In this test we apply first a chain of 2 blocks and then a chain of 4 blocks, both started with the same + * common block. We are expecting to observe "switching" here, though with non-chain structures there could be no + * notion of switching, so what we check finally is that last block from the second chain is in "open surface" + * (list of open blocks which do not have successors yet, size of the list is 1 in case of blockchain) + */ + property("NodeViewHolder: forking - switching") { + withFixture { ctx => + import ctx._ + val p = TestProbe() + + val opCountBeforeFork = 10 + val fork1OpCount = 2 + val fork2OpCount = 4 + + val waitDuration = 10.seconds + + //some base operations, we don't wanna have fork right from genesis + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => + totallyValidModifiers(v.history, v.state, opCountBeforeFork) + }) + val plainMods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) + plainMods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } + + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => + val mods = totallyValidModifiers(v.history, v.state, fork1OpCount) + assert(mods.head.parentId == v.history.bestFullBlockIdOpt.orElse(v.history.bestHeaderIdOpt).get) + mods + }) + val fork1Mods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) + + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => + totallyValidModifiers(v.history, v.state, fork2OpCount) + }) + val fork2Mods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) + + fork1Mods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } + fork2Mods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } + + p.send(node, GetDataFromCurrentView[ST, Boolean] { v => + v.history.bestFullBlockIdOpt.orElse(v.history.bestHeaderIdOpt).contains(fork2Mods.last.id) + }) + p.expectMsg(true) + } + } + + property("NodeViewHolder: forking - switching with an invalid block") { + withFixture { ctx => + import ctx._ + + val opCountBeforeFork = 10 + val fork1OpCount = 4 + + //some base operations, we don't wanna have fork right from genesis + withView(node) { v => + totallyValidModifiers(v.history, v.state, opCountBeforeFork) + }.foreach { + mod => node ! LocallyGeneratedModifier(mod) + } + // generate the first fork with valid blocks + val fork1Mods = withView(node) { v => + val mods = totallyValidModifiers(v.history, v.state, fork1OpCount) + assert(mods.head.parentId == v.history.bestFullBlockIdOpt.orElse(v.history.bestHeaderIdOpt).get) + mods + } + // generate the second fork with the invalid block + val fork2Mods = withView(node) { v => + customModifiers(v.history, v.state, + Seq[ModifierProducerTemplateItem](Valid, + SynInvalid, // invalid modifier + Valid, Valid, Valid, Valid, Valid, Valid)) + } + // apply the first fork with valid blocks + fork1Mods.foreach { mod => node ! LocallyGeneratedModifier(mod) } + // apply the second fork with invalid block + fork2Mods.foreach { mod => node ! LocallyGeneratedModifier(mod) } + // verify that open surface consist of last block of the first chain, + // or first block of the second chain, or both, but no any other option + withView(node) { v => + v.history.bestFullBlockIdOpt.orElse(v.history.bestHeaderIdOpt).toSeq should ( + contain only fork1Mods.last.id + or contain only fork2Mods.head.id + or contain only(fork1Mods.last.id, fork2Mods.head.id) + ) + } + } + } + +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/mempool/MemoryPoolTest.scala b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MemoryPoolTest.scala new file mode 100644 index 0000000000..8c9127d721 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MemoryPoolTest.scala @@ -0,0 +1,13 @@ +package org.ergoplatform.testkit.properties.mempool + +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.scalacheck.Gen + + +trait MemoryPoolTest { + val memPool: ErgoMemPool + val memPoolGenerator: Gen[ErgoMemPool] + val transactionGenerator: Gen[ErgoTransaction] + val unconfirmedTxGenerator: Gen[UnconfirmedTransaction] +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolFilterPerformanceTest.scala b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolFilterPerformanceTest.scala new file mode 100644 index 0000000000..6cb9b785fa --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolFilterPerformanceTest.scala @@ -0,0 +1,43 @@ +package org.ergoplatform.testkit.properties.mempool + +import java.security.MessageDigest +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +trait MempoolFilterPerformanceTest + extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with MemoryPoolTest { + + var initializedMempool: Option[ErgoMemPool] = None + + val thresholdInHashes = 500000 + + private val HeatJVMHashesCount = 1000000 //to heat up JVM, just in case it is cold + + val thresholdSecs: Double = { + //heat up + (1 to HeatJVMHashesCount).foreach(i => MessageDigest.getInstance("SHA-256").digest(("dummy" + i).getBytes())) + + val t0 = System.currentTimeMillis() + (1 to thresholdInHashes).foreach(i => MessageDigest.getInstance("SHA-256").digest(("dummy" + i).getBytes())) + val t = System.currentTimeMillis() + (t - t0) / 1000.0 + } + + property("Mempool should be able to store a lot of transactions") { + var m: ErgoMemPool = memPool + (0 until 1000) foreach { _ => + forAll(transactionGenerator) { tx: ErgoTransaction => + m = m.put(UnconfirmedTransaction(tx, None)) + } + } + m.size should be > 1000 + initializedMempool = Some(m) + } + +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolRemovalTest.scala b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolRemovalTest.scala new file mode 100644 index 0000000000..826fd601db --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolRemovalTest.scala @@ -0,0 +1,41 @@ +package org.ergoplatform.testkit.properties.mempool + +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.testkit.TestkitHelpers +import org.ergoplatform.testkit.generators.ArbitraryTransactionsCarryingModifierProducer +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import scorex.util.ScorexLogging + +trait MempoolRemovalTest extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with ScorexLogging + with TestkitHelpers + with MemoryPoolTest + with ArbitraryTransactionsCarryingModifierProducer { + + val historyGen: Gen[ErgoHistory] + + //todo: this test doesn't check anything. It should be reworked as a test for node view holder + property("Transactions once added to block should be removed from Mempool") { + val min = 1 + val max = 10 + forAll(Gen.choose(min, max)) { _ => + var m: ErgoMemPool = memPool + // var h: ErgoHistory = historyGen.sample.get + forAll(transactionGenerator) { tx: ErgoTransaction => + m = m.put(UnconfirmedTransaction(tx, None)) + } + // var prevMempoolSize = m.size + // val b = modifierWithTransactions(Some(m), None) + //todo: fix (m.size + b.transactions.get.size) shouldEqual prevMempoolSize + } + } +} + + diff --git a/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolTransactionsTest.scala b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolTransactionsTest.scala new file mode 100644 index 0000000000..7dbc08cd1b --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/mempool/MempoolTransactionsTest.scala @@ -0,0 +1,155 @@ +package org.ergoplatform.testkit.properties.mempool + +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +@SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) +trait MempoolTransactionsTest + extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with MemoryPoolTest { + + val transactionSeqGenerator: Gen[Seq[ErgoTransaction]] = Gen.nonEmptyContainerOf[Seq, ErgoTransaction](transactionGenerator) + val unconfirmedTxSeqGenerator: Gen[Seq[UnconfirmedTransaction]] = + transactionSeqGenerator.map(txs => txs.map(tx => UnconfirmedTransaction(tx, None))) + + property("Size of mempool should increase when adding a non-present transaction") { + forAll(memPoolGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTx) + m.size shouldEqual 1 + } + } + + property("Size of mempool should not increase when adding a present transaction") { + forAll(memPoolGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTx) + val m2: ErgoMemPool = m.put(unconfirmedTx) + m2.size shouldEqual 1 + } + } + + property("Size of mempool should increase when adding a collection of non-present transactions " + + "without duplicates (with check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should increase for a number of unique non-present transactions " + + "when adding a collection of non-present txs with duplicates (with check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs ++ unconfirmedTxs) + m.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should not increase when adding a collection of present transactions (with check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + val m2: ErgoMemPool = m.put(unconfirmedTxs) + m2.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should increase when adding a collection of non-present transactions " + + "without duplicates (without check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should increase for a number of unique non-present transactions " + + "when adding a collection of non-present transactions with duplicates (without check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs ++ unconfirmedTxs) + m.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should not increase when adding a collection of present transactions (without check)") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + val m2: ErgoMemPool = m.put(unconfirmedTxs) + m2.size shouldEqual unconfirmedTxs.size + } + } + + property("Size of mempool should decrease when removing a present transaction") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + val m2: ErgoMemPool = m.remove(unconfirmedTxs.headOption.get.transaction) + m2.size shouldBe unconfirmedTxs.size - 1 + } + } + + property("Size of mempool should not decrease when removing a non-present transaction") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + val m2: ErgoMemPool = m.remove(unconfirmedTx.transaction) + m2.size shouldBe unconfirmedTxs.size + } + } + + property("Present transactions should be available by id") { + forAll(memPoolGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTx) + m.modifierById(unconfirmedTx.transaction.id).isDefined shouldBe true + } + } + + property("Non-present transactions should not be available by id") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.modifierById(unconfirmedTx.transaction.id).isDefined shouldBe false + } + } + + property("Mempool should contain present transactions") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.contains(unconfirmedTxs.headOption.get.transaction.id) shouldBe true + } + } + + property("Mempool should not contain non-present transactions") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.contains(unconfirmedTx.transaction.id) shouldBe false + } + } + + property("Present transactions should be obtained by their ids") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTxs :+ unconfirmedTx) + m.getAll(unconfirmedTxs.map(_.transaction.id)) sameElements unconfirmedTxs + } + } + + property("Non-present transactions should not be obtained by their ids") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], unconfirmedTx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTx) + m.getAll(unconfirmedTxs.map(_.transaction.id)).size shouldBe 0 + } + } + + property("Required number of transactions should be taken from mempool") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator, unconfirmedTxGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction], tx: UnconfirmedTransaction) => + val m: ErgoMemPool = mp.put(unconfirmedTxs :+ tx) + m.take(unconfirmedTxs.size).size shouldBe unconfirmedTxs.size + } + } + + property("Maximum number of transactions that can be taken should equals mempool size") { + forAll(memPoolGenerator, unconfirmedTxSeqGenerator) { (mp: ErgoMemPool, unconfirmedTxs: Seq[UnconfirmedTransaction]) => + val m: ErgoMemPool = mp.put(unconfirmedTxs) + m.take(unconfirmedTxs.size + 1).size shouldBe m.size + } + } +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/state/StateApplicationTest.scala b/src/test/scala/org/ergoplatform/testkit/properties/state/StateApplicationTest.scala new file mode 100644 index 0000000000..bdfd1c764d --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/state/StateApplicationTest.scala @@ -0,0 +1,105 @@ +package org.ergoplatform.testkit.properties.state + +import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.nodeView.state.{DigestState, ErgoState} +import org.scalacheck.Gen +import scala.collection.mutable.ListBuffer + +trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { + + lazy val stateGenWithValidModifier: Gen[(ST, BlockSection)] = { + stateGen.map { s => (s, semanticallyValidModifier(s)) } + } + + lazy val stateGenWithInvalidModifier: Gen[(ST, BlockSection)] = { + stateGen.map { s => (s, semanticallyInvalidModifier(s))} + } + + private def propertyNameGenerator(propName: String): String = s"StateTests: $propName" + + property(propertyNameGenerator("apply modifier")) { + forAll(stateGenWithValidModifier) { case (s, m) => + val ver = s.version + val sTry = s.applyModifier(m, None)(_ => ()) + sTry.isSuccess shouldBe true + sTry.get.version == ver shouldBe false + } + } + + property(propertyNameGenerator("do not apply same valid modifier twice")) { + forAll(stateGenWithValidModifier) { case (s, m) => + val ver = s.version + val sTry = s.applyModifier(m, None)(_ => ()) + sTry.isSuccess shouldBe true + val s2 = sTry.get + s2.version == ver shouldBe false + s2.applyModifier(m, None)(_ => ()).isSuccess shouldBe false + } + } + + property(propertyNameGenerator("do not apply invalid modifier")) { + forAll(stateGenWithInvalidModifier) { case (s, m) => + val sTry = s.applyModifier(m, None)(_ => ()) + sTry.isSuccess shouldBe false + } + } + + property(propertyNameGenerator("apply valid modifier after rollback")) { + forAll(stateGenWithValidModifier) { case (s, m) => + val ver = s.version + s.store.setKeepVersions(10) + val sTry = s.applyModifier(m, Some(0))(_ => ()) + sTry.isSuccess shouldBe true + val s2 = sTry.get + s2.version == ver shouldBe false + val ver2 = s2.version + + val s3 = s2.rollbackTo(ver).get + s3.version == ver shouldBe true + + val sTry2 = s3.applyModifier(m, None)(_ => ()) + sTry2.isSuccess shouldBe true + val s4 = sTry2.get + s4.version == ver shouldBe false + s4.version == ver2 shouldBe true + } + } + + property(propertyNameGenerator("application after rollback is possible")) { + forAll(stateGen) { s => + s.store.setKeepVersions(10) + val maxRollbackDepth = s match { + case ds: DigestState => + ds.store.rollbackVersions().size + case _ => + 10 + } + @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) + val rollbackDepth = Gen.chooseNum(1, maxRollbackDepth).sample.get + val buf = new ListBuffer[BlockSection]() + val ver = s.version + + val s2 = (0 until rollbackDepth).foldLeft(s) { case (state, _) => + val modifier = semanticallyValidModifier(state) + buf += modifier + val sTry = state.applyModifier(modifier, Some(rollbackDepth))(_ => ()) + sTry shouldBe 'success + sTry.get + } + + val lastVersion = s2.version + val rollbackTry = s2.rollbackTo(ver) + rollbackTry.toOption shouldBe defined + val s3 = rollbackTry.get + s3.version == ver shouldBe true + + val s4 = buf.foldLeft(s3) { case (state, m) => + val sTry = state.applyModifier(m, Some(0))(_ => ()) + sTry shouldBe 'success + sTry.get + } + + s4.version == lastVersion shouldBe true + } + } +} diff --git a/src/test/scala/org/ergoplatform/testkit/properties/state/StateTests.scala b/src/test/scala/org/ergoplatform/testkit/properties/state/StateTests.scala new file mode 100644 index 0000000000..6cd442ba94 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/properties/state/StateTests.scala @@ -0,0 +1,23 @@ +package org.ergoplatform.testkit.properties.state + +import org.ergoplatform.nodeView.state.ErgoState +import org.ergoplatform.testkit.TestkitHelpers +import org.ergoplatform.testkit.generators.{CoreGenerators, SemanticallyInvalidModifierProducer, SemanticallyValidModifierProducer} +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +trait StateTests[ST <: ErgoState[ST]] + extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with CoreGenerators + with TestkitHelpers + with SemanticallyValidModifierProducer[ST] + with SemanticallyInvalidModifierProducer[ST] { + + val checksToMake = 10 + + val stateGen: Gen[ST] +} diff --git a/src/test/scala/org/ergoplatform/testkit/utils/AkkaFixture.scala b/src/test/scala/org/ergoplatform/testkit/utils/AkkaFixture.scala new file mode 100644 index 0000000000..21ea378464 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/utils/AkkaFixture.scala @@ -0,0 +1,15 @@ +package org.ergoplatform.testkit.utils + +import java.util.concurrent.atomic.AtomicInteger + +import akka.actor.ActorSystem +import akka.testkit.{ImplicitSender, TestKit} + +object SysId { + private val i = new AtomicInteger() + def incrementAndGet(): Int = i.incrementAndGet() +} + +class AkkaFixture + extends TestKit(ActorSystem("WithIsoFix-%d".format(SysId.incrementAndGet()))) + with ImplicitSender diff --git a/src/test/scala/org/ergoplatform/testkit/utils/NoShrink.scala b/src/test/scala/org/ergoplatform/testkit/utils/NoShrink.scala new file mode 100644 index 0000000000..0fa3909083 --- /dev/null +++ b/src/test/scala/org/ergoplatform/testkit/utils/NoShrink.scala @@ -0,0 +1,7 @@ +package org.ergoplatform.testkit.utils + +import org.scalacheck.Shrink + +trait NoShrink { + protected implicit def noShrink[A]: Shrink[A] = Shrink(_ => Stream.empty) +} diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 7fb07b62f9..a74c1f1a67 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -9,8 +9,8 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.ErgoHistory.Height -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.SortingOption import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings._ import org.ergoplatform.utils.{ErgoTestHelpers, HistoryTestHelpers} @@ -32,7 +32,7 @@ import scala.util.Try object ChainGenerator extends App with ErgoTestHelpers { val realNetworkSetting = { - val initSettings = ErgoSettings.read(Args(None, Some(NetworkType.TestNet))) + val initSettings = ErgoSettingsReader.read(Args(None, Some(NetworkType.TestNet))) initSettings.copy(chainSettings = initSettings.chainSettings.copy(genesisId = None)) } @@ -94,7 +94,7 @@ object ChainGenerator extends App with ErgoTestHelpers { acc: Seq[ModifierId]): Seq[ModifierId] = { val time: Long = last.map(_.timestamp + blockInterval.toMillis).getOrElse(startTime) if (time < System.currentTimeMillis()) { - val (txs, lastOut) = genTransactions(last.map(_.height).getOrElse(ErgoHistory.GenesisHeight), + val (txs, lastOut) = genTransactions(last.map(_.height).getOrElse(GenesisHeight), initBox, state.stateContext) val candidate = genCandidate(prover.hdPubKeys.head.key, last, time, txs, state) diff --git a/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala b/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala index 8c38a167e4..dd743deb3d 100644 --- a/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala +++ b/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala @@ -2,8 +2,8 @@ package org.ergoplatform.tools import org.ergoplatform.mining.difficulty.{DifficultyAdjustment, DifficultySerializer} import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.nodeView.history.ErgoHistoryUtils.Difficulty +import org.ergoplatform.settings.ErgoSettingsReader import org.ergoplatform.utils.generators.ErgoGenerators import scala.annotation.tailrec @@ -86,7 +86,7 @@ object DifficultyControlSimulator extends App with ErgoGenerators { def printTestnetData(): Unit = { val baseHeader = defaultHeaderGen.sample.get - val chainSettings = ErgoSettings.read().chainSettings.copy(epochLength = 1) + val chainSettings = ErgoSettingsReader.read().chainSettings.copy(epochLength = 1) val difficultyControl = new DifficultyAdjustment(chainSettings) val headers = Source.fromResource("difficulty.csv").getLines().toSeq.tail.map { line => diff --git a/src/test/scala/org/ergoplatform/utils/ErgoPropertyTest.scala b/src/test/scala/org/ergoplatform/utils/ErgoPropertyTest.scala index 7ec6cd2466..ebf658e012 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoPropertyTest.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoPropertyTest.scala @@ -1,9 +1,9 @@ package org.ergoplatform.utils +import org.ergoplatform.testkit.utils.NoShrink import org.ergoplatform.utils.generators.{ErgoGenerators, ValidBlocksGenerators} import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.testkit.utils.NoShrink trait ErgoPropertyTest extends AnyPropSpec with ScalaCheckPropertyChecks diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 51d7e80597..4cd5bc4311 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -6,6 +6,7 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.mining.{AutolykosPowScheme, DefaultFakePowScheme} import org.ergoplatform.modifiers.history.extension.ExtensionCandidate import org.ergoplatform.modifiers.history.popow.NipopowAlgos +import org.ergoplatform.network.{PeerSpec, Version} import org.ergoplatform.nodeView.state._ import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.settings.Constants.HashLength @@ -16,8 +17,6 @@ import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, ErgoProvingInterpreter} import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.{DataInput, ErgoBox, ErgoTreePredef} -import scorex.core.app.Version -import scorex.core.network.PeerSpec import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging @@ -46,7 +45,7 @@ trait ErgoTestConstants extends ScorexLogging { Parameters(0, Parameters.DefaultParameters ++ extension, ErgoValidationSettingsUpdate.empty) } - val initSettings: ErgoSettings = ErgoSettings.read(Args(Some("src/test/resources/application.conf"), None)) + val initSettings: ErgoSettings = ErgoSettingsReader.read(Args(Some("src/test/resources/application.conf"), None)) implicit val settings: ErgoSettings = initSettings @@ -82,9 +81,9 @@ trait ErgoTestConstants extends ScorexLogging { val defaultVersion: Byte = 0 lazy val powScheme: AutolykosPowScheme = settings.chainSettings.powScheme.ensuring(_.isInstanceOf[DefaultFakePowScheme]) val emptyVSUpdate = ErgoValidationSettingsUpdate.empty - val emptyStateContext: UpcomingStateContext = ErgoStateContext.empty(genesisStateDigest, settings, parameters) + val emptyStateContext: UpcomingStateContext = ErgoStateContext.empty(genesisStateDigest, settings.chainSettings, parameters) .upcoming(defaultMinerPkPoint, defaultTimestamp, defaultNBits, defaultVotes, emptyVSUpdate, defaultVersion) - def stateContextWith(parameters: Parameters): UpcomingStateContext = ErgoStateContext.empty(genesisStateDigest, settings, parameters) + def stateContextWith(parameters: Parameters): UpcomingStateContext = ErgoStateContext.empty(genesisStateDigest, settings.chainSettings, parameters) .upcoming(defaultMinerPkPoint, defaultTimestamp, defaultNBits, defaultVotes, emptyVSUpdate, defaultVersion) val startHeight: Int = emptyStateContext.currentHeight val startDigest: ADDigest = emptyStateContext.genesisStateDigest diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestHelpers.scala index bf9d61ac3e..b7164a6103 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestHelpers.scala @@ -3,8 +3,7 @@ package org.ergoplatform.utils import org.ergoplatform.ErgoBoxCandidate import org.ergoplatform.utils.generators.ValidBlocksGenerators import org.scalatest.{EitherValues, OptionValues} -import scorex.core.network.peer.PeerInfo -import scorex.core.utils.ScorexEncoding +import org.ergoplatform.network.peer.PeerInfo import scorex.util.ScorexLogging import java.net.InetSocketAddress diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index b38d920120..c6b1c00233 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -1,12 +1,12 @@ package org.ergoplatform.utils +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.storage.modifierprocessors.{EmptyBlockSectionProcessor, FullBlockPruningProcessor, ToDownloadProcessor} -import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.SortingOption import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.settings._ +import org.ergoplatform.settings.{ScorexSettings, _} import org.scalacheck.Gen -import scorex.core.settings.ScorexSettings import scorex.util.ModifierId import scorex.util.encode.Base16 @@ -84,7 +84,7 @@ object HistoryTestHelpers { val ppM = ru.typeOf[ToDownloadProcessor].member(ru.TermName("pruningProcessor")).asMethod val pp = procInstance.reflectMethod(ppM).apply().asInstanceOf[FullBlockPruningProcessor] val f = ru.typeOf[FullBlockPruningProcessor].member(ru.TermName("minimalFullBlockHeightVar")).asTerm.accessed.asTerm - runtimeMirror.reflect(pp).reflectField(f).set(ErgoHistory.GenesisHeight) + runtimeMirror.reflect(pp).reflectField(f).set(GenesisHeight) val f2 = ru.typeOf[FullBlockPruningProcessor].member(ru.TermName("isHeadersChainSyncedVar")).asTerm.accessed.asTerm runtimeMirror.reflect(pp).reflectField(f2).set(true) } diff --git a/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala b/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala index 8df386a697..3b708d9d06 100644 --- a/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala +++ b/src/test/scala/org/ergoplatform/utils/NodeViewTestConfig.scala @@ -2,7 +2,7 @@ package org.ergoplatform.utils import org.ergoplatform.mining.DefaultFakePowScheme import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.settings.{ErgoSettings, NipopowSettings} +import org.ergoplatform.settings.{ErgoSettings, ErgoSettingsReader, NipopowSettings} case class NodeViewTestConfig(stateType: StateType, @@ -10,7 +10,7 @@ case class NodeViewTestConfig(stateType: StateType, popowBootstrap: Boolean) { def toSettings: ErgoSettings = { - val defaultSettings = ErgoSettings.read() + val defaultSettings = ErgoSettingsReader.read() defaultSettings.copy( chainSettings = defaultSettings.chainSettings.copy( powScheme = new DefaultFakePowScheme(defaultSettings.chainSettings.powScheme.k, defaultSettings.chainSettings.powScheme.n) diff --git a/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala b/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala index d78b852d81..b161221615 100644 --- a/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala @@ -6,14 +6,15 @@ import akka.util.Timeout import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.{ErgoState, StateType, UtxoState} import org.ergoplatform.settings.Algos import org.ergoplatform.nodeView.ErgoNodeViewHolder.CurrentView -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetDataFromCurrentView, LocallyGeneratedModifier} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import scorex.core.validation.MalformedModifierError +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.GetDataFromCurrentView +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ +import org.ergoplatform.nodeView.LocallyGeneratedModifier +import org.ergoplatform.validation.MalformedModifierError import scorex.util.ModifierId import scala.concurrent.duration._ diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index b68886a573..53186f5a77 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -9,22 +9,23 @@ import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.network.{Handshake, PeerSpec, Version} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryUtils} import org.ergoplatform.nodeView.mempool.ErgoMemPool -import org.ergoplatform.nodeView.mempool.ErgoMemPool.{ProcessingOutcome, SortingOption} +import org.ergoplatform.nodeView.mempool.ErgoMemPoolUtils.{ProcessingOutcome, SortingOption} import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{DigestState, ErgoStateContext, StateType} -import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ -import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult +import org.ergoplatform.nodeView.wallet.ErgoWalletActorMessages._ +import org.ergoplatform.nodeView.wallet.ErgoWalletServiceUtils.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet._ import org.ergoplatform.nodeView.wallet.persistence.WalletDigest import org.ergoplatform.nodeView.wallet.scanning.Scan import org.ergoplatform.sanity.ErgoSanity.HT import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.settings.Constants.HashLength -import org.ergoplatform.settings._ +import org.ergoplatform.settings.{ScorexSettings, _} import org.ergoplatform.utils.generators.{ChainGenerator, ErgoGenerators, ErgoTransactionGenerators} import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import org.ergoplatform.wallet.boxes.{ChainStatus, TrackedBox} @@ -33,11 +34,9 @@ import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.utils.TestFileUtils import org.scalacheck.Gen -import scorex.core.app.Version import scorex.core.network.NetworkController.ReceivableMessages.GetConnectedPeers -import scorex.core.network.peer.PeerManager.ReceivableMessages.{GetAllPeers, GetBlacklistedPeers} -import scorex.core.network.{Handshake, PeerSpec} -import scorex.core.settings.ScorexSettings +import org.ergoplatform.network.peer.PeerManager.ReceivableMessages.{GetAllPeers, GetBlacklistedPeers} +import org.ergoplatform.network.PeerSpec import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.ByteArrayWrapper @@ -182,7 +181,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with case RescanWallet(_) => sender ! Success(()) - case GetWalletStatus => sender() ! WalletStatus(true, true, None, ErgoHistory.GenesisHeight, error = None) + case GetWalletStatus => sender() ! WalletStatus(true, true, None, ErgoHistoryUtils.GenesisHeight, error = None) case _: CheckSeed => sender() ! true @@ -257,7 +256,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with sender() ! Success(tx) case SignTransaction(tx, secrets, hints, boxesToSpendOpt, dataBoxesOpt) => - val sc = ErgoStateContext.empty(settings, parameters) + val sc = ErgoStateContext.empty(settings.chainSettings, parameters) sender() ! ergoWalletService.signTransaction(Some(prover), tx, secrets, hints, boxesToSpendOpt, dataBoxesOpt, parameters, sc) { boxId => utxoState.versionedBoxHolder.get(ByteArrayWrapper(boxId)) } diff --git a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala index 42407311f2..f88e087f4a 100644 --- a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala @@ -5,7 +5,7 @@ import org.ergoplatform._ import org.ergoplatform.mining.CandidateGenerator import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.{ErgoState, UtxoState} import org.ergoplatform.nodeView.wallet.ErgoWallet import org.ergoplatform.nodeView.wallet.IdUtils._ @@ -70,7 +70,7 @@ trait WalletTestOps extends NodeViewBaseOps { def getUtxoState(implicit ctx: Ctx): UtxoState = getCurrentState.asInstanceOf[UtxoState] def getHeightOf(state: ErgoState[_])(implicit ctx: Ctx): Option[Int] = - getHistory.heightOf(scorex.core.versionToId(state.version)) + getHistory.heightOf(org.ergoplatform.core.versionToId(state.version)) def makeGenesisBlock(script: ProveDlog, assets: Seq[(TokenId, Long)] = Seq.empty) (implicit ctx: Ctx): ErgoFullBlock = { @@ -86,7 +86,7 @@ trait WalletTestOps extends NodeViewBaseOps { } CandidateGenerator.collectRewards(Some(genesisEmissionBox), - ErgoHistory.EmptyHistoryHeight, + EmptyHistoryHeight, Seq.empty, publicKey, emptyStateContext, @@ -97,7 +97,7 @@ trait WalletTestOps extends NodeViewBaseOps { val inputs = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) val assets: Seq[(TokenId, Long)] = replaceNewAssetStub(assetsIn, inputs) CandidateGenerator.collectRewards(Some(genesisEmissionBox), - ErgoHistory.EmptyHistoryHeight, + EmptyHistoryHeight, Seq.empty, publicKey, emptyStateContext, diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala index ad49b7557a..311ce9d00b 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala @@ -13,6 +13,7 @@ import org.ergoplatform.nodeView.history.{ErgoSyncInfo, ErgoSyncInfoV1, ErgoSync import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.settings.{Constants, ErgoValidationSettings, ErgoValidationSettingsUpdate, ValidationRules} +import org.ergoplatform.testkit.generators.CoreGenerators import org.ergoplatform.utils.ErgoTestConstants import org.ergoplatform.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule} import org.ergoplatform.wallet.utils.Generators @@ -21,7 +22,6 @@ import org.scalacheck.{Arbitrary, Gen} import org.scalatest.matchers.should.Matchers import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.Digest32 -import scorex.testkit.generators.CoreGenerators import sigmastate.Values.ErgoTree import sigmastate.crypto.CryptoConstants.EcPointType import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 978617e43d..e80b368edb 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -5,7 +5,7 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.ErgoHistoryUtils._ import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{BoxHolder, ErgoStateContext, VotingData} import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionSigningRequest} @@ -61,7 +61,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { def ergoBoxGenForTokens(tokens: Seq[(TokenId, Long)], propositionGen: Gen[ErgoTree]): Gen[ErgoBox] = { - ergoBoxGen(propGen = propositionGen, tokensGen = Gen.oneOf(tokens, tokens), heightGen = ErgoHistory.EmptyHistoryHeight) + ergoBoxGen(propGen = propositionGen, tokensGen = Gen.oneOf(tokens, tokens), heightGen = EmptyHistoryHeight) } def unspendableErgoBoxGen(minValue: Long = parameters.minValuePerByte * 200, @@ -324,13 +324,13 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { } yield { blocks match { case _ :: _ => - val sc = new ErgoStateContext(Seq(), None, startDigest, parameters, validationSettingsNoIl, VotingData.empty) + val sc = new ErgoStateContext(Seq(), None, startDigest, parameters, validationSettingsNoIl, VotingData.empty)(settings.chainSettings) blocks.foldLeft(sc -> 1) { case ((c, h), b) => val block = b.copy(header = b.header.copy(height = h, votes = votes(h - 1))) c.appendFullBlock(block).get -> (h + 1) }._1 case _ => - ErgoStateContext.empty(stateRoot, settings, parameters) + ErgoStateContext.empty(stateRoot, settings.chainSettings, parameters) } } diff --git a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala index b579b50cd7..e24688951b 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala @@ -10,14 +10,14 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, Parameters} +import org.ergoplatform.testkit.TestkitHelpers import org.ergoplatform.utils.{LoggingUtil, RandomLike, RandomWrapper} import org.ergoplatform.wallet.utils.TestFileUtils import org.scalatest.matchers.should.Matchers -import scorex.core.VersionTag +import org.ergoplatform.core.VersionTag import scorex.crypto.authds.avltree.batch.Remove import scorex.crypto.authds.ADDigest import scorex.db.ByteArrayWrapper -import scorex.testkit.TestkitHelpers import scala.annotation.tailrec import scala.collection.mutable diff --git a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala index 4c7e5ad532..c5b6395e62 100644 --- a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala +++ b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala @@ -4,9 +4,9 @@ import akka.actor.{ActorRef, Cancellable} import io.circe._ import io.circe.syntax._ import org.ergoplatform.modifiers.NetworkObjectTypeId +import org.ergoplatform.testkit.generators.ObjectGenerators import org.ergoplatform.utils.ErgoPropertyTest import scorex.core.network.ModifiersStatus.Received -import scorex.testkit.generators.ObjectGenerators import scorex.util.ModifierId class DeliveryTrackerSpec extends ErgoPropertyTest with ObjectGenerators { diff --git a/src/test/scala/scorex/core/network/PeerSpecSerializerSpec.scala b/src/test/scala/scorex/core/network/PeerSpecSerializerSpec.scala index 13b8c3d0be..bc82776b49 100644 --- a/src/test/scala/scorex/core/network/PeerSpecSerializerSpec.scala +++ b/src/test/scala/scorex/core/network/PeerSpecSerializerSpec.scala @@ -1,7 +1,8 @@ package scorex.core.network +import org.ergoplatform.network.PeerSpecSerializer +import org.ergoplatform.testkit.generators.ObjectGenerators import org.ergoplatform.utils.ErgoPropertyTest -import scorex.testkit.generators.ObjectGenerators import scorex.util.ByteArrayBuilder import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} diff --git a/src/test/scala/scorex/testkit/SerializationTests.scala b/src/test/scala/scorex/testkit/SerializationTests.scala index b4ed96389e..b69e3f0f4a 100644 --- a/src/test/scala/scorex/testkit/SerializationTests.scala +++ b/src/test/scala/scorex/testkit/SerializationTests.scala @@ -4,7 +4,7 @@ import org.scalacheck.Gen import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.core.serialization.ErgoSerializer +import org.ergoplatform.serialization.ErgoSerializer trait SerializationTests extends ScalaCheckPropertyChecks with Matchers { def checkSerializationRoundtrip[A](generator: Gen[A], serializer: ErgoSerializer[A]): Assertion = { diff --git a/src/test/scala/scorex/testkit/generators/AllModifierProducers.scala b/src/test/scala/scorex/testkit/generators/AllModifierProducers.scala index 857fd49393..bb5b9feedc 100644 --- a/src/test/scala/scorex/testkit/generators/AllModifierProducers.scala +++ b/src/test/scala/scorex/testkit/generators/AllModifierProducers.scala @@ -2,10 +2,9 @@ package scorex.testkit.generators import org.ergoplatform.nodeView.state.ErgoState - trait AllModifierProducers[ST <: ErgoState[ST]] extends SemanticallyValidModifierProducer[ST] with SyntacticallyTargetedModifierProducer with ArbitraryTransactionsCarryingModifierProducer with TotallyValidModifierProducer[ST] - with SemanticallyValidTransactionsCarryingModifier[ST] + with SemanticallyValidTransactionsCarryingModifier[ST] \ No newline at end of file diff --git a/src/test/scala/scorex/testkit/generators/CoreGenerators.scala b/src/test/scala/scorex/testkit/generators/CoreGenerators.scala index 54c6c3d752..95e646742f 100644 --- a/src/test/scala/scorex/testkit/generators/CoreGenerators.scala +++ b/src/test/scala/scorex/testkit/generators/CoreGenerators.scala @@ -1,7 +1,7 @@ package scorex.testkit.generators import org.scalacheck.Gen -import scorex.core.{VersionTag, idToVersion} +import org.ergoplatform.core.{VersionTag, idToVersion} //Generators of objects from scorex-core trait CoreGenerators extends ObjectGenerators { diff --git a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala index f99112fab7..800b7ac8d6 100644 --- a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala +++ b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala @@ -3,16 +3,15 @@ package scorex.testkit.generators import java.net.{InetAddress, InetSocketAddress, URL} import akka.actor.ActorRef import akka.util.ByteString +import org.ergoplatform.NodeViewModifier import org.ergoplatform.modifiers.NetworkObjectTypeId -import org.ergoplatform.network.ModePeerFeature +import org.ergoplatform.network.{ModePeerFeature, PeerFeature, PeerSpec, Version} import org.ergoplatform.nodeView.state.StateType import org.scalacheck.Gen.{const, some} import org.scalacheck.{Arbitrary, Gen} -import scorex.core.app.Version -import scorex.core.network.message.{InvData, ModifiersData} +import org.ergoplatform.network.message.{InvData, ModifiersData} import scorex.core.network._ -import scorex.core.network.peer.{PeerInfo, RestApiUrlPeerFeature} -import scorex.core.NodeViewModifier +import org.ergoplatform.network.peer.{PeerInfo, RestApiUrlPeerFeature} import scorex.util.{ModifierId, bytesToId} trait ObjectGenerators { diff --git a/src/test/scala/scorex/testkit/properties/HistoryTests.scala b/src/test/scala/scorex/testkit/properties/HistoryTests.scala index 31e2d726e8..2e3daf1c52 100644 --- a/src/test/scala/scorex/testkit/properties/HistoryTests.scala +++ b/src/test/scala/scorex/testkit/properties/HistoryTests.scala @@ -1,14 +1,14 @@ package scorex.testkit.properties +import org.ergoplatform.consensus.ModifierSemanticValidity.Valid import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.testkit.TestkitHelpers +import org.ergoplatform.testkit.generators.SyntacticallyTargetedModifierProducer import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.core.consensus.ModifierSemanticValidity.Valid -import scorex.testkit.TestkitHelpers -import scorex.testkit.generators.SyntacticallyTargetedModifierProducer import scorex.util.ScorexLogging diff --git a/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala index 775b58d8d0..bf9118c1ba 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala @@ -6,12 +6,13 @@ import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec +import org.ergoplatform.network.ErgoNodeViewSynchronizerMessages._ import org.ergoplatform.nodeView.ErgoNodeViewHolder.CurrentView -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetDataFromCurrentView, LocallyGeneratedModifier} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.GetDataFromCurrentView +import org.ergoplatform.nodeView.LocallyGeneratedModifier import org.ergoplatform.nodeView.state.ErgoState -import scorex.testkit.generators._ -import scorex.testkit.utils.AkkaFixture +import org.ergoplatform.testkit.generators +import org.ergoplatform.testkit.utils.AkkaFixture import scorex.util.ScorexLogging import scala.concurrent.Await @@ -22,11 +23,11 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] extends AnyPropSpec with Matchers with ScorexLogging - with SyntacticallyTargetedModifierProducer - with TotallyValidModifierProducer[ST] - with SemanticallyInvalidModifierProducer[ST] - with CustomModifierProducer[ST] - with ObjectGenerators { + with generators.SyntacticallyTargetedModifierProducer + with generators.TotallyValidModifierProducer[ST] + with generators.SemanticallyInvalidModifierProducer[ST] + with generators.CustomModifierProducer[ST] + with generators.ObjectGenerators { def nodeViewHolder(implicit system: ActorSystem): (ActorRef, TestProbe, BlockSection, ST, ErgoHistory) @@ -314,9 +315,9 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] // generate the second fork with the invalid block val fork2Mods = withView(node) { v => customModifiers(v.history, v.state, - Seq[ModifierProducerTemplateItem](Valid, - SynInvalid, // invalid modifier - Valid, Valid, Valid, Valid, Valid, Valid)) + Seq[generators.ModifierProducerTemplateItem](generators.Valid, + generators.SynInvalid, // invalid modifier + generators.Valid, generators.Valid, generators.Valid, generators.Valid, generators.Valid, generators.Valid)) } // apply the first fork with valid blocks fork1Mods.foreach { mod => node ! LocallyGeneratedModifier(mod) } diff --git a/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala b/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala index 5506e6ac35..ed5361cbc3 100644 --- a/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala +++ b/src/test/scala/scorex/testkit/properties/mempool/MempoolRemovalTest.scala @@ -3,12 +3,12 @@ package scorex.testkit.properties.mempool import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.testkit.TestkitHelpers +import org.ergoplatform.testkit.generators.ArbitraryTransactionsCarryingModifierProducer import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.testkit.TestkitHelpers -import scorex.testkit.generators.ArbitraryTransactionsCarryingModifierProducer import scorex.util.ScorexLogging trait MempoolRemovalTest extends AnyPropSpec diff --git a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala index 805550fffe..410b5fcaa9 100644 --- a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala +++ b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala @@ -3,6 +3,7 @@ package scorex.testkit.properties.state import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.{DigestState, ErgoState} import org.scalacheck.Gen + import scala.collection.mutable.ListBuffer diff --git a/src/test/scala/scorex/testkit/properties/state/StateTests.scala b/src/test/scala/scorex/testkit/properties/state/StateTests.scala index 78c1452c8f..67a7aefdaa 100644 --- a/src/test/scala/scorex/testkit/properties/state/StateTests.scala +++ b/src/test/scala/scorex/testkit/properties/state/StateTests.scala @@ -1,12 +1,12 @@ package scorex.testkit.properties.state import org.ergoplatform.nodeView.state.ErgoState +import org.ergoplatform.testkit.{TestkitHelpers, generators} +import org.ergoplatform.testkit.generators.{CoreGenerators, SemanticallyInvalidModifierProducer} import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.testkit.TestkitHelpers -import scorex.testkit.generators.{CoreGenerators, SemanticallyInvalidModifierProducer, SemanticallyValidModifierProducer} trait StateTests[ST <: ErgoState[ST]] extends AnyPropSpec @@ -14,7 +14,7 @@ trait StateTests[ST <: ErgoState[ST]] with Matchers with CoreGenerators with TestkitHelpers - with SemanticallyValidModifierProducer[ST] + with generators.SemanticallyValidModifierProducer[ST] with SemanticallyInvalidModifierProducer[ST] { val checksToMake = 10 diff --git a/src/test/scala/scorex/testkit/utils/AkkaFixture.scala b/src/test/scala/scorex/testkit/utils/AkkaFixture.scala index c3aa3b978e..e5ff1f476f 100644 --- a/src/test/scala/scorex/testkit/utils/AkkaFixture.scala +++ b/src/test/scala/scorex/testkit/utils/AkkaFixture.scala @@ -1,7 +1,6 @@ package scorex.testkit.utils import java.util.concurrent.atomic.AtomicInteger - import akka.actor.ActorSystem import akka.testkit.{ImplicitSender, TestKit}