From a5b7bf048fb6f734112fb7fddcaeaf366be29021 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 13 Sep 2021 20:44:24 +0300 Subject: [PATCH 001/204] utxo.md started --- papers/utxo.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 papers/utxo.md diff --git a/papers/utxo.md b/papers/utxo.md new file mode 100644 index 0000000000..71d248eef1 --- /dev/null +++ b/papers/utxo.md @@ -0,0 +1,22 @@ +Bootstrapping with UTXO set snapshot +==================================== + +Motivation +---------- + +With time it consumes more and more resources to download, store and process the +whole blockchain with all the fullblocks. In blockchain where UTXO set snapshot is +authenticated (e.g. Ergo or Ethereum), it is possible to avoid that, downloading only some +historical UTXO set snapshot (accounts snapshot in Ethereum) and full-blocks after it. +It is shown (in [https://eprint.iacr.org/2018/129](https://eprint.iacr.org/2018/129)) that this +mode can be as secure as processing all the blocks (under the same assumptions). + +This specification defines how to implement bootstrapping with UTXO set snapshot in Ergo +protocol reference client. + +Implementation Details +---------------------- + +UTXO set is authenticated via AVL+ tree. + +Time is broken into epochs, 1 epoch = 51,840 blocks (~72 days). \ No newline at end of file From 1083fb990c946d3b485deb58175f04c86b9205b6 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 27 Sep 2021 20:02:47 +0300 Subject: [PATCH 002/204] snapshotsToKeep setting, UtxoSnapshotExample --- src/main/resources/application.conf | 2 ++ .../examples/UtxoSnapshotExample.scala | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 2fb429de98..08b0964862 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -80,6 +80,8 @@ ergo { # Block 417,792 is checkpointed by the protocol (so its UTXO set as well). # The node still applying transactions to UTXO set and so checks UTXO set digests for each block. skipV1TransactionsValidation = false + + snapshotsToKeep = 2 } cache { diff --git a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala new file mode 100644 index 0000000000..ef9241b901 --- /dev/null +++ b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala @@ -0,0 +1,27 @@ +package org.ergoplatform.examples + +import java.io.File + +import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} +import org.ergoplatform.settings.{Algos, Args, ErgoSettings, NetworkType} +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.hash.Digest32 + +object UtxoSnapshotExample extends App { + import Algos.HF + + val stateConstants = StateConstants(None, ErgoSettings.read(Args(Some("/home/kushti/ergo/mainnet/mainnet.conf"), Some(NetworkType.MainNet)))) + val state = UtxoState.create(new File("/home/kushti/ergo/mainnet/.ergo/state"), stateConstants) + + implicit val hf: HF = Algos.hash + val serializer = new BatchAVLProverSerializer[Digest32, HF] + + val avlProver = state.persistentProver + val ms0 = System.currentTimeMillis() + println(avlProver.height) + val sliced = serializer.slice(avlProver.avlProver, subtreeDepth = 16) + println("sliced: " + sliced._2) + val ms = System.currentTimeMillis() + println("Time: " + (ms - ms0)) + +} From 9fca93e5235d7ef05582a09ed039015b4db9f0ac Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Wed, 3 Nov 2021 12:49:32 +0300 Subject: [PATCH 003/204] leafDataStats --- src/main/resources/mainnet.conf | 10 +---- src/main/scala/org/ergoplatform/ErgoApp.scala | 2 +- .../examples/UtxoSnapshotExample.scala | 39 ++++++++++++++++++- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index ba4f08a1fc..8452b7ff89 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -41,15 +41,7 @@ scorex { nodeName = "ergo-mainnet-4.0.16" nodeName = ${?NODENAME} knownPeers = [ - "213.239.193.208:9030", - "159.65.11.55:9030", - "165.227.26.175:9030", - "159.89.116.15:9030", - "83.212.114.255:9030", - "136.244.110.145:9030", - "91.199.118.161:9030", - "209.217.206.254:9030", - "94.130.108.35:9030" + "159.89.116.15:9030" ] # Max number of delivery checks. Stop expecting modifier (and penalize peer) if it was not delivered after that diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 82d0165c2f..05f0d24cff 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -114,7 +114,7 @@ class ErgoApp(args: Args) extends ScorexLogging { ) // Launching PeerSynchronizer actor which is then registering itself at network controller - PeerSynchronizerRef("PeerSynchronizer", networkControllerRef, peerManagerRef, scorexSettings.network, featureSerializers) + // PeerSynchronizerRef("PeerSynchronizer", networkControllerRef, peerManagerRef, scorexSettings.network, featureSerializers) private val apiRoutes: Seq[ApiRoute] = Seq( EmissionApiRoute(ergoSettings), diff --git a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala index ef9241b901..a43b84476e 100644 --- a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala +++ b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala @@ -1,24 +1,59 @@ -package org.ergoplatform.examples +package scorex.crypto.authds.avltree.batch import java.io.File import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} import org.ergoplatform.settings.{Algos, Args, ErgoSettings, NetworkType} import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.hash import scorex.crypto.hash.Digest32 object UtxoSnapshotExample extends App { import Algos.HF + /** + * Count number of leaves and their cumulative size (so total weight of boxes) + * + * @param prover + * @return + */ + private def leafDataStats(prover: BatchAVLProver[hash.Digest32, HF]): (Long, Long) = { + + def step(node: ProverNodes[Digest32], acc: (Long, Long)): (Long, Long) = { + node match { + case in: InternalProverNode[Digest32] => + val leftRes = step(in.left, acc) + val rightRes = step(in.right, acc) + (leftRes._1 + rightRes._1, leftRes._2 + rightRes._2) + case l: ProverLeaf[Digest32] => + (1, l.value.size) + } + } + + step(prover.topNode, (0, 0)) + } + val stateConstants = StateConstants(None, ErgoSettings.read(Args(Some("/home/kushti/ergo/mainnet/mainnet.conf"), Some(NetworkType.MainNet)))) + + println("Init started") + val ims0 = System.currentTimeMillis() val state = UtxoState.create(new File("/home/kushti/ergo/mainnet/.ergo/state"), stateConstants) + val ims = System.currentTimeMillis() + println("Init took: " + (ims-ims0) + " ms.") implicit val hf: HF = Algos.hash val serializer = new BatchAVLProverSerializer[Digest32, HF] + val (elems, valSize) = leafDataStats(state.persistentProver.prover()) + + println("elems: " + elems) + + println("boxes total bytes count: " + valSize) + + println("height: " + state.persistentProver.height) + val avlProver = state.persistentProver val ms0 = System.currentTimeMillis() - println(avlProver.height) val sliced = serializer.slice(avlProver.avlProver, subtreeDepth = 16) println("sliced: " + sliced._2) val ms = System.currentTimeMillis() From 5b0229dbae19ea2d33fa9b02a1e795906357a553 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Wed, 3 Nov 2021 13:55:11 +0300 Subject: [PATCH 004/204] utxoBootstrap flag --- src/main/resources/application.conf | 2 ++ src/main/resources/mainnet.conf | 10 +++++++++- src/main/scala/org/ergoplatform/ErgoApp.scala | 2 +- .../settings/NodeConfigurationSettings.scala | 2 ++ .../settings/ErgoSettingsSpecification.scala | 3 +++ .../scala/org/ergoplatform/tools/ChainGenerator.scala | 2 +- .../org/ergoplatform/utils/HistoryTestHelpers.scala | 2 +- src/test/scala/org/ergoplatform/utils/Stubs.scala | 2 +- 8 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 883fff5a69..aae5f288fd 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -19,6 +19,8 @@ ergo { # Keep all blocks from genesis if negative blocksToKeep = -1 + utxoBootstrap = false + # Download PoPoW proof on node bootstrap PoPoWBootstrap = false diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index 8452b7ff89..ba4f08a1fc 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -41,7 +41,15 @@ scorex { nodeName = "ergo-mainnet-4.0.16" nodeName = ${?NODENAME} knownPeers = [ - "159.89.116.15:9030" + "213.239.193.208:9030", + "159.65.11.55:9030", + "165.227.26.175:9030", + "159.89.116.15:9030", + "83.212.114.255:9030", + "136.244.110.145:9030", + "91.199.118.161:9030", + "209.217.206.254:9030", + "94.130.108.35:9030" ] # Max number of delivery checks. Stop expecting modifier (and penalize peer) if it was not delivered after that diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 05f0d24cff..82d0165c2f 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -114,7 +114,7 @@ class ErgoApp(args: Args) extends ScorexLogging { ) // Launching PeerSynchronizer actor which is then registering itself at network controller - // PeerSynchronizerRef("PeerSynchronizer", networkControllerRef, peerManagerRef, scorexSettings.network, featureSerializers) + PeerSynchronizerRef("PeerSynchronizer", networkControllerRef, peerManagerRef, scorexSettings.network, featureSerializers) private val apiRoutes: Seq[ApiRoute] = Seq( EmissionApiRoute(ergoSettings), diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index ec386d0eaf..3616a8ec8e 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -14,6 +14,7 @@ import scala.concurrent.duration.FiniteDuration case class NodeConfigurationSettings(stateType: StateType, verifyTransactions: Boolean, blocksToKeep: Int, + utxoBootstrap: Boolean, poPoWBootstrap: Boolean, minimalSuffix: Int, mining: Boolean, @@ -47,6 +48,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with ModifierIdReader { stateType, cfg.as[Boolean](s"$path.verifyTransactions"), cfg.as[Int](s"$path.blocksToKeep"), + cfg.as[Boolean](s"$path.utxoBootstrap"), cfg.as[Boolean](s"$path.PoPoWBootstrap"), cfg.as[Int](s"$path.minimalSuffix"), cfg.as[Boolean](s"$path.mining"), diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index d90765fd14..375fc70a8c 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -20,6 +20,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 1000, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, @@ -61,6 +62,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 12, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, @@ -102,6 +104,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 13, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index e9d6730339..f2104fcdec 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -57,7 +57,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val minimalSuffix = 2 val complexityLimit = initSettings.nodeSettings.maxTransactionComplexity val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, poPoWBootstrap = false, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, + -1, utxoBootstrap = false, poPoWBootstrap = false, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, 200, 100000, 1.minute, rebroadcastCount = 20, 1000000, 100) val ms = settings.chainSettings.monetary.copy( diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index 220f2ad789..b7f6705c0f 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -44,7 +44,7 @@ trait HistoryTestHelpers extends ErgoPropertyTest { val minimalSuffix = 2 val complexityLimit = initSettings.nodeSettings.maxTransactionComplexity val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - PoPoWBootstrap, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, + utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, 200, 100000, 1.minute, rebroadcastCount = 200, 1000000, 100) val scorexSettings: ScorexSettings = null diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 92eeac90b1..0ac44e8fa3 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -340,7 +340,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val minimalSuffix = 2 val complexityLimit = initSettings.nodeSettings.maxTransactionComplexity val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - PoPoWBootstrap, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, + utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, complexityLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second,miningPubKeyHex = None, offlineGeneration = false, 200, 100000, 1.minute, rebroadcastCount = 200, 1000000, 100) val scorexSettings: ScorexSettings = null From b1226c67723f5ab1be888a658b8b62afde43833c Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Wed, 3 Nov 2021 15:05:46 +0300 Subject: [PATCH 005/204] UTXOSnapshotChunk rework #1 --- .../modifiers/state/UTXOSnapshotChunk.scala | 23 +++++++------ .../EmptyBlockSectionProcessor.scala | 4 +-- .../examples/UtxoSnapshotExample.scala | 32 +++++++++++-------- .../NonVerifyADHistorySpecification.scala | 1 + .../ErgoTransactionGenerators.scala | 6 ++-- 5 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala index bf9296dd3b..117efbd941 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala @@ -2,32 +2,37 @@ package org.ergoplatform.modifiers.state import org.ergoplatform.ErgoBox import org.ergoplatform.modifiers.ErgoPersistentModifier -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk.StateElement import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer -import scorex.crypto.authds.LeafData +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSubtree import scorex.crypto.hash.Digest32 import scorex.util.{ModifierId, bytesToId} -import scorex.utils.Random -case class UTXOSnapshotChunk(stateElements: Seq[StateElement], - index: Short) extends ErgoPersistentModifier { +/** +* Container for a chunk of sliced AVL+ tree +*/ +case class UTXOSnapshotChunk(subTree: Either[BatchAVLProverSubtree[Digest32, Algos.HF], Array[Byte]], + index: Int) extends ErgoPersistentModifier { override val modifierTypeId: ModifierTypeId = UTXOSnapshotChunk.modifierTypeId + lazy val subtreeDeserialized: BatchAVLProverSubtree[Digest32, Algos.HF] = subTree match { + case Left(st) => st + case Right(_) => ??? //todo: exception may happen here + } + //TODO implement correctly - override lazy val id: ModifierId = bytesToId(Random.randomBytes(32)) + override lazy val id: ModifierId = bytesToId(subtreeDeserialized.subtreeTop.label) override def parentId: ModifierId = ??? - override def serializedId: Array[Byte] = ??? + //todo: provide id from outside + override def serializedId: Array[Byte] = subtreeDeserialized.subtreeTop.label override type M = UTXOSnapshotChunk override def serializer: ScorexSerializer[UTXOSnapshotChunk] = ??? - lazy val rootHash: Digest32 = Algos.merkleTreeRoot(stateElements.map(LeafData @@ _.bytes)) - override val sizeOpt: Option[Int] = None } 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 fd9c88ff64..d266056e01 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 @@ -6,8 +6,8 @@ import scorex.core.consensus.History.ProgressInfo import scala.util.{Failure, Success, Try} /** - * Trait that implements BlockSectionProcessor interfaces for a regime where the node only - * downloads block headers + * Trait that implements BlockSectionProcessor interfaces for a regime where the node is only + * downloading block headers */ trait EmptyBlockSectionProcessor extends BlockSectionProcessor { diff --git a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala index a43b84476e..d5d355e3df 100644 --- a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala +++ b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala @@ -5,20 +5,34 @@ import java.io.File import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} import org.ergoplatform.settings.{Algos, Args, ErgoSettings, NetworkType} import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer -import scorex.crypto.hash import scorex.crypto.hash.Digest32 +import scorex.util.encode.Base16 object UtxoSnapshotExample extends App { import Algos.HF + val stateConstants = StateConstants(None, ErgoSettings.read(Args(Some("/home/kushti/ergo/mainnet/mainnet.conf"), Some(NetworkType.MainNet)))) + + println("Init started") + val ims0 = System.currentTimeMillis() + val state = UtxoState.create(new File("/home/kushti/ergo/mainnet/.ergo/state"), stateConstants) + val ims = System.currentTimeMillis() + println("Init took: " + (ims-ims0) + " ms.") + + implicit val hf: HF = Algos.hash + val serializer = new BatchAVLProverSerializer[Digest32, HF] + + /* /** * Count number of leaves and their cumulative size (so total weight of boxes) * * @param prover * @return */ + private def leafDataStats(prover: BatchAVLProver[hash.Digest32, HF]): (Long, Long) = { + def step(node: ProverNodes[Digest32], acc: (Long, Long)): (Long, Long) = { node match { case in: InternalProverNode[Digest32] => @@ -33,29 +47,21 @@ object UtxoSnapshotExample extends App { step(prover.topNode, (0, 0)) } - val stateConstants = StateConstants(None, ErgoSettings.read(Args(Some("/home/kushti/ergo/mainnet/mainnet.conf"), Some(NetworkType.MainNet)))) - - println("Init started") - val ims0 = System.currentTimeMillis() - val state = UtxoState.create(new File("/home/kushti/ergo/mainnet/.ergo/state"), stateConstants) - val ims = System.currentTimeMillis() - println("Init took: " + (ims-ims0) + " ms.") - - implicit val hf: HF = Algos.hash - val serializer = new BatchAVLProverSerializer[Digest32, HF] val (elems, valSize) = leafDataStats(state.persistentProver.prover()) - println("elems: " + elems) - println("boxes total bytes count: " + valSize) + */ println("height: " + state.persistentProver.height) val avlProver = state.persistentProver val ms0 = System.currentTimeMillis() val sliced = serializer.slice(avlProver.avlProver, subtreeDepth = 16) + + println("sliced count: " + sliced._2.size) println("sliced: " + sliced._2) + println("sliced labels: " + sliced._2.map(_.subtreeTop.label).map(Base16.encode)) val ms = System.currentTimeMillis() println("Time: " + (ms - ms0)) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala index c021234e35..58324006a9 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala @@ -22,6 +22,7 @@ class NonVerifyADHistorySpecification extends HistoryTestHelpers { private lazy val popowHistory = ensureMinimalHeight(genHistory(), 100) + //todo: make real test ignore("Should apply UTXOSnapshotChunks") { forAll(randomUTXOSnapshotChunkGen) { snapshot: UTXOSnapshotChunk => popowHistory.applicable(snapshot) shouldBe true diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index c89b5de23c..a31d23c12b 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -282,10 +282,10 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { } yield BlockTransactions(headerId, Header.InitialVersion, txs.foldLeft(Seq.empty[ErgoTransaction])((acc, tx) => if ((acc :+ tx).map(_.size).sum < (Parameters.MaxBlockSizeDefault - 150)) acc :+ tx else acc)) + // todo: fix generator lazy val randomUTXOSnapshotChunkGen: Gen[UTXOSnapshotChunk] = for { - index: Short <- Arbitrary.arbitrary[Short] - stateElements: Seq[ErgoBox] <- Gen.listOf(ergoBoxGenNoProp) - } yield UTXOSnapshotChunk(stateElements, index) + index: Int <- Arbitrary.arbitrary[Int] + } yield UTXOSnapshotChunk(null, index) lazy val invalidErgoFullBlockGen: Gen[ErgoFullBlock] = for { header <- defaultHeaderGen From 5673b2cbe899558226b45ac7ceb0b67526160d9e Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Fri, 5 Nov 2021 22:21:21 +0300 Subject: [PATCH 006/204] get tree stats via traversal --- .../org/ergoplatform/examples/UtxoSnapshotExample.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala index d5d355e3df..45e4333500 100644 --- a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala +++ b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala @@ -22,7 +22,7 @@ object UtxoSnapshotExample extends App { implicit val hf: HF = Algos.hash val serializer = new BatchAVLProverSerializer[Digest32, HF] - /* + /** * Count number of leaves and their cumulative size (so total weight of boxes) * @@ -30,7 +30,7 @@ object UtxoSnapshotExample extends App { * @return */ - private def leafDataStats(prover: BatchAVLProver[hash.Digest32, HF]): (Long, Long) = { + private def leafDataStats(prover: BatchAVLProver[scorex.crypto.hash.Digest32, HF]): (Long, Long) = { def step(node: ProverNodes[Digest32], acc: (Long, Long)): (Long, Long) = { @@ -48,10 +48,12 @@ object UtxoSnapshotExample extends App { } + val ss0 = System.currentTimeMillis() val (elems, valSize) = leafDataStats(state.persistentProver.prover()) + val ss = System.currentTimeMillis() + println("time to traverse the tree: " + (ss - ss0) + " ms.") println("elems: " + elems) println("boxes total bytes count: " + valSize) - */ println("height: " + state.persistentProver.height) From d17b9e3c8d1d13a7d09215791e2cdacd3512d302 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 11 Nov 2021 14:47:21 +0300 Subject: [PATCH 007/204] utxo snapshot entities rework #2 --- avldb/build.sbt | 6 ++---- .../modifiers/state/UTXOSnapshotChunk.scala | 13 ++++++------- .../modifiers/state/UTXOSnapshotManifest.scala | 18 +++++++++++------- .../generators/ErgoTransactionGenerators.scala | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/avldb/build.sbt b/avldb/build.sbt index 6b512bf12e..ddf67f56e0 100644 --- a/avldb/build.sbt +++ b/avldb/build.sbt @@ -1,11 +1,9 @@ -import sbt.Keys.testFrameworks - name := "avldb" libraryDependencies ++= Seq( "javax.xml.bind" % "jaxb-api" % "2.4.0-b180830.0359", "ch.qos.logback" % "logback-classic" % "1.2.3", - "org.scorexfoundation" %% "scrypto" % "2.1.10" + "org.scorexfoundation" %% "scrypto" % "2.1.10-34-40ab5fdd-SNAPSHOT" ) libraryDependencies ++= Seq( @@ -20,7 +18,7 @@ libraryDependencies ++= Seq( ) testOptions in Test := Seq(Tests.Filter(t => !t.matches(".*Benchmark$"))) -javaOptions in run += "-Xmx12G" +javaOptions in run += "-Xmx6G" //scalacOptions ++= Seq("-Xdisable-assertions") diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala index 117efbd941..6b75fd013b 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala @@ -2,7 +2,6 @@ package org.ergoplatform.modifiers.state import org.ergoplatform.ErgoBox import org.ergoplatform.modifiers.ErgoPersistentModifier -import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSubtree @@ -12,17 +11,17 @@ import scorex.util.{ModifierId, bytesToId} /** * Container for a chunk of sliced AVL+ tree */ -case class UTXOSnapshotChunk(subTree: Either[BatchAVLProverSubtree[Digest32, Algos.HF], Array[Byte]], - index: Int) extends ErgoPersistentModifier { +case class UTXOSnapshotChunk(subTree: Either[BatchAVLProverSubtree[Digest32], Array[Byte]]) + extends ErgoPersistentModifier { + override val modifierTypeId: ModifierTypeId = UTXOSnapshotChunk.modifierTypeId - lazy val subtreeDeserialized: BatchAVLProverSubtree[Digest32, Algos.HF] = subTree match { + lazy val subtreeDeserialized: BatchAVLProverSubtree[Digest32] = subTree match { case Left(st) => st - case Right(_) => ??? //todo: exception may happen here + case Right(_) => ??? //todo: exception may happen here if bytes } - //TODO implement correctly - override lazy val id: ModifierId = bytesToId(subtreeDeserialized.subtreeTop.label) + override lazy val id: ModifierId = bytesToId(subtreeDeserialized.id) override def parentId: ModifierId = ??? diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala index d4a4af14a9..8bc1d0bb9c 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala @@ -5,17 +5,21 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer -import scorex.core.utils.concatBytes -import scorex.crypto.authds.LeafData +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest +import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util.{ModifierId, bytesToId, idToBytes} import scala.util.Try -case class UTXOSnapshotManifest(chunkRootHashes: Seq[Array[Byte]], blockId: ModifierId) extends ErgoPersistentModifier { +case class UTXOSnapshotManifest(blockId: ModifierId, + utxoSetDigest: ADDigest, //33 bytes! extra byte with tree height here! + manifest: BatchAVLProverManifest[Digest32] + ) extends ErgoPersistentModifier { + override val modifierTypeId: ModifierTypeId = UTXOSnapshotManifest.modifierTypeId - override def serializedId: Array[Byte] = Algos.hash(concatBytes(chunkRootHashes :+ idToBytes(blockId))) + override lazy val serializedId: Array[Byte] = Algos.hash(idToBytes(blockId) ++ manifest.id) override lazy val id: ModifierId = bytesToId(serializedId) @@ -25,18 +29,18 @@ case class UTXOSnapshotManifest(chunkRootHashes: Seq[Array[Byte]], blockId: Modi override def parentId: ModifierId = ??? - lazy val rootHash: Digest32 = Algos.merkleTreeRoot(LeafData @@ chunkRootHashes) - override val sizeOpt: Option[Int] = None + } object UTXOSnapshotManifest { + val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (106: Byte) def validate(manifest: UTXOSnapshotManifest, header: Header): Try[Unit] = Try { require(manifest.blockId == header.id) - require(java.util.Arrays.equals(manifest.rootHash, header.stateRoot)) ??? } + } diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index a31d23c12b..74e11fc968 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -285,7 +285,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { // todo: fix generator lazy val randomUTXOSnapshotChunkGen: Gen[UTXOSnapshotChunk] = for { index: Int <- Arbitrary.arbitrary[Int] - } yield UTXOSnapshotChunk(null, index) + } yield UTXOSnapshotChunk(null) lazy val invalidErgoFullBlockGen: Gen[ErgoFullBlock] = for { header <- defaultHeaderGen From 956388d1388e2cb0eb7afa7d2e6e0f1d48d5180f Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 11 Nov 2021 17:55:21 +0300 Subject: [PATCH 008/204] estimatedTip parameter --- .../nodeView/state/UtxoStateBenchmark.scala | 2 +- .../nodeView/ErgoNodeViewHolder.scala | 25 ++++++++-- .../modifierprocessors/HeadersProcessor.scala | 5 +- .../nodeView/state/DigestState.scala | 3 +- .../nodeView/state/ErgoState.scala | 3 +- .../nodeView/state/UtxoState.scala | 3 +- .../local/MempoolAuditorSpec.scala | 4 +- .../mining/CandidateGeneratorPropSpec.scala | 16 +++---- .../nodeView/mempool/ErgoMemPoolSpec.scala | 18 +++---- .../nodeView/mempool/ScriptsSpec.scala | 2 +- .../state/DigestStateSpecification.scala | 14 +++--- .../state/ErgoStateSpecification.scala | 14 +++--- .../state/UtxoStateSpecification.scala | 48 +++++++++---------- .../state/wrapped/WrappedDigestState.scala | 5 +- .../state/wrapped/WrappedUtxoState.scala | 35 +++++++------- .../viewholder/ErgoNodeViewHolderSpec.scala | 18 +++---- .../viewholder/PrunedNodeViewHolderSpec.scala | 2 +- .../ergoplatform/tools/ChainGenerator.scala | 2 +- .../state/StateApplicationTest.scala | 16 +++---- 19 files changed, 130 insertions(+), 105 deletions(-) diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala index 915773c7a5..5e19ce2fd9 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala @@ -25,7 +25,7 @@ object UtxoStateBenchmark extends HistoryTestHelpers with NVBenchmark { val state = ErgoState.generateGenesisUtxoState(createTempDir, StateConstants(None, realNetworkSetting))._1 Utils.time { mods.foldLeft(state) { case (st, mod) => - st.applyModifier(mod).get + st.applyModifier(mod, None).get } }.toLong } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 5552590df0..d76c0edd25 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -8,6 +8,7 @@ import org.ergoplatform.ErgoApp import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.ErgoApp.CriticalSystemException +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} @@ -207,6 +208,14 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } } + private def estimatedTip(): Option[Height] = { + if(history.isHeadersChainSynced) { + Some(history.headersHeight) + } else { + None + } + } + private def applyState(history: ErgoHistory, stateToApply: State, suffixTrimmed: IndexedSeq[ErgoPersistentModifier], @@ -218,7 +227,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti f case (success@Success(updateInfo), modToApply) => if (updateInfo.failedMod.isEmpty) { - updateInfo.state.applyModifier(modToApply) match { + updateInfo.state.applyModifier(modToApply, estimatedTip()) match { case Success(stateAfterApply) => history.reportModifierIsValid(modToApply).map { newHis => context.system.eventStream.publish(SemanticallySuccessfulModifier(modToApply)) @@ -407,7 +416,11 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti history.getFullBlock(h) .fold(throw new Error(s"Failed to get full block for header $h"))(fb => fb) } - toApply.foldLeft[Try[State]](Success(initState))((acc, m) => acc.flatMap(_.applyModifier(m))) + toApply.foldLeft[Try[State]](Success(initState)){ + (acc, block) => + log.info(s"Applying block ${block.height} during consistent state restoration") + acc.flatMap(_.applyModifier(block, estimatedTip())) + } } } @@ -437,12 +450,16 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti recoveredStateTry match { case Success(state) => log.info("Recovering state using current epoch") - chainToApply.foldLeft[Try[DigestState]](Success(state))((acc, m) => acc.flatMap(_.applyModifier(m))) + chainToApply.foldLeft[Try[DigestState]](Success(state)) { (acc, m) => + acc.flatMap(_.applyModifier(m, estimatedTip())) + } case Failure(exception) => // recover using whole headers chain log.warn(s"Failed to recover state from current epoch, using whole chain: ${exception.getMessage}") val wholeChain = history.headerChainBack(Int.MaxValue, bestFullBlock.header, _.isGenesis).headers val genesisState = DigestState.create(None, None, stateDir(settings), constants) - wholeChain.foldLeft[Try[DigestState]](Success(genesisState))((acc, m) => acc.flatMap(_.applyModifier(m))) + wholeChain.foldLeft[Try[DigestState]](Success(genesisState)){(acc, m) => + acc.flatMap(_.applyModifier(m, estimatedTip())) + } } } 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 5a8c8334f8..c79f6767a7 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 @@ -2,6 +2,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoApp.CriticalSystemException +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.mining.difficulty.LinearDifficultyControl import org.ergoplatform.modifiers.ErgoPersistentModifier @@ -67,12 +68,12 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score /** * @return height of best header */ - def headersHeight: Int = bestHeaderIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) + def headersHeight: Height = bestHeaderIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) /** * @return height of best header with all block sections */ - def fullBlockHeight: Int = bestFullBlockIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) + def fullBlockHeight: Height = bestFullBlockIdOpt.flatMap(id => heightOf(id)).getOrElse(ErgoHistory.EmptyHistoryHeight) /** * @param id - id of ErgoPersistentModifier diff --git a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala index 6d5dbdb64f..f87d4f9a7f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.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.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction @@ -79,7 +80,7 @@ class DigestState protected(override val version: VersionTag, Failure(new Exception(s"Modifier not validated: $a")) } - override def applyModifier(mod: ErgoPersistentModifier): Try[DigestState] = + override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[DigestState] = (processFullBlock orElse processHeader orElse processOther) (mod) @SuppressWarnings(Array("OptionGet")) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 797f32f123..87dfd007f0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -3,6 +3,7 @@ package org.ergoplatform.nodeView.state import java.io.File import org.ergoplatform.ErgoBox.{AdditionalRegisters, R4, TokenId} +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform._ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.mining.groupElemFromBytes @@ -41,7 +42,7 @@ trait ErgoState[IState <: ErgoState[IState]] extends ErgoStateReader { self: IState => - def applyModifier(mod: ErgoPersistentModifier): Try[IState] + def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[IState] def rollbackTo(version: VersionTag): Try[IState] diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index c11ab6605d..0dc2dd1a67 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -4,6 +4,7 @@ import java.io.File import cats.Traverse import org.ergoplatform.ErgoBox +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction @@ -95,7 +96,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } - override def applyModifier(mod: ErgoPersistentModifier): Try[UtxoState] = mod match { + override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[UtxoState] = mod match { case fb: ErgoFullBlock => persistentProver.synchronized { val height = fb.header.height diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index f55aa9dafb..e6bdf9a130 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -45,7 +45,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get applyBlock(genesis) shouldBe 'success getRootHash shouldBe Algos.encode(wusAfterGenesis.rootHash) @@ -92,7 +92,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH val (txs0, bh1) = validTransactionsFromBoxHolder(bh0) val b1 = validFullBlock(None, us0, txs0) - val us = us0.applyModifier(b1).get + val us = us0.applyModifier(b1, None).get val bxs = bh1.boxes.values.toList.filter(_.proposition != genesisEmissionBox.proposition) val txs = validTransactionsFromBoxes(200000, bxs, new Random())._1 diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index 4506d29a73..b6b43995c4 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -68,8 +68,8 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { defaultMinerPk ) - us.applyModifier(validFullBlock(None, us, incorrectTxs)) shouldBe 'failure - us.applyModifier(validFullBlock(None, us, txs)) shouldBe 'success + us.applyModifier(validFullBlock(None, us, incorrectTxs), None) shouldBe 'failure + us.applyModifier(validFullBlock(None, us, txs), None) shouldBe 'success } property("collect reward from transaction fees only") { @@ -94,8 +94,8 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { defaultMinerPk ) - us.applyModifier(validFullBlock(None, us, blockTx +: incorrect)) shouldBe 'failure - us.applyModifier(validFullBlock(None, us, blockTx +: txs)) shouldBe 'success + us.applyModifier(validFullBlock(None, us, blockTx +: incorrect), None) shouldBe 'failure + us.applyModifier(validFullBlock(None, us, blockTx +: txs), None) shouldBe 'success } property("filter out double spend txs") { @@ -218,7 +218,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { .toSeq val block = validFullBlock(None, us, blockTx +: txs) - us = us.applyModifier(block).get + us = us.applyModifier(block, None).get val blockTx2 = validTransactionFromBoxes(txBoxes(1), outputsProposition = feeProposition) @@ -230,9 +230,9 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { val invalidBlock2 = validFullBlock(Some(block), us, IndexedSeq(earlySpendingTx, blockTx2)) - us.applyModifier(invalidBlock2) shouldBe 'failure + us.applyModifier(invalidBlock2, None) shouldBe 'failure - us = us.applyModifier(block2).get + us = us.applyModifier(block2, None).get val earlySpendingTx2 = validTransactionFromBoxes(txs.head.outputs, stateCtxOpt = Some(us.stateContext)) @@ -241,7 +241,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { validTransactionFromBoxes(txBoxes(2), outputsProposition = feeProposition) val block3 = validFullBlock(Some(block2), us, IndexedSeq(earlySpendingTx2, blockTx3)) - us.applyModifier(block3) shouldBe 'success + us.applyModifier(block3, None) shouldBe 'success } property("collect reward from both emission box and fees") { diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index 818b8351e9..9ee8bc479a 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -21,7 +21,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "accept valid transaction" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val txs = validTransactionsFromUtxoState(wus, Random) val pool0 = ErgoMemPool.empty(settings) val poolAfter = txs.foldLeft(pool0) { case (pool, tx) => @@ -43,7 +43,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "decline already contained transaction" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val txs = validTransactionsFromUtxoState(wus, Random) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -59,7 +59,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec whenever(n1 != n2) { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val feeProp = settings.chainSettings.monetary.feeProposition val inputBox = wus.takeBoxes(1).head @@ -115,7 +115,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "decline transactions not meeting min fee" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val txs = validTransactionsFromUtxoState(wus, Random) val maxSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(minimalFeeAmount = Long.MaxValue)) @@ -173,7 +173,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "Accept output of pooled transactions" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val txs = validTransactionsFromUtxoState(wus, Random) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -191,7 +191,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "consider families for replacement policy" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get var txs = validTransactionsFromUtxoState(wus, Random) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -221,7 +221,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "correctly remove transaction from pool and rebuild families" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get var txs = validTransactionsFromUtxoState(wus, Random) var allTxs = txs val family_depth = 10 @@ -254,7 +254,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get var txs = validTransactionsFromUtxoState(wus, Random) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -295,7 +295,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "add removed transaction to mempool statistics" in { val (us, bh) = createUtxoState() val genesis = validFullBlock(None, us, bh, Random) - val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wus = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get var txs = validTransactionsFromUtxoState(wus, Random) var allTxs = txs val family_depth = 10 diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index 85a379db68..d77ad21855 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -80,6 +80,6 @@ class ScriptsSpec extends ErgoPropertyTest { assert(us.boxById(boxId).isDefined, s"Box ${Algos.encode(boxId)} missed") } val block = validFullBlock(None, us, tx, Some(1234L)) - us.applyModifier(block) + us.applyModifier(block, None) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala index 7128304bf6..251ae04b42 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala @@ -24,7 +24,7 @@ class DigestStateSpecification extends ErgoPropertyTest { val fb = validFullBlock(parentOpt = None, us, bh) val dir2 = createTempDir val ds = DigestState.create(Some(us.version), Some(us.rootHash), dir2, stateConstants) - ds.applyModifier(fb) shouldBe 'success + ds.applyModifier(fb, None) shouldBe 'success ds.close() val state = DigestState.create(None, None, dir2, stateConstants) @@ -42,8 +42,8 @@ class DigestStateSpecification extends ErgoPropertyTest { val blBh = validFullBlockWithBoxHolder(parentOpt, us, bh, new Random(seed)) val block = blBh._1 bh = blBh._2 - ds = ds.applyModifier(block).get - us = us.applyModifier(block).get + ds = ds.applyModifier(block, None).get + us = us.applyModifier(block, None).get parentOpt = Some(block) } } @@ -64,14 +64,14 @@ class DigestStateSpecification extends ErgoPropertyTest { block.blockTransactions.transactions.exists(_.dataInputs.nonEmpty) shouldBe true val ds = createDigestState(us.version, us.rootHash) - ds.applyModifier(block) shouldBe 'success + ds.applyModifier(block, None) shouldBe 'success } } property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => val state = createDigestState(emptyVersion, emptyAdDigest) - state.applyModifier(b).isFailure shouldBe true + state.applyModifier(b, None).isFailure shouldBe true } } @@ -86,7 +86,7 @@ class DigestStateSpecification extends ErgoPropertyTest { ds.rollbackVersions.size shouldEqual 1 - val ds2 = ds.applyModifier(block).get + val ds2 = ds.applyModifier(block, None).get ds2.rollbackVersions.size shouldEqual 2 @@ -99,7 +99,7 @@ class DigestStateSpecification extends ErgoPropertyTest { ds3.stateContext.lastHeaders.size shouldEqual 0 - ds3.applyModifier(block).get.rootHash shouldBe ds2.rootHash + ds3.applyModifier(block, None).get.rootHash shouldBe ds2.rootHash } } diff --git a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala index 75e1a37c37..0846d66433 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala @@ -28,11 +28,11 @@ class ErgoStateSpecification extends ErgoPropertyTest { val bt = BlockTransactions(dsHeader.id, version, dsTxs) val doubleSpendBlock = ErgoFullBlock(dsHeader, bt, validBlock.extension, validBlock.adProofs) - us.applyModifier(doubleSpendBlock) shouldBe 'failure - us.applyModifier(validBlock) shouldBe 'success + us.applyModifier(doubleSpendBlock, None) shouldBe 'failure + us.applyModifier(validBlock, None) shouldBe 'success - ds.applyModifier(doubleSpendBlock) shouldBe 'failure - ds.applyModifier(validBlock) shouldBe 'success + ds.applyModifier(doubleSpendBlock, None) shouldBe 'failure + ds.applyModifier(validBlock, None) shouldBe 'success } } @@ -55,8 +55,8 @@ class ErgoStateSpecification extends ErgoPropertyTest { val blBh = validFullBlockWithBoxHolder(lastBlocks.headOption, us, bh, new Random(seed)) val block = blBh._1 bh = blBh._2 - ds = ds.applyModifier(block).get - us = us.applyModifier(block).get + ds = ds.applyModifier(block, None).get + us = us.applyModifier(block, None).get lastBlocks = block +: lastBlocks requireEqualStateContexts(us.stateContext, ds.stateContext, lastBlocks.map(_.header)) } @@ -79,7 +79,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { val block = blBh._1 parentOpt = Some(block) bh = blBh._2 - us = us.applyModifier(block).get + us = us.applyModifier(block, None).get val changes1 = ErgoState.boxChanges(block.transactions) val changes2 = ErgoState.boxChanges(block.transactions) diff --git a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala index 8221a5fac4..bc32cbb325 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala @@ -35,7 +35,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera var (us, bh) = createUtxoState() var foundersBox = genesisBoxes.last var lastBlock = validFullBlock(parentOpt = None, us, bh) - us = us.applyModifier(lastBlock).get + us = us.applyModifier(lastBlock, None).get // spent founders box, leaving the same proposition (0 until 10) foreach { _ => @@ -50,7 +50,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val complexityLimit = initSettings.nodeSettings.maxTransactionComplexity us.validateWithCost(tx, None, complexityLimit).get should be <= 100000L val block1 = validFullBlock(Some(lastBlock), us, Seq(ErgoTransaction(tx))) - us = us.applyModifier(block1).get + us = us.applyModifier(block1, None).get foundersBox = tx.outputs.head lastBlock = block1 } @@ -80,7 +80,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val adProofs = ADProofs(realHeader.id, adProofBytes) val bt = BlockTransactions(realHeader.id, Header.InitialVersion, txs) val fb = ErgoFullBlock(realHeader, bt, genExtension(realHeader, us.stateContext), Some(adProofs)) - us = us.applyModifier(fb).get + us = us.applyModifier(fb, None).get val remaining = emission.remainingFoundationRewardAtHeight(height) // check validity of transaction, spending founders box @@ -130,7 +130,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera us.extractEmissionBox(block) should not be None lastBlockOpt = Some(block) bh = blBh._2 - us = us.applyModifier(block).get + us = us.applyModifier(block, None).get } } @@ -158,7 +158,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val adProofs = ADProofs(realHeader.id, adProofBytes) val bt = BlockTransactions(realHeader.id, 1: Byte, txs) val fb = ErgoFullBlock(realHeader, bt, genExtension(realHeader, us.stateContext), Some(adProofs)) - us = us.applyModifier(fb).get + us = us.applyModifier(fb, None).get height = height + 1 } } @@ -185,7 +185,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera height = height + 1 val bt = BlockTransactions(realHeader.id, Header.InitialVersion, txs) val fb = ErgoFullBlock(realHeader, bt, genExtension(realHeader, us.stateContext), Some(adProofs)) - us = us.applyModifier(fb).get + us = us.applyModifier(fb, None).get fb } // create new genesis state @@ -203,7 +203,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } // apply chain of headers full block to state chain.foreach { fb => - us2 = us2.applyModifier(fb).get + us2 = us2.applyModifier(fb, None).get } Await.result(f, Duration.Inf); } @@ -376,14 +376,14 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera bh.sortedBoxes.foreach(box => us.boxById(box.id) should not be None) val block = validFullBlock(parentOpt = None, us, bh) - us.applyModifier(block).get + us.applyModifier(block, None).get } } property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => val state = createUtxoState()._1 - state.applyModifier(b).isFailure shouldBe true + state.applyModifier(b, None).isFailure shouldBe true } } @@ -401,37 +401,37 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } val invalidBlock = validFullBlock(parentOpt = None, us2, bh2) - us.applyModifier(invalidBlock).isSuccess shouldBe false - us.applyModifier(validBlock).isSuccess shouldBe true + us.applyModifier(invalidBlock, None).isSuccess shouldBe false + us.applyModifier(validBlock, None).isSuccess shouldBe true } property("2 forks switching") { val (us, bh) = createUtxoState() val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get val chain1block1 = validFullBlock(Some(genesis), wusAfterGenesis) - val wusChain1Block1 = wusAfterGenesis.applyModifier(chain1block1).get + val wusChain1Block1 = wusAfterGenesis.applyModifier(chain1block1, None).get val chain1block2 = validFullBlock(Some(chain1block1), wusChain1Block1) val (us2, bh2) = createUtxoState() - val wus2AfterGenesis = WrappedUtxoState(us2, bh2, stateConstants).applyModifier(genesis).get + val wus2AfterGenesis = WrappedUtxoState(us2, bh2, stateConstants).applyModifier(genesis, None).get val chain2block1 = validFullBlock(Some(genesis), wus2AfterGenesis) - val wusChain2Block1 = wus2AfterGenesis.applyModifier(chain2block1).get + val wusChain2Block1 = wus2AfterGenesis.applyModifier(chain2block1, None).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) var (state, _) = createUtxoState() - state = state.applyModifier(genesis).get + state = state.applyModifier(genesis, None).get - state = state.applyModifier(chain1block1).get + state = state.applyModifier(chain1block1, None).get state = state.rollbackTo(idToVersion(genesis.id)).get - state = state.applyModifier(chain2block1).get - state = state.applyModifier(chain2block2).get + state = state.applyModifier(chain2block1, None).get + state = state.applyModifier(chain2block2, None).get state = state.rollbackTo(idToVersion(genesis.id)).get - state = state.applyModifier(chain1block1).get - state = state.applyModifier(chain1block2).get + state = state.applyModifier(chain1block1, None).get + state = state.applyModifier(chain1block2, None).get } @@ -441,14 +441,14 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val us = createUtxoState(bh) bh.sortedBoxes.foreach(box => us.boxById(box.id) should not be None) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get wusAfterGenesis.rootHash shouldEqual genesis.header.stateRoot val (finalState: WrappedUtxoState, chain: Seq[ErgoFullBlock]) = (0 until depth) .foldLeft((wusAfterGenesis, Seq(genesis))) { (sb, _) => val state = sb._1 val block = validFullBlock(parentOpt = Some(sb._2.last), state) - (state.applyModifier(block).get, sb._2 ++ Seq(block)) + (state.applyModifier(block, None).get, sb._2 ++ Seq(block)) } val finalRoot = finalState.rootHash finalRoot shouldEqual chain.last.header.stateRoot @@ -457,7 +457,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera rollbackedState.rootHash shouldEqual genesis.header.stateRoot val finalState2: WrappedUtxoState = chain.tail.foldLeft(rollbackedState) { (state, block) => - state.applyModifier(block).get + state.applyModifier(block, None).get } finalState2.rootHash shouldEqual finalRoot 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 fe1829d977..3e0deb09cb 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala @@ -1,5 +1,6 @@ package org.ergoplatform.nodeView.state.wrapped +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.ErgoPersistentModifier import org.ergoplatform.nodeView.state.DigestState import org.ergoplatform.settings.ErgoSettings @@ -12,8 +13,8 @@ class WrappedDigestState(val digestState: DigestState, val settings: ErgoSettings) extends DigestState(digestState.version, digestState.rootHash, digestState.store, settings) { - override def applyModifier(mod: ErgoPersistentModifier): Try[WrappedDigestState] = { - wrapped(super.applyModifier(mod), wrappedUtxoState.applyModifier(mod)) + override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[WrappedDigestState] = { + wrapped(super.applyModifier(mod, estimatedTip), wrappedUtxoState.applyModifier(mod, estimatedTip)) } override def rollbackTo(version: VersionTag): Try[WrappedDigestState] = { 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 ca52a31178..fb0d64e083 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala @@ -4,6 +4,7 @@ import java.io.File import akka.actor.ActorRef import org.ergoplatform.ErgoBox +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.ErgoPersistentModifier import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.ErgoSettings @@ -33,23 +34,25 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], case Failure(e) => Failure(e) } - override def applyModifier(mod: ErgoPersistentModifier): Try[WrappedUtxoState] = super.applyModifier(mod) match { - case Success(us) => - mod match { - case ct: TransactionsCarryingPersistentNodeViewModifier => - // You can not get block with transactions not being of ErgoTransaction type so no type checks here. + override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[WrappedUtxoState] = { + super.applyModifier(mod, estimatedTip) match { + case Success(us) => + mod match { + case ct: TransactionsCarryingPersistentNodeViewModifier => + // You can not get block with transactions not being of ErgoTransaction type so no type checks here. - val changes = ErgoState.stateChanges(ct.transactions) - val updHolder = versionedBoxHolder.applyChanges( - us.version, - changes.toRemove.map(_.boxId).map(ByteArrayWrapper.apply), - changes.toAppend.map(_.box)) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) - case _ => - val updHolder = versionedBoxHolder.applyChanges(us.version, Seq(), Seq()) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) - } - case Failure(e) => Failure(e) + val changes = ErgoState.stateChanges(ct.transactions) + val updHolder = versionedBoxHolder.applyChanges( + us.version, + changes.toRemove.map(_.boxId).map(ByteArrayWrapper.apply), + changes.toAppend.map(_.box)) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + case _ => + val updHolder = versionedBoxHolder.applyChanges(us.version, Seq(), Seq()) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + } + case Failure(e) => Failure(e) + } } } diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala index da628cc920..646e69c310 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala @@ -72,7 +72,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with import fixture._ val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get applyBlock(genesis) shouldBe 'success val block = validFullBlock(Some(genesis), wusAfterGenesis) @@ -108,13 +108,13 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with import fixture._ val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get // TODO looks like another bug is still present here, see https://github.com/ergoplatform/ergo/issues/309 if (verifyTransactions) { applyBlock(genesis) shouldBe 'success val block = validFullBlock(Some(genesis), wusAfterGenesis) - val wusAfterBlock = wusAfterGenesis.applyModifier(block).get + val wusAfterBlock = wusAfterGenesis.applyModifier(block, None).get applyBlock(block) shouldBe 'success getBestHeaderOpt shouldBe Some(block.header) @@ -160,7 +160,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with import fixture._ val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get applyBlock(genesis) shouldBe 'success getRootHash shouldBe Algos.encode(wusAfterGenesis.rootHash) @@ -176,7 +176,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with getBestFullBlockOpt shouldBe expectedBestFullBlockOpt getBestHeaderOpt shouldBe Some(chain1block1.header) - val wusChain2Block1 = wusAfterGenesis.applyModifier(chain2block1).get + val wusChain2Block1 = wusAfterGenesis.applyModifier(chain2block1, None).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) chain2block1.header.stateRoot shouldEqual wusChain2Block1.rootHash @@ -208,7 +208,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with import fixture._ val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get applyBlock(genesis) shouldBe 'success val block1 = validFullBlock(Some(genesis), wusAfterGenesis) @@ -228,13 +228,13 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with import fixture._ val (us, bh) = createUtxoState(Some(nodeViewHolderRef)) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis).get + val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants).applyModifier(genesis, None).get applyBlock(genesis) shouldBe 'success getRootHash shouldBe Algos.encode(wusAfterGenesis.rootHash) val chain2block1 = validFullBlock(Some(genesis), wusAfterGenesis) - val wusChain2Block1 = wusAfterGenesis.applyModifier(chain2block1).get + val wusChain2Block1 = wusAfterGenesis.applyModifier(chain2block1, None).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -387,7 +387,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps with getBestFullBlockOpt shouldBe expectedBestFullBlockOpt getBestHeaderOpt shouldBe Some(chain1block1.header) - val wusChain2Block1 = wusGenesis.applyModifier(chain2block1).get + val wusChain2Block1 = wusGenesis.applyModifier(chain2block1, None).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) chain2block1.header.stateRoot shouldEqual wusChain2Block1.rootHash diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala index 40b7d7d03e..eb1ae6392e 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala @@ -39,7 +39,7 @@ class PrunedNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps wit (1 to howMany).foldLeft((Seq[ErgoFullBlock](), genesisState, None: Option[ErgoFullBlock])) { case ((chain, wus, parentOpt), h) => val time = System.currentTimeMillis() - (howMany - h) * (BlockInterval.toMillis * 20) val block = validFullBlock(parentOpt, wus, time) - val newState = wus.applyModifier(block).get + val newState = wus.applyModifier(block, None).get (chain :+ block, newState, Some(block)) }._1 } diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index f2104fcdec..216ca6e4b6 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -111,7 +111,7 @@ object ChainGenerator extends App with ErgoTestHelpers { log.info( s"Block ${block.id} with ${block.transactions.size} transactions at height ${block.header.height} generated") - loop(state.applyModifier(block).get, outToPassNext, Some(block.header), acc :+ block.id) + loop(state.applyModifier(block, None).get, outToPassNext, Some(block.header), acc :+ block.id) } else { acc } diff --git a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala index 4a59394421..262900e0fc 100644 --- a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala +++ b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala @@ -21,7 +21,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { property(propertyNameGenerator("apply modifier")) { forAll(stateGenWithValidModifier) { case (s, m) => val ver = s.version - val sTry = s.applyModifier(m) + val sTry = s.applyModifier(m, None) sTry.isSuccess shouldBe true sTry.get.version == ver shouldBe false } @@ -30,17 +30,17 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { property(propertyNameGenerator("do not apply same valid modifier twice")) { forAll(stateGenWithValidModifier) { case (s, m) => val ver = s.version - val sTry = s.applyModifier(m) + val sTry = s.applyModifier(m, None) sTry.isSuccess shouldBe true val s2 = sTry.get s2.version == ver shouldBe false - s2.applyModifier(m).isSuccess 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) + val sTry = s.applyModifier(m, None) sTry.isSuccess shouldBe false } } @@ -48,7 +48,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { property(propertyNameGenerator("apply valid modifier after rollback")) { forAll(stateGenWithValidModifier) { case (s, m) => val ver = s.version - val sTry = s.applyModifier(m) + val sTry = s.applyModifier(m, None) sTry.isSuccess shouldBe true val s2 = sTry.get s2.version == ver shouldBe false @@ -57,7 +57,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { val s3 = s2.rollbackTo(ver).get s3.version == ver shouldBe true - val sTry2 = s3.applyModifier(m) + val sTry2 = s3.applyModifier(m, None) sTry2.isSuccess shouldBe true val s4 = sTry2.get s4.version == ver shouldBe false @@ -82,7 +82,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { val s2 = (0 until rollbackDepth).foldLeft(s) { case (state, _) => val modifier = semanticallyValidModifier(state) buf += modifier - val sTry = state.applyModifier(modifier) + val sTry = state.applyModifier(modifier, None) sTry shouldBe 'success sTry.get } @@ -94,7 +94,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { s3.version == ver shouldBe true val s4 = buf.foldLeft(s3) { case (state, m) => - val sTry = state.applyModifier(m) + val sTry = state.applyModifier(m, None) sTry shouldBe 'success sTry.get } From 72eba60d3370f7b2671d691c5778b6e4812a7343 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 11 Nov 2021 18:07:00 +0300 Subject: [PATCH 009/204] save snapshot condition --- .../scala/org/ergoplatform/nodeView/state/UtxoState.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 0dc2dd1a67..cbc678a5e6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -133,6 +133,10 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 Failure(e) } + if (estimatedTip.nonEmpty && (fb.height % 51480 == 0) && fb.height - estimatedTip.get <= 51480) { + // todo: save snapshot + } + } case h: Header => From dcab0a33976b93e25fdcc37108d0745aa1de567a Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 11 Nov 2021 22:30:14 +0300 Subject: [PATCH 010/204] saveSnapshotIfNeeded stub --- .../nodeView/state/UtxoState.scala | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index cbc678a5e6..3c976649d5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -19,6 +19,7 @@ import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} @@ -96,6 +97,21 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } + private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { + val SnapshotEvery = 20 // test value, switch to 51840 after testing + + if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && height - estimatedTip.get <= SnapshotEvery) { + + val serializer = new BatchAVLProverSerializer[Digest32, HF] + val (manifest, subtrees) = serializer.slice(persistentProver.avlProver, subtreeDepth = 12) + + val manifestBytes = serializer.manifestToBytes(manifest) + println("manifest size: " + manifestBytes.length) + println("subtrees count: " + subtrees.size) + // todo: save manifest and subtrees into a database + } + } + override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[UtxoState] = mod match { case fb: ErgoFullBlock => persistentProver.synchronized { @@ -120,8 +136,10 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } else if (!java.util.Arrays.equals(fb.header.stateRoot, persistentProver.digest)) { throw new Error("Calculated stateRoot is not equal to the declared one") } + log.info(s"Valid modifier with header ${fb.header.encodedId} and emission box " + s"${emissionBox.map(e => Algos.encode(e.id))} applied to UtxoState at height ${fb.header.height}") + saveSnapshotIfNeeded(fb.height, estimatedTip) new UtxoState(persistentProver, idToVersion(fb.id), store, constants) } } @@ -132,11 +150,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 .ensuring(java.util.Arrays.equals(persistentProver.digest, inRoot)) Failure(e) } - - if (estimatedTip.nonEmpty && (fb.height % 51480 == 0) && fb.height - estimatedTip.get <= 51480) { - // todo: save snapshot - } - } case h: Header => From 72c91f974b639a59fef95358e629a1943f26e4e2 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Sat, 13 Nov 2021 22:11:57 +0300 Subject: [PATCH 011/204] comparing common subtrees experiment --- .../nodeView/state/UtxoState.scala | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 3c976649d5..b19cd0e422 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -19,10 +19,11 @@ import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer} import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} +import scorex.util.encode.Base16 import scala.util.{Failure, Success, Try} @@ -100,7 +101,11 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { val SnapshotEvery = 20 // test value, switch to 51840 after testing - if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && height - estimatedTip.get <= SnapshotEvery) { + var prevManifest: BatchAVLProverManifest[Digest32] = null + + if (estimatedTip.nonEmpty && + (height % SnapshotEvery == 0) && + height - estimatedTip.get <= SnapshotEvery) { val serializer = new BatchAVLProverSerializer[Digest32, HF] val (manifest, subtrees) = serializer.slice(persistentProver.avlProver, subtreeDepth = 12) @@ -109,6 +114,17 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 println("manifest size: " + manifestBytes.length) println("subtrees count: " + subtrees.size) // todo: save manifest and subtrees into a database + + if (prevManifest != null) { + val prevSubtrees = prevManifest.subtreesIds.map(Base16.encode).toSet + val subtrees = manifest.subtreesIds.map(Base16.encode).toSet + + val common = subtrees.map { id => + if (prevSubtrees.contains(id)) 1 else 0 + }.sum + println("Subtrees not changed: " + common) + } + prevManifest = manifest } } From e6924aadf1080f369391cfe126d0a0f7b21a0b72 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Sat, 13 Nov 2021 23:15:18 +0300 Subject: [PATCH 012/204] starting SnapshotsDb --- .../state/UTXOSnapshotManifest.scala | 5 ++- .../nodeView/state/SnapshotsInfo.scala | 35 +++++++++++++++++++ .../nodeView/state/StateConstants.scala | 2 +- .../nodeView/state/UtxoState.scala | 9 +++-- 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala index 8bc1d0bb9c..7cdf8bd5c2 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala @@ -2,6 +2,7 @@ package org.ergoplatform.modifiers.state import org.ergoplatform.modifiers.ErgoPersistentModifier import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer @@ -12,7 +13,9 @@ import scorex.util.{ModifierId, bytesToId, idToBytes} import scala.util.Try -case class UTXOSnapshotManifest(blockId: ModifierId, +case class UTXOSnapshotManifest( + height: Height, + blockId: ModifierId, utxoSetDigest: ADDigest, //33 bytes! extra byte with tree height here! manifest: BatchAVLProverManifest[Digest32] ) extends ErgoPersistentModifier { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala new file mode 100644 index 0000000000..4a1b49681a --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -0,0 +1,35 @@ +package org.ergoplatform.nodeView.state + +import java.io.File + +import com.google.common.primitives.{Bytes, Ints} +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.ManifestId +import scorex.db.LDBKVStore + +case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) + +class SnapshotsDb(store: LDBKVStore) { + + private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { + Bytes.concat( + snapshotsInfo.availableManifests.map{case (h, manifestId) => + Bytes.concat(Ints.toByteArray(h), manifestId) + } + ) + } + + def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { + store.insert() + } + +} + +object SnapshotsDb { + + + def create(dir: File): Unit ={ + val store = new LDBKVStore(dir) + SnapshotsDb(store) + } +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala b/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala index 5547a574c1..2cadaf5420 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala @@ -5,7 +5,7 @@ import org.ergoplatform.settings.{ErgoSettings, VotingSettings} import scorex.crypto.authds.ADDigest /** - * Constants that do not change with state version changes + * Constants that do not change when state version changes * * @param nodeViewHolderRef - actor ref of node view holder * @param settings - node settings diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index b19cd0e422..bf0bcbdf46 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -19,7 +19,7 @@ import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer} +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} @@ -101,7 +101,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { val SnapshotEvery = 20 // test value, switch to 51840 after testing - var prevManifest: BatchAVLProverManifest[Digest32] = null + var prevManifest: UtxoState.Manifest = null if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && @@ -191,6 +191,11 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 object UtxoState { + type Manifest = BatchAVLProverManifest[Digest32] + type Subtree = BatchAVLProverSubtree[Digest32] + + type ManifestId = Digest32 + private lazy val bestVersionKey = Algos.hash("best state version") val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") From e4f569c8678aab47d6bf6e53817472fb876f0e67 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 15 Nov 2021 12:53:54 +0300 Subject: [PATCH 013/204] estimatedTip fix, postponing wallet scanning --- .../nodeView/ErgoNodeViewHolder.scala | 4 +- .../nodeView/state/SnapshotsInfo.scala | 17 ++++---- .../nodeView/state/UtxoState.scala | 19 ++++----- .../nodeView/wallet/ErgoWalletActor.scala | 42 ++++++++++--------- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index d76c0edd25..e6e33271bd 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -208,13 +208,13 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } } - private def estimatedTip(): Option[Height] = { + private def estimatedTip(): Option[Height] = Try { //error may happen if history not initialized if(history.isHeadersChainSynced) { Some(history.headersHeight) } else { None } - } + }.getOrElse(None) private def applyState(history: ErgoHistory, stateToApply: State, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 4a1b49681a..d9d4a7e66c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -1,11 +1,10 @@ package org.ergoplatform.nodeView.state -import java.io.File - import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId -import scorex.db.LDBKVStore +import org.ergoplatform.settings.ErgoSettings +import scorex.db.{LDBFactory, LDBKVStore} case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) @@ -15,21 +14,19 @@ class SnapshotsDb(store: LDBKVStore) { Bytes.concat( snapshotsInfo.availableManifests.map{case (h, manifestId) => Bytes.concat(Ints.toByteArray(h), manifestId) - } + }.toSeq :_* ) } def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { - store.insert() + snapshotsInfoToBytes(null) } } object SnapshotsDb { - - - def create(dir: File): Unit ={ - val store = new LDBKVStore(dir) - SnapshotsDb(store) + def create(ergoSettings: ErgoSettings): Unit ={ + val store = LDBFactory.createKvDb(s"${ergoSettings.directory}/snapshots") + new SnapshotsDb(store) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index bf0bcbdf46..bc0a6e8ffe 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -99,13 +99,11 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val SnapshotEvery = 20 // test value, switch to 51840 after testing - - var prevManifest: UtxoState.Manifest = null + val SnapshotEvery = 10 // test value, switch to 51840 after testing if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && - height - estimatedTip.get <= SnapshotEvery) { + estimatedTip.get - height <= SnapshotEvery) { val serializer = new BatchAVLProverSerializer[Digest32, HF] val (manifest, subtrees) = serializer.slice(persistentProver.avlProver, subtreeDepth = 12) @@ -115,16 +113,14 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 println("subtrees count: " + subtrees.size) // todo: save manifest and subtrees into a database - if (prevManifest != null) { - val prevSubtrees = prevManifest.subtreesIds.map(Base16.encode).toSet + if (UtxoState.prevManifest != null) { + val prevSubtrees = UtxoState.prevManifest.subtreesIds.map(Base16.encode).toSet val subtrees = manifest.subtreesIds.map(Base16.encode).toSet - val common = subtrees.map { id => - if (prevSubtrees.contains(id)) 1 else 0 - }.sum + val common = subtrees.count(prevSubtrees.contains) println("Subtrees not changed: " + common) } - prevManifest = manifest + UtxoState.prevManifest = manifest } } @@ -191,6 +187,9 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 object UtxoState { + //todo: remove after experiment + var prevManifest: UtxoState.Manifest = null + type Manifest = BatchAVLProverManifest[Digest32] type Subtree = BatchAVLProverSubtree[Digest32] diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index 6919345ddb..26f32c655d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -197,26 +197,28 @@ class ErgoWalletActor(settings: ErgoSettings, context.become(loadedWallet(newState)) case ScanInThePast(blockHeight) => - val nextBlockHeight = state.expectedNextBlockHeight(blockHeight, settings.nodeSettings.isFullBlocksPruned) - if (nextBlockHeight == blockHeight) { - val newState = - historyReader.bestFullBlockAt(blockHeight) match { - case Some(block) => - log.info(s"Wallet is going to scan a block ${block.id} in the past at height ${block.height}") - ergoWalletService.scanBlockUpdate(state, block) match { - case Failure(ex) => - val errorMsg = s"Scanning block ${block.id} at height $blockHeight failed : ${ex.getMessage}" - log.error(errorMsg, ex) - state.copy(error = Some(errorMsg)) - case Success(updatedState) => - updatedState - } - case None => - state // We may do not have a block if, for example, the blockchain is pruned. This is okay, just skip it. - } - context.become(loadedWallet(newState)) - if (blockHeight < newState.fullHeight) { - self ! ScanInThePast(blockHeight + 1) + if(state.fullHeight >= historyReader.headersHeight - 10) { //todo: move this to a separate PR, test + val nextBlockHeight = state.expectedNextBlockHeight(blockHeight, settings.nodeSettings.isFullBlocksPruned) + if (nextBlockHeight == blockHeight) { + val newState = + historyReader.bestFullBlockAt(blockHeight) match { + case Some(block) => + log.info(s"Wallet is going to scan a block ${block.id} in the past at height ${block.height}") + ergoWalletService.scanBlockUpdate(state, block) match { + case Failure(ex) => + val errorMsg = s"Scanning block ${block.id} at height $blockHeight failed : ${ex.getMessage}" + log.error(errorMsg, ex) + state.copy(error = Some(errorMsg)) + case Success(updatedState) => + updatedState + } + case None => + state // We may do not have a block if, for example, the blockchain is pruned. This is okay, just skip it. + } + context.become(loadedWallet(newState)) + if (blockHeight < newState.fullHeight) { + self ! ScanInThePast(blockHeight + 1) + } } } From d2ab04e1e00beb4d7db9f0a86497d46b3a378f98 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 18 Nov 2021 01:06:39 +0300 Subject: [PATCH 014/204] writeSnapshotsInfo / readSnapshotsInfo --- avldb/build.sbt | 2 +- .../nodeView/state/SnapshotsInfo.scala | 39 +++++++++++++++++-- .../state/SnapshotsDbSpecification.scala | 26 +++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala diff --git a/avldb/build.sbt b/avldb/build.sbt index ddf67f56e0..75163f7b57 100644 --- a/avldb/build.sbt +++ b/avldb/build.sbt @@ -3,7 +3,7 @@ name := "avldb" libraryDependencies ++= Seq( "javax.xml.bind" % "jaxb-api" % "2.4.0-b180830.0359", "ch.qos.logback" % "logback-classic" % "1.2.3", - "org.scorexfoundation" %% "scrypto" % "2.1.10-34-40ab5fdd-SNAPSHOT" + "org.scorexfoundation" %% "scrypto" % "2.2.0-0-ffcdb031-20211118-0034-SNAPSHOT" ) libraryDependencies ++= Seq( diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index d9d4a7e66c..2beee56690 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -4,12 +4,17 @@ import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.settings.ErgoSettings +import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} +import scala.util.Try + case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) class SnapshotsDb(store: LDBKVStore) { + private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) + private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { Bytes.concat( snapshotsInfo.availableManifests.map{case (h, manifestId) => @@ -18,15 +23,41 @@ class SnapshotsDb(store: LDBKVStore) { ) } - def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { - snapshotsInfoToBytes(null) + private def snapshotsInfoFromBytes(bytes: Array[Byte]): SnapshotsInfo = { + val manifests = bytes.grouped(36).map {rowBytes => + val height = Ints.fromByteArray(rowBytes.take(4)) + val manifestId = Digest32 @@ rowBytes.drop(4) + height -> manifestId + }.toMap + SnapshotsInfo(manifests) + } + + def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { + store.insert(Seq(snapshotInfoKey -> snapshotsInfoToBytes(snapshotsInfo))) + } + + def readSnapshotsInfo: Option[SnapshotsInfo] = { + store.get(snapshotInfoKey).map(snapshotsInfoFromBytes) + } + + def writeSnapshot(height: Height, + manifest: UtxoState.Manifest, + subtrees: Seq[UtxoState.Subtree]): Unit = { + ??? } } object SnapshotsDb { - def create(ergoSettings: ErgoSettings): Unit ={ - val store = LDBFactory.createKvDb(s"${ergoSettings.directory}/snapshots") + + def create(ergoSettings: ErgoSettings): Unit = { + val dir = s"${ergoSettings.directory}/snapshots" + create(dir) + } + + private[state] def create(dir: String): SnapshotsDb = { + val store = LDBFactory.createKvDb(dir) new SnapshotsDb(store) } + } diff --git a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala new file mode 100644 index 0000000000..15107ffe08 --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala @@ -0,0 +1,26 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.utils.ErgoPropertyTest +import org.scalacheck.Gen +import scorex.crypto.hash.Digest32 +import scorex.util.{idToBytes, bytesToId} +import scala.util.Random + +class SnapshotsDbSpecification extends ErgoPropertyTest { + property("snapshotsInfo round-trip") { + forAll(Gen.nonEmptyListOf(modifierIdGen)){manifestIds => + val m = manifestIds.map{mid => + Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mid)) + }.toMap + val si = SnapshotsInfo(m) + val dir = createTempDir.getAbsolutePath + val db = SnapshotsDb.create(dir) + db.writeSnapshotsInfo(si) + + val read = db.readSnapshotsInfo.get.availableManifests.mapValues(bs => bytesToId(bs)) + val siTocompare = si.availableManifests.mapValues(bs => bytesToId(bs)) + + read shouldBe siTocompare + } + } +} From 229b55bb75ffa63180947349f9d47c04563e06ab Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 18 Nov 2021 12:34:07 +0300 Subject: [PATCH 015/204] writeSnapshot / readManifestBytes / readSubtreeBytes --- .../nodeView/state/SnapshotsInfo.scala | 32 +++++++++++++++++-- .../nodeView/state/UtxoState.scala | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 2beee56690..091e799d54 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -3,16 +3,29 @@ package org.ergoplatform.nodeView.state import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.Algos.HF +import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} +import org.ergoplatform.wallet.Constants +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} import scala.util.Try -case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) +case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { + def withNewManifest(height: Height, manifestId: ManifestId): SnapshotsInfo = { + SnapshotsInfo(availableManifests.updated(height, manifestId)) + } +} + +object SnapshotsInfo { + val empty = SnapshotsInfo(Map.empty) +} class SnapshotsDb(store: LDBKVStore) { + private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { @@ -43,9 +56,22 @@ class SnapshotsDb(store: LDBKVStore) { def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { - ??? + val manifestBytes = serializer.manifestToBytes(manifest) + val manifestId = manifest.id + //todo: RAM consumption doubles here, avoid it + val subTreesToWrite = subtrees.map(s => s.id -> serializer.subtreeToBytes(s)) + store.insert(Seq(manifestId -> manifestBytes) ++ subTreesToWrite) + val si = readSnapshotsInfo.getOrElse(SnapshotsInfo.empty).withNewManifest(height, manifestId) + writeSnapshotsInfo(si) + } + + def readManifestBytes(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { + store.get(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) } + def readSubtreeBytes(id: ManifestId): Option[BatchAVLProverSubtree[Digest32]] = { + store.get(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) + } } object SnapshotsDb { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index bc0a6e8ffe..6965e2afbd 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -194,6 +194,7 @@ object UtxoState { type Subtree = BatchAVLProverSubtree[Digest32] type ManifestId = Digest32 + type SubtreeId = Digest32 private lazy val bestVersionKey = Algos.hash("best state version") val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") From 506776f4e454d833c1fb72683729959f4f65df05 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 18 Nov 2021 12:42:26 +0300 Subject: [PATCH 016/204] writing snapshots to the disk --- .../org/ergoplatform/nodeView/state/SnapshotsInfo.scala | 2 +- .../scala/org/ergoplatform/nodeView/state/UtxoState.scala | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 091e799d54..9ed1a50f7f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -76,7 +76,7 @@ class SnapshotsDb(store: LDBKVStore) { object SnapshotsDb { - def create(ergoSettings: ErgoSettings): Unit = { + def create(ergoSettings: ErgoSettings): SnapshotsDb = { val dir = s"${ergoSettings.directory}/snapshots" create(dir) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 6965e2afbd..4dffedd78b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -99,6 +99,9 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { + + val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) + val SnapshotEvery = 10 // test value, switch to 51840 after testing if (estimatedTip.nonEmpty && @@ -113,6 +116,11 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 println("subtrees count: " + subtrees.size) // todo: save manifest and subtrees into a database + val ms0 = System.currentTimeMillis() + snapshotsDb.writeSnapshot(height, manifest, subtrees) + val ms = System.currentTimeMillis() + println("Time to dump utxo set snapshot: " + (ms - ms0)) + if (UtxoState.prevManifest != null) { val prevSubtrees = UtxoState.prevManifest.subtreesIds.map(Base16.encode).toSet val subtrees = manifest.subtreesIds.map(Base16.encode).toSet From 4b0aa89bf9575cd78fd7035fdafc51464dc3a255 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 25 Nov 2021 22:56:00 +0300 Subject: [PATCH 017/204] pruneSnapshots --- .../nodeView/state/SnapshotsInfo.scala | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 9ed1a50f7f..8603df4ad6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -9,8 +9,10 @@ import org.ergoplatform.wallet.Constants import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} +import scorex.util.ScorexLogging +import scorex.util.encode.Base16 -import scala.util.Try +import scala.util.{Failure, Success, Try} case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { def withNewManifest(height: Height, manifestId: ManifestId): SnapshotsInfo = { @@ -22,7 +24,7 @@ object SnapshotsInfo { val empty = SnapshotsInfo(Map.empty) } -class SnapshotsDb(store: LDBKVStore) { +class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) @@ -53,6 +55,30 @@ class SnapshotsDb(store: LDBKVStore) { store.get(snapshotInfoKey).map(snapshotsInfoFromBytes) } + def pruneSnapshots(before: Height): Unit = { + log.info("Starting snapshots pruning") + readSnapshotsInfo.foreach { si => + si.availableManifests.filterKeys(_ < before).foreach { case (h, manifestId) => + log.info(s"Pruning snapshot at height $h") + val keysToRemove = store.get(manifestId) match { + case Some(manifestBytes) => + serializer.manifestFromBytes(manifestBytes, Constants.ModifierIdLength) match { + case Success(m) => + m.subtreesIds += manifestId + case Failure(e) => + log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) + Seq.empty + } + case None => + log.error(s"Manifest ${Base16.encode(manifestId)} not found:") + Seq.empty + } + store.remove(keysToRemove) + } + } + log.info("Snapshots pruning finished") + } + def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { From 2d3aa31882164e1d351a628df17ddb4da4f0138b Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Fri, 26 Nov 2021 10:43:55 +0300 Subject: [PATCH 018/204] calling prune() --- src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 4dffedd78b..6e763beb37 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -117,6 +117,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 // todo: save manifest and subtrees into a database val ms0 = System.currentTimeMillis() + snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) snapshotsDb.writeSnapshot(height, manifest, subtrees) val ms = System.currentTimeMillis() println("Time to dump utxo set snapshot: " + (ms - ms0)) From cea355df6412a4e8661b0286cce2fb4c6e852759 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 29 Nov 2021 15:51:37 +0300 Subject: [PATCH 019/204] removing experimental manifests comparison code --- .../nodeView/state/UtxoState.scala | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 6e763beb37..f1f8cf1cf7 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -23,7 +23,6 @@ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} -import scorex.util.encode.Base16 import scala.util.{Failure, Success, Try} @@ -111,25 +110,11 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 val serializer = new BatchAVLProverSerializer[Digest32, HF] val (manifest, subtrees) = serializer.slice(persistentProver.avlProver, subtreeDepth = 12) - val manifestBytes = serializer.manifestToBytes(manifest) - println("manifest size: " + manifestBytes.length) - println("subtrees count: " + subtrees.size) - // todo: save manifest and subtrees into a database - val ms0 = System.currentTimeMillis() snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) snapshotsDb.writeSnapshot(height, manifest, subtrees) val ms = System.currentTimeMillis() println("Time to dump utxo set snapshot: " + (ms - ms0)) - - if (UtxoState.prevManifest != null) { - val prevSubtrees = UtxoState.prevManifest.subtreesIds.map(Base16.encode).toSet - val subtrees = manifest.subtreesIds.map(Base16.encode).toSet - - val common = subtrees.count(prevSubtrees.contains) - println("Subtrees not changed: " + common) - } - UtxoState.prevManifest = manifest } } @@ -196,9 +181,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 object UtxoState { - //todo: remove after experiment - var prevManifest: UtxoState.Manifest = null - type Manifest = BatchAVLProverManifest[Digest32] type Subtree = BatchAVLProverSubtree[Digest32] From 8c479022d3ae2c04fc4b6e0fc1e48675c3837279 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 29 Nov 2021 16:10:19 +0300 Subject: [PATCH 020/204] removing snapshotsToKeep setting --- src/main/resources/application.conf | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 244b99be89..36f69f19d0 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -79,8 +79,6 @@ ergo { # Block 417,792 is checkpointed by the protocol (so its UTXO set as well). # The node still applying transactions to UTXO set and so checks UTXO set digests for each block. skipV1TransactionsValidation = false - - snapshotsToKeep = 2 } cache { From 90060b9c52d834de438503c53da11c125cf23fe0 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 29 Nov 2021 16:22:44 +0300 Subject: [PATCH 021/204] BlockSection, comment for applyModifier --- .../ModifiersApplicationBench.scala | 16 +++---- .../test/scala/org/ergoplatform/Utils.scala | 4 +- .../org/ergoplatform/bench/BenchRunner.scala | 4 +- .../bench/misc/ModifierWriter.scala | 6 +-- .../ergoplatform/nodeView/NVBenchmark.scala | 4 +- .../nodeView/state/UtxoStateBenchmark.scala | 4 +- .../http/api/BlocksApiRoute.scala | 6 +-- .../ergoplatform/modifiers/BlockSection.scala | 37 ++++++-------- .../modifiers/ErgoFullBlock.scala | 8 ++-- .../modifiers/ErgoPersistentModifier.scala | 21 -------- .../modifiers/NonHeaderBlockSection.scala | 30 ++++++++++++ .../modifiers/history/ADProofs.scala | 4 +- .../modifiers/history/BlockTransactions.scala | 4 +- .../history/HistoryModifierSerializer.scala | 8 ++-- .../history/NipopowProofModifier.scala | 4 +- .../history/extension/Extension.scala | 4 +- .../modifiers/history/header/Header.scala | 12 ++--- .../modifiers/state/UTXOSnapshotChunk.scala | 4 +- .../state/UTXOSnapshotManifest.scala | 4 +- .../network/ErgoNodeViewSynchronizer.scala | 24 +++++----- .../nodeView/ErgoModifiersCache.scala | 6 +-- .../nodeView/ErgoNodeViewHolder.scala | 44 ++++++++--------- .../nodeView/history/ErgoHistory.scala | 24 +++++----- .../nodeView/history/ErgoHistoryReader.scala | 14 +++--- .../history/storage/HistoryStorage.scala | 8 ++-- .../modifierprocessors/BasicReaders.scala | 4 +- .../BlockSectionProcessor.scala | 6 +-- .../EmptyBlockSectionProcessor.scala | 8 ++-- .../FullBlockProcessor.scala | 10 ++-- .../FullBlockSectionProcessor.scala | 16 +++---- .../modifierprocessors/HeadersProcessor.scala | 8 ++-- .../UTXOSnapshotChunkProcessor.scala | 4 +- .../popow/EmptyPoPoWProofsProcessor.scala | 4 +- .../popow/FullPoPoWProofsProcessor.scala | 4 +- .../popow/PoPoWProofsProcessor.scala | 4 +- .../nodeView/state/DigestState.scala | 8 ++-- .../nodeView/state/ErgoState.scala | 13 +++-- .../nodeView/state/UtxoState.scala | 4 +- .../nodeView/wallet/ErgoWallet.scala | 6 +-- .../BlockSectionValidationSpecification.scala | 6 +-- .../VerifyADHistorySpecification.scala | 4 +- .../state/wrapped/WrappedDigestState.scala | 4 +- .../state/wrapped/WrappedUtxoState.scala | 4 +- .../org/ergoplatform/sanity/ErgoSanity.scala | 4 +- .../ergoplatform/utils/NodeViewTestOps.scala | 6 +-- .../utils/generators/ChainGenerator.scala | 6 +-- .../generators/CustomModifierProducer.scala | 4 +- .../SemanticallyInvalidModifierProducer.scala | 4 +- .../SemanticallyValidModifierProducer.scala | 4 +- ...yntacticallyTargetedModifierProducer.scala | 6 +-- .../TotallyValidModifierProducer.scala | 6 +-- .../testkit/properties/HistoryTests.scala | 6 +-- .../properties/NodeViewHolderTests.scala | 48 +++++++++---------- .../NodeViewSynchronizerTests.scala | 4 +- .../state/StateApplicationTest.scala | 8 ++-- 55 files changed, 267 insertions(+), 260 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/modifiers/ErgoPersistentModifier.scala create mode 100644 src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala diff --git a/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala b/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala index 82cd1b2911..60c2837582 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/ModifiersApplicationBench.scala @@ -1,7 +1,7 @@ package org.ergoplatform import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.modifiers.ErgoPersistentModifier +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 @@ -25,17 +25,17 @@ object ModifiersApplicationBench extends HistoryTestHelpers with NVBenchmark { val extensions: Seq[Extension] = readExtensions def bench(benchCase: String) - (applicator: (Seq[ErgoPersistentModifier], ErgoHistory) => Any, - mods: Seq[ErgoPersistentModifier]): (String, Long) = { + (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[ErgoPersistentModifier], his: ErgoHistory): (ErgoHistory, Int) = { + def applyModifiersWithCache(mods: Seq[BlockSection], his: ErgoHistory): (ErgoHistory, Int) = { mods.foreach(m => cache.put(m.id, m)) - @tailrec def applyLoop(applied: Seq[ErgoPersistentModifier]): Seq[ErgoPersistentModifier] = { + @tailrec def applyLoop(applied: Seq[BlockSection]): Seq[BlockSection] = { cache.popCandidate(his) match { case Some(mod) => his.append(mod).get @@ -49,9 +49,9 @@ object ModifiersApplicationBench extends HistoryTestHelpers with NVBenchmark { his -> appliedModsQty } - def applyModifiers(mods: Seq[ErgoPersistentModifier], his: ErgoHistory): (ErgoHistory, Int) = { - @tailrec def applyLoop(rem: Seq[ErgoPersistentModifier], - applied: Seq[ErgoPersistentModifier]): Seq[ErgoPersistentModifier] = { + 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) diff --git a/benchmarks/src/test/scala/org/ergoplatform/Utils.scala b/benchmarks/src/test/scala/org/ergoplatform/Utils.scala index 1663470e11..70eaec61bf 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/Utils.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/Utils.scala @@ -5,7 +5,7 @@ import java.net.URL import com.google.common.primitives.Ints import javax.net.ssl.HttpsURLConnection -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.HistoryModifierSerializer object Utils { @@ -31,7 +31,7 @@ object Utils { def readBytes(length: Int)(implicit fis: InputStream): Option[Array[Byte]] = Some(Stream.continually(fis.read().toByte).take(length).toArray) - def readModifier[M <: ErgoPersistentModifier](implicit fis: InputStream): Option[M] = { + def readModifier[M <: BlockSection](implicit fis: InputStream): Option[M] = { for { length <- readLength bytes <- readBytes(length) diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala index c0a8190305..c8fbcbc157 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/BenchRunner.scala @@ -2,7 +2,7 @@ package org.ergoplatform.bench import akka.actor.{ActorRef, ActorSystem} import org.ergoplatform.bench.misc.TempDir -import org.ergoplatform.modifiers.ErgoPersistentModifier +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} @@ -71,7 +71,7 @@ object BenchRunner extends ScorexLogging with NVBenchmark { () } - private def runBench(benchRef: ActorRef, nodeRef: ActorRef, modifiers: Vector[ErgoPersistentModifier]): Unit = { + 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/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala index 80de0afb14..3f448d9125 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala @@ -4,7 +4,7 @@ import java.io.{InputStream, OutputStream} import com.google.common.primitives.Ints import org.ergoplatform.Utils._ -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import scorex.core.serialization.ScorexSerializer @@ -12,7 +12,7 @@ import scorex.core.{ModifierTypeId, NodeViewModifier} object ModifierWriter { - val modifierSerializers: Map[ModifierTypeId, ScorexSerializer[_ <: ErgoPersistentModifier]] = + val modifierSerializers: Map[ModifierTypeId, ScorexSerializer[_ <: BlockSection]] = Map(Header.modifierTypeId -> HeaderSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, ADProofs.modifierTypeId -> ADProofsSerializer) @@ -27,7 +27,7 @@ object ModifierWriter { fos.flush() } - def read(implicit fis: InputStream): Option[ErgoPersistentModifier] = for { + def read(implicit fis: InputStream): Option[BlockSection] = for { typeId <- readModId length <- readLength bytes <- readBytes(length) diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala index fd080222f5..96ba461c5a 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/NVBenchmark.scala @@ -3,7 +3,7 @@ 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, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.modifiers.history.BlockTransactions trait NVBenchmark { @@ -16,7 +16,7 @@ trait NVBenchmark { def readBlocks: Seq[ErgoFullBlock] = readHeaders.zip(readPayloads).zip(readExtensions) .map { case ((h, txs), ext) => ErgoFullBlock(h, txs, ext, None) } - def readModifiers[M <: ErgoPersistentModifier](path: String): Seq[M] = { + def readModifiers[M <: BlockSection](path: String): Seq[M] = { val is = Utils.getUrlInputStream(path) Stream .continually { diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala index 5e19ce2fd9..c9367ee9b7 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.Utils import org.ergoplatform.Utils.BenchReport -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.NVBenchmark import org.ergoplatform.settings.{Args, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers @@ -21,7 +21,7 @@ object UtxoStateBenchmark extends HistoryTestHelpers with NVBenchmark { val transactionsQty = blocks.flatMap(_.transactions).size - def bench(mods: Seq[ErgoPersistentModifier]): Long = { + def bench(mods: Seq[BlockSection]): Long = { val state = ErgoState.generateGenesisUtxoState(createTempDir, StateConstants(None, realNetworkSetting))._1 Utils.time { mods.foldLeft(state) { case (st, mod) => diff --git a/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala index 6fa55a915c..d99ba02c23 100644 --- a/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/BlocksApiRoute.scala @@ -7,7 +7,7 @@ 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.{BlockSection, ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.ErgoReadersHolder.GetDataFromHistory import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.settings.{Algos, ErgoSettings} @@ -66,13 +66,13 @@ case class BlocksApiRoute(viewHolderRef: ActorRef, readersHolder: ActorRef, ergo history.typedModifierById[Header](headerId).flatMap(history.getFullBlock) } - private def getModifierById(modifierId: ModifierId): Future[Option[ErgoPersistentModifier]] = + private def getModifierById(modifierId: ModifierId): Future[Option[BlockSection]] = getHistory.map(_.modifierById(modifierId)) private def getProofForTx(headerId: ModifierId, txId: ModifierId): Future[Option[MerkleProof[Digest32]]] = getModifierById(headerId).flatMap { case Some(header: Header) => - val blockTxsId = BlockSection.computeId( + val blockTxsId = NonHeaderBlockSection.computeId( BlockTransactions.modifierTypeId, headerId, header.transactionsRoot.asInstanceOf[Array[Byte]] diff --git a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala b/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala index 4ba7114147..c104213f9d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala +++ b/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala @@ -1,30 +1,21 @@ package org.ergoplatform.modifiers -import org.ergoplatform.settings.Algos -import scorex.core.ModifierTypeId -import scorex.crypto.hash.Digest32 -import scorex.util.{ModifierId, bytesToId, idToBytes} +import io.circe.Encoder +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 -/** - * An interface for Ergo block section which contains corresponding header id and a digest of its payload. - */ -trait BlockSection extends ErgoPersistentModifier { - - override lazy val serializedId: Array[Byte] = BlockSection.computeIdBytes(modifierTypeId, headerId, digest) - - override lazy val id: ModifierId = bytesToId(serializedId) - - def digest: Digest32 - - def headerId: ModifierId - - override def parentId: ModifierId = headerId -} +trait BlockSection extends PersistentNodeViewModifier with ErgoNodeViewModifier object BlockSection { - def computeId(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): ModifierId = - bytesToId(computeIdBytes(modifierType, headerId, digest)) - def computeIdBytes(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = - Algos.hash.prefixedHash(modifierType, idToBytes(headerId), digest) + implicit val jsonEncoder: Encoder[BlockSection] = { + case h: Header => Header.jsonEncoder(h) + case bt: BlockTransactions => BlockTransactions.jsonEncoder(bt) + case adp: ADProofs => ADProofs.jsonEncoder(adp) + case ext: Extension => Extension.jsonEncoder(ext) + case other => throw new Exception(s"Unknown persistent modifier type: $other") + } + } diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 036a50341b..80240799d0 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -15,7 +15,7 @@ case class ErgoFullBlock(header: Header, blockTransactions: BlockTransactions, extension: Extension, adProofs: Option[ADProofs]) - extends ErgoPersistentModifier + extends BlockSection with TransactionsCarryingPersistentNodeViewModifier { override type M = ErgoFullBlock @@ -28,11 +28,11 @@ case class ErgoFullBlock(header: Header, override def parentId: ModifierId = header.parentId - lazy val mandatoryBlockSections: Seq[BlockSection] = Seq(blockTransactions, extension) + lazy val mandatoryBlockSections: Seq[NonHeaderBlockSection] = Seq(blockTransactions, extension) - lazy val blockSections: Seq[BlockSection] = adProofs.toSeq ++ mandatoryBlockSections + lazy val blockSections: Seq[NonHeaderBlockSection] = adProofs.toSeq ++ mandatoryBlockSections - lazy val toSeq: Seq[ErgoPersistentModifier] = header +: blockSections + lazy val toSeq: Seq[BlockSection] = header +: blockSections override lazy val transactions: Seq[ErgoTransaction] = blockTransactions.txs diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoPersistentModifier.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoPersistentModifier.scala deleted file mode 100644 index 8e4747541f..0000000000 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoPersistentModifier.scala +++ /dev/null @@ -1,21 +0,0 @@ -package org.ergoplatform.modifiers - -import io.circe.Encoder -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 - -trait ErgoPersistentModifier extends PersistentNodeViewModifier with ErgoNodeViewModifier - -object ErgoPersistentModifier { - - implicit val jsonEncoder: Encoder[ErgoPersistentModifier] = { - case h: Header => Header.jsonEncoder(h) - case bt: BlockTransactions => BlockTransactions.jsonEncoder(bt) - case adp: ADProofs => ADProofs.jsonEncoder(adp) - case ext: Extension => Extension.jsonEncoder(ext) - case other => throw new Exception(s"Unknown persistent modifier type: $other") - } - -} diff --git a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala new file mode 100644 index 0000000000..bbed96e1ff --- /dev/null +++ b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala @@ -0,0 +1,30 @@ +package org.ergoplatform.modifiers + +import org.ergoplatform.settings.Algos +import scorex.core.ModifierTypeId +import scorex.crypto.hash.Digest32 +import scorex.util.{ModifierId, bytesToId, idToBytes} + +/** + * An interface for Ergo block section which contains corresponding header id and a digest of its payload. + */ +trait NonHeaderBlockSection extends BlockSection { + + override lazy val serializedId: Array[Byte] = NonHeaderBlockSection.computeIdBytes(modifierTypeId, headerId, digest) + + override lazy val id: ModifierId = bytesToId(serializedId) + + def digest: Digest32 + + def headerId: ModifierId + + override def parentId: ModifierId = headerId +} + +object NonHeaderBlockSection { + def computeId(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): ModifierId = + bytesToId(computeIdBytes(modifierType, headerId, digest)) + + def computeIdBytes(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = + Algos.hash.prefixedHash(modifierType, idToBytes(headerId), digest) +} diff --git a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala index 264506600f..a592c3c374 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.NonHeaderBlockSection import org.ergoplatform.modifiers.state._ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, Constants} @@ -20,7 +20,7 @@ import scala.util.{Failure, Success, Try} case class ADProofs(headerId: ModifierId, proofBytes: SerializedAdProof, - override val sizeOpt: Option[Int] = None) extends BlockSection { + override val sizeOpt: Option[Int] = None) extends NonHeaderBlockSection { override def digest: Digest32 = ADProofs.proofDigest(proofBytes) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala index 78f47854c6..a608639a4e 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.NonHeaderBlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.nodeView.mempool.TransactionMembershipProof @@ -31,7 +31,7 @@ case class BlockTransactions(headerId: ModifierId, blockVersion: Version, txs: Seq[ErgoTransaction], override val sizeOpt: Option[Int] = None) - extends BlockSection with TransactionsCarryingPersistentNodeViewModifier { + extends NonHeaderBlockSection with TransactionsCarryingPersistentNodeViewModifier { assert(txs.nonEmpty, "Block should always contain at least 1 coinbase-like transaction") diff --git a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala b/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala index 9ff6fa767d..d1ef4eb208 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala @@ -1,14 +1,14 @@ package org.ergoplatform.modifiers.history -import org.ergoplatform.modifiers.ErgoPersistentModifier +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.ScorexSerializer import scorex.util.serialization.{Reader, Writer} -object HistoryModifierSerializer extends ScorexSerializer[ErgoPersistentModifier] { +object HistoryModifierSerializer extends ScorexSerializer[BlockSection] { - override def serialize(obj: ErgoPersistentModifier, w: Writer): Unit = { + override def serialize(obj: BlockSection, w: Writer): Unit = { obj match { case m: Header => w.put(Header.modifierTypeId) @@ -27,7 +27,7 @@ object HistoryModifierSerializer extends ScorexSerializer[ErgoPersistentModifier } } - override def parse(r: Reader): ErgoPersistentModifier = { + override def parse(r: Reader): BlockSection = { r.getByte() match { case Header.`modifierTypeId` => HeaderSerializer.parse(r) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala b/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala index 111f211ac3..fddf3e5779 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala @@ -1,6 +1,6 @@ package org.ergoplatform.modifiers.history -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.popow.NipopowProof import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId @@ -16,7 +16,7 @@ import scorex.util.{ModifierId, bytesToId} * @param sizeOpt - optionally, serialized network message size */ case class NipopowProofModifier(proof: NipopowProof, override val sizeOpt: Option[Int] = None) - extends Comparable[NipopowProofModifier] with Ordered[NipopowProofModifier] with ErgoPersistentModifier { + extends Comparable[NipopowProofModifier] with Ordered[NipopowProofModifier] with BlockSection { override val modifierTypeId: ModifierTypeId = NipopowProofModifier.modifierTypeId diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index e89868ca78..38667c2ebd 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala @@ -4,7 +4,7 @@ import com.google.common.primitives.Bytes import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.NonHeaderBlockSection import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer @@ -23,7 +23,7 @@ import scorex.util.ModifierId case class Extension(headerId: ModifierId, override val fields: Seq[(Array[Byte], Array[Byte])], override val sizeOpt: Option[Int] = None) - extends ExtensionCandidate(fields) with BlockSection { + extends ExtensionCandidate(fields) with NonHeaderBlockSection { override val modifierTypeId: ModifierTypeId = Extension.modifierTypeId diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index b5cb0bede5..a1297a2421 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -7,7 +7,7 @@ import org.ergoplatform.mining.difficulty.RequiredDifficulty import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions, PreHeader} -import org.ergoplatform.modifiers.{BlockSection, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.{Algos, Constants} @@ -54,7 +54,7 @@ case class Header(override val version: Header.Version, powSolution: AutolykosSolution, override val votes: Array[Byte], //3 bytes override val sizeOpt: Option[Int] = None) extends HeaderWithoutPow(version, parentId, ADProofsRoot, stateRoot, transactionsRoot, timestamp, - nBits, height, extensionRoot, votes) with PreHeader with ErgoPersistentModifier { + nBits, height, extensionRoot, votes) with PreHeader with BlockSection { override def serializedId: Array[Header.Version] = Algos.hash(bytes) @@ -64,11 +64,11 @@ case class Header(override val version: Header.Version, lazy val requiredDifficulty: Difficulty = RequiredDifficulty.decodeCompactBits(nBits) - lazy val ADProofsId: ModifierId = BlockSection.computeId(ADProofs.modifierTypeId, id, ADProofsRoot) + lazy val ADProofsId: ModifierId = NonHeaderBlockSection.computeId(ADProofs.modifierTypeId, id, ADProofsRoot) - lazy val transactionsId: ModifierId = BlockSection.computeId(BlockTransactions.modifierTypeId, id, transactionsRoot) + lazy val transactionsId: ModifierId = NonHeaderBlockSection.computeId(BlockTransactions.modifierTypeId, id, transactionsRoot) - lazy val extensionId: ModifierId = BlockSection.computeId(Extension.modifierTypeId, id, extensionRoot) + lazy val extensionId: ModifierId = NonHeaderBlockSection.computeId(Extension.modifierTypeId, id, extensionRoot) override def minerPk: EcPointType = powSolution.pk @@ -89,7 +89,7 @@ case class Header(override val version: Header.Version, /** * Checks that modifier m corresponds to this header */ - def isCorrespondingModifier(m: ErgoPersistentModifier): Boolean = sectionIds.exists(_._2 == m.id) + def isCorrespondingModifier(m: BlockSection): Boolean = sectionIds.exists(_._2 == m.id) /** * Estimate that this header is recent enough to possibly be the best header diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala index 6b75fd013b..73e03d6b29 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala @@ -1,7 +1,7 @@ package org.ergoplatform.modifiers.state import org.ergoplatform.ErgoBox -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSubtree @@ -12,7 +12,7 @@ import scorex.util.{ModifierId, bytesToId} * Container for a chunk of sliced AVL+ tree */ case class UTXOSnapshotChunk(subTree: Either[BatchAVLProverSubtree[Digest32], Array[Byte]]) - extends ErgoPersistentModifier { + extends BlockSection { override val modifierTypeId: ModifierTypeId = UTXOSnapshotChunk.modifierTypeId diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala index 7cdf8bd5c2..75d51bdd47 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala @@ -1,6 +1,6 @@ package org.ergoplatform.modifiers.state -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.settings.Algos @@ -18,7 +18,7 @@ case class UTXOSnapshotManifest( blockId: ModifierId, utxoSetDigest: ADDigest, //33 bytes! extra byte with tree height here! manifest: BatchAVLProverManifest[Digest32] - ) extends ErgoPersistentModifier { + ) extends BlockSection { override val modifierTypeId: ModifierTypeId = UTXOSnapshotManifest.modifierTypeId diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 33b12595dd..6d165f9490 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -5,7 +5,7 @@ import java.net.InetSocketAddress import akka.actor.{Actor, ActorRef, ActorRefFactory, Props} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import org.ergoplatform.network.ErgoNodeViewSynchronizer.{CheckModifiersToDownload, PeerSyncState} @@ -420,9 +420,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val parsed: Iterable[ErgoTransaction] = parseModifiers(requestedModifiers, serializer, remote) viewHolderRef ! TransactionsFromRemote(parsed) - case Some(serializer: ScorexSerializer[ErgoPersistentModifier]@unchecked) => + case Some(serializer: ScorexSerializer[BlockSection]@unchecked) => // parse all modifiers and put them to modifiers cache - val parsed: Iterable[ErgoPersistentModifier] = parseModifiers(requestedModifiers, serializer, remote) + val parsed: Iterable[BlockSection] = parseModifiers(requestedModifiers, serializer, remote) val valid = parsed.filter(validateAndSetStatus(remote, _)) if (valid.nonEmpty) { viewHolderRef ! ModifiersFromRemote(valid) @@ -542,7 +542,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * - headers, if our headers chain is not synced yet (by sending sync message) * - block sections, if our headers chain is synced */ - protected def requestMoreModifiers(applied: Seq[ErgoPersistentModifier]): Unit = { + protected def requestMoreModifiers(applied: Seq[BlockSection]): Unit = { historyReaderOpt foreach { h => if (h.isHeadersChainSynced) { // our requested list is is half empty - request more missed modifiers @@ -578,7 +578,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * Move `pmod` to `Invalid` if it is permanently invalid, to `Received` otherwise */ @SuppressWarnings(Array("org.wartremover.warts.IsInstanceOf")) - def validateAndSetStatus(remote: ConnectedPeer, pmod: ErgoPersistentModifier): Boolean = { + def validateAndSetStatus(remote: ConnectedPeer, pmod: BlockSection): Boolean = { historyReaderOpt match { case Some(hr) => hr.applicableTry(pmod) match { @@ -734,7 +734,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case ChangedMempool(reader: ErgoMemPool) => mempoolReaderOpt = Some(reader) - case ModifiersProcessingResult(applied: Seq[ErgoPersistentModifier], cleared: Seq[ErgoPersistentModifier]) => + case ModifiersProcessingResult(applied: Seq[BlockSection], cleared: Seq[BlockSection]) => // stop processing for cleared modifiers // applied modifiers state was already changed at `SyntacticallySuccessfulModifier` cleared.foreach(m => deliveryTracker.setUnknown(m.id)) @@ -833,13 +833,13 @@ object ErgoNodeViewSynchronizer { case class NewOpenSurface(newSurface: Seq[ModifierId]) extends NodeViewHolderEvent - case class StartingPersistentModifierApplication(modifier: ErgoPersistentModifier) extends NodeViewHolderEvent + case class StartingPersistentModifierApplication(modifier: BlockSection) extends NodeViewHolderEvent /** * After application of batch of modifiers from cache to History, NodeViewHolder sends this message, * containing all just applied modifiers and cleared from cache */ - case class ModifiersProcessingResult(applied: Seq[ErgoPersistentModifier], cleared: Seq[ErgoPersistentModifier]) + case class ModifiersProcessingResult(applied: Seq[BlockSection], cleared: Seq[BlockSection]) // hierarchy of events regarding modifiers application outcome trait ModificationOutcome extends NodeViewHolderEvent @@ -851,13 +851,13 @@ object ErgoNodeViewSynchronizer { case class SuccessfulTransaction(transaction: ErgoTransaction) extends ModificationOutcome - case class SyntacticallyFailedModification(modifier: ErgoPersistentModifier, error: Throwable) extends ModificationOutcome + case class SyntacticallyFailedModification(modifier: BlockSection, error: Throwable) extends ModificationOutcome - case class SemanticallyFailedModification(modifier: ErgoPersistentModifier, error: Throwable) extends ModificationOutcome + case class SemanticallyFailedModification(modifier: BlockSection, error: Throwable) extends ModificationOutcome - case class SyntacticallySuccessfulModifier(modifier: ErgoPersistentModifier) extends ModificationOutcome + case class SyntacticallySuccessfulModifier(modifier: BlockSection) extends ModificationOutcome - case class SemanticallySuccessfulModifier(modifier: ErgoPersistentModifier) extends ModificationOutcome + case class SemanticallySuccessfulModifier(modifier: BlockSection) extends ModificationOutcome } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala index 63b72d4442..0f8bc1e89a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoModifiersCache.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.ErgoHistory import scorex.core.DefaultModifiersCache @@ -9,10 +9,10 @@ import scorex.core.validation.MalformedModifierError import scala.util.Failure class ErgoModifiersCache(override val maxSize: Int) - extends DefaultModifiersCache[ErgoPersistentModifier, ErgoHistory](maxSize) { + extends DefaultModifiersCache[BlockSection, ErgoHistory](maxSize) { override def findCandidateKey(history: ErgoHistory): Option[K] = { - def tryToApply(k: K, v: ErgoPersistentModifier): Boolean = { + def tryToApply(k: K, v: BlockSection): Boolean = { history.applicableTry(v) match { case Failure(e) if e.isInstanceOf[MalformedModifierError] => log.warn(s"Modifier ${v.encodedId} is permanently invalid and will be removed from cache", e) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index e88ebdb44e..b8c1c97677 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -10,7 +10,7 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.ErgoApp.CriticalSystemException import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.mempool.ErgoMemPool @@ -52,9 +52,9 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case class UpdateInformation(history: ErgoHistory, state: State, - failedMod: Option[ErgoPersistentModifier], - alternativeProgressInfo: Option[ProgressInfo[ErgoPersistentModifier]], - suffix: IndexedSeq[ErgoPersistentModifier]) + failedMod: Option[BlockSection], + alternativeProgressInfo: Option[ProgressInfo[BlockSection]], + suffix: IndexedSeq[BlockSection]) val scorexSettings: ScorexSettings = settings.scorexSettings @@ -124,19 +124,19 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti nodeView = newNodeView } - protected def extractTransactions(mod: ErgoPersistentModifier): Seq[ErgoTransaction] = mod match { + protected def extractTransactions(mod: BlockSection): Seq[ErgoTransaction] = mod match { case tcm: TransactionsCarryingPersistentNodeViewModifier => tcm.transactions case _ => Seq() } - protected def requestDownloads(pi: ProgressInfo[ErgoPersistentModifier]): Unit = + protected def requestDownloads(pi: ProgressInfo[BlockSection]): Unit = pi.toDownload.foreach { case (tid, id) => context.system.eventStream.publish(DownloadRequest(tid, id)) } - private def trimChainSuffix(suffix: IndexedSeq[ErgoPersistentModifier], - rollbackPoint: scorex.util.ModifierId): IndexedSeq[ErgoPersistentModifier] = { + private def trimChainSuffix(suffix: IndexedSeq[BlockSection], + rollbackPoint: scorex.util.ModifierId): IndexedSeq[BlockSection] = { val idx = suffix.indexWhere(_.id == rollbackPoint) if (idx == -1) IndexedSeq() else suffix.drop(idx) } @@ -176,11 +176,11 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti @tailrec protected final def updateState(history: ErgoHistory, state: State, - progressInfo: ProgressInfo[ErgoPersistentModifier], - suffixApplied: IndexedSeq[ErgoPersistentModifier]): (ErgoHistory, Try[State], Seq[ErgoPersistentModifier]) = { + progressInfo: ProgressInfo[BlockSection], + suffixApplied: IndexedSeq[BlockSection]): (ErgoHistory, Try[State], Seq[BlockSection]) = { requestDownloads(progressInfo) - val (stateToApplyTry: Try[State], suffixTrimmed: IndexedSeq[ErgoPersistentModifier]) = if (progressInfo.chainSwitchingNeeded) { + val (stateToApplyTry: Try[State], suffixTrimmed: IndexedSeq[BlockSection]) = if (progressInfo.chainSwitchingNeeded) { @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val branchingPoint = progressInfo.branchPoint.get //todo: .get if (state.version != branchingPoint) { @@ -221,8 +221,8 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti private def applyState(history: ErgoHistory, stateToApply: State, - suffixTrimmed: IndexedSeq[ErgoPersistentModifier], - progressInfo: ProgressInfo[ErgoPersistentModifier]): Try[UpdateInformation] = { + suffixTrimmed: IndexedSeq[BlockSection], + progressInfo: ProgressInfo[BlockSection]): Try[UpdateInformation] = { val updateInfoSample = UpdateInformation(history, stateToApply, None, None, suffixTrimmed) progressInfo.toApply.foldLeft[Try[UpdateInformation]](Success(updateInfoSample)) { case (f@Failure(ex), _) => @@ -271,9 +271,9 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti * Publish `ModifiersProcessingResult` message with all just applied and removed from cache modifiers. */ protected def processRemoteModifiers: Receive = { - case ModifiersFromRemote(mods: Seq[ErgoPersistentModifier]@unchecked) => + case ModifiersFromRemote(mods: Seq[BlockSection]@unchecked) => @tailrec - def applyFromCacheLoop(applied: Seq[ErgoPersistentModifier]): Seq[ErgoPersistentModifier] = { + def applyFromCacheLoop(applied: Seq[BlockSection]): Seq[BlockSection] = { modifiersCache.popCandidate(history()) match { case Some(mod) => pmodModify(mod) @@ -337,10 +337,10 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti * from rolled back block are to be returned to the pool, and transactions * included in applied block are to be removed. */ - protected def updateMemPool(blocksRemoved: Seq[ErgoPersistentModifier], - blocksApplied: Seq[ErgoPersistentModifier], - memPool: ErgoMemPool, - state: State): ErgoMemPool = { + protected def updateMemPool(blocksRemoved: Seq[BlockSection], + blocksApplied: Seq[BlockSection], + memPool: ErgoMemPool, + state: State): ErgoMemPool = { val rolledBackTxs = blocksRemoved.flatMap(extractTransactions) val appliedTxs = blocksApplied.flatMap(extractTransactions) context.system.eventStream.publish(BlockAppliedTransactions(appliedTxs.map(_.id))) @@ -398,7 +398,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti * which also needs to be propagated to mempool and wallet * @param pmod Remote or local persistent modifier */ - protected def pmodModify(pmod: ErgoPersistentModifier): Unit = + protected def pmodModify(pmod: BlockSection): Unit = if (!history().contains(pmod.id)) { context.system.eventStream.publish(StartingPersistentModifierApplication(pmod)) @@ -595,7 +595,7 @@ object ErgoNodeViewHolder { case class GetDataFromCurrentView[State, A](f: CurrentView[State] => A) // Modifiers received from the remote peer with new elements in it - case class ModifiersFromRemote(modifiers: Iterable[ErgoPersistentModifier]) + case class ModifiersFromRemote(modifiers: Iterable[BlockSection]) sealed trait NewTransactions{ val txs: Iterable[ErgoTransaction] @@ -607,7 +607,7 @@ object ErgoNodeViewHolder { case class TransactionsFromRemote(override val txs: Iterable[ErgoTransaction]) extends NewTransactions - case class LocallyGeneratedModifier(pmod: ErgoPersistentModifier) + case class LocallyGeneratedModifier(pmod: BlockSection) case class EliminateTransactions(ids: Seq[scorex.util.ModifierId]) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index fd880f549d..25e0aa0211 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -7,7 +7,7 @@ import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.state.UTXOSnapshotChunk -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors._ import org.ergoplatform.nodeView.history.storage.modifierprocessors.popow.{EmptyPoPoWProofsProcessor, FullPoPoWProofsProcessor} @@ -40,7 +40,7 @@ import scala.util.{Failure, Success, Try} * 2. Be ignored by history (verifyTransactions == false) */ trait ErgoHistory - extends History[ErgoPersistentModifier, ErgoSyncInfo, ErgoHistory] + extends History[BlockSection, ErgoSyncInfo, ErgoHistory] with ErgoHistoryReader { override protected lazy val requireProofs: Boolean = nodeSettings.stateType.requireProofs @@ -50,13 +50,13 @@ trait ErgoHistory /** * Append ErgoPersistentModifier to History if valid */ - override def append(modifier: ErgoPersistentModifier): Try[(ErgoHistory, ProgressInfo[ErgoPersistentModifier])] = synchronized { + override def append(modifier: BlockSection): Try[(ErgoHistory, ProgressInfo[BlockSection])] = synchronized { log.debug(s"Trying to append modifier ${modifier.encodedId} of type ${modifier.modifierTypeId} to history") applicableTry(modifier).flatMap { _ => modifier match { case header: Header => process(header) - case section: BlockSection => + case section: NonHeaderBlockSection => process(section) case poPoWProof: NipopowProofModifier => process(poPoWProof) @@ -75,7 +75,7 @@ trait ErgoHistory /** * Mark modifier as valid */ - override def reportModifierIsValid(modifier: ErgoPersistentModifier): Try[ErgoHistory] = synchronized { + override def reportModifierIsValid(modifier: BlockSection): Try[ErgoHistory] = synchronized { log.debug(s"Modifier ${modifier.encodedId} of type ${modifier.modifierTypeId} is marked as valid ") modifier match { case fb: ErgoFullBlock => @@ -101,9 +101,9 @@ trait ErgoHistory * @return ProgressInfo with next modifier to try to apply */ @SuppressWarnings(Array("OptionGet", "TraversableHead")) - override def reportModifierIsInvalid(modifier: ErgoPersistentModifier, - progressInfo: ProgressInfo[ErgoPersistentModifier] - ): Try[(ErgoHistory, ProgressInfo[ErgoPersistentModifier])] = synchronized { + override def reportModifierIsInvalid(modifier: BlockSection, + progressInfo: ProgressInfo[BlockSection] + ): Try[(ErgoHistory, ProgressInfo[BlockSection])] = synchronized { log.debug(s"Modifier ${modifier.encodedId} of type ${modifier.modifierTypeId} is marked as invalid") correspondingHeader(modifier) match { case Some(invalidatedHeader) => @@ -118,7 +118,7 @@ trait ErgoHistory case (false, false) => // Modifiers from best header and best full chain are not involved, no rollback and links change required historyStorage.insert(validityRow, Seq.empty).map { _ => - this -> ProgressInfo[ErgoPersistentModifier](None, Seq.empty, Seq.empty, Seq.empty) + this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } case _ => // Modifiers from best header and best full chain are involved, links change required @@ -130,7 +130,7 @@ trait ErgoHistory newBestHeaderOpt.map(h => BestHeaderKey -> idToBytes(h.id)).toSeq, Seq.empty ).map { _ => - this -> ProgressInfo[ErgoPersistentModifier](None, Seq.empty, Seq.empty, Seq.empty) + this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } } else { val invalidatedChain: Seq[ErgoFullBlock] = bestFullBlockOpt.toSeq @@ -168,7 +168,7 @@ trait ErgoHistory historyStorage.insert( Seq(validityKey(modifier.id) -> Array(0.toByte)), Seq.empty).map { _ => - this -> ProgressInfo[ErgoPersistentModifier](None, Seq.empty, Seq.empty, Seq.empty) + this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } } } @@ -176,7 +176,7 @@ trait ErgoHistory /** * @return header, that corresponds to modifier */ - protected def correspondingHeader(modifier: ErgoPersistentModifier): Option[Header] = modifier match { + protected def correspondingHeader(modifier: BlockSection): Option[Header] = modifier match { case h: Header => Some(h) case full: ErgoFullBlock => Some(full.header) case proof: ADProofs => typedModifierById[Header](proof.headerId) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index b360fbced1..210ccbf64d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -5,7 +5,7 @@ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, PoPowHeader, PoPowParams} import org.ergoplatform.modifiers.state.UTXOSnapshotChunk -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.storage._ import org.ergoplatform.nodeView.history.storage.modifierprocessors._ @@ -25,7 +25,7 @@ import scala.util.{Failure, Try} * Read-only copy of ErgoHistory */ trait ErgoHistoryReader - extends HistoryReader[ErgoPersistentModifier, ErgoSyncInfo] + extends HistoryReader[BlockSection, ErgoSyncInfo] with HeadersProcessor with PoPoWProofsProcessor with UTXOSnapshotChunkProcessor @@ -71,7 +71,7 @@ trait ErgoHistoryReader * @param id - modifier id * @return semantically valid ErgoPersistentModifier with the given id it is in history */ - override def modifierById(id: ModifierId): Option[ErgoPersistentModifier] = + override def modifierById(id: ModifierId): Option[BlockSection] = if (isSemanticallyValid(id) != ModifierSemanticValidity.Invalid) { historyStorage.modifierById(id) } else { @@ -84,7 +84,7 @@ trait ErgoHistoryReader * @tparam T - expected Type * @return semantically valid ErgoPersistentModifier of type T with the given id it is in history */ - def typedModifierById[T <: ErgoPersistentModifier : ClassTag](id: ModifierId): Option[T] = modifierById(id) match { + def typedModifierById[T <: BlockSection : ClassTag](id: ModifierId): Option[T] = modifierById(id) match { case Some(m: T) => Some(m) case _ => None } @@ -94,7 +94,7 @@ trait ErgoHistoryReader /** * Check, that it's possible to apply modifier to history */ - def applicable(modifier: ErgoPersistentModifier): Boolean = applicableTry(modifier).isSuccess + def applicable(modifier: BlockSection): Boolean = applicableTry(modifier).isSuccess /** * For given headers (sorted in reverse chronological order), find first one (most recent one) which is known @@ -363,11 +363,11 @@ trait ErgoHistoryReader (offset until (limit + offset)).flatMap(height => bestHeaderIdAtHeight(height)) } - override def applicableTry(modifier: ErgoPersistentModifier): Try[Unit] = { + override def applicableTry(modifier: BlockSection): Try[Unit] = { modifier match { case header: Header => validate(header) - case m: BlockSection => + case m: NonHeaderBlockSection => validate(m) case m: NipopowProofModifier => validate(m) 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 45fc46e60d..22dc8ebdb9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.history.storage import com.google.common.cache.CacheBuilder -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.HistoryModifierSerializer import org.ergoplatform.settings.{Algos, CacheSettings} import scorex.core.utils.ScorexEncoding @@ -25,7 +25,7 @@ class HistoryStorage(indexStore: LDBKVStore, objectsStore: LDBKVStore, config: C private val modifiersCache = CacheBuilder.newBuilder() .maximumSize(config.history.modifiersCacheSize) - .build[String, ErgoPersistentModifier] + .build[String, BlockSection] private val indexCache = CacheBuilder.newBuilder() .maximumSize(config.history.indexesCacheSize) @@ -34,7 +34,7 @@ class HistoryStorage(indexStore: LDBKVStore, objectsStore: LDBKVStore, config: C def modifierBytesById(id: ModifierId): Option[Array[Byte]] = objectsStore.get(idToBytes(id)) - def modifierById(id: ModifierId): Option[ErgoPersistentModifier] = + def modifierById(id: ModifierId): Option[BlockSection] = Option(modifiersCache.getIfPresent(id)) orElse objectsStore.get(idToBytes(id)).flatMap { bytes => HistoryModifierSerializer.parseBytesTry(bytes) match { @@ -61,7 +61,7 @@ class HistoryStorage(indexStore: LDBKVStore, objectsStore: LDBKVStore, config: C def contains(id: ModifierId): Boolean = objectsStore.get(idToBytes(id)).isDefined def insert(indexesToInsert: Seq[(ByteArrayWrapper, Array[Byte])], - objectsToInsert: Seq[ErgoPersistentModifier]): Try[Unit] = { + objectsToInsert: Seq[BlockSection]): Try[Unit] = { objectsStore.insert( objectsToInsert.map(m => idToBytes(m.id) -> HistoryModifierSerializer.toBytes(m)) ).flatMap { _ => diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BasicReaders.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BasicReaders.scala index ca5de458d4..dd8146b6d5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BasicReaders.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/BasicReaders.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import scorex.util.ModifierId import scala.reflect.ClassTag @@ -10,7 +10,7 @@ trait BasicReaders { def headerIdsAtHeight(height: Int): Seq[ModifierId] - def typedModifierById[T <: ErgoPersistentModifier : ClassTag](id: ModifierId): Option[T] + def typedModifierById[T <: BlockSection : ClassTag](id: ModifierId): Option[T] def contains(id: ModifierId): Boolean } 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 e6518a16d1..6bdb0cd4eb 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,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.{BlockSection, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} import scorex.core.consensus.History.ProgressInfo import scorex.core.utils.ScorexEncoding @@ -21,12 +21,12 @@ trait BlockSectionProcessor extends ScorexEncoding { * @param m - modifier to process * @return ProgressInfo - info required for State to be consistent with History */ - protected def process(m: BlockSection): Try[ProgressInfo[ErgoPersistentModifier]] + protected def process(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]] /** * @param m - modifier to validate * @return Success() if modifier is valid from History point of view, Failure(error) otherwise */ - protected def validate(m: BlockSection): Try[Unit] + protected def validate(m: NonHeaderBlockSection): Try[Unit] } 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 d266056e01..bcc0eee61a 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,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.{BlockSection, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} import scorex.core.consensus.History.ProgressInfo import scala.util.{Failure, Success, Try} @@ -11,10 +11,10 @@ import scala.util.{Failure, Success, Try} */ trait EmptyBlockSectionProcessor extends BlockSectionProcessor { - override protected def process(m: BlockSection): Try[ProgressInfo[ErgoPersistentModifier]] = - Success(ProgressInfo[ErgoPersistentModifier](None, Seq.empty, Seq.empty, Seq.empty)) + override protected def process(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]] = + Success(ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty)) - override protected def validate(m: BlockSection): Try[Unit] = + override protected def validate(m: NonHeaderBlockSection): Try[Unit] = Failure(new Error("Regime that does not support block sections processing")) } 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 3d8a478824..5d6fb026c1 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 @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.settings.Algos import scorex.core.consensus.History.ProgressInfo @@ -46,7 +46,7 @@ trait FullBlockProcessor extends HeadersProcessor { * @return ProgressInfo required for State to process to be consistent with the history */ protected def processFullBlock(fullBlock: ErgoFullBlock, - newMod: ErgoPersistentModifier): Try[ProgressInfo[ErgoPersistentModifier]] = { + newMod: BlockSection): Try[ProgressInfo[BlockSection]] = { val bestFullChainAfter = calculateBestChain(fullBlock.header) val newBestBlockHeader = typedModifierById[Header](bestFullChainAfter.last).ensuring(_.isDefined) processing(ToProcess(fullBlock, newMod, newBestBlockHeader, bestFullChainAfter)) @@ -235,7 +235,7 @@ trait FullBlockProcessor extends HeadersProcessor { historyStorage.remove(toRemove) } - private def updateStorage(newModRow: ErgoPersistentModifier, + private def updateStorage(newModRow: BlockSection, bestFullHeaderId: ModifierId, additionalIndexes: Seq[(ByteArrayWrapper, Array[Byte])]): Try[Unit] = { val indicesToInsert = Seq(BestFullBlockKey -> idToBytes(bestFullHeaderId)) ++ additionalIndexes @@ -252,10 +252,10 @@ trait FullBlockProcessor extends HeadersProcessor { object FullBlockProcessor { - type BlockProcessing = PartialFunction[ToProcess, Try[ProgressInfo[ErgoPersistentModifier]]] + type BlockProcessing = PartialFunction[ToProcess, Try[ProgressInfo[BlockSection]]] case class ToProcess(fullBlock: ErgoFullBlock, - newModRow: ErgoPersistentModifier, + newModRow: BlockSection, newBestBlockHeaderOpt: Option[Header], newBestChain: Seq[ModifierId]) 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 90ac059aeb..2de95affd8 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 @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors 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, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings.{Algos, ErgoValidationSettings} import scorex.core.consensus.History.ProgressInfo @@ -28,7 +28,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc * Otherwise - try to construct full block with this block section, if possible - process this new full block, * if not - just put new block section to storage. */ - override protected def process(m: BlockSection): Try[ProgressInfo[ErgoPersistentModifier]] = { + override protected def process(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]] = { m match { case _: ADProofs if !requireProofs => // got proofs in UTXO mode. Don't need to try to update better chain @@ -43,7 +43,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc } } - override protected def validate(m: BlockSection): Try[Unit] = { + override protected def validate(m: NonHeaderBlockSection): Try[Unit] = { typedModifierById[Header](m.headerId).map(header => new PayloadValidator().validate(m, header) ).getOrElse( @@ -60,8 +60,8 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc * @param m - new modifier * @return Some(ErgoFullBlock) if block construction is possible, None otherwise */ - private def getFullBlockByBlockSection(m: BlockSection): Option[ErgoFullBlock] = { - def getOrRead[T <: ErgoPersistentModifier : ClassTag](id: ModifierId): Option[T] = m match { + private def getFullBlockByBlockSection(m: NonHeaderBlockSection): Option[ErgoFullBlock] = { + def getOrRead[T <: BlockSection : ClassTag](id: ModifierId): Option[T] = m match { case mod: T if m.id == id => Some(mod) case _ => typedModifierById[T](id) } @@ -81,7 +81,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc } } - private def justPutToHistory(m: BlockSection): Try[ProgressInfo[ErgoPersistentModifier]] = { + private def justPutToHistory(m: NonHeaderBlockSection): Try[ProgressInfo[BlockSection]] = { historyStorage.insert(Seq.empty, Seq(m)).map { _ => ProgressInfo(None, Seq.empty, Seq.empty, Seq.empty) } @@ -92,7 +92,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc */ class PayloadValidator extends ScorexEncoding { - def validate(m: BlockSection, header: Header): ValidationResult[Unit] = { + def validate(m: NonHeaderBlockSection, header: Header): ValidationResult[Unit] = { initialValidationState .validate(alreadyApplied, !historyStorage.contains(m.id), s"${m.encodedId}") .validate(bsCorrespondsToHeader, header.isCorrespondingModifier(m), s"header=${header.encodedId}, id=${m.encodedId}") @@ -103,7 +103,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc .result } - private def isHistoryADProof(m: BlockSection, header: Header): Boolean = m match { + private def isHistoryADProof(m: NonHeaderBlockSection, header: Header): Boolean = m match { // ADProofs for block transactions that are already in history. Do not validate whether ADProofs are too old case _: ADProofs if contains(header.transactionsId) => true case _ => false 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 d08114dc40..6999506ca3 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 @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoApp.CriticalSystemException import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.mining.difficulty.LinearDifficultyControl -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.NipopowAlgos @@ -88,8 +88,8 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score * @param h - header to process * @return ProgressInfo - info required for State to be consistent with History */ - protected def process(h: Header): Try[ProgressInfo[ErgoPersistentModifier]] = synchronized { - val dataToInsert: (Seq[(ByteArrayWrapper, Array[Byte])], Seq[ErgoPersistentModifier]) = toInsert(h) + protected def process(h: Header): Try[ProgressInfo[BlockSection]] = synchronized { + val dataToInsert: (Seq[(ByteArrayWrapper, Array[Byte])], Seq[BlockSection]) = toInsert(h) historyStorage.insert(dataToInsert._1, dataToInsert._2).flatMap { _ => bestHeaderIdOpt match { @@ -107,7 +107,7 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score /** * Data to add to and remove from the storage to process this modifier */ - private def toInsert(h: Header): (Seq[(ByteArrayWrapper, Array[Byte])], Seq[ErgoPersistentModifier]) = { + private def toInsert(h: Header): (Seq[(ByteArrayWrapper, Array[Byte])], Seq[BlockSection]) = { val requiredDifficulty: Difficulty = h.requiredDifficulty val score = scoreOf(h.parentId).getOrElse(BigInt(0)) + requiredDifficulty val bestRow: Seq[(ByteArrayWrapper, Array[Byte])] = diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala index 70b55652fc..1f3122ac0d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.nodeView.history.storage.HistoryStorage import scorex.core.consensus.History.ProgressInfo @@ -16,7 +16,7 @@ trait UTXOSnapshotChunkProcessor extends ScorexLogging with ScorexEncoding { protected val historyStorage: HistoryStorage - def process(m: UTXOSnapshotChunk): Try[ProgressInfo[ErgoPersistentModifier]] = ??? + def process(m: UTXOSnapshotChunk): Try[ProgressInfo[BlockSection]] = ??? /* { //TODO diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala index 2fd9604c09..c5aa89d319 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.NipopowProofModifier import scorex.core.consensus.History.ProgressInfo @@ -13,7 +13,7 @@ trait EmptyPoPoWProofsProcessor extends PoPoWProofsProcessor { def validate(m: NipopowProofModifier): Try[Unit] = Failure(new Error("Regime that do not process PoPoWProof")) - def process(m: NipopowProofModifier): Try[ProgressInfo[ErgoPersistentModifier]] = + def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] = Success(ProgressInfo(None, Seq.empty, Seq.empty, Seq.empty)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala index 764104f1fa..50d11c0690 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.NipopowProofModifier import org.ergoplatform.nodeView.history.storage.modifierprocessors.HeadersProcessor import scorex.core.consensus.History.ProgressInfo @@ -14,7 +14,7 @@ trait FullPoPoWProofsProcessor extends PoPoWProofsProcessor with HeadersProcesso def validate(m: NipopowProofModifier): Try[Unit] = throw new Error("PoPow not yet supported") - def process(m: NipopowProofModifier): Try[ProgressInfo[ErgoPersistentModifier]] = + def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] = Success(ProgressInfo(None, Seq.empty, Seq.empty, Seq.empty)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala index 1299df0995..bb96324c75 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.{HeaderChain, NipopowProofModifier} import org.ergoplatform.nodeView.history.storage.modifierprocessors.HeadersProcessor import scorex.core.consensus.History.ProgressInfo @@ -15,7 +15,7 @@ trait PoPoWProofsProcessor extends HeadersProcessor with ScorexLogging { def validate(m: NipopowProofModifier): Try[Unit] - def process(m: NipopowProofModifier): Try[ProgressInfo[ErgoPersistentModifier]] + def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] def lastHeaders(count: Int, offset: Int = 0): HeaderChain } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala index f87d4f9a7f..837b94c55b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala @@ -7,7 +7,7 @@ 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, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.state.ErgoState.ModifierProcessing import org.ergoplatform.settings._ import org.ergoplatform.utils.LoggingUtil @@ -30,7 +30,7 @@ class DigestState protected(override val version: VersionTag, override val store: LDBVersionedStore, ergoSettings: ErgoSettings) extends ErgoState[DigestState] - with ModifierValidation[ErgoPersistentModifier] + with ModifierValidation[BlockSection] with ScorexLogging with ScorexEncoding { @@ -59,7 +59,7 @@ class DigestState protected(override val version: VersionTag, .map(_ => ()) } - def validate(mod: ErgoPersistentModifier): Try[Unit] = mod match { + def validate(mod: BlockSection): Try[Unit] = mod match { case fb: ErgoFullBlock => fb.adProofs match { case None => @@ -80,7 +80,7 @@ class DigestState protected(override val version: VersionTag, Failure(new Exception(s"Modifier not validated: $a")) } - override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[DigestState] = + override def applyModifier(mod: BlockSection, estimatedTip: Option[Height]): Try[DigestState] = (processFullBlock orElse processHeader orElse processOther) (mod) @SuppressWarnings(Array("OptionGet")) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 87dfd007f0..075aba897b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -7,7 +7,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform._ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.mining.groupElemFromBytes -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.state.{Insertion, Lookup, Removal, StateChanges} @@ -42,7 +42,14 @@ trait ErgoState[IState <: ErgoState[IState]] extends ErgoStateReader { self: IState => - def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[IState] + /** + * A method to modify the state by applying a block section to it + * + * @param mod - block section to apply + * @param estimatedTip - estimated height of blockchain tip + * @return - modified state, or application error + */ + def applyModifier(mod: BlockSection, estimatedTip: Option[Height]): Try[IState] def rollbackTo(version: VersionTag): Try[IState] @@ -57,7 +64,7 @@ trait ErgoState[IState <: ErgoState[IState]] extends ErgoStateReader { object ErgoState extends ScorexLogging { - type ModifierProcessing[T <: ErgoState[T]] = PartialFunction[ErgoPersistentModifier, Try[T]] + type ModifierProcessing[T <: ErgoState[T]] = PartialFunction[BlockSection, Try[T]] def stateDir(settings: ErgoSettings): File = new File(s"${settings.directory}/state") diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index f1f8cf1cf7..a3b78ce774 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -8,7 +8,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} import org.ergoplatform.settings.Algos @@ -118,7 +118,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } - override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[UtxoState] = mod match { + override def applyModifier(mod: BlockSection, estimatedTip: Option[Height]): Try[UtxoState] = mod match { case fb: ErgoFullBlock => persistentProver.synchronized { val height = fb.header.height diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala index 7fed00a240..2eeb54ab04 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWallet.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.wallet import akka.actor.{ActorRef, ActorSystem} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state.ErgoState import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ @@ -16,7 +16,7 @@ import scala.util.{Failure, Success, Try} class ErgoWallet(historyReader: ErgoHistoryReader, settings: ErgoSettings) (implicit val actorSystem: ActorSystem) - extends Vault[ErgoTransaction, ErgoPersistentModifier, ErgoWallet] + extends Vault[ErgoTransaction, BlockSection, ErgoWallet] with ErgoWalletReader with ScorexLogging { @@ -41,7 +41,7 @@ class ErgoWallet(historyReader: ErgoHistoryReader, settings: ErgoSettings) this } - override def scanPersistent(modifier: ErgoPersistentModifier): ErgoWallet = { + override def scanPersistent(modifier: BlockSection): ErgoWallet = { modifier match { case fb: ErgoFullBlock => walletActor ! ScanOnChain(fb) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala index 19a38e89fe..d4290b4ba6 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.NonHeaderBlockSection import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header @@ -65,7 +65,7 @@ class BlockSectionValidationSpecification extends HistoryTestHelpers { (history, chain.last) } - private def commonChecks(history: ErgoHistory, section: BlockSection, header: Header) = { + private def commonChecks(history: ErgoHistory, section: NonHeaderBlockSection, header: Header) = { history.applicableTry(section) shouldBe 'success // header should contain correct digest history.applicableTry(withUpdatedHeaderId(section, section.id)) shouldBe 'failure @@ -93,7 +93,7 @@ class BlockSectionValidationSpecification extends HistoryTestHelpers { private def genHistory() = generateHistory(verifyTransactions = true, StateType.Utxo, PoPoWBootstrap = false, BlocksToKeep) - private def withUpdatedHeaderId[T <: BlockSection](section: T, newId: ModifierId): T = section match { + private def withUpdatedHeaderId[T <: NonHeaderBlockSection](section: T, newId: ModifierId): T = section match { case s: Extension => s.copy(headerId = newId).asInstanceOf[T] case s: BlockTransactions => s.copy(headerId = newId).asInstanceOf[T] case s: ADProofs => s.copy(headerId = newId).asInstanceOf[T] diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala index 81d7591e24..5362e77c70 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.HeaderChain import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.ErgoModifiersCache import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.utils.HistoryTestHelpers @@ -16,7 +16,7 @@ import scala.util.Random class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { - type PM = ErgoPersistentModifier + type PM = BlockSection private def genHistory(blocksNum: Int = 0, minFullHeight: Option[Int] = Some(ErgoHistory.GenesisHeight)): (ErgoHistory, Seq[ErgoFullBlock]) = { 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 3e0deb09cb..b48b21787f 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedDigestState.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state.wrapped import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.DigestState import org.ergoplatform.settings.ErgoSettings import scorex.core.VersionTag @@ -13,7 +13,7 @@ class WrappedDigestState(val digestState: DigestState, val settings: ErgoSettings) extends DigestState(digestState.version, digestState.rootHash, digestState.store, settings) { - override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[WrappedDigestState] = { + override def applyModifier(mod: BlockSection, estimatedTip: Option[Height]): Try[WrappedDigestState] = { wrapped(super.applyModifier(mod, estimatedTip), wrappedUtxoState.applyModifier(mod, estimatedTip)) } 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 fb0d64e083..34bb93fcbf 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala @@ -5,7 +5,7 @@ import java.io.File import akka.actor.ActorRef import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.settings.Algos.HF @@ -34,7 +34,7 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], case Failure(e) => Failure(e) } - override def applyModifier(mod: ErgoPersistentModifier, estimatedTip: Option[Height]): Try[WrappedUtxoState] = { + override def applyModifier(mod: BlockSection, estimatedTip: Option[Height]): Try[WrappedUtxoState] = { super.applyModifier(mod, estimatedTip) match { case Success(us) => mod match { diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala index aff7ff09ed..ae409a8d17 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.network.{ErgoNodeViewSynchronizer, ErgoSyncTracker} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.ErgoMemPool @@ -123,7 +123,7 @@ trait ErgoSanity[ST <: ErgoState[ST]] extends HistoryTests object ErgoSanity { type TX = ErgoTransaction type B = ErgoBox - type PM = ErgoPersistentModifier + type PM = BlockSection type CTM = BlockTransactions type SI = ErgoSyncInfo type HT = ErgoHistory diff --git a/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala b/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala index 35b103a23d..d0ded56b9e 100644 --- a/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/NodeViewTestOps.scala @@ -6,7 +6,7 @@ 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, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.{ErgoState, StateType, UtxoState} import org.ergoplatform.settings.Algos @@ -76,7 +76,7 @@ trait NodeViewBaseOps extends ErgoTestHelpers { subscribeEvents(classOf[SyntacticallyFailedModification]) } - def expectModificationOutcome(section: ErgoPersistentModifier)(implicit ctx: Ctx): Try[Unit] = { + def expectModificationOutcome(section: BlockSection)(implicit ctx: Ctx): Try[Unit] = { expectMsgType[ModificationOutcome] match { case SyntacticallySuccessfulModifier(mod) if mod.id == section.id => Success(()) @@ -147,7 +147,7 @@ trait NodeViewTestOps extends NodeViewBaseOps { def getLastHeadersLength(count: Int)(implicit ctx: Ctx): Int = getHistory.lastHeaders(count).size - def getModifierById(id: ModifierId)(implicit ctx: Ctx): Option[ErgoPersistentModifier] = getHistory.modifierById(id) + def getModifierById(id: ModifierId)(implicit ctx: Ctx): Option[BlockSection] = getHistory.modifierById(id) def getGenesisStateDigest(implicit ctx: Ctx): Array[Byte] = ctx.settings.chainSettings.genesisStateDigest diff --git a/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala b/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala index 29f0517add..f716b1b9ca 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ChainGenerator.scala @@ -7,7 +7,7 @@ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandida import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, PoPowHeader} import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ErgoPersistentModifier} +import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.settings.Constants import org.ergoplatform.utils.{BoxUtils, ErgoTestConstants} @@ -173,7 +173,7 @@ trait ChainGenerator extends ErgoTestConstants { } def applyChain(historyIn: ErgoHistory, blocks: Seq[ErgoFullBlock]): ErgoHistory = { - def appendOrPass(mod: ErgoPersistentModifier, history: ErgoHistory) = + def appendOrPass(mod: BlockSection, history: ErgoHistory) = if (history.contains(mod)) history else history.append(mod).get._1 blocks.foldLeft(historyIn) { (history, block) => val historyWithBlockHeader = appendOrPass(block.header, history) @@ -185,6 +185,6 @@ trait ChainGenerator extends ErgoTestConstants { def applyBlock(historyIn: ErgoHistory, block: ErgoFullBlock): ErgoHistory = applyChain(historyIn, Seq(block)) - def applySection(historyIn: ErgoHistory, section: BlockSection): ErgoHistory = historyIn.append(section).get._1 + def applySection(historyIn: ErgoHistory, section: NonHeaderBlockSection): ErgoHistory = historyIn.append(section).get._1 } diff --git a/src/test/scala/scorex/testkit/generators/CustomModifierProducer.scala b/src/test/scala/scorex/testkit/generators/CustomModifierProducer.scala index 9b034d6fce..42d4278fe2 100644 --- a/src/test/scala/scorex/testkit/generators/CustomModifierProducer.scala +++ b/src/test/scala/scorex/testkit/generators/CustomModifierProducer.scala @@ -1,6 +1,6 @@ package scorex.testkit.generators -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.ErgoState @@ -13,5 +13,5 @@ trait CustomModifierProducer[ST <: ErgoState[ST]] { def customModifiers(history: ErgoHistory, state: ST, - template: Seq[ModifierProducerTemplateItem]): Seq[ErgoPersistentModifier] + template: Seq[ModifierProducerTemplateItem]): Seq[BlockSection] } diff --git a/src/test/scala/scorex/testkit/generators/SemanticallyInvalidModifierProducer.scala b/src/test/scala/scorex/testkit/generators/SemanticallyInvalidModifierProducer.scala index 2fd1410af6..015456a936 100644 --- a/src/test/scala/scorex/testkit/generators/SemanticallyInvalidModifierProducer.scala +++ b/src/test/scala/scorex/testkit/generators/SemanticallyInvalidModifierProducer.scala @@ -1,9 +1,9 @@ package scorex.testkit.generators -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.ErgoState trait SemanticallyInvalidModifierProducer[ST <: ErgoState[ST]] { - def semanticallyInvalidModifier(state: ST): ErgoPersistentModifier + def semanticallyInvalidModifier(state: ST): BlockSection } diff --git a/src/test/scala/scorex/testkit/generators/SemanticallyValidModifierProducer.scala b/src/test/scala/scorex/testkit/generators/SemanticallyValidModifierProducer.scala index a1a9a58b1b..33a4e9188b 100644 --- a/src/test/scala/scorex/testkit/generators/SemanticallyValidModifierProducer.scala +++ b/src/test/scala/scorex/testkit/generators/SemanticallyValidModifierProducer.scala @@ -1,10 +1,10 @@ package scorex.testkit.generators -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.ErgoState trait SemanticallyValidModifierProducer[ST <: ErgoState[ST]] { - def semanticallyValidModifier(state: ST): ErgoPersistentModifier + def semanticallyValidModifier(state: ST): BlockSection } diff --git a/src/test/scala/scorex/testkit/generators/SyntacticallyTargetedModifierProducer.scala b/src/test/scala/scorex/testkit/generators/SyntacticallyTargetedModifierProducer.scala index 90c38a19c9..c7964259e9 100644 --- a/src/test/scala/scorex/testkit/generators/SyntacticallyTargetedModifierProducer.scala +++ b/src/test/scala/scorex/testkit/generators/SyntacticallyTargetedModifierProducer.scala @@ -1,11 +1,11 @@ package scorex.testkit.generators -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory trait SyntacticallyTargetedModifierProducer { - def syntacticallyValidModifier(history: ErgoHistory): ErgoPersistentModifier + def syntacticallyValidModifier(history: ErgoHistory): BlockSection - def syntacticallyInvalidModifier(history: ErgoHistory): ErgoPersistentModifier + def syntacticallyInvalidModifier(history: ErgoHistory): BlockSection } diff --git a/src/test/scala/scorex/testkit/generators/TotallyValidModifierProducer.scala b/src/test/scala/scorex/testkit/generators/TotallyValidModifierProducer.scala index b0d753475e..4373af4332 100644 --- a/src/test/scala/scorex/testkit/generators/TotallyValidModifierProducer.scala +++ b/src/test/scala/scorex/testkit/generators/TotallyValidModifierProducer.scala @@ -1,13 +1,13 @@ package scorex.testkit.generators -import org.ergoplatform.modifiers.ErgoPersistentModifier +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): ErgoPersistentModifier + def totallyValidModifier(history: ErgoHistory, state: ST): BlockSection - def totallyValidModifiers(history: ErgoHistory, state: ST, count: Int): Seq[ErgoPersistentModifier] + def totallyValidModifiers(history: ErgoHistory, state: ST, count: Int): Seq[BlockSection] } diff --git a/src/test/scala/scorex/testkit/properties/HistoryTests.scala b/src/test/scala/scorex/testkit/properties/HistoryTests.scala index 61d751c49d..31e2d726e8 100644 --- a/src/test/scala/scorex/testkit/properties/HistoryTests.scala +++ b/src/test/scala/scorex/testkit/properties/HistoryTests.scala @@ -1,6 +1,6 @@ package scorex.testkit.properties -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers @@ -22,11 +22,11 @@ trait HistoryTests val historyGen: Gen[ErgoHistory] - lazy val generatorWithValidModifier: Gen[(ErgoHistory, ErgoPersistentModifier)] = { + lazy val generatorWithValidModifier: Gen[(ErgoHistory, BlockSection)] = { historyGen.map { h => (h, syntacticallyValidModifier(h))} } - lazy val generatorWithInvalidModifier: Gen[(ErgoHistory, ErgoPersistentModifier)] = { + lazy val generatorWithInvalidModifier: Gen[(ErgoHistory, BlockSection)] = { historyGen.map { h => (h, syntacticallyInvalidModifier(h))} } diff --git a/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala index aa8420cbaa..455f69b546 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewHolderTests.scala @@ -2,7 +2,7 @@ package scorex.testkit.properties import akka.actor._ import akka.testkit.TestProbe -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.ErgoHistory import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec @@ -28,7 +28,7 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] with CustomModifierProducer[ST] with ObjectGenerators { - def nodeViewHolder(implicit system: ActorSystem): (ActorRef, TestProbe, ErgoPersistentModifier, ST, ErgoHistory) + def nodeViewHolder(implicit system: ActorSystem): (ActorRef, TestProbe, BlockSection, ST, ErgoHistory) class HolderFixture extends AkkaFixture { @SuppressWarnings(Array("org.wartremover.warts.PublicInference")) @@ -72,8 +72,8 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] val p = TestProbe() system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) - p.send(node, GetDataFromCurrentView[ST, ErgoPersistentModifier] { v => totallyValidModifiers(v.history, v.state, 2).head }) - val mod = p.expectMsgClass(classOf[ErgoPersistentModifier]) + 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] } @@ -98,8 +98,8 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) system.eventStream.subscribe(eventListener.ref, classOf[SemanticallySuccessfulModifier]) - p.send(node, GetDataFromCurrentView[ST, ErgoPersistentModifier] { v => totallyValidModifiers(v.history, v.state, 2).head }) - val mod = p.expectMsgClass(classOf[ErgoPersistentModifier]) + 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[SemanticallySuccessfulModifier] @@ -113,8 +113,8 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) system.eventStream.subscribe(eventListener.ref, classOf[SemanticallyFailedModification]) - p.send(node, GetDataFromCurrentView[ST, ErgoPersistentModifier] { v => semanticallyInvalidModifier(v.state) }) - val invalid = p.expectMsgClass(classOf[ErgoPersistentModifier]) + 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] @@ -128,8 +128,8 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) system.eventStream.subscribe(eventListener.ref, classOf[SemanticallySuccessfulModifier]) - p.send(node, GetDataFromCurrentView[ST, ErgoPersistentModifier] { v => totallyValidModifiers(v.history, v.state, 2).head }) - val mod = p.expectMsgClass(classOf[ErgoPersistentModifier]) + 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[SemanticallySuccessfulModifier] @@ -167,10 +167,10 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) - p.send(node, GetDataFromCurrentView[ST, Seq[ErgoPersistentModifier]] { v => + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => totallyValidModifiers(v.history, v.state, 10) //todo: fix magic number }) - val mods = p.expectMsgClass(classOf[Seq[ErgoPersistentModifier]]) + val mods = p.expectMsgClass(classOf[Seq[BlockSection]]) mods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) @@ -216,22 +216,22 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallySuccessfulModifier]) system.eventStream.subscribe(eventListener.ref, classOf[SyntacticallyFailedModification]) - p.send(node, GetDataFromCurrentView[ST, Seq[ErgoPersistentModifier]] { v => totallyValidModifiers(v.history, v.state, 2) }) - val initMods = p.expectMsgClass(waitDuration, classOf[Seq[ErgoPersistentModifier]]) + 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, ErgoPersistentModifier] { v => + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => totallyValidModifiers(v.history, v.state, 2).head }) - val fork1Mod = p.expectMsgClass(waitDuration, classOf[ErgoPersistentModifier]) + val fork1Mod = p.expectMsgClass(waitDuration, classOf[BlockSection]) - p.send(node, GetDataFromCurrentView[ST, ErgoPersistentModifier] { v => + p.send(node, GetDataFromCurrentView[ST, BlockSection] { v => totallyValidModifiers(v.history, v.state, 2).head }) - val fork2Mod = p.expectMsgClass(waitDuration, classOf[ErgoPersistentModifier]) + val fork2Mod = p.expectMsgClass(waitDuration, classOf[BlockSection]) p.send(node, LocallyGeneratedModifier(fork1Mod)) p.send(node, LocallyGeneratedModifier(fork2Mod)) @@ -264,23 +264,23 @@ trait NodeViewHolderTests[ST <: ErgoState[ST]] val waitDuration = 10.seconds //some base operations, we don't wanna have fork right from genesis - p.send(node, GetDataFromCurrentView[ST, Seq[ErgoPersistentModifier]] { v => + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => totallyValidModifiers(v.history, v.state, opCountBeforeFork) }) - val plainMods = p.expectMsgClass(waitDuration, classOf[Seq[ErgoPersistentModifier]]) + val plainMods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) plainMods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } - p.send(node, GetDataFromCurrentView[ST, Seq[ErgoPersistentModifier]] { v => + 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[ErgoPersistentModifier]]) + val fork1Mods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) - p.send(node, GetDataFromCurrentView[ST, Seq[ErgoPersistentModifier]] { v => + p.send(node, GetDataFromCurrentView[ST, Seq[BlockSection]] { v => totallyValidModifiers(v.history, v.state, fork2OpCount) }) - val fork2Mods = p.expectMsgClass(waitDuration, classOf[Seq[ErgoPersistentModifier]]) + val fork2Mods = p.expectMsgClass(waitDuration, classOf[Seq[BlockSection]]) fork1Mods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } fork2Mods.foreach { mod => p.send(node, LocallyGeneratedModifier(mod)) } diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index f33dbbd088..305570d7ea 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -2,7 +2,7 @@ package scorex.testkit.properties import akka.actor._ import akka.testkit.TestProbe -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo} import org.ergoplatform.nodeView.mempool.ErgoMemPool @@ -39,7 +39,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val memPool: ErgoMemPool def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, ErgoSyncInfo, ErgoPersistentModifier, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[ErgoPersistentModifier]) + (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[BlockSection]) class SynchronizerFixture extends AkkaFixture { @SuppressWarnings(Array("org.wartremover.warts.PublicInference")) diff --git a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala index 262900e0fc..2666f83ef7 100644 --- a/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala +++ b/src/test/scala/scorex/testkit/properties/state/StateApplicationTest.scala @@ -1,6 +1,6 @@ package scorex.testkit.properties.state -import org.ergoplatform.modifiers.ErgoPersistentModifier +import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.state.{DigestState, ErgoState} import org.scalacheck.Gen import scala.collection.mutable.ListBuffer @@ -8,11 +8,11 @@ import scala.collection.mutable.ListBuffer trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { - lazy val stateGenWithValidModifier: Gen[(ST, ErgoPersistentModifier)] = { + lazy val stateGenWithValidModifier: Gen[(ST, BlockSection)] = { stateGen.map { s => (s, semanticallyValidModifier(s)) } } - lazy val stateGenWithInvalidModifier: Gen[(ST, ErgoPersistentModifier)] = { + lazy val stateGenWithInvalidModifier: Gen[(ST, BlockSection)] = { stateGen.map { s => (s, semanticallyInvalidModifier(s))} } @@ -76,7 +76,7 @@ trait StateApplicationTest[ST <: ErgoState[ST]] extends StateTests[ST] { } @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val rollbackDepth = Gen.chooseNum(1, maxRollbackDepth).sample.get - val buf = new ListBuffer[ErgoPersistentModifier]() + val buf = new ListBuffer[BlockSection]() val ver = s.version val s2 = (0 until rollbackDepth).foldLeft(s) { case (state, _) => From 6b2a24856843d0beb5fdf3dfd58a87ea84837c20 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 29 Nov 2021 17:05:22 +0300 Subject: [PATCH 022/204] optimizing apply code in *Synchronizer actors --- .../network/ErgoNodeViewSynchronizer.scala | 31 +++++++------------ .../core/network/PeerSynchronizer.scala | 24 +++++++------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6d165f9490..8cc46b9864 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -761,25 +761,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, object ErgoNodeViewSynchronizer { - def props(networkControllerRef: ActorRef, - viewHolderRef: ActorRef, - syncInfoSpec: ErgoSyncInfoMessageSpec.type, - settings: ErgoSettings, - timeProvider: NetworkTimeProvider, - syncTracker: ErgoSyncTracker) - (implicit ex: ExecutionContext): Props = - Props(new ErgoNodeViewSynchronizer(networkControllerRef, viewHolderRef, syncInfoSpec, settings, - timeProvider, syncTracker)) - - def apply(networkControllerRef: ActorRef, - viewHolderRef: ActorRef, - syncInfoSpec: ErgoSyncInfoMessageSpec.type, - settings: ErgoSettings, - timeProvider: NetworkTimeProvider, - syncTracker: ErgoSyncTracker) - (implicit context: ActorRefFactory, ex: ExecutionContext): ActorRef = - context.actorOf(props(networkControllerRef, viewHolderRef, syncInfoSpec, settings, timeProvider, syncTracker)) - case object CheckModifiersToDownload object Events { @@ -874,4 +855,16 @@ object ErgoNodeViewSynchronizer { case object UnknownOrFork extends PeerSyncState } + def apply(networkControllerRef: ActorRef, + viewHolderRef: ActorRef, + syncInfoSpec: ErgoSyncInfoMessageSpec.type, + settings: ErgoSettings, + timeProvider: NetworkTimeProvider, + syncTracker: ErgoSyncTracker) + (implicit context: ActorRefFactory, ex: ExecutionContext): ActorRef = { + val props = Props(new ErgoNodeViewSynchronizer(networkControllerRef, viewHolderRef, syncInfoSpec, settings, + timeProvider, syncTracker)) + context.actorOf(props) + } + } diff --git a/src/main/scala/scorex/core/network/PeerSynchronizer.scala b/src/main/scala/scorex/core/network/PeerSynchronizer.scala index 0ebd92cda5..6ff903f221 100644 --- a/src/main/scala/scorex/core/network/PeerSynchronizer.scala +++ b/src/main/scala/scorex/core/network/PeerSynchronizer.scala @@ -83,15 +83,15 @@ class PeerSynchronizer(val networkControllerRef: ActorRef, } object PeerSynchronizerRef { - def props(networkControllerRef: ActorRef, peerManager: ActorRef, settings: NetworkSettings, - featureSerializers: PeerFeature.Serializers)(implicit ec: ExecutionContext): Props = - Props(new PeerSynchronizer(networkControllerRef, peerManager, settings, featureSerializers)) - - def apply(networkControllerRef: ActorRef, peerManager: ActorRef, settings: NetworkSettings, - featureSerializers: PeerFeature.Serializers)(implicit system: ActorSystem, ec: ExecutionContext): ActorRef = - system.actorOf(props(networkControllerRef, peerManager, settings, featureSerializers)) - - def apply(name: String, networkControllerRef: ActorRef, peerManager: ActorRef, settings: NetworkSettings, - featureSerializers: PeerFeature.Serializers)(implicit system: ActorSystem, ec: ExecutionContext): ActorRef = - system.actorOf(props(networkControllerRef, peerManager, settings, featureSerializers), name) -} \ No newline at end of file + + def apply(name: String, + networkControllerRef: ActorRef, + peerManager: ActorRef, + settings: NetworkSettings, + featureSerializers: PeerFeature.Serializers) + (implicit system: ActorSystem, ec: ExecutionContext): ActorRef = { + val props = Props(new PeerSynchronizer(networkControllerRef, peerManager, settings, featureSerializers)) + system.actorOf(props, name) + } + +} From a76d546ef7c7a69768e44ca047ccde795f1ccebf Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 29 Nov 2021 17:14:47 +0300 Subject: [PATCH 023/204] utxoNetworkingSupported --- .../network/ErgoNodeViewSynchronizer.scala | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 8cc46b9864..074013bf9c 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -56,6 +56,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, )(implicit ex: ExecutionContext) extends Actor with Synchronizer with ScorexLogging with ScorexEncoding { + import ErgoNodeViewSynchronizer.syncV2Supported + // bloom filters with transaction ids that were already applied to history private var blockAppliedTxsCache: FixedSizeBloomFilterQueue = FixedSizeBloomFilterQueue.empty(bloomFilterQueueSize = 5) @@ -139,15 +141,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.status(id, Array(historyReader)) == ModifiersStatus.Unknown } - /** - * Whether neighbour peer `remote` supports sync protocol V2. - */ - def syncV2Supported(remote: ConnectedPeer): Boolean = { - // If neighbour version is >= 4.0.16, the neighbour supports sync V2 - val syncV2Version = Version(4, 0, 16) - remote.peerInfo.exists(_.peerSpec.protocolVersion >= syncV2Version) - } - /** * Send synchronization statuses to neighbour peers * @@ -761,6 +754,23 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, object ErgoNodeViewSynchronizer { + val UtxoSnapsnotActivationVersion = Version(4, 0, 20) + val SyncV2ActivationVersion = Version(4, 0, 16) + + + def utxoNetworkingSupported(remote: ConnectedPeer): Boolean = { + // If neighbour version is >= 4.0.20, the neighbour supports utxo snapshots exchange + remote.peerInfo.exists(_.peerSpec.protocolVersion >= UtxoSnapsnotActivationVersion) + } + + /** + * Whether neighbour peer `remote` supports sync protocol V2. + */ + def syncV2Supported(remote: ConnectedPeer): Boolean = { + // If neighbour version is >= 4.0.16, the neighbour supports sync V2 + remote.peerInfo.exists(_.peerSpec.protocolVersion >= SyncV2ActivationVersion) + } + case object CheckModifiersToDownload object Events { From 035291210ff6e728e44d35c8d15e7b5dba2e8fcb Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Mon, 6 Dec 2021 15:21:30 +0300 Subject: [PATCH 024/204] Implement getSnapshotsInfo message refer #1517 --- src/main/resources/api/openapi.yaml | 33 +++++++++++++++++++ .../org/ergoplatform/http/api/ApiCodecs.scala | 16 +++++++++ .../ergoplatform/http/api/UtxoApiRoute.scala | 7 ++++ .../nodeView/state/UtxoStateReader.scala | 4 +++ 4 files changed, 60 insertions(+) diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index f6f7ee736e..abb4a52a95 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -94,6 +94,17 @@ components: bytes: $ref: '#/components/schemas/HexString' + SnapshotsInfo: + type: object + required: + - availableManifests + properties: + availableManifests: + description: Map of available manifests height -> manifestId + type: array + items: + type: object + ErgoTransactionOutput: type: object required: @@ -4178,6 +4189,28 @@ paths: schema: $ref: '#/components/schemas/ApiError' + /utxo/getSnapshotsInfo: + get: + summary: Get information about saved UTXO snapshots + operationId: getSnapshotsInfo + tags: + - utxo + responses: + '200': + description: A list of saved snapshots + content: + application/json: + schema: + $ref: '#/components/schemas/SnapshotsInfo' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + + + /utxo/genesis: get: summary: Get genesis boxes (boxes existed before the very first block) diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 319cd43290..eeb280c199 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -29,6 +29,8 @@ import sigmastate.interpreter._ import sigmastate.interpreter.CryptoConstants.EcPointType import io.circe.syntax._ import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest, HintExtractionRequest} +import org.ergoplatform.nodeView.state.SnapshotsInfo +import org.ergoplatform.nodeView.state.UtxoState.ManifestId import scorex.crypto.authds.{LeafData, Side} import scorex.crypto.authds.merkle.MerkleProof import scorex.crypto.hash.Digest @@ -396,6 +398,20 @@ trait ApiCodecs extends JsonCodecs { } 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 SnapshotsInfo(availableManifests) + } + implicit val transactionSigningRequestEncoder: Encoder[TransactionSigningRequest] = { tsr => Json.obj( "tx" -> tsr.unsignedTx.asJson, diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index c350e46690..f9dc19fabc 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -74,4 +74,11 @@ case class UtxoApiRoute(readersHolder: ActorRef, override val settings: RESTApiS ApiResponse(getState.map(_.genesisBoxes)) } + def getSnapshotsInfo: Route = (get & path("getSnapshotsInfo")) { + ApiResponse(getState.map { + case usr: UtxoStateReader => + usr.getSnapshotInfo() + case _ => None + }) + } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 547122fa55..7b570983c0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -139,4 +139,8 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { */ def withMempool(mp: ErgoMemPoolReader): UtxoState = withTransactions(mp.getAll) + def getSnapshotInfo(): Option[SnapshotsInfo] = { + val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) + snapshotsDb.readSnapshotsInfo + } } From 58d9a1b54ae999c5bfd33b37c4821d27d46fdb20 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Tue, 7 Dec 2021 12:58:05 +0300 Subject: [PATCH 025/204] Add GetSnapshotsInfoSpec/SnapshotsInfoSpec --- src/main/scala/org/ergoplatform/ErgoApp.scala | 3 +- .../network/message/BasicMessagesRepo.scala | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 82d0165c2f..17a00514ab 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -67,7 +67,8 @@ class ErgoApp(args: Args) extends ScorexLogging { new PeersSpec(featureSerializers, scorexSettings.network.maxPeerSpecObjects), invSpec, requestModifierSpec, - modifiersSpec + modifiersSpec, + new GetSnapshotsInfoSpec ) } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index b1fcc04031..4e32435194 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -1,11 +1,14 @@ package scorex.core.network.message +import org.ergoplatform.nodeView.state.SnapshotsInfo +import org.ergoplatform.wallet.Constants import scorex.core.consensus.SyncInfo import scorex.core.network._ import scorex.core.network.message.Message.MessageCode import scorex.core.serialization.ScorexSerializer import scorex.core.{ModifierTypeId, NodeViewModifier} +import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} @@ -273,3 +276,46 @@ class HandshakeSpec(featureSerializers: PeerFeature.Serializers, sizeLimit: Int) Handshake(data, t) } } + + +/** + * The `GetSnapshotsInfo` message requests an `SnapshotsInfo` message from the receiving node + */ +class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { + override val messageCode: Message.MessageCode = 76: Byte + + override val messageName: String = "GetSnapshotsInfo" + + override def serialize(obj: Unit, w: Writer): Unit = { + } + + override def parse(r: Reader): Unit = { + require(r.remaining == 0, "Non-empty data for GetPeers") + } +} + +/** + * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. + */ +class SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { + override val messageCode: Message.MessageCode = 77: Byte + + override val messageName: String = "SnapshotsInfo" + + override def serialize(si: SnapshotsInfo, w: Writer): Unit = { + w.putUInt(si.availableManifests.size) + for ((height, manifest) <- si.availableManifests) { + w.putInt(height) + w.putBytes(manifest) + } + } + + override def parse(r: Reader): SnapshotsInfo = { + val length = r.getUInt().toIntExact + SnapshotsInfo((0 until length).map { _ => + val height = r.getInt() + val manifest = Digest32 @@ r.getBytes(Constants.ModifierIdLength) + height -> manifest + }.toMap) + } +} From 2d3f4d9defe24bbbad9a3f4639c7194b679c2937 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Tue, 7 Dec 2021 22:25:31 +0300 Subject: [PATCH 026/204] fromLatestSnapshot --- .../nodeView/state/SnapshotsInfo.scala | 4 +-- .../nodeView/state/UtxoState.scala | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 8603df4ad6..63c336b9fe 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.state import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.nodeView.state.UtxoState.ManifestId +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} import org.ergoplatform.wallet.Constants @@ -95,7 +95,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { store.get(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) } - def readSubtreeBytes(id: ManifestId): Option[BatchAVLProverSubtree[Digest32]] = { + def readSubtreeBytes(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { store.get(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index a3b78ce774..b7135e2109 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -8,10 +8,10 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{ErgoFullBlock, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.{Algos, ErgoAlgos} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -23,6 +23,7 @@ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} +import scorex.util.ScorexLogging import scala.util.{Failure, Success, Try} @@ -179,7 +180,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } -object UtxoState { +object UtxoState extends ScorexLogging { type Manifest = BatchAVLProverManifest[Digest32] type Subtree = BatchAVLProverSubtree[Digest32] @@ -245,4 +246,32 @@ object UtxoState { new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) } + def fromLatestSnapshot(dir: File, + snapshotDb: SnapshotsDb, + constants: StateConstants): Try[UtxoState] = Try { + snapshotDb.readSnapshotsInfo match { + case Some(snapshotsInfo) => + val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) + log.info(s"Reading snapshot from height $h") + val manifest = snapshotDb.readManifestBytes(manifestId).get + val subtreeIds = manifest.subtreesIds + val subtrees = subtreeIds.map(sid => snapshotDb.readSubtreeBytes(sid).get) + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get + + //todo: code below is mostly copied from .create, unify ? + val store = new LDBVersionedStore(dir, keepVersions = constants.keepVersions) + val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) + .getOrElse(ErgoState.genesisStateVersion) + val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { + val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) + val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + PersistentBatchAVLProver.create(prover, storage).get + } + new UtxoState(persistentProver, version, store, constants) + + case None => ??? + } + } + } From 80ed88406e51ebc67a88eaea1dd7facf29657e67 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Fri, 10 Dec 2021 18:39:09 +0300 Subject: [PATCH 027/204] Handle GetSnapshotsInfo message in ErgoNodeViewSycnhronizer refer #1517 --- src/main/scala/org/ergoplatform/ErgoApp.scala | 2 +- .../network/ErgoNodeViewSynchronizer.scala | 51 ++++++++++++++----- .../scala/scorex/core/app/Application.scala | 3 +- .../network/message/BasicMessagesRepo.scala | 5 +- .../NodeViewSynchronizerTests.scala | 28 +++++++++- 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 17a00514ab..cbcbbfa2e3 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -68,7 +68,7 @@ class ErgoApp(args: Args) extends ScorexLogging { invSpec, requestModifierSpec, modifiersSpec, - new GetSnapshotsInfoSpec + GetSnapshotsInfoSpec ) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6d165f9490..e2f68b1dc5 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -5,7 +5,7 @@ import java.net.InetSocketAddress import akka.actor.{Actor, ActorRef, ActorRefFactory, Props} 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.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import org.ergoplatform.network.ErgoNodeViewSynchronizer.{CheckModifiersToDownload, PeerSyncState} @@ -22,11 +22,10 @@ import scorex.core.network.ModifiersStatus.Requested import scorex.core.{ModifierTypeId, NodeViewModifier, PersistentNodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, RegisterMessageSpecs} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.ErgoStateReader -import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} +import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} +import scorex.core.network.message.{GetSnapshotsInfoSpec, InvData, InvSpec, Message, MessageSpec, ModifiersData, ModifiersSpec, RequestModifierSpec, SnapshotsInfoSpec} import scorex.core.network._ import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork -import scorex.core.network.message.{InvData, Message, ModifiersData} import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPeers} import scorex.core.serialization.ScorexSerializer import scorex.core.settings.NetworkSettings @@ -70,10 +69,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected val modifiersSpec = new ModifiersSpec(networkSettings.maxPacketSize) protected val msgHandlers: PartialFunction[(MessageSpec[_], _, ConnectedPeer), Unit] = { - case (_: ErgoSyncInfoMessageSpec.type @unchecked, data: ErgoSyncInfo @unchecked, remote) => processSync(data, remote) - case (_: InvSpec, data: InvData, remote) => processInv(data, remote) - case (_: RequestModifierSpec, data: InvData, remote) => modifiersReq(data, remote) - case (_: ModifiersSpec, data: ModifiersData, remote) => modifiersFromRemote(data, remote) + case (_: ErgoSyncInfoMessageSpec.type@unchecked, data: ErgoSyncInfo@unchecked, remote) => processSync(data, remote) + case (_: InvSpec, data: InvData, remote) => processInv(data, remote) + case (_: RequestModifierSpec, data: InvData, remote) => modifiersReq(data, remote) + case (_: ModifiersSpec, data: ModifiersData, remote) => modifiersFromRemote(data, remote) + case (spec: MessageSpec[_], _, remote) if spec.messageCode == GetSnapshotsInfoSpec.messageCode => sendSnapshotsInfo(remote) } protected val deliveryTracker: DeliveryTracker = @@ -81,6 +81,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected var historyReaderOpt: Option[ErgoHistory] = None protected var mempoolReaderOpt: Option[ErgoMemPool] = None + protected var utxoStateReaderOpt: Option[UtxoStateReader] = None /** * Approximate number of modifiers to be downloaded simultaneously, headers are much faster to process @@ -316,6 +317,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, /** * Headers should be downloaded from an Older node, it is triggered by received sync message from an older node + * * @param callingPeer that can be used to download headers, it must be Older * @return available peers to download headers from together with the state/origin of the peer */ @@ -328,6 +330,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, /** * Other persistent modifiers besides headers should be downloaded from either Older or Equal node, with fallback to Unknown or Fork + * * @return available peers to download persistent modifiers from together with the state/origin of the peer */ private def getPeersForDownloadingBlocks: Option[(PeerSyncState, Iterable[ConnectedPeer])] = { @@ -345,11 +348,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, /** * Modifier download method that is given min/max constraints for modifiers to download from peers. * It sends requests for modifiers to given peers in optimally sized batches. - * @param maxModifiers maximum modifiers to download + * + * @param maxModifiers maximum modifiers to download * @param minModifiersPerBucket minimum modifiers to download per bucket * @param maxModifiersPerBucket maximum modifiers to download per bucket - * @param getPeersOpt optionally get peers to download from, all peers have the same PeerSyncState - * @param fetchMax function that fetches modifiers, it is passed how many of them tops + * @param getPeersOpt optionally get peers to download from, all peers have the same PeerSyncState + * @param fetchMax function that fetches modifiers, it is passed how many of them tops */ protected def requestDownload(maxModifiers: Int, minModifiersPerBucket: Int, maxModifiersPerBucket: Int) (getPeersOpt: => Option[(PeerSyncState, Iterable[ConnectedPeer])]) @@ -474,8 +478,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return ids and bytes of modifiers that were requested by our node */ def processSpam(remote: ConnectedPeer, - typeId: ModifierTypeId, - modifiers: Map[ModifierId, Array[Byte]]): Map[ModifierId, Array[Byte]] = { + typeId: ModifierTypeId, + modifiers: Map[ModifierId, Array[Byte]]): Map[ModifierId, Array[Byte]] = { val (requested, spam) = modifiers.partition { case (id, _) => deliveryTracker.status(id) == Requested } @@ -496,6 +500,22 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, requested } + protected def sendSnapshotsInfo(peer: ConnectedPeer): Unit = { + utxoStateReaderOpt match { + case Some(reader) => { + val snapInfoSpec = new SnapshotsInfoSpec + reader.getSnapshotInfo() match { + case Some(snapInfo) => { + val msg = Message(snapInfoSpec, Right(snapInfo), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) + } + case _ => log.warn(s"No snapshots avaialble") + } + } + case _ => log.warn(s"Got data from peer while readers are not ready") + } + } + /** * Object ids coming from other node. * Filter out modifier ids that are already in process (requested, received or applied), @@ -734,6 +754,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case ChangedMempool(reader: ErgoMemPool) => mempoolReaderOpt = Some(reader) + case ChangedUtxoState(reader: UtxoStateReader) => + utxoStateReaderOpt = Some(reader) + case ModifiersProcessingResult(applied: Seq[BlockSection], cleared: Seq[BlockSection]) => // stop processing for cleared modifiers // applied modifiers state was already changed at `SyntacticallySuccessfulModifier` @@ -827,6 +850,8 @@ object ErgoNodeViewSynchronizer { case class ChangedState(reader: ErgoStateReader) extends NodeViewChange + case class ChangedUtxoState(reader: UtxoStateReader) extends NodeViewChange + //todo: consider sending info on the rollback case object RollbackFailed extends NodeViewHolderEvent diff --git a/src/main/scala/scorex/core/app/Application.scala b/src/main/scala/scorex/core/app/Application.scala index bc8bf6bab5..8ecbdba9a3 100644 --- a/src/main/scala/scorex/core/app/Application.scala +++ b/src/main/scala/scorex/core/app/Application.scala @@ -51,7 +51,8 @@ trait Application extends ScorexLogging { new PeersSpec(featureSerializers, settings.network.maxPeerSpecObjects), invSpec, requestModifierSpec, - modifiersSpec + modifiersSpec, + GetSnapshotsInfoSpec ) } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 4e32435194..224137d9e8 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -281,7 +281,7 @@ class HandshakeSpec(featureSerializers: PeerFeature.Serializers, sizeLimit: Int) /** * The `GetSnapshotsInfo` message requests an `SnapshotsInfo` message from the receiving node */ -class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { +object GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { override val messageCode: Message.MessageCode = 76: Byte override val messageName: String = "GetSnapshotsInfo" @@ -298,8 +298,7 @@ class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. */ class SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { - override val messageCode: Message.MessageCode = 77: Byte - + override val messageCode: MessageCode = 77: Byte override val messageName: String = "SnapshotsInfo" override def serialize(si: SnapshotsInfo, w: Writer): Unit = { diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index 305570d7ea..ed0b9af945 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -13,14 +13,14 @@ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeV import scorex.core.consensus.SyncInfo import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.ErgoState +import org.ergoplatform.nodeView.state.{ErgoState} import scorex.core.network._ import scorex.core.network.message._ import scorex.core.network.peer.PenaltyType import scorex.core.serialization.{BytesSerializable, ScorexSerializer} import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture -import scorex.util.ScorexLogging +import scorex.util.{ScorexLogging} import scorex.util.serialization._ import scala.concurrent.Await @@ -226,4 +226,28 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec } } + property("NodeViewSynchronizer: GetSnapshotInfo") { + withFixture { ctx => + import ctx._ + + /* First store snapshots info in DB + val m = (0 until 100).map { _ => + Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mod)) + }.toMap; + val si = SnapshotsInfo(m) + val dir = createTempDir.getAbsolutePath + val db = SnapshotsDb.create(dir) + db.writeSnapshotsInfo(si) + */ + + // Then send message to request it + node ! Message[Unit](GetSnapshotsInfoSpec, Right(Unit), None) + pchProbe.fishForMessage(5 seconds) { + case _: SnapshotsInfoSpec => true + case _ => false + } + + + } + } } From 7c743dc8a7ad35896d8e18c4a91515a85ef348fb Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Tue, 14 Dec 2021 11:46:44 +0300 Subject: [PATCH 028/204] fixing messages sizes --- .../scorex/core/network/message/BasicMessagesRepo.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 4e32435194..c7052c3c9c 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -282,6 +282,8 @@ class HandshakeSpec(featureSerializers: PeerFeature.Serializers, sizeLimit: Int) * The `GetSnapshotsInfo` message requests an `SnapshotsInfo` message from the receiving node */ class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { + private val SizeLimit = 100 + override val messageCode: Message.MessageCode = 76: Byte override val messageName: String = "GetSnapshotsInfo" @@ -290,7 +292,7 @@ class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { } override def parse(r: Reader): Unit = { - require(r.remaining == 0, "Non-empty data for GetPeers") + require(r.remaining < SizeLimit, "Too big GetSnapshotsInfo message") } } @@ -298,6 +300,8 @@ class GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. */ class SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { + private val SizeLimit = 1000 + override val messageCode: Message.MessageCode = 77: Byte override val messageName: String = "SnapshotsInfo" @@ -311,6 +315,8 @@ class SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { } override def parse(r: Reader): SnapshotsInfo = { + require(r.remaining <= SizeLimit, s"Too big SnapshotsInfo message.") + val length = r.getUInt().toIntExact SnapshotsInfo((0 until length).map { _ => val height = r.getInt() From f9c6ead1d5bfb2f0d7ecd0b93c8a8fdac6d12d15 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Tue, 14 Dec 2021 12:32:02 +0300 Subject: [PATCH 029/204] ChangedState --- .../network/ErgoNodeViewSynchronizer.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 80da8524ff..eeebd7eb58 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -119,6 +119,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, context.system.eventStream.subscribe(self, classOf[BlockAppliedTransactions]) context.system.eventStream.subscribe(self, classOf[ModifiersProcessingResult]) + context.system.eventStream.subscribe(self, classOf[ChangedState]) + // subscribe for history and mempool changes viewHolderRef ! GetNodeViewChanges(history = true, state = false, vault = false, mempool = true) @@ -746,8 +748,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case ChangedMempool(reader: ErgoMemPool) => mempoolReaderOpt = Some(reader) - case ChangedUtxoState(reader: UtxoStateReader) => - utxoStateReaderOpt = Some(reader) + case ChangedState(reader: ErgoStateReader) => + reader match { + case utxoStateReader: UtxoStateReader => utxoStateReaderOpt = Some(utxoStateReader) + case _ => + } case ModifiersProcessingResult(applied: Seq[BlockSection], cleared: Seq[BlockSection]) => // stop processing for cleared modifiers @@ -840,8 +845,6 @@ object ErgoNodeViewSynchronizer { case class ChangedState(reader: ErgoStateReader) extends NodeViewChange - case class ChangedUtxoState(reader: UtxoStateReader) extends NodeViewChange - //todo: consider sending info on the rollback case object RollbackFailed extends NodeViewHolderEvent From 26f4f3c21ec53743068fb389939ec2fe70aed611 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Sat, 18 Dec 2021 22:10:57 +0300 Subject: [PATCH 030/204] Fix sending GetSnapshotsInfo message --- .../scorex/testkit/properties/NodeViewSynchronizerTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index 78cc539352..2698b73dc8 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -241,7 +241,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec */ // Then send message to request it - node ! Message[Unit](GetSnapshotsInfoSpec, Right(Unit), None) + node ! Message[Unit](GetSnapshotsInfoSpec, Left(Array.empty[Byte]), Option(peer)) pchProbe.fishForMessage(5 seconds) { case _: SnapshotsInfoSpec.type => true case _ => false From 6c63fa7c9d2d29581a0bc8f9a52938680138c685 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Tue, 21 Dec 2021 20:30:16 +0300 Subject: [PATCH 031/204] fixing GetSnapshotInfo --- .../NodeViewSynchronizerTests.scala | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index 2698b73dc8..ce79bd2efb 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -13,11 +13,13 @@ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeV import scorex.core.consensus.SyncInfo import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.ErgoState +import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoStateReader} +import org.ergoplatform.settings.Algos import scorex.core.network._ import scorex.core.network.message._ import scorex.core.network.peer.PenaltyType import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture import scorex.util.ScorexLogging @@ -27,6 +29,7 @@ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.language.postfixOps +import scala.util.Random @SuppressWarnings(Array("org.wartremover.warts.IsInstanceOf")) trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec @@ -38,6 +41,8 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val historyGen: Gen[ErgoHistory] val memPool: ErgoMemPool + val stateGen: Gen[ST] + def nodeViewSynchronizer(implicit system: ActorSystem): (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[BlockSection]) @@ -230,24 +235,30 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec withFixture { ctx => import ctx._ - /* First store snapshots info in DB - val m = (0 until 100).map { _ => - Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mod)) - }.toMap; - val si = SnapshotsInfo(m) - val dir = createTempDir.getAbsolutePath - val db = SnapshotsDb.create(dir) - db.writeSnapshotsInfo(si) - */ - - // Then send message to request it - node ! Message[Unit](GetSnapshotsInfoSpec, Left(Array.empty[Byte]), Option(peer)) - pchProbe.fishForMessage(5 seconds) { - case _: SnapshotsInfoSpec.type => true - case _ => false + val s = stateGen.sample.get + + if(s.isInstanceOf[UtxoStateReader]) { + // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer + node ! ChangedState(s) + + // First, store snapshots info in DB + val m = (0 until 100).map { _ => + Random.nextInt(1000000) -> (Digest32 @@ Algos.decode(mod.id).get) + }.toMap + val si = SnapshotsInfo(m) + val db = SnapshotsDb.create(s.constants.settings) + db.writeSnapshotsInfo(si) + + // Then send message to request it + node ! Message[Unit](GetSnapshotsInfoSpec, Left(Array.empty[Byte]), Option(peer)) + ncProbe.fishForMessage(5 seconds) { + case stn: SendToNetwork if stn.message.spec.isInstanceOf[SnapshotsInfoSpec.type] => true + case _: Any => false + } + } else { + log.info("Snapshots not supported by digest-state") } - - } } + } From a77205fe8c6300ce154b35b65c90f583b6216b16 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 30 Dec 2021 12:46:16 +0300 Subject: [PATCH 032/204] fixing getsnapshot processing --- .../network/ErgoNodeViewSynchronizer.scala | 68 ++++++++++++------- .../NodeViewSynchronizerTests.scala | 2 +- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index b63b390c7d..260fa09f1f 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -483,18 +483,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, modifiersByStatus.getOrElse(Requested, Map.empty) } - protected def sendSnapshotsInfo(peer: ConnectedPeer): Unit = { - utxoStateReaderOpt match { - case Some(reader) => { - reader.getSnapshotInfo() match { - case Some(snapInfo) => { - val msg = Message(SnapshotsInfoSpec, Right(snapInfo), None) - networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) - } - case _ => log.warn(s"No snapshots avaialble") - } + protected def sendSnapshotsInfo(usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + usr.getSnapshotInfo() match { + case Some(snapInfo) => { + val msg = Message(SnapshotsInfoSpec, Right(snapInfo), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - case _ => log.warn(s"Got data from peer while readers are not ready") + case _ => log.warn(s"No snapshots avaialble") } } @@ -674,7 +669,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Message(spec, Left(msgBytes), Some(source)) => parseAndHandle(msgHandlers, spec, msgBytes, source) } - protected def viewHolderEvents(historyReader: ErgoHistory, mempoolReader: ErgoMemPool, blockAppliedTxsCache: FixedSizeBloomFilterQueue): Receive = { + protected def viewHolderEvents(historyReader: ErgoHistory, + mempoolReader: ErgoMemPool, + utxoStateReaderOpt: Option[UtxoStateReader], + blockAppliedTxsCache: FixedSizeBloomFilterQueue): Receive = { // Requests BlockSections with `Unknown` status that are defined by block headers but not downloaded yet. // Trying to keep size of requested queue equals to `desiredSizeOfExpectingQueue`. @@ -711,14 +709,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.setInvalid(mod.id, mod.modifierTypeId).foreach(penalizeMisbehavingPeer) case ChangedHistory(newHistoryReader: ErgoHistory) => - context.become(initialized(newHistoryReader, mempoolReader, blockAppliedTxsCache)) + context.become(initialized(newHistoryReader, mempoolReader, utxoStateReaderOpt, blockAppliedTxsCache)) case ChangedMempool(newMempoolReader: ErgoMemPool) => - context.become(initialized(historyReader, newMempoolReader, blockAppliedTxsCache)) + context.become(initialized(historyReader, newMempoolReader, utxoStateReaderOpt, blockAppliedTxsCache)) case ChangedState(reader: ErgoStateReader) => reader match { - case utxoStateReader: UtxoStateReader => utxoStateReaderOpt = Some(utxoStateReader) + case utxoStateReader: UtxoStateReader => + context.become(initialized(historyReader, mempoolReader, Some(utxoStateReader), blockAppliedTxsCache)) case _ => } @@ -731,12 +730,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case BlockAppliedTransactions(transactionIds: Seq[ModifierId]) => // We collect applied TXs to history in order to avoid banning peers that sent these afterwards logger.info("Caching applied transactions") - context.become(initialized(historyReader, mempoolReader, blockAppliedTxsCache.putAll(transactionIds))) + context.become(initialized(historyReader, mempoolReader, utxoStateReaderOpt, blockAppliedTxsCache.putAll(transactionIds))) } /** get handlers of messages coming from peers */ private def msgHandlers(hr: ErgoHistory, mp: ErgoMemPool, + usrOpt: Option[UtxoStateReader], blockAppliedTxsCache: FixedSizeBloomFilterQueue ): PartialFunction[(MessageSpec[_], _, ConnectedPeer), Unit] = { case (_: ErgoSyncInfoMessageSpec.type @unchecked, data: ErgoSyncInfo @unchecked, remote) => @@ -747,14 +747,22 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, modifiersReq(hr, mp, data, remote) case (_: ModifiersSpec, data: ModifiersData, remote) => modifiersFromRemote(hr, data, remote, blockAppliedTxsCache) + case (spec: MessageSpec[_], _, remote) if spec.messageCode == GetSnapshotsInfoSpec.messageCode => + usrOpt match { + case Some(usr) => sendSnapshotsInfo(usr, remote) + case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") + } } - def initialized(hr: ErgoHistory, mp: ErgoMemPool, blockAppliedTxsCache: FixedSizeBloomFilterQueue): PartialFunction[Any, Unit] = { - processDataFromPeer(msgHandlers(hr, mp, blockAppliedTxsCache)) orElse + def initialized(hr: ErgoHistory, + mp: ErgoMemPool, + usr: Option[UtxoStateReader], + blockAppliedTxsCache: FixedSizeBloomFilterQueue): PartialFunction[Any, Unit] = { + processDataFromPeer(msgHandlers(hr, mp, usr, blockAppliedTxsCache)) orElse onDownloadRequest(hr) orElse getLocalSyncInfo(hr) orElse responseFromLocal orElse - viewHolderEvents(hr, mp, blockAppliedTxsCache) orElse + viewHolderEvents(hr, mp, usr, blockAppliedTxsCache) orElse peerManagerEvents orElse checkDelivery orElse { case a: Any => log.error("Strange input: " + a) @@ -762,27 +770,37 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } /** Wait until both historyReader and mempoolReader instances are received so actor can be operational */ - def initializing(hr: Option[ErgoHistory], mp: Option[ErgoMemPool], blockAppliedTxsCache: FixedSizeBloomFilterQueue): PartialFunction[Any, Unit] = { + def initializing(hr: Option[ErgoHistory], + mp: Option[ErgoMemPool], + usr: Option[UtxoStateReader], + blockAppliedTxsCache: FixedSizeBloomFilterQueue): PartialFunction[Any, Unit] = { case ChangedHistory(historyReader: ErgoHistory) => mp match { case Some(mempoolReader) => - context.become(initialized(historyReader, mempoolReader, blockAppliedTxsCache)) + context.become(initialized(historyReader, mempoolReader, usr, blockAppliedTxsCache)) case _ => - context.become(initializing(Option(historyReader), mp, blockAppliedTxsCache)) + context.become(initializing(Option(historyReader), mp, usr, blockAppliedTxsCache)) } case ChangedMempool(mempoolReader: ErgoMemPool) => hr match { case Some(historyReader) => - context.become(initialized(historyReader, mempoolReader, blockAppliedTxsCache)) + context.become(initialized(historyReader, mempoolReader, usr, blockAppliedTxsCache)) + case _ => + context.become(initializing(hr, Option(mempoolReader), usr, blockAppliedTxsCache)) + } + case ChangedState(reader: ErgoStateReader) => + reader match { + case utxoStateReader: UtxoStateReader => + context.become(initializing(hr, mp, Some(utxoStateReader), blockAppliedTxsCache)) case _ => - context.become(initializing(hr, Option(mempoolReader), blockAppliedTxsCache)) + context.become(initializing(hr, mp, None, blockAppliedTxsCache)) } case msg => // Actor not initialized yet, scheduling message until it is context.system.scheduler.scheduleOnce(1.second, self, msg) } - override def receive: Receive = initializing(None, None, FixedSizeBloomFilterQueue.empty(bloomFilterQueueSize = 5)) + override def receive: Receive = initializing(None, None, None, FixedSizeBloomFilterQueue.empty(bloomFilterQueueSize = 5)) } diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index ce79bd2efb..3a27bf461b 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -237,7 +237,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val s = stateGen.sample.get - if(s.isInstanceOf[UtxoStateReader]) { + if (s.isInstanceOf[UtxoStateReader]) { // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer node ! ChangedState(s) From f37c72ee5d5074346d6ca9846c9ad52cb7953cf1 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Tue, 11 Jan 2022 17:43:34 +0300 Subject: [PATCH 033/204] Add GetManifest/GetSnapshotChunk messages refer #1552 --- src/main/scala/org/ergoplatform/ErgoApp.scala | 4 +- .../network/ErgoNodeViewSynchronizer.scala | 35 +++++++- .../nodeView/state/SnapshotsInfo.scala | 16 +++- .../nodeView/state/UtxoState.scala | 4 +- .../nodeView/state/UtxoStateReader.scala | 13 ++- .../scala/scorex/core/app/Application.scala | 4 +- .../network/message/BasicMessagesRepo.scala | 88 +++++++++++++++++++ .../NodeViewSynchronizerTests.scala | 73 +++++++++++++++ 8 files changed, 226 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index cbcbbfa2e3..709948f8f3 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -68,7 +68,9 @@ class ErgoApp(args: Args) extends ScorexLogging { invSpec, requestModifierSpec, modifiersSpec, - GetSnapshotsInfoSpec + GetSnapshotsInfoSpec, + new GetManifestSpec, + new GetUtxoSnapshotChunkSpec ) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 260fa09f1f..e130524b95 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1,8 +1,8 @@ package org.ergoplatform.network import akka.actor.SupervisorStrategy.{Restart, Stop} - import java.net.InetSocketAddress + import akka.actor.{Actor, ActorInitializationException, ActorKilledException, ActorRef, ActorRefFactory, DeathPactException, OneForOneStrategy, Props} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction @@ -23,6 +23,7 @@ import scorex.core.network.ModifiersStatus.Requested import scorex.core.{ModifierTypeId, NodeViewModifier, PersistentNodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, RegisterMessageSpecs} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} import scorex.core.network.message._ import scorex.core.network._ @@ -489,7 +490,27 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val msg = Message(SnapshotsInfoSpec, Right(snapInfo), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - case _ => log.warn(s"No snapshots avaialble") + case _ => log.warn(s"No snapshots available") + } + } + + protected def sendManifest(id: ManifestId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + usr.getManifest(id) match { + case Some(manifest) => { + val msg = Message(ManifestSpec, Right(manifest), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) + } + case _ => log.warn(s"No snapshots available") + } + } + + protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + usr.getUtxoSnapshotChunk(id) match { + case Some(snapChunk) => { + val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) + } + case _ => log.warn(s"No snapshots available") } } @@ -752,6 +773,16 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(usr) => sendSnapshotsInfo(usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } + case (_: GetManifestSpec, id: ManifestId @unchecked, remote) => + usrOpt match { + case Some(usr) => sendManifest(id, usr, remote) + case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") + } + case (_: GetUtxoSnapshotChunkSpec, id: SubtreeId @unchecked, remote) => + usrOpt match { + case Some(usr) => sendUtxoSnapshotChunk(id, usr, remote) + case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") + } } def initialized(hr: ErgoHistory, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 63c336b9fe..249d836610 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -91,12 +91,20 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { writeSnapshotsInfo(si) } - def readManifestBytes(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { - store.get(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) + def readManifest(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { + readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) } - def readSubtreeBytes(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { - store.get(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) + def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { + readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) + } + + def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { + store.get(id) + } + + def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { + store.get(id) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index b7135e2109..dfab8a6b77 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -253,9 +253,9 @@ object UtxoState extends ScorexLogging { case Some(snapshotsInfo) => val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) log.info(s"Reading snapshot from height $h") - val manifest = snapshotDb.readManifestBytes(manifestId).get + val manifest = snapshotDb.readManifest(manifestId).get val subtreeIds = manifest.subtreesIds - val subtrees = subtreeIds.map(sid => snapshotDb.readSubtreeBytes(sid).get) + val subtrees = subtreeIds.map(sid => snapshotDb.readSubtree(sid).get) val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 9bc8b4f881..898e5a4f34 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,6 +5,7 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer @@ -25,7 +26,7 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) protected lazy val storage = new VersionedLDBAVLStorage(store, np) - protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] + val persistentProver: PersistentBatchAVLProver[Digest32, HF] /** * Validate transaction against provided state context, if specified, @@ -149,4 +150,14 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) snapshotsDb.readSnapshotsInfo } + + def getManifest(id: ManifestId): Option[Array[Byte]] = { + val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) + snapshotsDb.readManifestBytes(id) + } + + def getUtxoSnapshotChunk(id: SubtreeId): Option[Array[Byte]] = { + val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) + snapshotsDb.readSubtreeBytes(id) + } } diff --git a/src/main/scala/scorex/core/app/Application.scala b/src/main/scala/scorex/core/app/Application.scala index 8ecbdba9a3..c9fa94f634 100644 --- a/src/main/scala/scorex/core/app/Application.scala +++ b/src/main/scala/scorex/core/app/Application.scala @@ -52,7 +52,9 @@ trait Application extends ScorexLogging { invSpec, requestModifierSpec, modifiersSpec, - GetSnapshotsInfoSpec + GetSnapshotsInfoSpec, + new GetManifestSpec, + new GetUtxoSnapshotChunkSpec ) } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 3e669a98a4..23712b6828 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -2,6 +2,7 @@ package scorex.core.network.message import org.ergoplatform.nodeView.state.SnapshotsInfo +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.wallet.Constants import scorex.core.consensus.SyncInfo import scorex.core.network._ @@ -325,3 +326,90 @@ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { }.toMap) } } + +/** + * The `GetManifest` sends manifest (BatchAVLProverManifest) identifier + */ +class GetManifestSpec extends MessageSpecV1[ManifestId] { + private val SizeLimit = 100 + + override val messageCode: MessageCode = 78: Byte + override val messageName: String = "GetManifest" + + override def serialize(id: ManifestId, w: Writer): Unit = { + w.putBytes(id) + } + + override def parse(r: Reader): ManifestId = { + require(r.remaining < SizeLimit, "Too big GetManifest message") + Digest32 @@ r.getBytes(Constants.ModifierIdLength) + } +} + +/** + * The `Manifest` message is a reply to a `GetManifest` message. + */ +object ManifestSpec extends MessageSpecV1[Array[Byte]] { + private val SizeLimit = 1000 + + override val messageCode: MessageCode = 79: Byte + + override val messageName: String = "Manifest" + + override def serialize(manifestBytes: Array[Byte], w: Writer): Unit = { + w.putUInt(manifestBytes.size) + w.putBytes(manifestBytes) + } + + override def parse(r: Reader): Array[Byte] = { + require(r.remaining <= SizeLimit, s"Too big Manifest message.") + + val length = r.getUInt().toIntExact + r.getBytes(length) + } +} + +/** + * The `GetManifest` sends send utxo subtree (BatchAVLProverSubtree) identifier + */ +class GetUtxoSnapshotChunkSpec() extends MessageSpecV1[SubtreeId] { + private val SizeLimit = 100 + + override val messageCode: MessageCode = 80: Byte + + override val messageName: String = "GetUtxoSnapshotChunk" + + override def serialize(id: SubtreeId, w: Writer): Unit = { + w.putBytes(id) + } + + override def parse(r: Reader): SubtreeId = { + require(r.remaining < SizeLimit, "Too big GetManifest message") + Digest32 @@ r.getBytes(Constants.ModifierIdLength) + } +} + +/** + * The `Manifest` message is a reply to a `GetManifest` message. + */ +object UtxoSnapshotChunkSpec extends MessageSpecV1[Array[Byte]] { + private val SizeLimit = 1000 + + override val messageCode: MessageCode = 81: Byte + + override val messageName: String = "UtxoSnapshotChunk" + + override def serialize(subtree: Array[Byte], w: Writer): Unit = { + w.putUInt(subtree.size) + w.putBytes(subtree) + } + + override def parse(r: Reader): Array[Byte] = { + require(r.remaining <= SizeLimit, s"Too big UtxoSnapshotChunk message.") + + val length = r.getUInt().toIntExact + r.getBytes(length) + } +} + + diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index 3a27bf461b..fa2fc43201 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -13,12 +13,15 @@ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeV import scorex.core.consensus.SyncInfo import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoStateReader} import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.Algos.HF import scorex.core.network._ import scorex.core.network.message._ import scorex.core.network.peer.PenaltyType import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture @@ -261,4 +264,74 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec } } + property("NodeViewSynchronizer: GetManifest") { + withFixture { ctx => + import ctx._ + + val s = stateGen.sample.get + + s match { + case usr: UtxoStateReader => { + // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer + node ! ChangedState(s) + + // Generate some snapshot + val height = 1 + usr.applyModifier(mod, Some(height)) + usr.persistentProver.generateProofAndUpdateStorage() + implicit val hf: HF = Algos.hash + val serializer = new BatchAVLProverSerializer[Digest32, HF] + val (manifest, subtrees) = serializer.slice(usr.persistentProver.avlProver, subtreeDepth = 12) + + val db = SnapshotsDb.create(s.constants.settings) + db.writeSnapshot(height, manifest, subtrees) + + // Then send message to request it + node ! Message[ManifestId](new GetManifestSpec, Left(manifest.id), Option(peer)) + ncProbe.fishForMessage(5 seconds) { + case stn: SendToNetwork if stn.message.spec.isInstanceOf[ManifestSpec.type] => true + case _: Any => false + } + } + case _ => + log.info("Snapshots not supported by digest-state") + } + } + } + + + property("NodeViewSynchronizer: GetSnapshotChunk") { + withFixture { ctx => + import ctx._ + + val s = stateGen.sample.get + + s match { + case usr: UtxoStateReader => { + // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer + node ! ChangedState(s) + + // Generate some snapshot + val height = 1 + usr.applyModifier(mod, Some(height)) + usr.persistentProver.generateProofAndUpdateStorage() + implicit val hf: HF = Algos.hash + val serializer = new BatchAVLProverSerializer[Digest32, HF] + val (manifest, subtrees) = serializer.slice(usr.persistentProver.avlProver, subtreeDepth = 12) + + val db = SnapshotsDb.create(s.constants.settings) + db.writeSnapshot(height, manifest, subtrees) + + // Then send message to request it + node ! Message[ManifestId](new GetUtxoSnapshotChunkSpec, Left(subtrees.last.id), Option(peer)) + ncProbe.fishForMessage(5 seconds) { + case stn: SendToNetwork if stn.message.spec.isInstanceOf[UtxoSnapshotChunkSpec.type] => true + case _: Any => false + } + } + case _ => + log.info("Snapshots not supported by digest-state") + } + } + } } From 427aec2adf0cde45afdc484970978db00762e305 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Wed, 12 Jan 2022 18:26:46 +0300 Subject: [PATCH 034/204] Fix matching messages in msgHandlers refer #1552 --- .../ergoplatform/network/ErgoNodeViewSynchronizer.scala | 9 +++++---- .../scorex/core/network/message/BasicMessagesRepo.scala | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e130524b95..1e424a4c2e 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -38,6 +38,7 @@ import scorex.util.{ModifierId, ScorexLogging} import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.wallet.VaultReader +import scorex.crypto.hash.Digest32 import scala.annotation.tailrec import scala.collection.mutable @@ -773,14 +774,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(usr) => sendSnapshotsInfo(usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } - case (_: GetManifestSpec, id: ManifestId @unchecked, remote) => + case (_: GetManifestSpec, id: Array[Byte], remote) => usrOpt match { - case Some(usr) => sendManifest(id, usr, remote) + case Some(usr) => sendManifest(Digest32 @@ id, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } - case (_: GetUtxoSnapshotChunkSpec, id: SubtreeId @unchecked, remote) => + case (_: GetUtxoSnapshotChunkSpec, id: Array[Byte], remote) => usrOpt match { - case Some(usr) => sendUtxoSnapshotChunk(id, usr, remote) + case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ id, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 23712b6828..2335ea95e2 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -384,7 +384,7 @@ class GetUtxoSnapshotChunkSpec() extends MessageSpecV1[SubtreeId] { } override def parse(r: Reader): SubtreeId = { - require(r.remaining < SizeLimit, "Too big GetManifest message") + require(r.remaining < SizeLimit, "Too big GetUtxoSnapshotChunk message") Digest32 @@ r.getBytes(Constants.ModifierIdLength) } } From a4050384127851fbc62bf357320b657a16d61124 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Fri, 14 Jan 2022 00:01:18 +0300 Subject: [PATCH 035/204] slicedTree, simplifying tests --- .../network/ErgoNodeViewSynchronizer.scala | 6 +++--- .../org/ergoplatform/nodeView/state/UtxoState.scala | 3 +-- .../nodeView/state/UtxoStateReader.scala | 12 +++++++++++- .../properties/NodeViewSynchronizerTests.scala | 12 ++---------- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 1e424a4c2e..f4e5dbc8fb 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -13,7 +13,7 @@ import org.ergoplatform.network.ErgoNodeViewSynchronizer.{CheckModifiersToDownlo 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.{Constants, ErgoSettings} +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeViewChanges, ModifiersFromRemote, TransactionsFromRemote} import org.ergoplatform.nodeView.ErgoNodeViewHolder._ import scorex.core.app.Version @@ -501,7 +501,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val msg = Message(ManifestSpec, Right(manifest), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - case _ => log.warn(s"No snapshots available") + case _ => log.warn(s"No manifest ${Algos.encode(id)} available") } } @@ -511,7 +511,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - case _ => log.warn(s"No snapshots available") + case _ => log.warn(s"No chunk ${Algos.encode(id)} available") } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index dfab8a6b77..5ed122c862 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -108,8 +108,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 (height % SnapshotEvery == 0) && estimatedTip.get - height <= SnapshotEvery) { - val serializer = new BatchAVLProverSerializer[Digest32, HF] - val (manifest, subtrees) = serializer.slice(persistentProver.avlProver, subtreeDepth = 12) + val (manifest, subtrees) = slicedTree() val ms0 = System.currentTimeMillis() snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 898e5a4f34..99afc22a49 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -11,6 +11,7 @@ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter import scorex.core.transaction.state.TransactionValidation +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.avltree.batch.{NodeParameters, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 @@ -26,7 +27,16 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) protected lazy val storage = new VersionedLDBAVLStorage(store, np) - val persistentProver: PersistentBatchAVLProver[Digest32, HF] + protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] + + //todo: scaladoc + //todo: used in tests only, make private[] ? + def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { + persistentProver.synchronized { + val serializer = new BatchAVLProverSerializer[Digest32, HF] + serializer.slice(persistentProver.avlProver, subtreeDepth = 12) + } + } /** * Validate transaction against provided state context, if specified, diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index fa2fc43201..a12452bffb 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -16,12 +16,10 @@ import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoStateReader} import org.ergoplatform.settings.Algos -import org.ergoplatform.settings.Algos.HF import scorex.core.network._ import scorex.core.network.message._ import scorex.core.network.peer.PenaltyType import scorex.core.serialization.{BytesSerializable, ScorexSerializer} -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture @@ -278,10 +276,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec // Generate some snapshot val height = 1 usr.applyModifier(mod, Some(height)) - usr.persistentProver.generateProofAndUpdateStorage() - implicit val hf: HF = Algos.hash - val serializer = new BatchAVLProverSerializer[Digest32, HF] - val (manifest, subtrees) = serializer.slice(usr.persistentProver.avlProver, subtreeDepth = 12) + val (manifest, subtrees) = usr.slicedTree() val db = SnapshotsDb.create(s.constants.settings) db.writeSnapshot(height, manifest, subtrees) @@ -314,10 +309,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec // Generate some snapshot val height = 1 usr.applyModifier(mod, Some(height)) - usr.persistentProver.generateProofAndUpdateStorage() - implicit val hf: HF = Algos.hash - val serializer = new BatchAVLProverSerializer[Digest32, HF] - val (manifest, subtrees) = serializer.slice(usr.persistentProver.avlProver, subtreeDepth = 12) + val (manifest, subtrees) = usr.slicedTree() val db = SnapshotsDb.create(s.constants.settings) db.writeSnapshot(height, manifest, subtrees) From 71e20da5820fd32e4bd8f98b46cfd169ad3ee8eb Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Fri, 14 Jan 2022 00:06:34 +0300 Subject: [PATCH 036/204] increased manifest and chunk message size limits --- .../org/ergoplatform/nodeView/state/UtxoStateReader.scala | 2 +- .../scala/scorex/core/network/message/BasicMessagesRepo.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 99afc22a49..17f130a8ec 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -30,7 +30,7 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] //todo: scaladoc - //todo: used in tests only, make private[] ? + //todo: used in tests only, make private[] ? def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { persistentProver.synchronized { val serializer = new BatchAVLProverSerializer[Digest32, HF] diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 2335ea95e2..3a09836f04 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -350,7 +350,7 @@ class GetManifestSpec extends MessageSpecV1[ManifestId] { * The `Manifest` message is a reply to a `GetManifest` message. */ object ManifestSpec extends MessageSpecV1[Array[Byte]] { - private val SizeLimit = 1000 + private val SizeLimit = 10000000 override val messageCode: MessageCode = 79: Byte @@ -393,7 +393,7 @@ class GetUtxoSnapshotChunkSpec() extends MessageSpecV1[SubtreeId] { * The `Manifest` message is a reply to a `GetManifest` message. */ object UtxoSnapshotChunkSpec extends MessageSpecV1[Array[Byte]] { - private val SizeLimit = 1000 + private val SizeLimit = 10000000 override val messageCode: MessageCode = 81: Byte From 3f73ad7557a39708f6aa787fb8487d232ce89d4b Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Wed, 3 Aug 2022 22:39:42 +0300 Subject: [PATCH 037/204] UtxoSetNetworkingFilter --- .../network/ErgoNodeViewSynchronizer.scala | 10 +--------- .../org/ergoplatform/network/PeerFilteringRule.scala | 9 +++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 5d2b698ca8..9070ed9b68 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -19,11 +19,10 @@ import scorex.core.network.ModifiersStatus.Requested import scorex.core.{ModifierTypeId, NodeViewModifier, PersistentNodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{DisconnectFrom, PenalizePeer, RegisterMessageSpecs, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} +import org.ergoplatform.nodeView.state.UtxoStateReader import scorex.core.network.message._ import org.ergoplatform.nodeView.state.ErgoStateReader import org.ergoplatform.nodeView.wallet.ErgoWalletReader -import scorex.core.app.Version import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} import scorex.core.network._ import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork @@ -970,13 +969,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, object ErgoNodeViewSynchronizer { - val UtxoSnapsnotActivationVersion = Version(4, 0, 20) - - def utxoNetworkingSupported(remote: ConnectedPeer): Boolean = { - // If neighbour version is >= 4.0.20, the neighbour supports utxo snapshots exchange - remote.peerInfo.exists(_.peerSpec.protocolVersion >= UtxoSnapsnotActivationVersion) - } - def props(networkControllerRef: ActorRef, viewHolderRef: ActorRef, syncInfoSpec: ErgoSyncInfoMessageSpec.type, diff --git a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala index 09af67319d..2dd6a30583 100644 --- a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala @@ -85,3 +85,12 @@ object SyncV2Filter extends PeerFilteringRule { } } + +object UtxoSetNetworkingFilter extends PeerFilteringRule { + val UtxoSnapsnotActivationVersion = Version(4, 0, 20) + + def condition(version: Version): Boolean = { + // If neighbour version is >= `UtxoSnapsnotActivationVersion`, the neighbour supports utxo snapshots exchange + version.compare(UtxoSnapsnotActivationVersion) >= 0 + } +} From 007ad88ad0ba456a65683983d61c5c27142624d9 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Thu, 4 Aug 2022 00:02:14 +0300 Subject: [PATCH 038/204] fixing imports --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 2 -- .../scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala | 1 - .../scorex/testkit/properties/NodeViewSynchronizerTests.scala | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e41e880fc3..4c941f0401 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -13,8 +13,6 @@ 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.{GetNodeViewChanges, ModifiersFromRemote, TransactionsFromRemote} -import org.ergoplatform.settings.{Constants, ErgoSettings} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsHealthy, ChainIsStuck, GetNodeViewChanges, IsChainHealthy, ModifiersFromRemote, TransactionsFromRemote} import org.ergoplatform.nodeView.ErgoNodeViewHolder._ import scorex.core.consensus.{Equal, Fork, Nonsense, Older, Unknown, Younger} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index caa217bad1..d3ab5a1792 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -15,7 +15,6 @@ import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.validation.MalformedModifierError import scorex.crypto.authds.avltree.batch.{Lookup, NodeParameters, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} -import scorex.crypto.authds.avltree.batch.{NodeParameters, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index cfa694b1df..9f119fee64 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -291,7 +291,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec } } } - + property("NodeViewSynchronizer: GetSnapshotChunk") { withFixture { ctx => import ctx._ From c60a69b25a139f3eaa152c0e392ce205615b2735 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 8 Aug 2022 21:53:18 +0300 Subject: [PATCH 039/204] MapPimp moved to score.core.utils --- .../org/ergoplatform/network/ErgoSyncTracker.scala | 2 +- .../modifierprocessors/ToDownloadProcessor.scala | 14 +------------- src/main/scala/scorex/core/utils/utils.scala | 12 +++++++++++- .../history/VerifyNonADHistorySpecification.scala | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala index 3c0fcec684..6ef7e286c2 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala @@ -11,7 +11,7 @@ import scorex.util.ScorexLogging import scala.collection.mutable import scala.concurrent.duration._ -import scorex.core.utils.MapPimp +import scorex.core.utils.MapPimpMutable final case class ErgoSyncTracker(networkSettings: NetworkSettings, timeProvider: TimeProvider) extends ScorexLogging { 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 4d2057ea8d..e7b4c3b8a0 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 @@ -13,7 +13,7 @@ import scala.annotation.tailrec * Trait that calculates next modifiers we should download to synchronize our full chain with headers chain */ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { - import ToDownloadProcessor._ + import scorex.core.utils.MapPimp protected val timeProvider: NetworkTimeProvider @@ -109,15 +109,3 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { } } - -object ToDownloadProcessor { - implicit class MapPimp[K, V](underlying: Map[K, V]) { - /** - * One liner for updating a Map with the possibility to handle case of missing Key - * @param k map key - * @param f function that is passed Option depending on Key being present or missing, returning new Value - * @return new Map with value updated under given key - */ - def adjust(k: K)(f: Option[V] => V): Map[K, V] = underlying.updated(k, f(underlying.get(k))) - } -} diff --git a/src/main/scala/scorex/core/utils/utils.scala b/src/main/scala/scorex/core/utils/utils.scala index a3532d56fd..93eaf37b0f 100644 --- a/src/main/scala/scorex/core/utils/utils.scala +++ b/src/main/scala/scorex/core/utils/utils.scala @@ -74,8 +74,17 @@ package object utils { 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 + * @param k map key + * @param f function that is passed Option depending on Key being present or missing, returning new Value + * @return new Map with value updated under given key + */ + def adjust(k: K)(f: Option[V] => V): Map[K, V] = underlying.updated(k, f(underlying.get(k))) + } - implicit class MapPimp[K, V](underlying: mutable.Map[K, V]) { + implicit class MapPimpMutable[K, V](underlying: mutable.Map[K, V]) { /** * One liner for updating a Map with the possibility to handle case of missing Key * @param k map key @@ -97,4 +106,5 @@ package object utils { case Some(v) => underlying.put(k, v) } } + } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index ce63ea22c5..998c2a04cd 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -4,7 +4,7 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.HeaderSerializer -import org.ergoplatform.nodeView.history.storage.modifierprocessors.{FullBlockProcessor, ToDownloadProcessor} +import org.ergoplatform.nodeView.history.storage.modifierprocessors.FullBlockProcessor import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.settings.Algos import org.ergoplatform.utils.HistoryTestHelpers @@ -12,7 +12,7 @@ import scorex.core.ModifierTypeId import scorex.core.consensus.ProgressInfo class VerifyNonADHistorySpecification extends HistoryTestHelpers { - import ToDownloadProcessor._ + import scorex.core.utils.MapPimp private def genHistory() = generateHistory(verifyTransactions = true, StateType.Utxo, PoPoWBootstrap = false, BlocksToKeep) From f33b2186514e518eae1b8827202e8c5820726f6c Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 8 Aug 2022 23:16:20 +0300 Subject: [PATCH 040/204] cosmetic improvements in nextModifiersToDownload --- .../storage/modifierprocessors/ToDownloadProcessor.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e7b4c3b8a0..d79ea4efa6 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 @@ -41,10 +41,10 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch - * @param condition filter only ModifierIds that pass this condition + * @param filterFn only ModifierIds which pass filter are included into results * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition */ - def nextModifiersToDownload(howManyPerType: Int, condition: (ModifierTypeId, ModifierId) => Boolean): Map[ModifierTypeId, Seq[ModifierId]] = { + def nextModifiersToDownload(howManyPerType: Int, filterFn: (ModifierTypeId, ModifierId) => Boolean): Map[ModifierTypeId, Seq[ModifierId]] = { @tailrec def continuation(height: Int, acc: Map[ModifierTypeId, Vector[ModifierId]]): Map[ModifierTypeId, Vector[ModifierId]] = { // return if at least one of Modifier types reaches howManyPerType limit for modifier ids @@ -54,7 +54,7 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { val headersAtThisHeight = headerIdsAtHeight(height).flatMap(id => typedModifierById[Header](id)) if (headersAtThisHeight.nonEmpty) { - val toDownload = headersAtThisHeight.flatMap(requiredModifiersForHeader).filter{ case (mtid, mid) => condition(mtid, mid) } + val toDownload = headersAtThisHeight.flatMap(requiredModifiersForHeader).filter{ case (mtid, mid) => filterFn(mtid, mid) } // add new modifiers to download to accumulator val newAcc = toDownload.foldLeft(acc) { case (newAcc, (mType, mId)) => newAcc.adjust(mType)(_.fold(Vector(mId))(_ :+ mId)) } continuation(height + 1, newAcc) @@ -72,7 +72,7 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { // download children blocks of last 100 full blocks applied to the best chain val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty) - case _ => + case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from continuation(pruningProcessor.minimalFullBlockHeight, Map.empty) } From a7cce4d8faaaf8591c7f74e7950c3fc65f3d0b79 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Mon, 8 Aug 2022 23:52:02 +0300 Subject: [PATCH 041/204] synthetic snapshotsinfo id, requesting snapshotsinfo from nextModifiersToDownload --- .../modifierprocessors/ToDownloadProcessor.scala | 10 ++++++++++ .../ergoplatform/nodeView/state/SnapshotsInfo.scala | 3 +++ 2 files changed, 13 insertions(+) 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 d79ea4efa6..ee90f43612 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 @@ -2,6 +2,8 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.nodeView.state.SnapshotsInfo +import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.settings.{ChainSettings, ErgoSettings, NodeConfigurationSettings} import scorex.core.ModifierTypeId import scorex.core.utils.NetworkTimeProvider @@ -38,6 +40,8 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { */ def isHeadersChainSynced: Boolean = pruningProcessor.isHeadersChainSynced + private var manifestChosen: Option[ManifestId] = None + /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch @@ -72,6 +76,12 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { // download children blocks of last 100 full blocks applied to the best chain val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty) + case None if nodeSettings.utxoBootstrap => + if (manifestChosen.nonEmpty) { + + } else { + Map(SnapshotsInfo -> Seq.empty) + } case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from continuation(pruningProcessor.minimalFullBlockHeight, Map.empty) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 249d836610..363dfdef00 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -6,6 +6,7 @@ import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} import org.ergoplatform.wallet.Constants +import scorex.core.ModifierTypeId import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} @@ -21,6 +22,8 @@ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { } object SnapshotsInfo { + val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (199: Byte) + val empty = SnapshotsInfo(Map.empty) } From 537eebc0da744532a9c4a498e1da059bb8c32a87 Mon Sep 17 00:00:00 2001 From: Alex Chepurnoy Date: Wed, 10 Aug 2022 13:52:24 +0300 Subject: [PATCH 042/204] ElementPartitioner.distribute signature changed --- .../network/ElementPartitioner.scala | 28 +++++------- .../network/ErgoNodeViewSynchronizer.scala | 9 +++- .../ToDownloadProcessor.scala | 8 ++-- .../nodeView/state/SnapshotsInfo.scala | 2 +- .../ElementPartitionerSpecification.scala | 43 ++++++++++++------- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala index de0ad54be2..4b890189bd 100644 --- a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala +++ b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala @@ -7,29 +7,19 @@ object ElementPartitioner { /** * Evenly distributes elements under unique bucket-type keys given min/max limits + * * @param buckets to distribute elements into - * @param maxElements maximum elements to fetch * @param minElementsPerBucket minimum elements to distribute per bucket - * @param maxElementsPerBucket maximum elements to distribute per bucket - * @param fetchMaxElems function that returns elements by type, given a limit (it depends on interpretation of the provider) * @return elements evenly grouped under unique bucket-type keys */ def distribute[B, T, I]( buckets: Iterable[B], - maxElements: Int, minElementsPerBucket: Int, - maxElementsPerBucket: Int - )(fetchMaxElems: Int => Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { + fetchedElems: Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { - if (buckets.isEmpty) { - Map.empty - } else { - val bucketsCount = buckets.size - val maxElementsToFetch = Math.min(maxElements, bucketsCount * maxElementsPerBucket) - if (maxElementsToFetch <= 0) { - Map.empty - } else { - fetchMaxElems(maxElementsToFetch).foldLeft(Map.empty[(B, T), Seq[I]]) { + val bucketsCount = buckets.size + + fetchedElems.foldLeft(Map.empty[(B, T), Seq[I]]) { case (acc, (elemType, elements)) => val elementsSize = elements.size if (elementsSize < 1) { @@ -38,7 +28,9 @@ object ElementPartitioner { val lessBuckets = if (elementsSize / bucketsCount < minElementsPerBucket) { buckets.take(Math.max(elementsSize / minElementsPerBucket, 1)) // there must always be at least one bucket - } else buckets + } else { + buckets + } // now let's distribute elements evenly into buckets val (quot, rem) = (elementsSize / lessBuckets.size, elementsSize % lessBuckets.size) @@ -49,6 +41,6 @@ object ElementPartitioner { } } } - } - } + + } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index bb2e6807c5..ac31bafd99 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -492,7 +492,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, (fetchMax: Int => Map[ModifierTypeId, Seq[ModifierId]]): Unit = getPeersOpt .foreach { peers => - val modifiersByBucket = ElementPartitioner.distribute(peers, maxModifiers, minModifiersPerBucket, maxModifiersPerBucket)(fetchMax) + val peersCount = peers.size + val maxElementsToFetch = Math.min(maxModifiers, peersCount * maxModifiersPerBucket) + val fetched = if (maxElementsToFetch <= 0) { + Map.empty + } else { + fetchMax(maxElementsToFetch) + } + val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) // collect and log useful downloading progress information, don't worry it does not run frequently modifiersByBucket.headOption.foreach { _ => modifiersByBucket 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 ee90f43612..76b3eafccd 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 @@ -40,7 +40,7 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { */ def isHeadersChainSynced: Boolean = pruningProcessor.isHeadersChainSynced - private var manifestChosen: Option[ManifestId] = None + private val manifestChosen: Option[ManifestId] = None /** * Get modifier ids to download to synchronize full blocks @@ -77,10 +77,10 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty) case None if nodeSettings.utxoBootstrap => - if (manifestChosen.nonEmpty) { - + if (manifestChosen.nonEmpty){ + ??? } else { - Map(SnapshotsInfo -> Seq.empty) + Map(SnapshotsInfo.modifierTypeId -> Seq.empty) } case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 363dfdef00..99da7a7f09 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -22,7 +22,7 @@ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { } object SnapshotsInfo { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (199: Byte) + val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (127: Byte) val empty = SnapshotsInfo(Map.empty) } diff --git a/src/test/scala/org/ergoplatform/network/ElementPartitionerSpecification.scala b/src/test/scala/org/ergoplatform/network/ElementPartitionerSpecification.scala index 89cfab2531..3104f18265 100644 --- a/src/test/scala/org/ergoplatform/network/ElementPartitionerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ElementPartitionerSpecification.scala @@ -5,17 +5,33 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + class ElementPartitionerSpecification extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { + def distribute[B, T, I](buckets: Iterable[B], + maxElements: Int, + minElementsPerBucket: Int, + maxElementsPerBucket: Int + )(fetchMax: Int => Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { + val peersCount = buckets.size + val maxElementsToFetch = Math.min(maxElements, peersCount * maxElementsPerBucket) + val fetched = if (maxElementsToFetch <= 0) { + Map.empty[T, Seq[I]] + } else { + fetchMax(maxElementsToFetch) + } + ElementPartitioner.distribute(buckets, minElementsPerBucket, fetched) + } + property("elements should be evenly distributed in buckets limited by bucket size") { forAll(Gen.nonEmptyListOf(Gen.alphaNumChar), Gen.nonEmptyListOf(Gen.alphaNumChar)) { case (buckets, elements) => val (elemsType_1, elemsType_2) = elements.splitAt(elements.size / 2) val elemsByBucket = - ElementPartitioner.distribute(buckets, Integer.MAX_VALUE, 1, 5) { count => + distribute(buckets, Integer.MAX_VALUE, 1, 5) { count => count shouldBe buckets.size * 5 Map("A" -> elemsType_1.take(count), "B" -> elemsType_2.take(count)) } @@ -30,7 +46,7 @@ class ElementPartitionerSpecification forAll(Gen.nonEmptyListOf(Gen.alphaNumChar), Gen.nonEmptyListOf(Gen.alphaNumChar)) { case (buckets, elements) => val (elemsType_1, elemsType_2) = elements.splitAt(elements.size / 2) - val elemsByBucket = ElementPartitioner.distribute(buckets, 5, 1, 100) { count => + val elemsByBucket = distribute(buckets, 5, 1, 100) { count => count shouldBe 5 Map("A" -> elemsType_1.take(count), "B" -> elemsType_2.take(count)) } @@ -44,7 +60,7 @@ class ElementPartitionerSpecification forAll(Gen.listOf(Gen.alphaNumChar), Gen.listOf(Gen.alphaNumChar)) { case (buckets, elements) => val elemsByBucket = - ElementPartitioner.distribute(buckets, Integer.MAX_VALUE, 5, 10) { count => + distribute(buckets, Integer.MAX_VALUE, 5, 10) { count => assert(count <= 10 * buckets.size) Map("A" -> elements.take(count)) } @@ -58,34 +74,29 @@ class ElementPartitionerSpecification } property("empty buckets or elements cannot be partitioned") { - ElementPartitioner - .distribute(List.empty, Integer.MAX_VALUE, 1, 5)(_ => Map("A" -> List(1))) + distribute(List.empty, Integer.MAX_VALUE, 1, 5)(_ => Map("A" -> List(1))) .size shouldBe 0 - ElementPartitioner - .distribute(List(1), Integer.MAX_VALUE, 1, 5)(_ => Map.empty[String, Seq[Int]]) + distribute(List(1), Integer.MAX_VALUE, 1, 5)(_ => Map.empty[String, Seq[Int]]) .size shouldBe 0 - ElementPartitioner - .distribute(List.empty, Integer.MAX_VALUE, 1, 5)(_ => Map.empty[String, Seq[Int]]) + distribute(List.empty, Integer.MAX_VALUE, 1, 5)(_ => Map.empty[String, Seq[Int]]) .size shouldBe 0 } property("0 or negative count of elements to fetch cannot be partitioned") { - ElementPartitioner - .distribute(List.empty, -1, 1, 5)(_ => Map("A" -> List(1))) + distribute(List.empty, -1, 1, 5)(_ => Map("A" -> List(1))) .size shouldBe 0 - ElementPartitioner - .distribute(List.empty, 5, 1, 0)(_ => Map("A" -> List(1))) + distribute(List.empty, 5, 1, 0)(_ => Map("A" -> List(1))) .size shouldBe 0 } property("less or equal elements than buckets should return one element per bucket") { - ElementPartitioner.distribute(List(1, 2, 3), Integer.MAX_VALUE, 1, 5) { _ => + distribute(List(1, 2, 3), Integer.MAX_VALUE, 1, 5) { _ => Map("A" -> List(1)) } shouldBe Map((1, "A") -> List(1)) } property("elements should be distributed into bucket-types") { - ElementPartitioner.distribute(List(1, 2), Integer.MAX_VALUE, 1, 1) { _ => + distribute(List(1, 2), Integer.MAX_VALUE, 1, 1) { _ => Map("A" -> List(1, 2), "B" -> List(1, 2), "C" -> List(1, 2)) } shouldBe Map( (2, "B") -> List(2), @@ -101,7 +112,7 @@ class ElementPartitionerSpecification "minElementsPerBucket constraint should not be used if there is less elements available" ) { val elems = - ElementPartitioner.distribute(List(1), Integer.MAX_VALUE, 100, 1) { _ => + distribute(List(1), Integer.MAX_VALUE, 100, 1) { _ => Map("A" -> List(1)) } elems.size shouldBe 1 From d961daa73640dcc6f4bb96cacd90317f1ecda9e9 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 24 Nov 2022 14:35:40 +0300 Subject: [PATCH 043/204] new sections in EIP draft --- papers/utxo.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/papers/utxo.md b/papers/utxo.md index 71d248eef1..4f00af125c 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -19,4 +19,14 @@ Implementation Details UTXO set is authenticated via AVL+ tree. -Time is broken into epochs, 1 epoch = 51,840 blocks (~72 days). \ No newline at end of file +Time is broken into epochs, 1 epoch = 51,840 blocks (~72 days). + +Networking Layer +---------------- + +Bootstrapping +------------- + + +Node Config +----------- From b9284c22429cc1ce6b4aec7da6a7e0bc32552fe4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 25 Nov 2022 01:22:17 +0300 Subject: [PATCH 044/204] trying to read snapshot on start if state is empty --- papers/utxo.md | 3 + .../nodeView/ErgoNodeViewHolder.scala | 24 ++++++-- .../nodeView/state/ErgoState.scala | 8 ++- .../nodeView/state/SnapshotsInfo.scala | 24 ++++++-- .../nodeView/state/UtxoState.scala | 61 +++++++++++-------- .../state/SnapshotsDbSpecification.scala | 2 +- 6 files changed, 82 insertions(+), 40 deletions(-) diff --git a/papers/utxo.md b/papers/utxo.md index 4f00af125c..49da30a046 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -30,3 +30,6 @@ Bootstrapping Node Config ----------- + +Sync Info V3 +------------ diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index cdb20682d2..b28e7aefbe 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -407,7 +407,24 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti log.info("History database read") val memPool = ErgoMemPool.empty(settings) val constants = StateConstants(settings) - restoreConsistentState(ErgoState.readOrGenerate(settings, constants).asInstanceOf[State], history) match { + val stateRead = ErgoState.readOrGenerate(settings, constants).asInstanceOf[State] + val stateRestored: State = stateRead match { + case us: UtxoState => + // todo: check if wallet is initialized + if (us.isGenesis && us.snapshotsAvailable().nonEmpty) { + // start from UTXO set snapshot available + UtxoState.fromLatestSnapshot(settings) match { + case Success(s) => s.asInstanceOf[State] + case Failure(e) => + log.error("Can't restore UTXO set from snapshot ", e) + stateRead + } + } else { + stateRead + } + case _ => stateRead + } + restoreConsistentState(stateRestored, history) match { case Success(state) => log.info(s"State database read, state synchronized") val wallet = ErgoWallet.readOrGenerate( @@ -530,10 +547,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val constants = StateConstants(settings) ErgoState.readOrGenerate(settings, constants) .asInstanceOf[State] - .ensuring( - state => java.util.Arrays.equals(state.rootHash, settings.chainSettings.genesisStateDigest), - "State root is incorrect" - ) + .ensuring(_.isGenesis, "State root is incorrect") } private def restoreConsistentState(stateIn: State, history: ErgoHistory): Try[State] = { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 327cca2242..31dd3436e2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -64,13 +64,19 @@ trait ErgoState[IState <: ErgoState[IState]] extends ErgoStateReader { */ def getReader: ErgoStateReader = this + def isGenesis: Boolean = { + rootHash.sameElements(constants.settings.chainSettings.genesisStateDigest) + } + } object ErgoState extends ScorexLogging { type ModifierProcessing[T <: ErgoState[T]] = PartialFunction[BlockSection, Try[T]] - def stateDir(settings: ErgoSettings): File = new File(s"${settings.directory}/state") + def stateDir(settings: ErgoSettings): File = { + new File(s"${settings.directory}/state") + } /** * Resolves state changing operations from transactions. There could be invalid sequence diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 63c336b9fe..2e4e70d34f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -14,10 +14,16 @@ import scorex.util.encode.Base16 import scala.util.{Failure, Success, Try} +/** + * Container for available UTXO set snapshots + * @param availableManifests - available UTXO set snapshot manifests and corresponding heights + */ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { def withNewManifest(height: Height, manifestId: ManifestId): SnapshotsInfo = { SnapshotsInfo(availableManifests.updated(height, manifestId)) } + + def nonEmpty: Boolean = availableManifests.nonEmpty } object SnapshotsInfo { @@ -51,14 +57,20 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { store.insert(Seq(snapshotInfoKey -> snapshotsInfoToBytes(snapshotsInfo))) } - def readSnapshotsInfo: Option[SnapshotsInfo] = { - store.get(snapshotInfoKey).map(snapshotsInfoFromBytes) + def readSnapshotsInfo: SnapshotsInfo = { + store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.empty) + } + + def notEmpty(): Boolean = { + store.get(snapshotInfoKey).isDefined } def pruneSnapshots(before: Height): Unit = { log.info("Starting snapshots pruning") - readSnapshotsInfo.foreach { si => - si.availableManifests.filterKeys(_ < before).foreach { case (h, manifestId) => + readSnapshotsInfo + .availableManifests + .filterKeys(_ < before) + .foreach { case (h, manifestId) => log.info(s"Pruning snapshot at height $h") val keysToRemove = store.get(manifestId) match { case Some(manifestBytes) => @@ -75,7 +87,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { } store.remove(keysToRemove) } - } + log.info("Snapshots pruning finished") } @@ -87,7 +99,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { //todo: RAM consumption doubles here, avoid it val subTreesToWrite = subtrees.map(s => s.id -> serializer.subtreeToBytes(s)) store.insert(Seq(manifestId -> manifestBytes) ++ subTreesToWrite) - val si = readSnapshotsInfo.getOrElse(SnapshotsInfo.empty).withNewManifest(height, manifestId) + val si = readSnapshotsInfo.withNewManifest(height, manifestId) writeSnapshotsInfo(si) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index a99a432bd1..43af858e40 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,15 +9,14 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, Parameters} -import org.ergoplatform.settings.{Algos, ErgoAlgos} +import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ import scorex.core.transaction.Transaction import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{ModifierValidator} +import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} @@ -45,6 +44,8 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 with UtxoStateReader with ScorexEncoding { + private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? + override def rootHash: ADDigest = persistentProver.synchronized { persistentProver.digest } @@ -115,8 +116,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) - val SnapshotEvery = 10 // test value, switch to 51840 after testing if (estimatedTip.nonEmpty && @@ -256,6 +255,10 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } + def snapshotsAvailable(): SnapshotsInfo = { + snapshotsDb.readSnapshotsInfo + } + } object UtxoState extends ScorexLogging { @@ -326,32 +329,36 @@ object UtxoState extends ScorexLogging { new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) } + def fromLatestSnapshot(settings: ErgoSettings): Try[UtxoState] = { + val stateDir = ErgoState.stateDir(settings) + stateDir.mkdirs() + val snapshotsDb = SnapshotsDb.create(settings) + val constants = StateConstants(settings) + fromLatestSnapshot(stateDir, snapshotsDb, constants) + } + def fromLatestSnapshot(dir: File, snapshotDb: SnapshotsDb, constants: StateConstants): Try[UtxoState] = Try { - snapshotDb.readSnapshotsInfo match { - case Some(snapshotsInfo) => - val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) - log.info(s"Reading snapshot from height $h") - val manifest = snapshotDb.readManifestBytes(manifestId).get - val subtreeIds = manifest.subtreesIds - val subtrees = subtreeIds.map(sid => snapshotDb.readSubtreeBytes(sid).get) - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) - val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get - - //todo: code below is mostly copied from .create, unify ? - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) - val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) - .getOrElse(ErgoState.genesisStateVersion) - val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { - val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) - PersistentBatchAVLProver.create(prover, storage).get - } - new UtxoState(persistentProver, version, store, constants) - - case None => ??? + val snapshotsInfo = snapshotDb.readSnapshotsInfo + val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) + log.info(s"Reading snapshot from height $h") + val manifest = snapshotDb.readManifestBytes(manifestId).get + val subtreeIds = manifest.subtreesIds + val subtrees = subtreeIds.map(sid => snapshotDb.readSubtreeBytes(sid).get) + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get + + //todo: code below is mostly copied from .create, unify ? + val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) + .getOrElse(ErgoState.genesisStateVersion) + val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { + val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) + val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + PersistentBatchAVLProver.create(prover, storage).get } + new UtxoState(persistentProver, version, store, constants) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala index 15107ffe08..da57fddbbe 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala @@ -17,7 +17,7 @@ class SnapshotsDbSpecification extends ErgoPropertyTest { val db = SnapshotsDb.create(dir) db.writeSnapshotsInfo(si) - val read = db.readSnapshotsInfo.get.availableManifests.mapValues(bs => bytesToId(bs)) + val read = db.readSnapshotsInfo.availableManifests.mapValues(bs => bytesToId(bs)) val siTocompare = si.availableManifests.mapValues(bs => bytesToId(bs)) read shouldBe siTocompare From 6aaeb2735e3fe90da34ebceda5369422a6963bb4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 27 Nov 2022 00:39:55 +0300 Subject: [PATCH 045/204] log.info instead of println --- .../scala/org/ergoplatform/nodeView/state/UtxoState.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 43af858e40..b632136f0f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -116,7 +116,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val SnapshotEvery = 10 // test value, switch to 51840 after testing + val SnapshotEvery = 5 // test value, switch to 51840 after testing if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && @@ -129,7 +129,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) snapshotsDb.writeSnapshot(height, manifest, subtrees) val ms = System.currentTimeMillis() - println("Time to dump utxo set snapshot: " + (ms - ms0)) + log.info("Time to dump utxo set snapshot: " + (ms - ms0)) } } From b2a52d8b2bfef47406699a2d30519911f9aed624 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 30 Nov 2022 12:56:51 +0300 Subject: [PATCH 046/204] sectionIdsWithNoProof --- .../modifiers/history/header/Header.scala | 11 +++++++---- .../modifierprocessors/ToDownloadProcessor.scala | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 4dc07bf7c7..8f3139a49d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -72,13 +72,16 @@ case class Header(override val version: Header.Version, override def minerPk: EcPointType = powSolution.pk + val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = Seq( + (BlockTransactions.modifierTypeId, transactionsId), + (Extension.modifierTypeId, extensionId)) + /** * Expected identifiers of the block sections */ - lazy val sectionIds: Seq[(ModifierTypeId, ModifierId)] = Seq( - (ADProofs.modifierTypeId, ADProofsId), - (BlockTransactions.modifierTypeId, transactionsId), - (Extension.modifierTypeId, extensionId)) + val sectionIds: Seq[(ModifierTypeId, ModifierId)] = + Seq((ADProofs.modifierTypeId, ADProofsId)) ++ + sectionIdsWithNoProof override lazy val toString: String = s"Header(${this.asJson.noSpaces})" 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 bb814acc00..97af3cc922 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 @@ -123,7 +123,7 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { } else if (nodeSettings.stateType.requireProofs) { h.sectionIds } else { - h.sectionIds.tail // do not download UTXO set transformation proofs if UTXO set is stored + h.sectionIdsWithNoProof // do not download UTXO set transformation proofs if UTXO set is stored } } From 035b43a2f131c166a1d8b9a6dbee3a611aae2126 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 30 Nov 2022 13:19:25 +0300 Subject: [PATCH 047/204] trait FullBlockPruningProcessor --- .../modifierprocessors/FullBlockProcessor.scala | 6 +++--- .../FullBlockPruningProcessor.scala | 9 +++++++-- .../FullBlockSectionProcessor.scala | 2 +- .../modifierprocessors/ToDownloadProcessor.scala | 15 ++++----------- .../BlockSectionValidationSpecification.scala | 6 +++--- .../history/VerifyADHistorySpecification.scala | 8 ++++---- .../history/VerifyNonADHistorySpecification.scala | 8 ++++---- 7 files changed, 26 insertions(+), 28 deletions(-) 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 c17816d69b..c5cc7bacb6 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 @@ -59,8 +59,8 @@ trait FullBlockProcessor extends HeadersProcessor { nonBestBlock private def isValidFirstFullBlock(header: Header): Boolean = { - pruningProcessor.isHeadersChainSynced && - header.height == pruningProcessor.minimalFullBlockHeight && + isHeadersChainSynced && + header.height == minimalFullBlockHeight && bestFullBlockIdOpt.isEmpty } @@ -106,7 +106,7 @@ trait FullBlockProcessor extends HeadersProcessor { if (nonBestChainsCache.nonEmpty) nonBestChainsCache = nonBestChainsCache.dropUntil(minForkRootHeight) if (nodeSettings.isFullBlocksPruned) { - val lastKept = pruningProcessor.updateBestFullBlock(fullBlock.header) + val lastKept = updateBestFullBlock(fullBlock.header) val bestHeight: Int = newBestBlockHeader.height val diff = bestHeight - prevBest.header.height pruneBlockDataAt(((lastKept - diff) until lastKept).filter(_ >= 0)) 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 aee18fd00d..93bcd8c0bf 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 @@ -2,13 +2,18 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.settings.{ChainSettings, NodeConfigurationSettings} +import org.ergoplatform.settings.ErgoSettings /** * A class that keeps and calculates minimal height for full blocks starting from which we need to download these full * blocks from the network and keep them in our history. */ -class FullBlockPruningProcessor(nodeConfig: NodeConfigurationSettings, chainSettings: ChainSettings) { +trait FullBlockPruningProcessor { + + protected val settings: ErgoSettings + + private val nodeConfig = settings.nodeSettings + private val chainSettings = settings.chainSettings @volatile private[history] var isHeadersChainSyncedVar: Boolean = false @volatile private[history] var minimalFullBlockHeightVar: Int = ErgoHistory.GenesisHeight 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 9e207d8a12..a7c8c95e54 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 @@ -98,7 +98,7 @@ trait FullBlockSectionProcessor extends BlockSectionProcessor with FullBlockProc .validate(bsCorrespondsToHeader, header.isCorrespondingModifier(m), InvalidModifier(s"header=${header.encodedId}, id=${m.encodedId}", m.id, m.modifierTypeId)) .validateSemantics(bsHeaderValid, isSemanticallyValid(header.id), InvalidModifier(s"header=${header.encodedId}, id=${m.encodedId}", m.id, m.modifierTypeId)) .validate(bsHeadersChainSynced, isHeadersChainSynced, InvalidModifier(header.id, m.id, m.modifierTypeId)) - .validate(bsTooOld, isHistoryADProof(m, header) || pruningProcessor.shouldDownloadBlockAtHeight(header.height), + .validate(bsTooOld, isHistoryADProof(m, header) || shouldDownloadBlockAtHeight(header.height), InvalidModifier(s"header=${header.encodedId}, id=${m.encodedId}", m.id, m.modifierTypeId)) .result } 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 97af3cc922..0b4290f0b7 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 @@ -13,7 +13,7 @@ import scala.annotation.tailrec /** * Trait that calculates next modifiers we should download to synchronize our full chain with headers chain */ -trait ToDownloadProcessor extends BasicReaders with ScorexLogging { +trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders with ScorexLogging { import ToDownloadProcessor._ protected val timeProvider: NetworkTimeProvider @@ -24,9 +24,6 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { // than headerChainDiff blocks on average from future private lazy val headerChainDiff = settings.nodeSettings.headerChainDiff - protected[history] lazy val pruningProcessor: FullBlockPruningProcessor = - new FullBlockPruningProcessor(nodeSettings, chainSettings) - protected def nodeSettings: NodeConfigurationSettings = settings.nodeSettings protected def chainSettings: ChainSettings = settings.chainSettings @@ -35,10 +32,6 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { def isInBestChain(id: ModifierId): Boolean - /** Returns true if we estimate that our chain is synced with the network. Start downloading full blocks after that - */ - def isHeadersChainSynced: Boolean = pruningProcessor.isHeadersChainSynced - /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch @@ -93,7 +86,7 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { continuation(minHeight, Map.empty) case _ => // if headers-chain is synced and no full blocks applied yet, find full block height to go from - continuation(pruningProcessor.minimalFullBlockHeight, Map.empty) + continuation(minimalFullBlockHeight, Map.empty) } } @@ -104,12 +97,12 @@ trait ToDownloadProcessor extends BasicReaders with ScorexLogging { if (!nodeSettings.verifyTransactions) { // A regime that do not download and verify transaction Nil - } else if (pruningProcessor.shouldDownloadBlockAtHeight(header.height)) { + } else if (shouldDownloadBlockAtHeight(header.height)) { // Already synced and header is not too far back. Download required modifiers. requiredModifiersForHeader(header) } else if (!isHeadersChainSynced && header.isNew(timeProvider, chainSettings.blockInterval * headerChainDiff)) { // Headers chain is synced after this header. Start downloading full blocks - pruningProcessor.updateBestFullBlock(header) + updateBestFullBlock(header) log.info(s"Headers chain is likely synced after header ${header.encodedId} at height ${header.height}") Nil } else { diff --git a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala index d4290b4ba6..3b0f07a8cc 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala @@ -72,10 +72,10 @@ class BlockSectionValidationSpecification extends HistoryTestHelpers { // should not be able to apply when blocks at this height are already pruned history.applicableTry(section) shouldBe 'success - history.pruningProcessor.minimalFullBlockHeightVar = history.bestHeaderOpt.get.height + 1 - history.pruningProcessor.isHeadersChainSyncedVar = true + history.minimalFullBlockHeightVar = history.bestHeaderOpt.get.height + 1 + history.isHeadersChainSyncedVar = true history.applicableTry(section) shouldBe 'failure - history.pruningProcessor.minimalFullBlockHeightVar = ErgoHistory.GenesisHeight + history.minimalFullBlockHeightVar = ErgoHistory.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/VerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala index 8c489cf3eb..72a8431768 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala @@ -22,8 +22,8 @@ class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { minFullHeight: Option[Int] = Some(ErgoHistory.GenesisHeight)): (ErgoHistory, Seq[ErgoFullBlock]) = { val inHistory = generateHistory(verifyTransactions = true, StateType.Digest, PoPoWBootstrap = false, BlocksToKeep) minFullHeight.foreach { h => - inHistory.pruningProcessor.minimalFullBlockHeightVar = h - inHistory.pruningProcessor.isHeadersChainSyncedVar = true + inHistory.minimalFullBlockHeightVar = h + inHistory.isHeadersChainSyncedVar = true } if (blocksNum > 0) { @@ -87,7 +87,7 @@ class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { history.bestFullBlockOpt shouldBe None val fullBlocksToApply = chain.tail - history.pruningProcessor.updateBestFullBlock(fullBlocksToApply(BlocksToKeep - 1).header) + history.updateBestFullBlock(fullBlocksToApply(BlocksToKeep - 1).header) history.applicable(chain.head.blockTransactions) shouldBe false @@ -200,7 +200,7 @@ class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { history = applyHeaderChain(history, HeaderChain(chain.map(_.header))) history.bestHeaderOpt.value shouldBe chain.last.header history.bestFullBlockOpt shouldBe None - history.pruningProcessor.updateBestFullBlock(chain.last.header) + history.updateBestFullBlock(chain.last.header) val fullBlocksToApply = chain.takeRight(BlocksToKeep) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index 4ef3e68b54..da28398c31 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -20,8 +20,8 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { property("block sections application in incorrect order") { var history = genHistory() val chain = genChain(6, history) - if (!history.pruningProcessor.isHeadersChainSynced) { - history.pruningProcessor.updateBestFullBlock(chain.last.header) + if (!history.isHeadersChainSynced) { + history.updateBestFullBlock(chain.last.header) } history = applyHeaderChain(history, HeaderChain(chain.map(_.header))) chain.foreach(fb => history.append(fb.extension).get) @@ -96,8 +96,8 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { history.bestHeaderOpt.value shouldBe chain.last.header history.bestFullBlockOpt shouldBe None - if (!history.pruningProcessor.isHeadersChainSynced) { - history.pruningProcessor.updateBestFullBlock(chain.last.header) + if (!history.isHeadersChainSynced) { + history.updateBestFullBlock(chain.last.header) } // Until UTXO snapshot synchronization is implemented, we should always start to apply full blocks from genesis From e3e2c59a4d43b5e3cbc355d6a8c502c8e2ff194a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 30 Nov 2022 13:39:26 +0300 Subject: [PATCH 048/204] estimatedTip parameter removed from nextModifiersToDownload --- .../network/ErgoNodeViewSynchronizer.scala | 3 +-- .../modifierprocessors/ToDownloadProcessor.scala | 13 +++++++------ .../history/VerifyNonADHistorySpecification.scala | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 647b1e4dac..a0af2612e6 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1028,8 +1028,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, minModifiersPerBucket, maxModifiersPerBucket )(getPeersForDownloadingBlocks) { howManyPerType => - val tip = historyReader.estimatedTip() - historyReader.nextModifiersToDownload(howManyPerType, tip, downloadRequired(historyReader)) + historyReader.nextModifiersToDownload(howManyPerType, downloadRequired(historyReader)) } } 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 0b4290f0b7..42ec746980 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 @@ -1,5 +1,6 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header @@ -32,6 +33,8 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi def isInBestChain(id: ModifierId): Boolean + def estimatedTip(): Option[Height] + /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch @@ -39,18 +42,16 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition */ def nextModifiersToDownload(howManyPerType: Int, - estimatedTip: Option[Int], condition: (ModifierTypeId, ModifierId) => Boolean): Map[ModifierTypeId, Seq[ModifierId]] = { val FullBlocksToDownloadAhead = 192 // how many full blocks to download forwards during active sync - - def farAwayFromBeingSynced(fb: ErgoFullBlock) = fb.height < (estimatedTip.getOrElse(0) - 128) + def farAwayFromBeingSynced(fb: ErgoFullBlock) = fb.height < (estimatedTip().getOrElse(0) - 128) @tailrec def continuation(height: Int, acc: Map[ModifierTypeId, Vector[ModifierId]], - maxHeight: Int = Int.MaxValue): Map[ModifierTypeId, Vector[ModifierId]] = { + maxHeight: Int): Map[ModifierTypeId, Vector[ModifierId]] = { if (height > maxHeight) { acc } else { @@ -83,10 +84,10 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi // when blockchain is about to be synced, // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) - continuation(minHeight, Map.empty) + continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) case _ => // if headers-chain is synced and no full blocks applied yet, find full block height to go from - continuation(minimalFullBlockHeight, Map.empty) + continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index da28398c31..073665d2bf 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -122,13 +122,13 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { newAcc.adjust(mType)(_.fold(Seq(mId))(_ :+ mId)) } - history.nextModifiersToDownload(1, None, (_, id) => !history.contains(id)) + history.nextModifiersToDownload(1, (_, id) => !history.contains(id)) .map(id => (id._1, id._2.map(Algos.encode))) shouldEqual missedBS.mapValues(_.take(1)).view.force - history.nextModifiersToDownload(2 * (BlocksToKeep - 1), None, (_, id) => !history.contains(id)) + history.nextModifiersToDownload(2 * (BlocksToKeep - 1), (_, id) => !history.contains(id)) .map(id => (id._1, id._2.map(Algos.encode))) shouldEqual missedBS - history.nextModifiersToDownload(2, None, (_, id) => !history.contains(id) && (id != missedChain.head.blockTransactions.id)) + history.nextModifiersToDownload(2, (_, id) => !history.contains(id) && (id != missedChain.head.blockTransactions.id)) .map(id => (id._1, id._2.map(Algos.encode))) shouldEqual missedBS.mapValues(_.take(2).filter( _ != missedChain.head.blockTransactions.id)).view.force } From e613f939876f9b592fe2cf88c5e5a5501c3e7c2f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 2 Dec 2022 14:20:49 +0300 Subject: [PATCH 049/204] requestSnapshotInfo --- src/main/scala/org/ergoplatform/ErgoApp.scala | 4 ++-- .../network/ErgoNodeViewSynchronizer.scala | 16 +++++++++++++--- src/main/scala/scorex/core/app/Application.scala | 4 ++-- .../core/network/message/BasicMessagesRepo.scala | 4 ++-- .../properties/NodeViewSynchronizerTests.scala | 4 ++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 0867ddffe5..a389bc77e8 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -77,8 +77,8 @@ class ErgoApp(args: Args) extends ScorexLogging { RequestModifierSpec, ModifiersSpec, GetSnapshotsInfoSpec, - new GetManifestSpec, - new GetUtxoSnapshotChunkSpec + GetManifestSpec, + GetUtxoSnapshotChunkSpec ) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 4bc25de44a..6d9fb2e71b 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -21,7 +21,7 @@ import scorex.core.network.ModifiersStatus.Requested import scorex.core.{ModifierTypeId, NodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoStateReader} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import scorex.core.network.message._ import org.ergoplatform.nodeView.wallet.ErgoWalletReader @@ -552,6 +552,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + def requestSnapshotsInfo() = { + val msg = Message(GetSnapshotsInfoSpec, Right(()), None) + val peers = UtxoSetNetworkingFilter.filter(syncTracker.peersToSyncWith()).toSeq + val stn = SendToNetwork(msg, SendToPeers(peers)) + networkControllerRef ! stn + } + def onDownloadRequest(historyReader: ErgoHistory): Receive = { case DownloadRequest(modifiersToFetch: Map[ModifierTypeId, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") @@ -598,6 +605,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } else { fetchMax(maxElementsToFetch) } + if (fetched.size == 1 && fetched.head._1 == SnapshotsInfo.modifierTypeId) { + requestSnapshotsInfo() + } val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) // collect and log useful downloading progress information, don't worry it does not run frequently modifiersByBucket.headOption.foreach { _ => @@ -1193,12 +1203,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(usr) => sendSnapshotsInfo(usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } - case (_: GetManifestSpec, id: Array[Byte], remote) => + case (_: GetManifestSpec.type, id: Array[Byte], remote) => usrOpt match { case Some(usr) => sendManifest(Digest32 @@ id, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } - case (_: GetUtxoSnapshotChunkSpec, id: Array[Byte], remote) => + case (_: GetUtxoSnapshotChunkSpec.type, id: Array[Byte], remote) => usrOpt match { case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ id, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") diff --git a/src/main/scala/scorex/core/app/Application.scala b/src/main/scala/scorex/core/app/Application.scala index 28ee517948..63b626e8f5 100644 --- a/src/main/scala/scorex/core/app/Application.scala +++ b/src/main/scala/scorex/core/app/Application.scala @@ -60,8 +60,8 @@ trait Application extends ScorexLogging { RequestModifierSpec, ModifiersSpec, GetSnapshotsInfoSpec, - new GetManifestSpec, - new GetUtxoSnapshotChunkSpec + GetManifestSpec, + GetUtxoSnapshotChunkSpec ) } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index d68c7353be..19c65e3f3a 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -301,7 +301,7 @@ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { /** * The `GetManifest` sends manifest (BatchAVLProverManifest) identifier */ -class GetManifestSpec extends MessageSpecV1[ManifestId] { +object GetManifestSpec extends MessageSpecV1[ManifestId] { private val SizeLimit = 100 override val messageCode: MessageCode = 78: Byte @@ -343,7 +343,7 @@ object ManifestSpec extends MessageSpecV1[Array[Byte]] { /** * The `GetManifest` sends send utxo subtree (BatchAVLProverSubtree) identifier */ -class GetUtxoSnapshotChunkSpec() extends MessageSpecV1[SubtreeId] { +object GetUtxoSnapshotChunkSpec extends MessageSpecV1[SubtreeId] { private val SizeLimit = 100 override val messageCode: MessageCode = 80: Byte diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index a545b02a30..828b583dc4 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -281,7 +281,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec db.writeSnapshot(height, manifest, subtrees) // Then send message to request it - node ! Message[ManifestId](new GetManifestSpec, Left(manifest.id), Option(peer)) + node ! Message[ManifestId](GetManifestSpec, Left(manifest.id), Option(peer)) ncProbe.fishForMessage(5 seconds) { case stn: SendToNetwork if stn.message.spec.isInstanceOf[ManifestSpec.type] => true case _: Any => false @@ -313,7 +313,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec db.writeSnapshot(height, manifest, subtrees) // Then send message to request it - node ! Message[ManifestId](new GetUtxoSnapshotChunkSpec, Left(subtrees.last.id), Option(peer)) + node ! Message[ManifestId](GetUtxoSnapshotChunkSpec, Left(subtrees.last.id), Option(peer)) ncProbe.fishForMessage(5 seconds) { case stn: SendToNetwork if stn.message.spec.isInstanceOf[UtxoSnapshotChunkSpec.type] => true case _: Any => false From 346c8abf9b03f299c42768741486137578ae9c79 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 2 Dec 2022 20:28:13 +0300 Subject: [PATCH 050/204] requestsManifest, processSnapshotsInfo, checkUtxoSetManifests --- .../network/ErgoNodeViewSynchronizer.scala | 47 +++++++++++++++++++ .../network/message/BasicMessagesRepo.scala | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6d9fb2e71b..28dc9475f4 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -8,6 +8,7 @@ import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} +import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader} @@ -40,6 +41,7 @@ import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version import scorex.crypto.hash.Digest32 +import scorex.util.encode.Base16 import scala.annotation.tailrec import scala.collection.mutable @@ -559,6 +561,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, networkControllerRef ! stn } + def requestManifest(manifestId: ManifestId, peer: ConnectedPeer) = { + val msg = Message(GetManifestSpec, Right(manifestId), None) + val stn = SendToNetwork(msg, SendToPeer(peer)) + networkControllerRef ! stn + } + def onDownloadRequest(historyReader: ErgoHistory): Receive = { case DownloadRequest(modifiersToFetch: Map[ModifierTypeId, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") @@ -804,6 +812,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + + protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { + snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => + val existingOffers = availableManifests.getOrElse(height, Seq.empty) + availableManifests.put(height, existingOffers :+ (remote -> manifestId)) + } + } + protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { usr.getUtxoSnapshotChunk(id) match { case Some(snapChunk) => { @@ -1063,6 +1080,34 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + protected def checkUtxoSetManifests(historyReader: ErgoHistory) = { + val MinSnapshots = 3 //todo: set to 1 during testing + + if (settings.nodeSettings.utxoBootstrap && + historyReader.fullBlockHeight == 0 && + availableManifests.nonEmpty) { + val res = availableManifests.find { case (h, records) => + // todo: consider h when asking manifest + if (records.length >= MinSnapshots) { + val idsSet = records.map(_._2).map(Base16.encode).toSet + if (idsSet.size > 1) { + log.warn(s"Different manifests found at height $h: $idsSet") + false + } else { + requestManifest(records.head._2, records.head._1) + true + } + } else { + false + } + } + if (res.isEmpty) { + availableManifests.clear() + } + } + } + + protected def viewHolderEvents(historyReader: ErgoHistory, mempoolReader: ErgoMemPool, utxoStateReaderOpt: Option[UtxoStateReader], @@ -1203,6 +1248,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(usr) => sendSnapshotsInfo(usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } + case (spec: MessageSpec[_], data: SnapshotsInfo, remote) if spec.messageCode == SnapshotsInfoSpec.messageCode => + processSnapshotsInfo(data, remote) case (_: GetManifestSpec.type, id: Array[Byte], remote) => usrOpt match { case Some(usr) => sendManifest(Digest32 @@ id, usr, remote) diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 19c65e3f3a..a5d9773081 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -371,7 +371,7 @@ object UtxoSnapshotChunkSpec extends MessageSpecV1[Array[Byte]] { override val messageName: String = "UtxoSnapshotChunk" override def serialize(subtree: Array[Byte], w: Writer): Unit = { - w.putUInt(subtree.size) + w.putUInt(subtree.length) w.putBytes(subtree) } From e0595b71a056eb6632586c9015d6272b8477377a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 3 Dec 2022 00:21:46 +0300 Subject: [PATCH 051/204] finishing checkUtxoSetManifests --- .../network/ErgoNodeViewSynchronizer.scala | 24 +++++++++++-------- .../network/message/BasicMessagesRepo.scala | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 28dc9475f4..e6fa3e4bef 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -47,7 +47,7 @@ import scala.annotation.tailrec import scala.collection.mutable import scala.concurrent.ExecutionContext import scala.concurrent.duration._ -import scala.util.{Failure, Success} +import scala.util.{Failure, Random, Success} /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the @@ -554,17 +554,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - def requestSnapshotsInfo() = { + def requestSnapshotsInfo(): Unit = { val msg = Message(GetSnapshotsInfoSpec, Right(()), None) val peers = UtxoSetNetworkingFilter.filter(syncTracker.peersToSyncWith()).toSeq - val stn = SendToNetwork(msg, SendToPeers(peers)) - networkControllerRef ! stn + networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) } - def requestManifest(manifestId: ManifestId, peer: ConnectedPeer) = { + def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { val msg = Message(GetManifestSpec, Right(manifestId), None) - val stn = SendToNetwork(msg, SendToPeer(peer)) - networkControllerRef ! stn + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } def onDownloadRequest(historyReader: ErgoHistory): Receive = { @@ -1094,15 +1092,21 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.warn(s"Different manifests found at height $h: $idsSet") false } else { - requestManifest(records.head._2, records.head._1) true } } else { false } } - if (res.isEmpty) { - availableManifests.clear() + res match { + case Some((height, records)) => + log.info(s"Downloading manifest for height $height from ${records.size} peers") + val manifestId = records.head._2 + val peers = records.map(_._1) + val randomPeer = peers(Random.nextInt(peers.length)) + requestManifest(manifestId, randomPeer) + case None => + log.info("No manifests to download found ") } } } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index a5d9773081..12e14c309f 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -328,7 +328,7 @@ object ManifestSpec extends MessageSpecV1[Array[Byte]] { override val messageName: String = "Manifest" override def serialize(manifestBytes: Array[Byte], w: Writer): Unit = { - w.putUInt(manifestBytes.size) + w.putUInt(manifestBytes.length) w.putBytes(manifestBytes) } From 593cfe4ef98be186d42b1abd932e0c4bcf5c6af2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 4 Dec 2022 02:29:01 +0300 Subject: [PATCH 052/204] requestUtxoSetChunk , processManifest --- .../network/ErgoNodeViewSynchronizer.scala | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e6fa3e4bef..90e1704ab2 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -12,9 +12,8 @@ import org.ergoplatform.ErgoLikeContext.Height 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.{Constants, ErgoSettings} +import org.ergoplatform.settings.{Algos, Constants, ErgoAlgos, ErgoSettings} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages._ -import org.ergoplatform.settings.Algos import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsHealthy, ChainIsStuck, GetNodeViewChanges, IsChainHealthy, ModifiersFromRemote} import org.ergoplatform.nodeView.ErgoNodeViewHolder._ import scorex.core.consensus.{Equal, Fork, Nonsense, Older, Unknown, Younger} @@ -26,6 +25,7 @@ import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoStat import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import scorex.core.network.message._ import org.ergoplatform.nodeView.wallet.ErgoWalletReader +import org.ergoplatform.settings.Algos.HF import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} import scorex.core.network._ import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPeers} @@ -40,6 +40,7 @@ import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 @@ -565,6 +566,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } + def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { + val msg = Message(GetUtxoSnapshotChunkSpec, Right(subtreeId), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) + } + def onDownloadRequest(historyReader: ErgoHistory): Receive = { case DownloadRequest(modifiersToFetch: Map[ModifierTypeId, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") @@ -812,6 +818,21 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + protected def processManifest(manifestBytes: Array[Byte], remote: ConnectedPeer) = { + //todo : check that mnifestBytes were requested + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { + case Success(manifest) => + manifest.subtreesIds.foreach {subtreeId => + requestUtxoSetChunk(subtreeId, remote) + } + case Failure(e) => + //todo: + log.info("Cant' restore manifest from bytes ", e) + ??? + } + } + protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => val existingOffers = availableManifests.getOrElse(height, Seq.empty) @@ -1259,6 +1280,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(usr) => sendManifest(Digest32 @@ id, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } + case (_: ManifestSpec.type, manifestBytes: Array[Byte], remote) => + processManifest(manifestBytes, remote) case (_: GetUtxoSnapshotChunkSpec.type, id: Array[Byte], remote) => usrOpt match { case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ id, usr, remote) From 80f87e7843df8e6591c71c6502f7f209491166ad Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 5 Dec 2022 00:19:24 +0300 Subject: [PATCH 053/204] processUtxoSnapshotChunk --- .../network/ErgoNodeViewSynchronizer.scala | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 90e1704ab2..46d87e04b5 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -40,7 +40,7 @@ import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 @@ -823,6 +823,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => + storedManifest = Some(manifest) manifest.subtreesIds.foreach {subtreeId => requestUtxoSetChunk(subtreeId, remote) } @@ -840,6 +841,42 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None + private val expectedSubtrees= mutable.Set[ModifierId]() + + //todo: store on disk + private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() + + protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], + usr: UtxoStateReader, + remote: ConnectedPeer): Unit = { + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + serializer.subtreeFromBytes(serializedChunk, 32) match { + case Success(subtree) => + //todo: set expectedSubtrees before + expectedSubtrees -= ModifierId @@ Base16.encode(subtree.id) + storedChunks += subtree + if(expectedSubtrees.isEmpty){ + storedManifest match { + case Some (manifest) => + serializer.combine(manifest -> storedChunks, 32, None) match { + case Success(_) => + ??? + //todo: set prover in the set + case Failure(_) => + ??? + //todo: process + } + case None => + } + } + case Failure(e) => + //todo: + log.info("Cant' restore manifest from bytes ", e) + ??? + } + } + protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { usr.getUtxoSnapshotChunk(id) match { case Some(snapChunk) => { @@ -1282,11 +1319,17 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } case (_: ManifestSpec.type, manifestBytes: Array[Byte], remote) => processManifest(manifestBytes, remote) - case (_: GetUtxoSnapshotChunkSpec.type, id: Array[Byte], remote) => + case (_: GetUtxoSnapshotChunkSpec.type, subtreeId: Array[Byte], remote) => usrOpt match { - case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ id, usr, remote) + case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ subtreeId, usr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } + case (_: UtxoSnapshotChunkSpec.type, serializedChunk: Array[Byte], remote) => + usrOpt match { + case Some(usr) => processUtxoSnapshotChunk(serializedChunk, usr, remote) + case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") + } + } def initialized(hr: ErgoHistory, From 60d66e2249586d0ac759e6eeb73ca7c0e2f59c4f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 5 Dec 2022 19:49:37 +0300 Subject: [PATCH 054/204] InitStateFromSnapshot --- .../network/ErgoNodeViewSynchronizer.scala | 37 ++++++++--------- .../nodeView/ErgoNodeViewHolder.scala | 7 ++++ .../nodeView/state/UtxoState.scala | 40 +++++++++++-------- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 46d87e04b5..60d900449e 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -40,8 +40,9 @@ import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version +import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} -import scorex.crypto.hash.Digest32 +import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.encode.Base16 import scala.annotation.tailrec @@ -817,6 +818,18 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None + private val expectedSubtrees= mutable.Set[ModifierId]() + + //todo: store on disk + private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() + + protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { + snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => + val existingOffers = availableManifests.getOrElse(height, Seq.empty) + availableManifests.put(height, existingOffers :+ (remote -> manifestId)) + } + } protected def processManifest(manifestBytes: Array[Byte], remote: ConnectedPeer) = { //todo : check that mnifestBytes were requested @@ -825,6 +838,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Success(manifest) => storedManifest = Some(manifest) manifest.subtreesIds.foreach {subtreeId => + expectedSubtrees += (ModifierId @@ Base16.encode(subtreeId)) requestUtxoSetChunk(subtreeId, remote) } case Failure(e) => @@ -834,35 +848,20 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { - snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => - val existingOffers = availableManifests.getOrElse(height, Seq.empty) - availableManifests.put(height, existingOffers :+ (remote -> manifestId)) - } - } - - private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None - private val expectedSubtrees= mutable.Set[ModifierId]() - - //todo: store on disk - private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() - protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], usr: UtxoStateReader, remote: ConnectedPeer): Unit = { val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => - //todo: set expectedSubtrees before expectedSubtrees -= ModifierId @@ Base16.encode(subtree.id) storedChunks += subtree if(expectedSubtrees.isEmpty){ storedManifest match { case Some (manifest) => serializer.combine(manifest -> storedChunks, 32, None) match { - case Success(_) => - ??? - //todo: set prover in the set + case Success(prover: BatchAVLProver[Digest32, Blake2b256.type]) => + viewHolderRef ! InitStateFromSnapshot(prover) case Failure(_) => ??? //todo: process @@ -1519,6 +1518,8 @@ object ErgoNodeViewSynchronizer { * @param mempool - mempool to check */ case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) + + case class InitStateFromSnapshot(prover: BatchAVLProver[Digest32, Blake2b256.type]) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index b28e7aefbe..d87078e045 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -279,6 +279,12 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti processingOutcome } + def processStateSnapshot: Receive = { + case InitStateFromSnapshot(prover) => + log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed") + updateNodeView(updatedState = Some(UtxoState.fromSnapshot(prover, settings).asInstanceOf[State])) + } + /** * Process new modifiers from remote. * Put all candidates to modifiersCache and then try to apply as much modifiers from cache as possible. @@ -683,6 +689,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti transactionsProcessing orElse getCurrentInfo orElse getNodeViewChanges orElse + processStateSnapshot orElse handleHealthCheck orElse { case a: Any => log.error("Strange input: " + a) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index a6e0f76a48..883dad73a2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -20,7 +20,7 @@ import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} -import scorex.crypto.hash.Digest32 +import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.{ByteArrayWrapper, LDBVersionedStore} import scorex.util.ModifierId import scorex.util.ScorexLogging @@ -328,17 +328,32 @@ object UtxoState extends ScorexLogging { new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) } - def fromLatestSnapshot(settings: ErgoSettings): Try[UtxoState] = { + def fromSnapshot(prover: BatchAVLProver[Digest32, Blake2b256.type], + settings: ErgoSettings) = { val stateDir = ErgoState.stateDir(settings) stateDir.mkdirs() - val snapshotsDb = SnapshotsDb.create(settings) val constants = StateConstants(settings) - fromLatestSnapshot(stateDir, snapshotsDb, constants) + + val store = new LDBVersionedStore(stateDir, initialKeepVersions = constants.keepVersions) + val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) + .getOrElse(ErgoState.genesisStateVersion) + val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { + val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) + val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + PersistentBatchAVLProver.create(prover, storage).get + } + new UtxoState(persistentProver, version, store, constants) + } + + // todo: do we need to restore from disk? + def fromLatestSnapshot(settings: ErgoSettings): Try[UtxoState] = { + val snapshotsDb = SnapshotsDb.create(settings) + fromLatestSnapshot(settings, snapshotsDb) } - def fromLatestSnapshot(dir: File, - snapshotDb: SnapshotsDb, - constants: StateConstants): Try[UtxoState] = Try { + // todo: do we need to restore from disk? + def fromLatestSnapshot(settings: ErgoSettings, + snapshotDb: SnapshotsDb): Try[UtxoState] = Try { val snapshotsInfo = snapshotDb.readSnapshotsInfo val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) log.info(s"Reading snapshot from height $h") @@ -348,16 +363,7 @@ object UtxoState extends ScorexLogging { val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get - //todo: code below is mostly copied from .create, unify ? - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) - val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) - .getOrElse(ErgoState.genesisStateVersion) - val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { - val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) - PersistentBatchAVLProver.create(prover, storage).get - } - new UtxoState(persistentProver, version, store, constants) + fromSnapshot(prover, settings) } } From 20b54377956f3f0ada2d8a683dc98585b971a785 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 8 Dec 2022 15:32:20 +0300 Subject: [PATCH 055/204] more log output --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 60d900449e..e8e04d21ed 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -826,6 +826,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => + log.debug(s"Got manifest $manifestId for height $height from $remote") val existingOffers = availableManifests.getOrElse(height, Seq.empty) availableManifests.put(height, existingOffers :+ (remote -> manifestId)) } @@ -836,6 +837,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => + log.info(s"Got manifest ${Algos.encode(manifest.id)}, going to download chunks for it") storedManifest = Some(manifest) manifest.subtreesIds.foreach {subtreeId => expectedSubtrees += (ModifierId @@ Base16.encode(subtreeId)) @@ -854,6 +856,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => + log.debug(s"Got utxo snapshot chunk, id: ${subtree.id}") expectedSubtrees -= ModifierId @@ Base16.encode(subtree.id) storedChunks += subtree if(expectedSubtrees.isEmpty){ @@ -1136,7 +1139,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def checkUtxoSetManifests(historyReader: ErgoHistory) = { - val MinSnapshots = 3 //todo: set to 1 during testing + val MinSnapshots = 1 //todo: set to 3 after testing if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && From 9781ef2745ddd84fbe6a6597f4837a4b4ac028ca Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 9 Dec 2022 00:02:28 +0300 Subject: [PATCH 056/204] fixin init issues --- .../ergoplatform/modifiers/history/header/Header.scala | 4 ++-- .../org/ergoplatform/nodeView/ErgoNodeViewHolder.scala | 6 +++--- .../modifierprocessors/FullBlockPruningProcessor.scala | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 8f3139a49d..ef6ddcad6a 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -72,14 +72,14 @@ case class Header(override val version: Header.Version, override def minerPk: EcPointType = powSolution.pk - val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = Seq( + lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = Seq( (BlockTransactions.modifierTypeId, transactionsId), (Extension.modifierTypeId, extensionId)) /** * Expected identifiers of the block sections */ - val sectionIds: Seq[(ModifierTypeId, ModifierId)] = + lazy val sectionIds: Seq[(ModifierTypeId, ModifierId)] = Seq((ADProofs.modifierTypeId, ADProofsId)) ++ sectionIdsWithNoProof diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index d87078e045..fa0761ec48 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -414,8 +414,8 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val memPool = ErgoMemPool.empty(settings) val constants = StateConstants(settings) val stateRead = ErgoState.readOrGenerate(settings, constants).asInstanceOf[State] - val stateRestored: State = stateRead match { - case us: UtxoState => + val stateRestored: State = stateRead /* match { + case us: UtxoState => // todo: check if wallet is initialized if (us.isGenesis && us.snapshotsAvailable().nonEmpty) { // start from UTXO set snapshot available @@ -429,7 +429,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti stateRead } case _ => stateRead - } + } */ restoreConsistentState(stateRestored, history) match { case Success(state) => log.info(s"State database read, state synchronized") 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 93bcd8c0bf..3381b78ccd 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 @@ -10,16 +10,16 @@ import org.ergoplatform.settings.ErgoSettings */ trait FullBlockPruningProcessor { - protected val settings: ErgoSettings + protected def settings: ErgoSettings - private val nodeConfig = settings.nodeSettings - private val chainSettings = settings.chainSettings + private def nodeConfig = settings.nodeSettings + private def chainSettings = settings.chainSettings + + private def VotingEpochLength = chainSettings.voting.votingLength @volatile private[history] var isHeadersChainSyncedVar: Boolean = false @volatile private[history] var minimalFullBlockHeightVar: Int = ErgoHistory.GenesisHeight - private val VotingEpochLength = chainSettings.voting.votingLength - private def extensionWithParametersHeight(height: Int): Int = { require(height >= VotingEpochLength) height - (height % VotingEpochLength) From 61331b572b2cf1eb5464dd9abb0c39b50d76fd95 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 9 Dec 2022 00:40:18 +0300 Subject: [PATCH 057/204] manifestChosen removed, comment added --- .../modifierprocessors/ToDownloadProcessor.scala | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) 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 df0967c9cb..3d81a94e67 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 @@ -5,7 +5,6 @@ import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.state.SnapshotsInfo -import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.settings.{ChainSettings, ErgoSettings, NodeConfigurationSettings} import scorex.core.ModifierTypeId import scorex.core.utils.NetworkTimeProvider @@ -37,8 +36,6 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi def estimatedTip(): Option[Height] - private val manifestChosen: Option[ManifestId] = None - /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch @@ -90,11 +87,8 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) case None if nodeSettings.utxoBootstrap => - if (manifestChosen.nonEmpty){ - ??? - } else { - Map(SnapshotsInfo.modifierTypeId -> Seq.empty) - } + // todo: can be requested multiple times, prevent it + Map(SnapshotsInfo.modifierTypeId -> Seq.empty) case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) From db5dd1a74bfa0187c49f81c11bd8970eaa52bcdb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 10 Dec 2022 01:01:04 +0300 Subject: [PATCH 058/204] UtxoSetSnapshotPersistence, more comments, testing section in doc --- papers/utxo.md | 7 +++ .../network/PeerFilteringRule.scala | 2 +- .../ToDownloadProcessor.scala | 2 +- .../state/UtxoSetSnapshotPersistence.scala | 48 +++++++++++++++++++ .../nodeView/state/UtxoState.scala | 27 ++--------- .../nodeView/state/UtxoStateReader.scala | 10 ---- .../NodeViewSynchronizerTests.scala | 6 +-- 7 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala diff --git a/papers/utxo.md b/papers/utxo.md index 49da30a046..85b2cd9ed3 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -33,3 +33,10 @@ Node Config Sync Info V3 ------------ + + + +Testing +------- + +1. No nodes with UTXO set snapshots around - stuck with headers synced - tested \ No newline at end of file diff --git a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala index 2dd6a30583..f1695d5a2a 100644 --- a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala @@ -87,7 +87,7 @@ object SyncV2Filter extends PeerFilteringRule { } object UtxoSetNetworkingFilter extends PeerFilteringRule { - val UtxoSnapsnotActivationVersion = Version(4, 0, 20) + val UtxoSnapsnotActivationVersion = Version(4, 0, 20) // todo: set proper version around release def condition(version: Version): Boolean = { // If neighbour version is >= `UtxoSnapsnotActivationVersion`, the neighbour supports utxo snapshots exchange 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 3d81a94e67..77dc475ab7 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 @@ -76,7 +76,7 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi bestFullBlockOpt match { case _ if !isHeadersChainSynced || !nodeSettings.verifyTransactions => - // do not download full blocks if no headers-chain synced yet and suffix enabled or SPV mode + // do not download full blocks if no headers-chain synced yet or SPV mode Map.empty case Some(fb) if farAwayFromBeingSynced(fb) => // when far away from blockchain tip diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala new file mode 100644 index 0000000000..3f0607be9f --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -0,0 +1,48 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.Algos.HF +import scorex.crypto.authds.avltree.batch.PersistentBatchAVLProver +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.hash.Digest32 +import scorex.util.ScorexLogging + +trait UtxoSetSnapshotPersistence extends ScorexLogging { + + def constants: StateConstants + def persistentProver: PersistentBatchAVLProver[Digest32, HF] + + private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? + + //todo: scaladoc + def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { + persistentProver.synchronized { + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) + serializer.slice(persistentProver.avlProver, subtreeDepth = 12) + } + } + + protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { + + val SnapshotEvery = 5 // test value, switch to 51840 after testing + + if (estimatedTip.nonEmpty && + (height % SnapshotEvery == 0) && + estimatedTip.get - height <= SnapshotEvery) { + + val (manifest, subtrees) = slicedTree() + + val ms0 = System.currentTimeMillis() + snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) + snapshotsDb.writeSnapshot(height, manifest, subtrees) + val ms = System.currentTimeMillis() + log.info("Time to dump utxo set snapshot: " + (ms - ms0)) + } + } + + def snapshotsAvailable(): SnapshotsInfo = { + snapshotsDb.readSnapshotsInfo + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 883dad73a2..f2df01a79f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -42,16 +42,15 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader + with UtxoSetSnapshotPersistence with ScorexEncoding { - private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? + import UtxoState.metadata override def rootHash: ADDigest = persistentProver.synchronized { persistentProver.digest } - import UtxoState.metadata - override def rollbackTo(version: VersionTag): Try[UtxoState] = persistentProver.synchronized { val p = persistentProver log.info(s"Rollback UtxoState to version ${Algos.encoder.encode(version)}") @@ -114,24 +113,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } - private def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - - val SnapshotEvery = 5 // test value, switch to 51840 after testing - - if (estimatedTip.nonEmpty && - (height % SnapshotEvery == 0) && - estimatedTip.get - height <= SnapshotEvery) { - - val (manifest, subtrees) = slicedTree() - - val ms0 = System.currentTimeMillis() - snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) - snapshotsDb.writeSnapshot(height, manifest, subtrees) - val ms = System.currentTimeMillis() - log.info("Time to dump utxo set snapshot: " + (ms - ms0)) - } - } - override def applyModifier(mod: BlockSection, estimatedTip: Option[Height]) (generate: LocallyGeneratedModifier => Unit): Try[UtxoState] = mod match { case fb: ErgoFullBlock => @@ -254,9 +235,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 } } - def snapshotsAvailable(): SnapshotsInfo = { - snapshotsDb.readSnapshotsInfo - } + } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 72fc252e53..0bdca20d53 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -14,7 +14,6 @@ import scorex.core.transaction.state.TransactionValidation import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.validation.MalformedModifierError import scorex.crypto.authds.avltree.batch.{Lookup, NodeParameters, PersistentBatchAVLProver, VersionedLDBAVLStorage} -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 @@ -36,15 +35,6 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { persistentProver.prover().generateProof() } - //todo: scaladoc - //todo: used in tests only, make private[] ? - def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { - persistentProver.synchronized { - val serializer = new BatchAVLProverSerializer[Digest32, HF] - serializer.slice(persistentProver.avlProver, subtreeDepth = 12) - } - } - /** * Validate transaction against provided state context, if specified, * or state context from the previous block if not diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala index 828b583dc4..fe1003ac20 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala @@ -15,7 +15,7 @@ import scorex.core.consensus.SyncInfo import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.state.UtxoState.ManifestId -import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoState, UtxoStateReader} import org.ergoplatform.settings.Algos import scorex.core.network._ import scorex.core.network.message._ @@ -268,7 +268,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val s = stateGen.sample.get s match { - case usr: UtxoStateReader => { + case usr: UtxoState => { // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer node ! ChangedState(s) @@ -300,7 +300,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val s = stateGen.sample.get s match { - case usr: UtxoStateReader => { + case usr: UtxoState => { // To initialize utxoStateReaderOpt in ErgoNodeView Synchronizer node ! ChangedState(s) From 94ff33e13bcef6b31ae50ab872ff8d9e80904fd3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 10 Dec 2022 13:39:08 +0300 Subject: [PATCH 059/204] utxo set snapshot filter test --- .../network/PeerFilteringRuleSpecification.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala index 7ff9ed7883..9fddcfc194 100644 --- a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala @@ -44,4 +44,16 @@ class PeerFilteringRuleSpecification extends ErgoPropertyTest { Seq(outPeer0, outPeer1, outPeer2) } + property("utxo set snapshot filter") { + val inPeer0 = peerWithVersion(Version(4, 0, 17)) + val inPeer1 = peerWithVersion(Version(4, 0, 18)) + val outPeer0 = peerWithVersion(Version(4, 0, 16)) + val outPeer1 = peerWithVersion(Version(4, 0, 19)) + val outPeer2 = peerWithVersion(Version(5, 0, 0)) + val outPeer3 = peerWithVersion(Version(5, 0, 5)) + + UtxoSetNetworkingFilter.filter(Seq(inPeer0, inPeer1, outPeer0, outPeer1, outPeer2, outPeer3)) shouldBe + Seq(outPeer2, outPeer3) + } + } From 5f059df8ca38975bf5271c920a6fdd3c933cb106 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 10 Dec 2022 14:12:25 +0300 Subject: [PATCH 060/204] known peers in sync tracker --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 2 +- src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e8e04d21ed..73e9da4e46 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -558,7 +558,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, def requestSnapshotsInfo(): Unit = { val msg = Message(GetSnapshotsInfoSpec, Right(()), None) - val peers = UtxoSetNetworkingFilter.filter(syncTracker.peersToSyncWith()).toSeq + val peers = UtxoSetNetworkingFilter.filter(syncTracker.knownPeers()).toSeq networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala index 6ef7e286c2..73028c68cb 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala @@ -155,6 +155,8 @@ final case class ErgoSyncTracker(networkSettings: NetworkSettings, timeProvider: } } + def knownPeers(): Iterable[ConnectedPeer] = statuses.keys + /** * Return the peers to which this node should send a sync signal, including: * outdated peers, if any, otherwise, all the peers with unknown status plus a random peer with From b387aa684cd393d3b42525d5db22f45a5b9b2e8a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 10 Dec 2022 22:36:14 +0300 Subject: [PATCH 061/204] log in sendSnapshotsInfo --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 73e9da4e46..adf21b7881 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -803,7 +803,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def sendSnapshotsInfo(usr: UtxoStateReader, peer: ConnectedPeer): Unit = { - val msg = Message(SnapshotsInfoSpec, Right(usr.getSnapshotInfo()), None) + val snapshotsInfo = usr.getSnapshotInfo() + log.debug(s"Sending snapshots info with ${snapshotsInfo.availableManifests.size} to $peer") + val msg = Message(SnapshotsInfoSpec, Right(snapshotsInfo), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } From 438bd7dcd745a85f3bcc44f3fcc6803f0ebe3c70 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 11 Dec 2022 00:22:48 +0300 Subject: [PATCH 062/204] messageHandlers fixed in ErgoApp, Application trait removed --- src/main/scala/org/ergoplatform/ErgoApp.scala | 9 +- .../scala/scorex/core/app/Application.scala | 160 ------------------ 2 files changed, 8 insertions(+), 161 deletions(-) delete mode 100644 src/main/scala/scorex/core/app/Application.scala diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index a389bc77e8..6022cd1b69 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -128,7 +128,14 @@ class ErgoApp(args: Args) extends ScorexLogging { InvSpec.messageCode -> ergoNodeViewSynchronizerRef, RequestModifierSpec.messageCode -> ergoNodeViewSynchronizerRef, ModifiersSpec.messageCode -> ergoNodeViewSynchronizerRef, - ErgoSyncInfoMessageSpec.messageCode -> ergoNodeViewSynchronizerRef + ErgoSyncInfoMessageSpec.messageCode -> ergoNodeViewSynchronizerRef, + // utxo set snapshot exchange related messages + GetSnapshotsInfoSpec.messageCode -> ergoNodeViewSynchronizerRef, + SnapshotsInfoSpec.messageCode -> ergoNodeViewSynchronizerRef, + GetManifestSpec.messageCode -> ergoNodeViewSynchronizerRef, + ManifestSpec.messageCode -> ergoNodeViewSynchronizerRef, + GetUtxoSnapshotChunkSpec.messageCode-> ergoNodeViewSynchronizerRef, + UtxoSnapshotChunkSpec.messageCode -> ergoNodeViewSynchronizerRef ) // Launching PeerSynchronizer actor which is then registering itself at network controller if (ergoSettings.scorexSettings.network.peerDiscovery) { diff --git a/src/main/scala/scorex/core/app/Application.scala b/src/main/scala/scorex/core/app/Application.scala deleted file mode 100644 index 63b626e8f5..0000000000 --- a/src/main/scala/scorex/core/app/Application.scala +++ /dev/null @@ -1,160 +0,0 @@ -package scorex.core.app - -import akka.actor.{ActorRef, ActorSystem} -import akka.http.scaladsl.Http -import akka.http.scaladsl.server.{ExceptionHandler, RejectionHandler, Route} -import org.ergoplatform.ErgoApp -import org.ergoplatform.nodeView.history.ErgoSyncInfoMessageSpec -import org.ergoplatform.settings.ErgoSettings -import scorex.core.api.http.{ - ApiErrorHandler, - ApiRejectionHandler, - ApiRoute, - CompositeHttpService -} -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 scorex.core.utils.NetworkTimeProvider -import scorex.util.ScorexLogging - -import java.net.InetSocketAddress -import scala.concurrent.ExecutionContext - -trait Application extends ScorexLogging { - - //settings - val ergoSettings: ErgoSettings - implicit val scorexSettings: ScorexSettings - - //api - val apiRoutes: Seq[ApiRoute] - - implicit def exceptionHandler: ExceptionHandler = ApiErrorHandler.exceptionHandler - implicit def rejectionHandler: RejectionHandler = ApiRejectionHandler.rejectionHandler - - implicit protected lazy val actorSystem: ActorSystem = ActorSystem( - scorexSettings.network.agentName - ) - - implicit val executionContext: ExecutionContext = - actorSystem.dispatchers.lookup("scorex.executionContext") - - protected val features: Seq[PeerFeature] - protected val additionalMessageSpecs: Seq[MessageSpec[_]] - - //p2p - private val upnpGateway: Option[UPnPGateway] = - if (scorexSettings.network.upnpEnabled) UPnP.getValidGateway(scorexSettings.network) - else None - // TODO use available port on gateway instead settings.network.bindAddress.getPort - upnpGateway.foreach(_.addPort(scorexSettings.network.bindAddress.getPort)) - - private lazy val basicSpecs = { - Seq( - GetPeersSpec, - new PeersSpec(scorexSettings.network.maxPeerSpecObjects), - InvSpec, - RequestModifierSpec, - ModifiersSpec, - GetSnapshotsInfoSpec, - GetManifestSpec, - GetUtxoSnapshotChunkSpec - ) - } - - val nodeViewHolderRef: ActorRef - val nodeViewSynchronizer: ActorRef - - /** API description in openapi format in YAML or JSON */ - val swaggerConfig: String - - val timeProvider = new NetworkTimeProvider(scorexSettings.ntp) - - //an address to send to peers - lazy val externalSocketAddress: Option[InetSocketAddress] = { - scorexSettings.network.declaredAddress orElse { - // TODO use available port on gateway instead settings.bindAddress.getPort - upnpGateway.map(u => - new InetSocketAddress( - u.externalAddress, - scorexSettings.network.bindAddress.getPort - ) - ) - } - } - - val scorexContext = ScorexContext( - messageSpecs = basicSpecs ++ additionalMessageSpecs, - upnpGateway = upnpGateway, - timeProvider = timeProvider, - externalNodeAddress = externalSocketAddress - ) - - val peerManagerRef = PeerManagerRef(ergoSettings, scorexContext) - - private val messageHandlers: ActorRef => Map[MessageCode, ActorRef] = - networkControllerRef => { - Map( - InvSpec.messageCode -> nodeViewSynchronizer, - RequestModifierSpec.messageCode -> nodeViewSynchronizer, - ModifiersSpec.messageCode -> nodeViewSynchronizer, - ErgoSyncInfoMessageSpec.messageCode -> nodeViewSynchronizer, - PeersSpec.messageCode -> PeerSynchronizerRef( - "PeerSynchronizer", - networkControllerRef, - peerManagerRef, - scorexSettings.network - ) - ) - } - - val networkControllerRef: ActorRef = - NetworkControllerRef( - "networkController", - ergoSettings, - peerManagerRef, - scorexContext, - messageHandlers - ) - - val peerSynchronizer: ActorRef = - PeerSynchronizerRef( - "PeerSynchronizer", - networkControllerRef, - peerManagerRef, - scorexSettings.network - ) - - lazy val combinedRoute: Route = CompositeHttpService( - actorSystem, - apiRoutes, - scorexSettings.restApi, - swaggerConfig - ).compositeRoute - - def run(): Unit = { - val applicationNameLimit: Int = 50 - require(scorexSettings.network.agentName.length <= applicationNameLimit) - - log.debug(s"Available processors: ${Runtime.getRuntime.availableProcessors}") - log.debug(s"Max memory available: ${Runtime.getRuntime.maxMemory}") - log.debug(s"RPC is allowed at ${scorexSettings.restApi.bindAddress.toString}") - - val bindAddress = scorexSettings.restApi.bindAddress - - Http() - .newServerAt(bindAddress.getAddress.getHostAddress, bindAddress.getPort) - .bindFlow(combinedRoute) - - //on unexpected shutdown - Runtime.getRuntime.addShutdownHook(new Thread() { - override def run() { - log.error("Unexpected shutdown") - ErgoApp.shutdownSystem() - } - }) - } -} From 00fd66f5fb7b5e64173d2046422daf9183241bc1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 11 Dec 2022 01:02:24 +0300 Subject: [PATCH 063/204] fixing basicSpecs, increased max size for SnapshotsInfo --- src/main/scala/org/ergoplatform/ErgoApp.scala | 10 ++++++---- .../core/network/message/BasicMessagesRepo.scala | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 6022cd1b69..90733b7952 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -73,19 +73,21 @@ class ErgoApp(args: Args) extends ScorexLogging { Seq( GetPeersSpec, new PeersSpec(scorexSettings.network.maxPeerSpecObjects), + ErgoSyncInfoMessageSpec, InvSpec, RequestModifierSpec, ModifiersSpec, GetSnapshotsInfoSpec, + SnapshotsInfoSpec, GetManifestSpec, - GetUtxoSnapshotChunkSpec + ManifestSpec, + GetUtxoSnapshotChunkSpec, + UtxoSnapshotChunkSpec ) } - private val additionalMessageSpecs: Seq[MessageSpec[_]] = Seq(ErgoSyncInfoMessageSpec) - private val scorexContext = ScorexContext( - messageSpecs = basicSpecs ++ additionalMessageSpecs, + messageSpecs = basicSpecs, upnpGateway = upnpGateway, timeProvider = timeProvider, externalNodeAddress = externalSocketAddress diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 12e14c309f..984044d269 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -272,7 +272,7 @@ object GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. */ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { - private val SizeLimit = 1000 + private val SizeLimit = 10000 override val messageCode: MessageCode = 77: Byte @@ -287,7 +287,7 @@ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { } override def parse(r: Reader): SnapshotsInfo = { - require(r.remaining <= SizeLimit, s"Too big SnapshotsInfo message.") + require(r.remaining <= SizeLimit, s"Too big SnapshotsInfo message: ${r.remaining} bytes found, $SizeLimit max expected.") val length = r.getUInt().toIntExact SnapshotsInfo((0 until length).map { _ => From b6962c0f17da57109fe94b24a7ab72cf96d89def Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 11 Dec 2022 01:19:50 +0300 Subject: [PATCH 064/204] checkUtxoSetManifests --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index adf21b7881..fe3b458381 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -826,12 +826,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, //todo: store on disk private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() - protected def processSnapshotsInfo(snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { + protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => log.debug(s"Got manifest $manifestId for height $height from $remote") val existingOffers = availableManifests.getOrElse(height, Seq.empty) availableManifests.put(height, existingOffers :+ (remote -> manifestId)) } + checkUtxoSetManifests(hr) } protected def processManifest(manifestBytes: Array[Byte], remote: ConnectedPeer) = { @@ -1143,6 +1144,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def checkUtxoSetManifests(historyReader: ErgoHistory) = { val MinSnapshots = 1 //todo: set to 3 after testing + //todo: choose latest snapshot if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty) { @@ -1315,7 +1317,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } case (spec: MessageSpec[_], data: SnapshotsInfo, remote) if spec.messageCode == SnapshotsInfoSpec.messageCode => - processSnapshotsInfo(data, remote) + processSnapshotsInfo(hr, data, remote) case (_: GetManifestSpec.type, id: Array[Byte], remote) => usrOpt match { case Some(usr) => sendManifest(Digest32 @@ id, usr, remote) From 637c5c8d61979d05ee176c77d3a0e980953072b6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Dec 2022 16:17:10 +0300 Subject: [PATCH 065/204] getSnapshotInfo, getManifest, getUtxoSnapshotChunk moved to UtxoSetSnapshotPersistence --- .../ergoplatform/http/api/UtxoApiRoute.scala | 4 ++-- .../network/ErgoNodeViewSynchronizer.scala | 8 ++++---- .../nodeView/state/SnapshotsInfo.scala | 11 +++++++---- .../state/UtxoSetSnapshotPersistence.scala | 16 +++++++++++++++- .../nodeView/state/UtxoState.scala | 1 - .../nodeView/state/UtxoStateReader.scala | 19 +------------------ .../network/message/BasicMessagesRepo.scala | 2 +- 7 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index 9a216d02b9..850b12b4c7 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -6,7 +6,7 @@ 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, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoSetSnapshotPersistence, UtxoStateReader} import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.core.api.http.ApiResponse import scorex.core.settings.RESTApiSettings @@ -85,7 +85,7 @@ case class UtxoApiRoute(readersHolder: ActorRef, } def getSnapshotsInfo: Route = (get & path("getSnapshotsInfo")) { ApiResponse(getState.map { - case usr: UtxoStateReader => + case usr: UtxoSetSnapshotPersistence => Some(usr.getSnapshotInfo()) case _ => None }) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index fe3b458381..ee8659accb 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -21,7 +21,7 @@ import scorex.core.network.ModifiersStatus.Requested import scorex.core.{ModifierTypeId, NodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoSetSnapshotPersistence, UtxoStateReader} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import scorex.core.network.message._ import org.ergoplatform.nodeView.wallet.ErgoWalletReader @@ -802,14 +802,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, modifiersByStatus.getOrElse(Requested, Map.empty) } - protected def sendSnapshotsInfo(usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + protected def sendSnapshotsInfo(usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { val snapshotsInfo = usr.getSnapshotInfo() log.debug(s"Sending snapshots info with ${snapshotsInfo.availableManifests.size} to $peer") val msg = Message(SnapshotsInfoSpec, Right(snapshotsInfo), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - protected def sendManifest(id: ManifestId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + protected def sendManifest(id: ManifestId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { usr.getManifest(id) match { case Some(manifest) => { val msg = Message(ManifestSpec, Right(manifest), None) @@ -882,7 +882,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoStateReader, peer: ConnectedPeer): Unit = { + protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { usr.getUtxoSnapshotChunk(id) match { case Some(snapChunk) => { val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 03a5be0ce7..cf51a19e97 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -30,7 +30,7 @@ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { object SnapshotsInfo { val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (127: Byte) - val empty = SnapshotsInfo(Map.empty) + def empty: SnapshotsInfo = SnapshotsInfo(Map.empty) } class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { @@ -41,14 +41,14 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { Bytes.concat( - snapshotsInfo.availableManifests.map{case (h, manifestId) => + snapshotsInfo.availableManifests.map { case (h, manifestId) => Bytes.concat(Ints.toByteArray(h), manifestId) - }.toSeq :_* + }.toSeq: _* ) } private def snapshotsInfoFromBytes(bytes: Array[Byte]): SnapshotsInfo = { - val manifests = bytes.grouped(36).map {rowBytes => + val manifests = bytes.grouped(36).map { rowBytes => val height = Ints.fromByteArray(rowBytes.take(4)) val manifestId = Digest32 @@ rowBytes.drop(4) height -> manifestId @@ -91,6 +91,8 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { store.remove(keysToRemove) } + + log.info("Snapshots pruning finished") } @@ -121,6 +123,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { store.get(id) } + } object SnapshotsDb { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 3f0607be9f..65bc87b539 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -1,6 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF import scorex.crypto.authds.avltree.batch.PersistentBatchAVLProver @@ -11,7 +12,7 @@ import scorex.util.ScorexLogging trait UtxoSetSnapshotPersistence extends ScorexLogging { def constants: StateConstants - def persistentProver: PersistentBatchAVLProver[Digest32, HF] + protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? @@ -45,4 +46,17 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { snapshotsDb.readSnapshotsInfo } + + def getSnapshotInfo(): SnapshotsInfo = { + snapshotsDb.readSnapshotsInfo + } + + def getManifest(id: ManifestId): Option[Array[Byte]] = { + snapshotsDb.readManifestBytes(id) + } + + def getUtxoSnapshotChunk(id: SubtreeId): Option[Array[Byte]] = { + snapshotsDb.readSubtreeBytes(id) + } + } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index f2df01a79f..615ee3b867 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -42,7 +42,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader - with UtxoSetSnapshotPersistence with ScorexEncoding { import UtxoState.metadata diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 0bdca20d53..68cc121b0a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,7 +5,6 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer @@ -19,7 +18,7 @@ import scorex.crypto.hash.Digest32 import scala.util.{Failure, Success, Try} -trait UtxoStateReader extends ErgoStateReader with TransactionValidation { +trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { protected implicit val hf: HF = Algos.hash @@ -196,20 +195,4 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { * Useful when checking mempool transactions. */ def withMempool(mp: ErgoMemPoolReader): UtxoState = withUnconfirmedTransactions(mp.getAll) - - def getSnapshotInfo(): SnapshotsInfo = { - val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) - snapshotsDb.readSnapshotsInfo - } - - def getManifest(id: ManifestId): Option[Array[Byte]] = { - val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) - snapshotsDb.readManifestBytes(id) - } - - def getUtxoSnapshotChunk(id: SubtreeId): Option[Array[Byte]] = { - val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move out (to constants?) - snapshotsDb.readSubtreeBytes(id) - } - } diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 984044d269..f2163a1fc0 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -272,7 +272,7 @@ object GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. */ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { - private val SizeLimit = 10000 + private val SizeLimit = 20000 override val messageCode: MessageCode = 77: Byte From edbb1c7379ebe0863a8b2c3acdc38888291341c5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Dec 2022 16:24:58 +0300 Subject: [PATCH 066/204] updating snapshots info on pruneSnapshots --- .../nodeView/state/SnapshotsDb.scala | 124 ++++++++++++++++++ .../nodeView/state/SnapshotsInfo.scala | 117 +---------------- 2 files changed, 126 insertions(+), 115 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala new file mode 100644 index 0000000000..0a4f0c2306 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -0,0 +1,124 @@ +package org.ergoplatform.nodeView.state + +import com.google.common.primitives.{Bytes, Ints} +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} +import org.ergoplatform.settings.Algos.HF +import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} +import org.ergoplatform.wallet.Constants +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.hash.Digest32 +import scorex.db.{LDBFactory, LDBKVStore} +import scorex.util.ScorexLogging +import scorex.util.encode.Base16 + +import scala.util.{Failure, Success, Try} + +class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { + + private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + + private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) + + private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { + Bytes.concat( + snapshotsInfo.availableManifests.map { case (h, manifestId) => + Bytes.concat(Ints.toByteArray(h), manifestId) + }.toSeq: _* + ) + } + + private def snapshotsInfoFromBytes(bytes: Array[Byte]): SnapshotsInfo = { + val manifests = bytes.grouped(36).map { rowBytes => + val height = Ints.fromByteArray(rowBytes.take(4)) + val manifestId = Digest32 @@ rowBytes.drop(4) + height -> manifestId + }.toMap + SnapshotsInfo(manifests) + } + + def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { + store.insert(Seq(snapshotInfoKey -> snapshotsInfoToBytes(snapshotsInfo))) + } + + def readSnapshotsInfo: SnapshotsInfo = { + store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.makeEmpty) + } + + def notEmpty(): Boolean = { + store.get(snapshotInfoKey).isDefined + } + + def pruneSnapshots(before: Height): Unit = { + log.info("Starting snapshots pruning") + val (toPrune, toLeave) = readSnapshotsInfo + .availableManifests + .partition(_._1 < before) + + toPrune.foreach { case (h, manifestId) => + log.info(s"Pruning snapshot at height $h") + val keysToRemove = store.get(manifestId) match { + case Some(manifestBytes) => + serializer.manifestFromBytes(manifestBytes, Constants.ModifierIdLength) match { + case Success(m) => + m.subtreesIds += manifestId + case Failure(e) => + log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) + Seq.empty + } + case None => + log.error(s"Manifest ${Base16.encode(manifestId)} not found:") + Seq.empty + } + store.remove(keysToRemove) + } + + val updInfo = SnapshotsInfo(toLeave) + writeSnapshotsInfo(updInfo) + + log.info("Snapshots pruning finished") + } + + def writeSnapshot(height: Height, + manifest: UtxoState.Manifest, + subtrees: Seq[UtxoState.Subtree]): Unit = { + val manifestBytes = serializer.manifestToBytes(manifest) + val manifestId = manifest.id + //todo: RAM consumption doubles here, avoid it + val subTreesToWrite = subtrees.map(s => s.id -> serializer.subtreeToBytes(s)) + store.insert(Seq(manifestId -> manifestBytes) ++ subTreesToWrite) + val si = readSnapshotsInfo.withNewManifest(height, manifestId) + writeSnapshotsInfo(si) + } + + def readManifest(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { + readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) + } + + def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { + readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) + } + + def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { + store.get(id) + } + + def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { + store.get(id) + } + +} + +object SnapshotsDb { + + def create(ergoSettings: ErgoSettings): SnapshotsDb = { + val dir = s"${ergoSettings.directory}/snapshots" + create(dir) + } + + private[state] def create(dir: String): SnapshotsDb = { + val store = LDBFactory.createKvDb(dir) + new SnapshotsDb(store) + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index cf51a19e97..b579001bc0 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -1,19 +1,8 @@ package org.ergoplatform.nodeView.state -import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} -import org.ergoplatform.settings.Algos.HF -import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} -import org.ergoplatform.wallet.Constants +import org.ergoplatform.nodeView.state.UtxoState.ManifestId import scorex.core.ModifierTypeId -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} -import scorex.crypto.hash.Digest32 -import scorex.db.{LDBFactory, LDBKVStore} -import scorex.util.ScorexLogging -import scorex.util.encode.Base16 - -import scala.util.{Failure, Success, Try} /** * Container for available UTXO set snapshots @@ -30,112 +19,10 @@ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { object SnapshotsInfo { val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (127: Byte) - def empty: SnapshotsInfo = SnapshotsInfo(Map.empty) -} - -class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { - - private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) - - private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) - - private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { - Bytes.concat( - snapshotsInfo.availableManifests.map { case (h, manifestId) => - Bytes.concat(Ints.toByteArray(h), manifestId) - }.toSeq: _* - ) - } - - private def snapshotsInfoFromBytes(bytes: Array[Byte]): SnapshotsInfo = { - val manifests = bytes.grouped(36).map { rowBytes => - val height = Ints.fromByteArray(rowBytes.take(4)) - val manifestId = Digest32 @@ rowBytes.drop(4) - height -> manifestId - }.toMap - SnapshotsInfo(manifests) - } - - def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { - store.insert(Seq(snapshotInfoKey -> snapshotsInfoToBytes(snapshotsInfo))) - } - - def readSnapshotsInfo: SnapshotsInfo = { - store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.empty) - } - - def notEmpty(): Boolean = { - store.get(snapshotInfoKey).isDefined - } - - def pruneSnapshots(before: Height): Unit = { - log.info("Starting snapshots pruning") - readSnapshotsInfo - .availableManifests - .filterKeys(_ < before) - .foreach { case (h, manifestId) => - log.info(s"Pruning snapshot at height $h") - val keysToRemove = store.get(manifestId) match { - case Some(manifestBytes) => - serializer.manifestFromBytes(manifestBytes, Constants.ModifierIdLength) match { - case Success(m) => - m.subtreesIds += manifestId - case Failure(e) => - log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) - Seq.empty - } - case None => - log.error(s"Manifest ${Base16.encode(manifestId)} not found:") - Seq.empty - } - store.remove(keysToRemove) - } - - - - log.info("Snapshots pruning finished") - } - - def writeSnapshot(height: Height, - manifest: UtxoState.Manifest, - subtrees: Seq[UtxoState.Subtree]): Unit = { - val manifestBytes = serializer.manifestToBytes(manifest) - val manifestId = manifest.id - //todo: RAM consumption doubles here, avoid it - val subTreesToWrite = subtrees.map(s => s.id -> serializer.subtreeToBytes(s)) - store.insert(Seq(manifestId -> manifestBytes) ++ subTreesToWrite) - val si = readSnapshotsInfo.withNewManifest(height, manifestId) - writeSnapshotsInfo(si) - } - - def readManifest(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { - readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) - } - - def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { - readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) - } - - def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { - store.get(id) - } - - def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { - store.get(id) - } + def makeEmpty(): SnapshotsInfo = SnapshotsInfo(Map.empty) } -object SnapshotsDb { - def create(ergoSettings: ErgoSettings): SnapshotsDb = { - val dir = s"${ergoSettings.directory}/snapshots" - create(dir) - } - private[state] def create(dir: String): SnapshotsDb = { - val store = LDBFactory.createKvDb(dir) - new SnapshotsDb(store) - } -} From 42bf44bbc45041c7ae97d35988c69028a6266ee5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Dec 2022 17:53:47 +0300 Subject: [PATCH 067/204] downloading latest snapshot --- .../network/ErgoNodeViewSynchronizer.scala | 28 +++++++++---------- .../state/UtxoSetSnapshotPersistence.scala | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index ee8659accb..ea7eea1e01 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -882,13 +882,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def sendUtxoSnapshotChunk(id: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { - usr.getUtxoSnapshotChunk(id) match { + protected def sendUtxoSnapshotChunk(subtreeId: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { + usr.getUtxoSnapshotChunk(subtreeId) match { case Some(snapChunk) => { val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - case _ => log.warn(s"No chunk ${Algos.encode(id)} available") + case _ => log.warn(s"No chunk ${Algos.encode(subtreeId)} available") } } @@ -1144,12 +1144,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def checkUtxoSetManifests(historyReader: ErgoHistory) = { val MinSnapshots = 1 //todo: set to 3 after testing - //todo: choose latest snapshot if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty) { - val res = availableManifests.find { case (h, records) => - // todo: consider h when asking manifest + val res = availableManifests.filter { case (h, records) => if (records.length >= MinSnapshots) { val idsSet = records.map(_._2).map(Base16.encode).toSet if (idsSet.size > 1) { @@ -1162,15 +1160,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, false } } - res match { - case Some((height, records)) => - log.info(s"Downloading manifest for height $height from ${records.size} peers") - val manifestId = records.head._2 - val peers = records.map(_._1) - val randomPeer = peers(Random.nextInt(peers.length)) - requestManifest(manifestId, randomPeer) - case None => - log.info("No manifests to download found ") + if (res.nonEmpty) { + val(height, records) = res.maxBy(_._1) + log.info(s"Downloading manifest for height $height from ${records.size} peers") + val manifestId = records.head._2 + val peers = records.map(_._1) + val randomPeer = peers(Random.nextInt(peers.length)) + requestManifest(manifestId, randomPeer) + } else { + log.info("No manifests to download found ") } } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 65bc87b539..63547e3058 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -26,7 +26,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val SnapshotEvery = 5 // test value, switch to 51840 after testing + val SnapshotEvery = 25 // test value, switch to 51840 after testing if (estimatedTip.nonEmpty && (height % SnapshotEvery == 0) && From 605c77d3bd6daa7d530ae92eceb1aa2c0201d4c5 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 12 Dec 2022 18:28:28 +0300 Subject: [PATCH 068/204] debug log on sending out chunks --- .../ergoplatform/network/ErgoNodeViewSynchronizer.scala | 1 + .../org/ergoplatform/nodeView/state/SnapshotsDb.scala | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index ea7eea1e01..048d343084 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -885,6 +885,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def sendUtxoSnapshotChunk(subtreeId: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { usr.getUtxoSnapshotChunk(subtreeId) match { case Some(snapChunk) => { + log.debug(s"Sending utxo snapshot chunk (${Algos.encode(subtreeId)}) to $peer") val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 0a4f0c2306..0e73f9af41 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -95,10 +95,6 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) } - def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { - readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) - } - def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { store.get(id) } @@ -107,6 +103,10 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { store.get(id) } + def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { + readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) + } + } object SnapshotsDb { From e64327116804f9e45d87c68b63609f53ced680c0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Dec 2022 11:30:28 +0300 Subject: [PATCH 069/204] requestMoreChunksIfNeeded --- .../network/ErgoNodeViewSynchronizer.scala | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 048d343084..27443ca3c9 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -821,7 +821,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None + private val expectedSubtrees= mutable.Set[ModifierId]() + private val requestedSubtrees= mutable.Set[ModifierId]() //todo: store on disk private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() @@ -835,17 +837,29 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, checkUtxoSetManifests(hr) } + private def requestMoreChunksIfNeeded(remote: ConnectedPeer): Unit = { + if (requestedSubtrees.size < 50) { + val toRequest = expectedSubtrees.take(50) + toRequest.foreach { subtreeId => + expectedSubtrees -= subtreeId + requestedSubtrees += subtreeId + requestUtxoSetChunk(Digest32 @@ Base16.decode(subtreeId).get, remote) + } + } + } + protected def processManifest(manifestBytes: Array[Byte], remote: ConnectedPeer) = { //todo : check that mnifestBytes were requested val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => - log.info(s"Got manifest ${Algos.encode(manifest.id)}, going to download chunks for it") + val subtrees = manifest.subtreesIds + log.info(s"Got manifest ${Algos.encode(manifest.id)}, going to download ${subtrees.size} chunks for it") storedManifest = Some(manifest) - manifest.subtreesIds.foreach {subtreeId => + subtrees.foreach {subtreeId => //todo: remove take() with gradual download expectedSubtrees += (ModifierId @@ Base16.encode(subtreeId)) - requestUtxoSetChunk(subtreeId, remote) } + requestMoreChunksIfNeeded(remote) case Failure(e) => //todo: log.info("Cant' restore manifest from bytes ", e) @@ -859,10 +873,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => - log.debug(s"Got utxo snapshot chunk, id: ${subtree.id}") - expectedSubtrees -= ModifierId @@ Base16.encode(subtree.id) + log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release + log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release + requestedSubtrees -= ModifierId @@ Base16.encode(subtree.id) storedChunks += subtree - if(expectedSubtrees.isEmpty){ + if(expectedSubtrees.isEmpty && requestedSubtrees.isEmpty){ storedManifest match { case Some (manifest) => serializer.combine(manifest -> storedChunks, 32, None) match { @@ -874,6 +889,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } case None => } + } else{ + requestMoreChunksIfNeeded(remote) } case Failure(e) => //todo: @@ -1147,7 +1164,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && - availableManifests.nonEmpty) { + availableManifests.nonEmpty && + storedManifest.isEmpty) { val res = availableManifests.filter { case (h, records) => if (records.length >= MinSnapshots) { val idsSet = records.map(_._2).map(Base16.encode).toSet From e2d0ba004fba0ee828530c41ec8c413adbb6d74b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Dec 2022 16:12:25 +0300 Subject: [PATCH 070/204] moving isGenesis to reader --- .../org/ergoplatform/nodeView/state/ErgoState.scala | 4 ---- .../nodeView/state/ErgoStateReader.scala | 13 ++++++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 31dd3436e2..3458b88ac3 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -64,10 +64,6 @@ trait ErgoState[IState <: ErgoState[IState]] extends ErgoStateReader { */ def getReader: ErgoStateReader = this - def isGenesis: Boolean = { - rootHash.sameElements(constants.settings.chainSettings.genesisStateDigest) - } - } object ErgoState extends ScorexLogging { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 7da217fc7c..fbf85d0ddf 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -10,8 +10,12 @@ import scorex.util.ScorexLogging trait ErgoStateReader extends NodeViewComponent with ScorexLogging { + // root hash and height of AVL+ tree authenticating UTXO set def rootHash: ADDigest + // must be ID of last applied block + def version: VersionTag + val store: LDBVersionedStore val constants: StateConstants @@ -19,6 +23,12 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { protected lazy val votingSettings: VotingSettings = chainSettings.voting + def isGenesis: Boolean = { + rootHash.sameElements(constants.settings.chainSettings.genesisStateDigest) + } + + def isInitialized: Boolean = !isGenesis + def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, constants) /** @@ -28,9 +38,6 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(chainSettings) - //must be ID of last applied modifier - def version: VersionTag - def closeStorage(): Unit = { log.warn("Closing state's store.") store.close() From 6ce1111106aec8bfb7a936305dc3228471d3e76d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 13 Dec 2022 22:57:57 +0300 Subject: [PATCH 071/204] utxoSnapshotApplied --- .../network/ErgoNodeViewSynchronizer.scala | 12 +++++++----- .../nodeView/ErgoNodeViewHolder.scala | 5 +++-- .../modifierprocessors/ToDownloadProcessor.scala | 15 ++++++++++++--- .../nodeView/state/ErgoStateReader.scala | 2 -- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 27443ca3c9..b3be90088b 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -820,6 +820,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + private var storedManifestHeight: Height = 0 private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None private val expectedSubtrees= mutable.Set[ModifierId]() @@ -877,15 +878,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release requestedSubtrees -= ModifierId @@ Base16.encode(subtree.id) storedChunks += subtree - if(expectedSubtrees.isEmpty && requestedSubtrees.isEmpty){ + if (expectedSubtrees.isEmpty && requestedSubtrees.isEmpty) { storedManifest match { - case Some (manifest) => + case Some(manifest) => serializer.combine(manifest -> storedChunks, 32, None) match { case Success(prover: BatchAVLProver[Digest32, Blake2b256.type]) => - viewHolderRef ! InitStateFromSnapshot(prover) + viewHolderRef ! InitStateFromSnapshot(storedManifestHeight, prover) case Failure(_) => ??? - //todo: process + //todo: process } case None => } @@ -1185,6 +1186,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val manifestId = records.head._2 val peers = records.map(_._1) val randomPeer = peers(Random.nextInt(peers.length)) + storedManifestHeight = height requestManifest(manifestId, randomPeer) } else { log.info("No manifests to download found ") @@ -1543,7 +1545,7 @@ object ErgoNodeViewSynchronizer { */ case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) - case class InitStateFromSnapshot(prover: BatchAVLProver[Digest32, Blake2b256.type]) + case class InitStateFromSnapshot(height: Height, prover: BatchAVLProver[Digest32, Blake2b256.type]) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index fa0761ec48..e2e2b03739 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -280,8 +280,9 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } def processStateSnapshot: Receive = { - case InitStateFromSnapshot(prover) => - log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed") + case InitStateFromSnapshot(height, prover) => + log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") + history().utxoSnapshotApplied(height) updateNodeView(updatedState = Some(UtxoState.fromSnapshot(prover, settings).asInstanceOf[State])) } 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 77dc475ab7..d2422a7fbc 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 @@ -36,10 +36,17 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi def estimatedTip(): Option[Height] + var _utxoSnapshotApplied = false + + def utxoSnapshotApplied(height: Height): Unit = { + _utxoSnapshotApplied = true + minimalFullBlockHeightVar = height + 1 //todo: or height + 1? + } + /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch - * @param filterFn only ModifierIds which pass filter are included into results + * @param condition only ModifierIds which pass filter are included into results * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition */ def nextModifiersToDownload(howManyPerType: Int, @@ -86,12 +93,14 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) - case None if nodeSettings.utxoBootstrap => + case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => // todo: can be requested multiple times, prevent it Map(SnapshotsInfo.modifierTypeId -> Seq.empty) case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from - continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) + val res = continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) + log.info("To download: " + res.size) + res } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index fbf85d0ddf..b7f3fc21e5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -27,8 +27,6 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { rootHash.sameElements(constants.settings.chainSettings.genesisStateDigest) } - def isInitialized: Boolean = !isGenesis - def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, constants) /** From 381e28199df9295166c354f9ed0f43dd0ccb0835 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 15 Dec 2022 13:28:13 +0300 Subject: [PATCH 072/204] UtxoSetSnapshotDownloadPlan, writeDownloadPlanToTheDb, readDownloadPlanFromDb --- .../history/storage/HistoryStorage.scala | 4 +- .../ToDownloadProcessor.scala | 11 +- .../UtxoSetSnapshotProcessor.scala | 126 ++++++++++++++++++ 3 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala 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 b139f61db9..83fbe54d29 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -84,7 +84,9 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, c } } - def get(id: ModifierId): Option[Array[Byte]] = objectsStore.get(idToBytes(id)) + def get(id: Array[Byte]): Option[Array[Byte]] = objectsStore.get(id) + + def get(id: ModifierId): Option[Array[Byte]] = get(idToBytes(id)) def contains(id: ModifierId): Boolean = objectsStore.get(idToBytes(id)).isDefined 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 d2422a7fbc..94e15d917c 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,9 @@ import scala.annotation.tailrec /** * Trait that calculates next modifiers we should download to synchronize our full chain with headers chain */ -trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders with ScorexLogging { +trait ToDownloadProcessor + extends FullBlockPruningProcessor with UtxoSetSnapshotProcessor with BasicReaders with ScorexLogging { + import scorex.core.utils.MapPimp protected val timeProvider: NetworkTimeProvider @@ -36,13 +38,6 @@ trait ToDownloadProcessor extends FullBlockPruningProcessor with BasicReaders wi def estimatedTip(): Option[Height] - var _utxoSnapshotApplied = false - - def utxoSnapshotApplied(height: Height): Unit = { - _utxoSnapshotApplied = true - minimalFullBlockHeightVar = height + 1 //todo: or height + 1? - } - /** * Get modifier ids to download to synchronize full blocks * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch 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 new file mode 100644 index 0000000000..dbc4bbac94 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotProcessor.scala @@ -0,0 +1,126 @@ +package org.ergoplatform.nodeView.history.storage.modifierprocessors + +import com.google.common.primitives.Ints +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.history.storage.HistoryStorage +import org.ergoplatform.nodeView.state.UtxoState.SubtreeId +import org.ergoplatform.settings.Constants +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest +import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.util.{ByteArrayBuilder, ScorexLogging} +import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} +import java.nio.ByteBuffer +import scala.collection.mutable + +/** + * Stores: + * - writes chunks + * - writes data for incomplete snapshots + */ +trait UtxoSetSnapshotProcessor extends ScorexLogging { + + protected val historyStorage: HistoryStorage + + private[history] var minimalFullBlockHeightVar: Int + + var _utxoSnapshotApplied = false + + def utxoSnapshotApplied(height: Height): Unit = { + _utxoSnapshotApplied = true + minimalFullBlockHeightVar = height + 1 //todo: or height + 1? + } + + private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) + private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) + + def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? + + def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { + val w = new VLQByteBufferWriter(new ByteArrayBuilder()) + w.putULong(plan.startingTime) + w.putULong(plan.latestChunkFetchTime) + w.putUInt(plan.snapshotHeight) + w.putBytes(plan.utxoSetRootHash) + w.put(plan.utxoSetTreeHeight) + w.putUInt(plan.totalChunks) + w.putUInt(plan.expectedChunkIds.size) + w.putUInt(plan.downloadedChunkIds.size) + val metaDataBytes = w.result().toBytes + + historyStorage.insert(plan.id, metaDataBytes) + + var idx = 0 + plan.expectedChunkIds.foreach { chunkId => + val idxBytes = Ints.toByteArray(idx) + historyStorage.insert(expectedChunksPrefix ++ idxBytes, chunkId) + idx = idx + 1 + } + + idx = 0 + plan.downloadedChunkIds.foreach { chunkId => + val idxBytes = Ints.toByteArray(idx) + historyStorage.insert(downloadedChunksPrefix ++ idxBytes, chunkId) + idx = idx + 1 + } + } + + def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { + historyStorage.get(id).map {bytes => + val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) + val startingTime = r.getULong() + val latestChunkFetchTime = r.getULong() + val snapshotHeight = r.getUInt().toInt + val utxoSetRootHash = r.getBytes(Constants.HashLength) + val utxoSetTreeHeight = r.getByte() + val totalChunks = r.getUInt().toInt + val expectedChunksSize = r.getUInt().toInt + val downloadedChunksSize = r.getUInt().toInt + val expectedChunks = new mutable.ArrayBuffer[SubtreeId](initialSize = expectedChunksSize) + (0 until expectedChunksSize).foreach {idx => + val idxBytes = Ints.toByteArray(idx) + historyStorage.get(expectedChunksPrefix ++ idxBytes) match { + case Some(chunkBytes) => expectedChunks += (Digest32 @@ chunkBytes) + case None => log.warn(s"Expected chunk #${id} not found in the database") + } + } + val downloadedChunks = new mutable.ArrayBuffer[SubtreeId](initialSize = expectedChunksSize) + (0 until downloadedChunksSize).foreach {idx => + val idxBytes = Ints.toByteArray(idx) + historyStorage.get(downloadedChunksPrefix ++ idxBytes) match { + case Some(chunkBytes) => downloadedChunks += (Digest32 @@ chunkBytes) + case None => log.warn(s"Downloaded chunk #${id} not found in the database") + } + } + UtxoSetSnapshotDownloadPlan( + startingTime, + latestChunkFetchTime, + snapshotHeight, + Digest32 @@ utxoSetRootHash, + utxoSetTreeHeight, + totalChunks, + expectedChunks, + downloadedChunks + ) + } + } +} + +case class UtxoSetSnapshotDownloadPlan(startingTime: Long, + latestChunkFetchTime: Long, + snapshotHeight: Height, + utxoSetRootHash: Digest32, + utxoSetTreeHeight: Byte, + totalChunks: Int, + expectedChunkIds: IndexedSeq[SubtreeId], + downloadedChunkIds: IndexedSeq[SubtreeId]) { + def id: Digest32 = utxoSetRootHash +} + +object UtxoSetSnapshotDownloadPlan { + def fromManifest(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height): UtxoSetSnapshotDownloadPlan = { + val subtrees = manifest.subtreesIds + val now = System.currentTimeMillis() + //todo: fix .toByte below by making height byte + UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.size, subtrees.toIndexedSeq, IndexedSeq.empty) + } +} From c22a4a4e3f0c5a90594237cdea5708e9a41090d1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 15 Dec 2022 23:28:35 +0300 Subject: [PATCH 073/204] registerManifestToDownload, getUtxoSetSnapshotDownloadPlan --- .../UtxoSetSnapshotProcessor.scala | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) 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 dbc4bbac94..4f78972c7b 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 @@ -4,11 +4,12 @@ import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.UtxoState.SubtreeId -import org.ergoplatform.settings.Constants +import org.ergoplatform.settings.{Algos, Constants} import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ByteArrayBuilder, ScorexLogging} import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} + import java.nio.ByteBuffer import scala.collection.mutable @@ -33,12 +34,35 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) - def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? + private val downloadPlanKey = Blake2b256.hash("download plan") + + private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None + + def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? //todo: implement + + def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height) = { + val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) + _cachedDownloadPlan = Some(plan) + historyStorage.insert(downloadPlanKey, plan.id) + writeDownloadPlanToTheDb(plan) + } + + def getUtxoSetSnapshotDownloadPlan() : Option[UtxoSetSnapshotDownloadPlan] = { + _cachedDownloadPlan match { + case s@Some(_) => s + case None => historyStorage.get(downloadPlanKey).flatMap { planId => + val planOpt = readDownloadPlanFromDb(Digest32 @@ planId) + if (planOpt.isEmpty) log.warn(s"No download plam with id ${Algos.encode(planId)} found") + planOpt + } + } + } + - def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { + private def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { val w = new VLQByteBufferWriter(new ByteArrayBuilder()) w.putULong(plan.startingTime) - w.putULong(plan.latestChunkFetchTime) + w.putULong(plan.latestUpdateTime) w.putUInt(plan.snapshotHeight) w.putBytes(plan.utxoSetRootHash) w.put(plan.utxoSetTreeHeight) @@ -64,7 +88,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } - def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { + private def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { historyStorage.get(id).map {bytes => val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) val startingTime = r.getULong() @@ -105,8 +129,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } +//todo: add peers to download from case class UtxoSetSnapshotDownloadPlan(startingTime: Long, - latestChunkFetchTime: Long, + latestUpdateTime: Long, snapshotHeight: Height, utxoSetRootHash: Digest32, utxoSetTreeHeight: Byte, From 566780c52af86215bf5bc155ef80f06f702ea1e3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 20 Dec 2022 00:12:02 +0300 Subject: [PATCH 074/204] persistence for utxo set snapshot chunks --- .../network/ErgoNodeViewSynchronizer.scala | 75 +++++------ .../history/storage/HistoryStorage.scala | 2 + .../UtxoSetSnapshotProcessor.scala | 116 +++++++++++++----- .../nodeView/state/SnapshotsDb.scala | 4 +- 4 files changed, 128 insertions(+), 69 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index b3be90088b..fd222d86c0 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -41,7 +41,7 @@ import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version import scorex.crypto.authds.avltree.batch.BatchAVLProver -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.encode.Base16 @@ -819,15 +819,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + //todo: clear the table below private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() - private var storedManifestHeight: Height = 0 - private var storedManifest: Option[BatchAVLProverManifest[Digest32]] = None - private val expectedSubtrees= mutable.Set[ModifierId]() - private val requestedSubtrees= mutable.Set[ModifierId]() - - //todo: store on disk - private val storedChunks = mutable.Buffer[BatchAVLProverSubtree[Digest32]]() + private def heightOfManifest(manifestId: ManifestId): Option[Height] = { + availableManifests + .find(_._2.exists(_._2.sameElements(manifestId))) + .map(_._1) + } protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => @@ -838,29 +837,35 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, checkUtxoSetManifests(hr) } - private def requestMoreChunksIfNeeded(remote: ConnectedPeer): Unit = { - if (requestedSubtrees.size < 50) { - val toRequest = expectedSubtrees.take(50) - toRequest.foreach { subtreeId => - expectedSubtrees -= subtreeId - requestedSubtrees += subtreeId - requestUtxoSetChunk(Digest32 @@ Base16.decode(subtreeId).get, remote) - } + private def requestMoreChunksIfNeeded(hr: ErgoHistory, remote: ConnectedPeer): Unit = { + hr.getUtxoSetSnapshotDownloadPlan() match { + case Some(downloadPlan) => + if (downloadPlan.downloadingChunks < 50) { + val toRequest = hr.getChunkIdsToDownload(50) + toRequest.foreach { + subtreeId => + requestUtxoSetChunk(subtreeId, remote) + } + } + case None => + // todo: log } } - protected def processManifest(manifestBytes: Array[Byte], remote: ConnectedPeer) = { + protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer) = { //todo : check that mnifestBytes were requested val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => - val subtrees = manifest.subtreesIds - log.info(s"Got manifest ${Algos.encode(manifest.id)}, going to download ${subtrees.size} chunks for it") - storedManifest = Some(manifest) - subtrees.foreach {subtreeId => //todo: remove take() with gradual download - expectedSubtrees += (ModifierId @@ Base16.encode(subtreeId)) + log.info(s"Got manifest ${Algos.encode(manifest.id)}") + heightOfManifest(manifest.id) match { + case Some(height) => + hr.registerManifestToDownload(manifest, height) + log.info(s"Going to download ${50} chunks for manifest ${Algos.encode(manifest.id)}") // todo: fix msg + requestMoreChunksIfNeeded(hr, remote) + case None => + log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") } - requestMoreChunksIfNeeded(remote) case Failure(e) => //todo: log.info("Cant' restore manifest from bytes ", e) @@ -869,17 +874,18 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], - usr: UtxoStateReader, + hr: ErgoHistory, remote: ConnectedPeer): Unit = { val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => + val downloadPlan = hr.getUtxoSetSnapshotDownloadPlan() log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release - log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release - requestedSubtrees -= ModifierId @@ Base16.encode(subtree.id) - storedChunks += subtree - if (expectedSubtrees.isEmpty && requestedSubtrees.isEmpty) { - storedManifest match { + // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release + hr.registerDownloadedChunk(subtree.id, serializedChunk) + if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { + ??? + /* storedManifest match { case Some(manifest) => serializer.combine(manifest -> storedChunks, 32, None) match { case Success(prover: BatchAVLProver[Digest32, Blake2b256.type]) => @@ -889,9 +895,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, //todo: process } case None => - } + } */ } else{ - requestMoreChunksIfNeeded(remote) + requestMoreChunksIfNeeded(hr, remote) } case Failure(e) => //todo: @@ -1166,7 +1172,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && - storedManifest.isEmpty) { + historyReader.getUtxoSetSnapshotDownloadPlan().isEmpty) { val res = availableManifests.filter { case (h, records) => if (records.length >= MinSnapshots) { val idsSet = records.map(_._2).map(Base16.encode).toSet @@ -1186,7 +1192,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val manifestId = records.head._2 val peers = records.map(_._1) val randomPeer = peers(Random.nextInt(peers.length)) - storedManifestHeight = height requestManifest(manifestId, randomPeer) } else { log.info("No manifests to download found ") @@ -1343,7 +1348,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } case (_: ManifestSpec.type, manifestBytes: Array[Byte], remote) => - processManifest(manifestBytes, remote) + processManifest(hr, manifestBytes, remote) case (_: GetUtxoSnapshotChunkSpec.type, subtreeId: Array[Byte], remote) => usrOpt match { case Some(usr) => sendUtxoSnapshotChunk(Digest32 @@ subtreeId, usr, remote) @@ -1351,7 +1356,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } case (_: UtxoSnapshotChunkSpec.type, serializedChunk: Array[Byte], remote) => usrOpt match { - case Some(usr) => processUtxoSnapshotChunk(serializedChunk, usr, remote) + case Some(_) => processUtxoSnapshotChunk(serializedChunk, hr, remote) case None => log.warn(s"Asked for snapshot when UTXO set is not supported, remote: $remote") } 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 83fbe54d29..e55626057a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -88,6 +88,8 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, c def get(id: ModifierId): Option[Array[Byte]] = get(idToBytes(id)) + def contains(id: Array[Byte]): Boolean = objectsStore.get(id).isDefined + def contains(id: ModifierId): Boolean = objectsStore.get(idToBytes(id)).isDefined def insert(indexesToInsert: Seq[(ByteArrayWrapper, Array[Byte])], 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 4f78972c7b..16fe79b5ab 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 @@ -9,6 +9,7 @@ import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ByteArrayBuilder, ScorexLogging} import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} +import spire.syntax.all.cfor import java.nio.ByteBuffer import scala.collection.mutable @@ -28,7 +29,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { def utxoSnapshotApplied(height: Height): Unit = { _utxoSnapshotApplied = true - minimalFullBlockHeightVar = height + 1 //todo: or height + 1? + minimalFullBlockHeightVar = height + 1 //todo: or height + 1? } private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) @@ -40,24 +41,71 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? //todo: implement - def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height) = { + def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], + blockHeight: Height): UtxoSetSnapshotDownloadPlan = { val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) - _cachedDownloadPlan = Some(plan) - historyStorage.insert(downloadPlanKey, plan.id) - writeDownloadPlanToTheDb(plan) + updateUtxoSetSnashotDownloadPlan(plan) } - def getUtxoSetSnapshotDownloadPlan() : Option[UtxoSetSnapshotDownloadPlan] = { + def getUtxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { _cachedDownloadPlan match { case s@Some(_) => s - case None => historyStorage.get(downloadPlanKey).flatMap { planId => - val planOpt = readDownloadPlanFromDb(Digest32 @@ planId) - if (planOpt.isEmpty) log.warn(s"No download plam with id ${Algos.encode(planId)} found") - planOpt - } + case None => + historyStorage.get(downloadPlanKey).flatMap { planId => + val planOpt = readDownloadPlanFromDb(Digest32 @@ planId) + if (planOpt.isEmpty) log.warn(s"No download plan with id ${Algos.encode(planId)} found") + if (planOpt.nonEmpty) _cachedDownloadPlan = planOpt + planOpt + } + } + } + + def getChunkIdsToDownload(howMany: Int): Seq[SubtreeId] = { + getUtxoSetSnapshotDownloadPlan() match { + case Some(plan) => + val expected = plan.expectedChunkIds + val downloadIndex = plan.downloadedChunkIds.size + val toDownload = if (expected.size > downloadIndex) { + expected.slice(downloadIndex, downloadIndex + howMany) + } else { + IndexedSeq.empty + } + val newDownloaded = plan.downloadedChunkIds ++ toDownload.map(_ => false) + val newDownloading = plan.downloadingChunks + toDownload.size + plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = newDownloaded, downloadingChunks = newDownloading) + _cachedDownloadPlan = Some(plan) // we update only in-memory cache, so in case of node restart, chunks won't be missed + toDownload + case None => + log.warn(s"No download plan is found when requested to propose $howMany chunks to download") + Seq.empty } } + def registerDownloadedChunk(chunkId: Array[Byte], chunkSerialized: Array[Byte]): Unit = { + getUtxoSetSnapshotDownloadPlan() match { + case Some(plan) => + cfor(0)(_ < plan.downloadedChunkIds.size, _ + 1) { idx => + if (!plan.downloadedChunkIds(idx) && plan.expectedChunkIds(idx).sameElements(chunkId)) { + val idxBytes = Ints.toByteArray(idx) + historyStorage.insert(downloadedChunksPrefix ++ idxBytes, chunkSerialized) + val updDownloaded = plan.downloadedChunkIds.updated(idx, true) + val updDownloading = plan.downloadingChunks - 1 + val updPlan = plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = updDownloaded, downloadingChunks = updDownloading) + updateUtxoSetSnashotDownloadPlan(updPlan) + return + } + } + case None => + log.warn(s"Chunk ${Algos.encode(chunkId)} downloaded but no download plan found") + } + } + + private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan) = { + _cachedDownloadPlan = Some(plan) + historyStorage.insert(downloadPlanKey, plan.id) + writeDownloadPlanToTheDb(plan) + plan + } private def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { val w = new VLQByteBufferWriter(new ByteArrayBuilder()) @@ -66,7 +114,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { w.putUInt(plan.snapshotHeight) w.putBytes(plan.utxoSetRootHash) w.put(plan.utxoSetTreeHeight) - w.putUInt(plan.totalChunks) w.putUInt(plan.expectedChunkIds.size) w.putUInt(plan.downloadedChunkIds.size) val metaDataBytes = w.result().toBytes @@ -80,53 +127,46 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { idx = idx + 1 } - idx = 0 - plan.downloadedChunkIds.foreach { chunkId => - val idxBytes = Ints.toByteArray(idx) - historyStorage.insert(downloadedChunksPrefix ++ idxBytes, chunkId) - idx = idx + 1 - } } private def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { - historyStorage.get(id).map {bytes => + historyStorage.get(id).map { bytes => val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) val startingTime = r.getULong() val latestChunkFetchTime = r.getULong() val snapshotHeight = r.getUInt().toInt val utxoSetRootHash = r.getBytes(Constants.HashLength) val utxoSetTreeHeight = r.getByte() - val totalChunks = r.getUInt().toInt val expectedChunksSize = r.getUInt().toInt val downloadedChunksSize = r.getUInt().toInt + val expectedChunks = new mutable.ArrayBuffer[SubtreeId](initialSize = expectedChunksSize) - (0 until expectedChunksSize).foreach {idx => + (0 until expectedChunksSize).foreach { idx => val idxBytes = Ints.toByteArray(idx) historyStorage.get(expectedChunksPrefix ++ idxBytes) match { case Some(chunkBytes) => expectedChunks += (Digest32 @@ chunkBytes) case None => log.warn(s"Expected chunk #${id} not found in the database") } } - val downloadedChunks = new mutable.ArrayBuffer[SubtreeId](initialSize = expectedChunksSize) - (0 until downloadedChunksSize).foreach {idx => + val downloadedChunks = new mutable.ArrayBuffer[Boolean](initialSize = downloadedChunksSize) + (0 until downloadedChunksSize).foreach { idx => val idxBytes = Ints.toByteArray(idx) - historyStorage.get(downloadedChunksPrefix ++ idxBytes) match { - case Some(chunkBytes) => downloadedChunks += (Digest32 @@ chunkBytes) - case None => log.warn(s"Downloaded chunk #${id} not found in the database") - } + downloadedChunks += historyStorage.contains(downloadedChunksPrefix ++ idxBytes) } + UtxoSetSnapshotDownloadPlan( startingTime, latestChunkFetchTime, snapshotHeight, Digest32 @@ utxoSetRootHash, utxoSetTreeHeight, - totalChunks, expectedChunks, - downloadedChunks + downloadedChunks, + downloadingChunks = 0 ) } } + } //todo: add peers to download from @@ -135,17 +175,29 @@ case class UtxoSetSnapshotDownloadPlan(startingTime: Long, snapshotHeight: Height, utxoSetRootHash: Digest32, utxoSetTreeHeight: Byte, - totalChunks: Int, expectedChunkIds: IndexedSeq[SubtreeId], - downloadedChunkIds: IndexedSeq[SubtreeId]) { + downloadedChunkIds: IndexedSeq[Boolean], + downloadingChunks: Int) { + def id: Digest32 = utxoSetRootHash + + def totalChunks: Int = expectedChunkIds.size + + def fullyDownloaded: Boolean = { + (expectedChunkIds.size == downloadedChunkIds.size) && + downloadingChunks == 0 && + downloadedChunkIds.forall(_ == true) + } + } object UtxoSetSnapshotDownloadPlan { + def fromManifest(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height): UtxoSetSnapshotDownloadPlan = { val subtrees = manifest.subtreesIds val now = System.currentTimeMillis() //todo: fix .toByte below by making height byte - UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.size, subtrees.toIndexedSeq, IndexedSeq.empty) + UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, IndexedSeq.empty, 0) } + } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 0e73f9af41..ba982e711b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -6,7 +6,7 @@ import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} import org.ergoplatform.wallet.Constants -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging @@ -91,7 +91,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { writeSnapshotsInfo(si) } - def readManifest(id: ManifestId): Option[BatchAVLProverManifest[Digest32]] = { + def readManifest(id: ManifestId): Option[UtxoState.Manifest] = { readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) } From 9b2d5f6697e315b72f52a74cc35a752dfd4df4df Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 21 Dec 2022 01:34:55 +0300 Subject: [PATCH 075/204] InitStateFromSnapshot not passing data --- papers/utxo.md | 6 ++++++ .../network/ErgoNodeViewSynchronizer.scala | 20 ++++--------------- .../nodeView/ErgoNodeViewHolder.scala | 7 ++++--- .../UtxoSetSnapshotProcessor.scala | 18 ++++++++++++++++- .../nodeView/state/UtxoState.scala | 1 + 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/papers/utxo.md b/papers/utxo.md index 85b2cd9ed3..7096066742 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -21,6 +21,12 @@ UTXO set is authenticated via AVL+ tree. Time is broken into epochs, 1 epoch = 51,840 blocks (~72 days). +Chunk format +------------ + +Manifest format +--------------- + Networking Layer ---------------- diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index fd222d86c0..720d540ea7 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -40,9 +40,8 @@ import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version -import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 import scala.annotation.tailrec @@ -879,23 +878,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => - val downloadPlan = hr.getUtxoSetSnapshotDownloadPlan() + val downloadPlan = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release hr.registerDownloadedChunk(subtree.id, serializedChunk) if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { - ??? - /* storedManifest match { - case Some(manifest) => - serializer.combine(manifest -> storedChunks, 32, None) match { - case Success(prover: BatchAVLProver[Digest32, Blake2b256.type]) => - viewHolderRef ! InitStateFromSnapshot(storedManifestHeight, prover) - case Failure(_) => - ??? - //todo: process - } - case None => - } */ + viewHolderRef ! InitStateFromSnapshot() } else{ requestMoreChunksIfNeeded(hr, remote) } @@ -1550,7 +1538,7 @@ object ErgoNodeViewSynchronizer { */ case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) - case class InitStateFromSnapshot(height: Height, prover: BatchAVLProver[Digest32, Blake2b256.type]) + case class InitStateFromSnapshot() } } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index e2e2b03739..5bb1e21f88 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -280,10 +280,11 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } def processStateSnapshot: Receive = { - case InitStateFromSnapshot(height, prover) => - log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") + case InitStateFromSnapshot() => + ??? + /* log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) - updateNodeView(updatedState = Some(UtxoState.fromSnapshot(prover, settings).asInstanceOf[State])) + updateNodeView(updatedState = Some(UtxoState.fromSnapshot(prover, settings).asInstanceOf[State])) */ } /** 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 16fe79b5ab..991b492cc0 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 @@ -5,6 +5,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, Constants} +import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ByteArrayBuilder, ScorexLogging} @@ -37,6 +38,8 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private val downloadPlanKey = Blake2b256.hash("download plan") + private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None + private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? //todo: implement @@ -44,6 +47,8 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height): UtxoSetSnapshotDownloadPlan = { val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) + _manifest = Some(manifest) + println(_manifest.get.id) updateUtxoSetSnashotDownloadPlan(plan) } @@ -103,7 +108,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan) = { _cachedDownloadPlan = Some(plan) historyStorage.insert(downloadPlanKey, plan.id) - writeDownloadPlanToTheDb(plan) + writeDownloadPlanToTheDb(plan) // todo: not always write to db plan } @@ -167,6 +172,17 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + + import scala.util.Try + import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLProver, VersionedAVLStorage, VersionedLDBAVLStorage} + import org.ergoplatform.settings.Algos.HF + + def createPersistentProver(): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { + val manifest = _manifest.get + var avlProver: BatchAVLProver[Digest32, HF] = + val storage: VersionedAVLStorage[Digest32] = new VersionedLDBAVLStorage(???, ???) + storage.update() + } } //todo: add peers to download from diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 615ee3b867..b1d90fbadd 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -12,6 +12,7 @@ import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperation import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier +import org.ergoplatform.nodeView.history.ErgoHistory import scorex.core._ import scorex.core.transaction.Transaction import scorex.core.transaction.state.TransactionValidation From 85864efc72cbbb77aea86b4662170eb100bae42b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 22 Dec 2022 13:32:07 +0300 Subject: [PATCH 076/204] createPersistentProver --- .../benchmarks/AVLTreeBatchPerformance.scala | 2 +- .../crypto/authds/benchmarks/Helper.scala | 6 +- .../batch/VersionedLDBAVLStorage.scala | 67 ++++++++++++++++--- .../scala/scorex/db/LDBVersionedStore.scala | 4 +- .../AVLStorageWithPersistentProverSpec.scala | 2 +- .../batch/benchmark/BatchingBenchmark.scala | 4 +- .../avltree/batch/benchmark/OOMTest.scala | 2 +- .../avltree/batch/helpers/TestHelper.scala | 2 +- .../UtxoSetSnapshotProcessor.scala | 40 ++++++++--- .../nodeView/state/UtxoState.scala | 20 ++++-- .../nodeView/state/UtxoStateReader.scala | 2 +- 11 files changed, 117 insertions(+), 34 deletions(-) diff --git a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala index dd1786a2db..4cfed4f425 100644 --- a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala +++ b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala @@ -21,7 +21,7 @@ object AVLTreeBatchPerformance extends { val logger = LoggerFactory.getLogger("TEST") var prover: Prover = _ var store: LDBVersionedStore = _ - var storage: VersionedLDBAVLStorage[Digest32] = _ + var storage: VersionedLDBAVLStorage[Digest32, HF] = _ var operations: Array[Operation] = _ @Setup(Level.Iteration) diff --git a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala index a3e1ead7af..8d9bce4fdb 100644 --- a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala +++ b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala @@ -12,7 +12,7 @@ object Helper { type HF = Blake2b256.type type Prover = PersistentBatchAVLProver[Digest32, HF] - implicit val hf = Blake2b256 + implicit val hf: HF = Blake2b256 val kl = 32 val vl = 8 @@ -34,11 +34,11 @@ object Helper { } def persistentProverWithVersionedStore(initialKeepVersions: Int, - baseOperationsCount: Int = 0): (Prover, LDBVersionedStore, VersionedLDBAVLStorage[Digest32]) = { + baseOperationsCount: Int = 0): (Prover, LDBVersionedStore, VersionedLDBAVLStorage[Digest32, HF]) = { val dir = java.nio.file.Files.createTempDirectory("bench_testing_" + scala.util.Random.alphanumeric.take(15)).toFile dir.deleteOnExit() val store = new LDBVersionedStore(dir, initialKeepVersions = initialKeepVersions) - val storage = new VersionedLDBAVLStorage(store, NodeParameters(kl, Some(vl), ll)) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, NodeParameters(kl, Some(vl), ll)) require(storage.isEmpty) val prover = new BatchAVLProver[Digest32, HF](kl, Some(vl)) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 7e13bf73ed..719d06728e 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -2,6 +2,7 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.{InternalNodePrefix, LeafPrefix} +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} import scorex.crypto.authds.{ADDigest, ADKey, ADValue, Balance} import scorex.util.encode.Base58 import scorex.crypto.hash @@ -20,9 +21,9 @@ import scala.util.{Failure, Try} * @param hf - hash function used to construct the tree * @tparam D - type of hash function digest */ -class VersionedLDBAVLStorage[D <: Digest](store: LDBVersionedStore, +class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDBVersionedStore, nodeParameters: NodeParameters) - (implicit val hf: CryptographicHash[D]) extends VersionedAVLStorage[D] with ScorexLogging { + (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { private lazy val labelSize = nodeParameters.labelSize @@ -31,20 +32,29 @@ class VersionedLDBAVLStorage[D <: Digest](store: LDBVersionedStore, private val fixedSizeValueMode = nodeParameters.valueSize.isDefined - override def rollback(version: ADDigest): Try[(ProverNodes[D], Int)] = Try { - if (!this.version.contains(version)) { // do not rollback to self - store.rollbackTo(version) + def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { + restorePrunedTopNode().map {recoveredTop => + new BatchAVLProver(nodeParameters.keySize, nodeParameters.valueSize, Some(recoveredTop))(hf) } + } + private def restorePrunedTopNode(): Try[(ProverNodes[D], Int)] = Try { val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(TopNodeKey).get)(hf, store, nodeParameters) val topHeight = Ints.fromByteArray(store.get(TopNodeHeight).get) top -> topHeight - }.recoverWith { case e => - log.warn(s"Failed to recover tree for digest ${Base58.encode(version)}:", e) - Failure(e) } + override def rollback(version: ADDigest): Try[(ProverNodes[D], Int)] = Try { + if (!this.version.contains(version)) { // do not rollback to self + store.rollbackTo(version) + } + }.flatMap(_ => restorePrunedTopNode()) + .recoverWith { case e => + log.warn(s"Failed to recover tree for digest ${Base58.encode(version)}:", e) + Failure(e) + } + override def version: Option[ADDigest] = store.lastVersionID.map(d => ADDigest @@ d) def rollbackVersions: Iterable[ADDigest] = store.rollbackVersions().map(d => ADDigest @@ d) @@ -64,6 +74,47 @@ class VersionedLDBAVLStorage[D <: Digest](store: LDBVersionedStore, store.update(digestWrapper, toRemove, toUpdateWithWrapped) } + def update(manifest: BatchAVLProverManifest[D], + chunks: Iterator[BatchAVLProverSubtree[D]], + additionalData: Iterator[(Array[Byte], Array[Byte])]): Try[Unit] = { + //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate + def digest(rootNode: Node[D], rootNodeHeight: Int): ADDigest = { + assert(rootNodeHeight >= 0 && rootNodeHeight < 256) + // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect + // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the + // verifier, by adding 256 if it's negative). + // The reason rootNodeHeight should never be more than 255 is that if height is more than 255, + // then the AVL tree has at least 2^{255/1.4405} = 2^177 leaves, which is more than the number of atoms on planet Earth. + ADDigest @@ (rootNode.label :+ rootNodeHeight.toByte) + } + + val rootNode = manifest.root + val rootNodeHeight = manifest.rootHeight + val digestWrapper = digest(rootNode, rootNodeHeight) + val indexes = Iterator(TopNodeKey -> nodeKey(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) + val nodesIterator = visitedNodesSerializer(manifest, chunks) + + store.update(digestWrapper, Nil, toUpdate = indexes ++ nodesIterator ++ additionalData) + } + + private def visitedNodesSerializer(manifest: BatchAVLProverManifest[D], + chunks: Iterator[BatchAVLProverSubtree[D]]) = { + def idCollector(node: ProverNodes[D], + acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { + val pair: (Array[Byte], Array[Byte]) = (nodeKey(node), toBytes(node)) + node match { + case n: ProxyInternalNode[D] if n.isEmpty => + acc ++ Iterator(pair) + case i : InternalProverNode[D] => + acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) + case _: ProverLeaf[D] => + acc ++ Iterator(pair) + } + } + + idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) + } + private def serializedVisitedNodes(node: ProverNodes[D], isTop: Boolean): Array[(Array[Byte], Array[Byte])] = { // Should always serialize top node. It may not be new if it is the creation of the tree diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 0cb937bb04..230966e0e1 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -232,8 +232,8 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e } def update(versionID: VersionID, - toRemove: Iterable[Array[Byte]], - toUpdate: Iterable[(Array[Byte], Array[Byte])]): Try[Unit] = Try { + toRemove: TraversableOnce[Array[Byte]], + toUpdate: TraversableOnce[(Array[Byte], Array[Byte])]): Try[Unit] = Try { lock.writeLock().lock() val lastLsn = lsn // remember current LSN value val batch = db.createWriteBatch() diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala index 5bec7d4907..e79716d01d 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala @@ -20,7 +20,7 @@ class AVLStorageWithPersistentProverSpec extends AnyPropSpec with Matchers { private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - protected lazy val storage = new VersionedLDBAVLStorage(stateStore, np) + protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) protected lazy val persistentProver: PersistentBatchAVLProver[Digest32, HF] = PersistentBatchAVLProver.create( diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala index ecadcd1ad5..3e12b45a9f 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala @@ -15,11 +15,11 @@ object BatchingBenchmark extends App with FileHelper { val NumMods = 200000 - implicit val hf = Blake2b256 + implicit val hf: HF = Blake2b256 type HF = Blake2b256.type val store = new LDBVersionedStore(getRandomTempDir, initialKeepVersions = 10) - val storage = new VersionedLDBAVLStorage(store, NodeParameters(KeyLength, Some(ValueLength), LabelLength)) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, NodeParameters(KeyLength, Some(ValueLength), LabelLength)) require(storage.isEmpty) val mods = generateModifications() var digest: ADDigest = ADDigest @@ Array[Byte]() diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala index 67e3c2c26c..11ea2c052b 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala @@ -24,7 +24,7 @@ object OOMTest extends App { val bestVersionKey = Blake2b256("best state version") private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - protected lazy val storage = new VersionedLDBAVLStorage(store, np) + protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, np) val afterGenesisStateDigestHex: String = "78b130095239561ecf5449a7794c0615326d1fd007cc79dcc286e46e4beb1d3f01" val afterGenesisStateDigest: ADDigest = ADDigest @@ Base16.decode(afterGenesisStateDigestHex).get diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala index a3e1c8ce60..756ed3fa4c 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala @@ -15,7 +15,7 @@ trait TestHelper extends FileHelper { type PROVER = BatchAVLProver[D, HF] type VERIFIER = BatchAVLVerifier[D, HF] type PERSISTENT_PROVER = PersistentBatchAVLProver[D, HF] - type STORAGE = VersionedLDBAVLStorage[D] + type STORAGE = VersionedLDBAVLStorage[D, HF] protected val KL: Int protected val VL: Int 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 991b492cc0..d049a3a3ef 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 @@ -5,8 +5,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, Constants} -import scorex.crypto.authds.avltree.batch.BatchAVLProver -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ByteArrayBuilder, ScorexLogging} import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} @@ -22,10 +21,15 @@ import scala.collection.mutable */ trait UtxoSetSnapshotProcessor extends ScorexLogging { + import org.ergoplatform.settings.ErgoAlgos.HF + protected val historyStorage: HistoryStorage private[history] var minimalFullBlockHeightVar: Int + private implicit val hf = Blake2b256 + private val avlTreeSerializer = new BatchAVLProverSerializer[Digest32, Blake2b256.type]() + var _utxoSnapshotApplied = false def utxoSnapshotApplied(height: Height): Unit = { @@ -105,6 +109,21 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + def chunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { + getUtxoSetSnapshotDownloadPlan() match { + case Some(plan) => + Iterator.range(0, plan.totalChunks).flatMap{idx => + val idxBytes = Ints.toByteArray(idx) + historyStorage + .get(downloadedChunksPrefix ++ idxBytes) + .flatMap(bs => avlTreeSerializer.subtreeFromBytes(bs, 32).toOption) + } + case None => + log.error("todo: msg") // todo: + Iterator.empty + } + } + private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan) = { _cachedDownloadPlan = Some(plan) historyStorage.insert(downloadPlanKey, plan.id) @@ -174,14 +193,19 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import scala.util.Try - import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLProver, VersionedAVLStorage, VersionedLDBAVLStorage} - import org.ergoplatform.settings.Algos.HF + import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} def createPersistentProver(): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { - val manifest = _manifest.get - var avlProver: BatchAVLProver[Digest32, HF] = - val storage: VersionedAVLStorage[Digest32] = new VersionedLDBAVLStorage(???, ???) - storage.update() + val manifest = _manifest.get //todo: .get + val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](null, null) + //todo: write metadata, see UtxoState.metadata + ldbStorage.update(manifest, chunksIterator(), Iterator.empty) + ldbStorage.restorePrunedProver().map { prunedAvlProver => + new PersistentBatchAVLProver[Digest32, HF] { + override var avlProver = prunedAvlProver + override val storage = ldbStorage + } + }.get //todo: .get } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index b1d90fbadd..78a99ebb4e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -12,7 +12,6 @@ import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperation import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier -import org.ergoplatform.nodeView.history.ErgoHistory import scorex.core._ import scorex.core.transaction.Transaction import scorex.core.transaction.state.TransactionValidation @@ -21,7 +20,7 @@ import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} import scorex.util.ModifierId import scorex.util.ScorexLogging @@ -251,6 +250,15 @@ object UtxoState extends ScorexLogging { val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") // block-specific metadata to write into database (in addition to AVL+ tree) + + /** + * + * @param modId - ID of a block (header) corresponding to UTXO set + * @param stateRoot - UTXO set digest (hash and tree height) AFTER applying block `modId` + * @param currentEmissionBoxOpt + * @param context + * @return + */ private def metadata(modId: VersionTag, stateRoot: ADDigest, currentEmissionBoxOpt: Option[ErgoBox], @@ -272,7 +280,7 @@ object UtxoState extends ScorexLogging { val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { val bp = new BatchAVLProver[Digest32, HF](keyLength = 32, valueLengthOpt = None) val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) PersistentBatchAVLProver.create(bp, storage).get } new UtxoState(persistentProver, version, store, constants) @@ -296,7 +304,7 @@ object UtxoState extends ScorexLogging { val defaultStateContext = ErgoStateContext.empty(constants, parameters) val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) val persistentProver = PersistentBatchAVLProver.create( p, storage, @@ -307,7 +315,7 @@ object UtxoState extends ScorexLogging { new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) } - def fromSnapshot(prover: BatchAVLProver[Digest32, Blake2b256.type], + def fromSnapshot(prover: BatchAVLProver[Digest32, HF], settings: ErgoSettings) = { val stateDir = ErgoState.stateDir(settings) stateDir.mkdirs() @@ -318,7 +326,7 @@ object UtxoState extends ScorexLogging { .getOrElse(ErgoState.genesisStateVersion) val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) PersistentBatchAVLProver.create(prover, storage).get } new UtxoState(persistentProver, version, store, constants) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 68cc121b0a..cb2624187a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -25,7 +25,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi val constants: StateConstants private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - protected lazy val storage = new VersionedLDBAVLStorage(store, np) + protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, np) protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] From 484c037b9d2eeec1a49d1a9da100ccdde99c4971 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 22 Dec 2022 14:15:54 +0300 Subject: [PATCH 077/204] polishing createPersistentProver, initial version of InitStateFromSnapshot processing --- .../nodeView/ErgoNodeViewHolder.scala | 109 ++++++++++-------- .../UtxoSetSnapshotProcessor.scala | 11 +- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 5bb1e21f88..1fbaa36e24 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -145,7 +145,6 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } - private def trimChainSuffix(suffix: IndexedSeq[BlockSection], rollbackPoint: ModifierId): IndexedSeq[BlockSection] = { val idx = suffix.indexWhere(_.id == rollbackPoint) @@ -153,36 +152,36 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } /** - - Assume that history knows the following blocktree: - - G - / \ - * G - / \ - * G - - where path with G-s is about canonical chain (G means semantically valid modifier), path with * is sidechain (* means - that semantic validity is unknown). New modifier is coming to the sidechain, it sends rollback to the root + - application of the sidechain to the state. Assume that state is finding that some modifier in the sidechain is - incorrect: - - G - / \ - G G - / \ - B G - / * - - In this case history should be informed about the bad modifier and it should retarget state - - //todo: improve the comment below - - We assume that we apply modifiers sequentially (on a single modifier coming from the network or generated locally), - and in case of failed application of some modifier in a progressInfo, rollback point in an alternative should be not - earlier than a rollback point of an initial progressInfo. - **/ + * Assume that history knows the following blocktree: + * + * G + * / \ + * G + * / \ + * G + * + * where path with G-s is about canonical chain (G means semantically valid modifier), path with * is sidechain (* means + * that semantic validity is unknown). New modifier is coming to the sidechain, it sends rollback to the root + + * application of the sidechain to the state. Assume that state is finding that some modifier in the sidechain is + * incorrect: + * + * G + * / \ + * G G + * / \ + * B G + * / + * + * + * In this case history should be informed about the bad modifier and it should retarget state + * + * //todo: improve the comment below + * + * We assume that we apply modifiers sequentially (on a single modifier coming from the network or generated locally), + * and in case of failed application of some modifier in a progressInfo, rollback point in an alternative should be not + * earlier than a rollback point of an initial progressInfo. + * */ @tailrec protected final def updateState(history: ErgoHistory, @@ -237,7 +236,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti updateInfo.state.applyModifier(modToApply, chainTipOpt)(lm => pmodModify(lm.pmod, local = true)) match { case Success(stateAfterApply) => history.reportModifierIsValid(modToApply).map { newHis => - if(modToApply.modifierTypeId == ErgoFullBlock.modifierTypeId) { + if (modToApply.modifierTypeId == ErgoFullBlock.modifierTypeId) { context.system.eventStream.publish(FullBlockApplied(modToApply.asInstanceOf[ErgoFullBlock].header)) } UpdateInformation(newHis, stateAfterApply, None, None, updateInfo.suffix :+ modToApply) @@ -281,10 +280,21 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti def processStateSnapshot: Receive = { case InitStateFromSnapshot() => - ??? - /* log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") - history().utxoSnapshotApplied(height) - updateNodeView(updatedState = Some(UtxoState.fromSnapshot(prover, settings).asInstanceOf[State])) */ + val store = minimalState().store + history().createPersistentProver(store) match { + //todo: pass metadata + case Success(pp) => + /* + todo: restore state? + log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") + history().utxoSnapshotApplied(height) + */ + // todo: pass version + val newState = new UtxoState(pp, version = null, store, StateConstants(settings)) + updateNodeView(updatedState = Some(newState.asInstanceOf[State])) + case Failure(_) => ??? + } + } /** @@ -352,7 +362,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val at0 = System.currentTimeMillis() applyFromCacheLoop(modifiersCache) val at = System.currentTimeMillis() - log.debug(s"Application time: ${at-at0}") + log.debug(s"Application time: ${at - at0}") val cleared = modifiersCache.cleanOverfull() @@ -449,10 +459,12 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } //todo: update state in async way? + /** * Remote and local persistent modifiers need to be appended to history, applied to state * which also needs to be git propagated to mempool and wallet - * @param pmod Remote or local persistent modifier + * + * @param pmod Remote or local persistent modifier * @param local whether the modifier was generated locally or not */ protected def pmodModify(pmod: BlockSection, local: Boolean): Unit = { @@ -487,7 +499,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val fullBlockHeight = newHistory.fullBlockHeight val almostSynced = (headersHeight - fullBlockHeight) < almostSyncedGap - val newMemPool = if(almostSynced) { + val newMemPool = if (almostSynced) { updateMemPool(progressInfo.toRemove, blocksApplied, memoryPool()) } else { memoryPool() @@ -693,8 +705,8 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti getNodeViewChanges orElse processStateSnapshot orElse handleHealthCheck orElse { - case a: Any => log.error("Strange input: " + a) - } + case a: Any => log.error("Strange input: " + a) + } } @@ -735,8 +747,11 @@ object ErgoNodeViewHolder { case class EliminateTransactions(ids: Seq[ModifierId]) case object IsChainHealthy + sealed trait HealthCheckResult + case object ChainIsHealthy extends HealthCheckResult + case class ChainIsStuck(reason: String) extends HealthCheckResult } @@ -748,18 +763,21 @@ object ErgoNodeViewHolder { /** * Checks whether chain got stuck by comparing timestamp of bestFullBlock or last time a modifier was applied to history. + * * @param progress metadata of last chain update * @return ChainIsHealthy if chain is healthy and ChainIsStuck(error) with details if it got stuck */ def checkChainIsHealthy( - progress: ChainProgress, - history: ErgoHistory, - timeProvider: NetworkTimeProvider, - settings: ErgoSettings): HealthCheckResult = { + progress: ChainProgress, + history: ErgoHistory, + timeProvider: NetworkTimeProvider, + settings: ErgoSettings): HealthCheckResult = { val ChainProgress(lastMod, headersHeight, blockHeight, lastUpdate) = progress val chainUpdateDelay = timeProvider.time() - lastUpdate val acceptableChainUpdateDelay = settings.nodeSettings.acceptableChainUpdateDelay + def chainUpdateDelayed = chainUpdateDelay > acceptableChainUpdateDelay.toMillis + def chainSynced = history.bestFullBlockOpt.map(_.id) == history.bestHeaderOpt.map(_.id) @@ -788,7 +806,6 @@ private[nodeView] class UtxoNodeViewHolder(settings: ErgoSettings, extends ErgoNodeViewHolder[UtxoState](settings, timeProvider) - object ErgoNodeViewRef { private def digestProps(settings: ErgoSettings, @@ -808,5 +825,5 @@ object ErgoNodeViewRef { def apply(settings: ErgoSettings, timeProvider: NetworkTimeProvider)(implicit system: ActorSystem): ActorRef = system.actorOf(props(settings, timeProvider)) - + } 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 d049a3a3ef..e126e9f85a 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 @@ -5,8 +5,10 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, Constants} +import scorex.crypto.authds.avltree.batch.NodeParameters import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.db.LDBVersionedStore import scorex.util.{ByteArrayBuilder, ScorexLogging} import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} import spire.syntax.all.cfor @@ -195,11 +197,14 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import scala.util.Try import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} - def createPersistentProver(): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { + def createPersistentProver(stateStore: LDBVersionedStore): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { val manifest = _manifest.get //todo: .get - val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](null, null) + val np = NodeParameters(32, None, 32) // todo: use constants + val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) //todo: write metadata, see UtxoState.metadata - ldbStorage.update(manifest, chunksIterator(), Iterator.empty) + log.info("Starting UTXO set snapshot transfer into state database") + ldbStorage.update(manifest, chunksIterator(), additionalData = Iterator.empty) + log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => new PersistentBatchAVLProver[Digest32, HF] { override var avlProver = prunedAvlProver From f96dc1fc26398f992d6eb74e1b33a65c47c4731f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 22 Dec 2022 19:06:53 +0300 Subject: [PATCH 078/204] writing metadata to state db --- .../batch/VersionedLDBAVLStorage.scala | 25 +++++++++++-------- papers/utxo.md | 3 ++- .../network/ErgoNodeViewSynchronizer.scala | 6 +++-- .../nodeView/ErgoNodeViewHolder.scala | 14 ++++------- .../UtxoSetSnapshotProcessor.scala | 18 ++++++++----- .../state/UtxoSetSnapshotPersistence.scala | 4 +-- .../nodeView/state/UtxoState.scala | 8 +++--- 7 files changed, 44 insertions(+), 34 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 719d06728e..642ccd1869 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -78,19 +78,10 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB chunks: Iterator[BatchAVLProverSubtree[D]], additionalData: Iterator[(Array[Byte], Array[Byte])]): Try[Unit] = { //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate - def digest(rootNode: Node[D], rootNodeHeight: Int): ADDigest = { - assert(rootNodeHeight >= 0 && rootNodeHeight < 256) - // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect - // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the - // verifier, by adding 256 if it's negative). - // The reason rootNodeHeight should never be more than 255 is that if height is more than 255, - // then the AVL tree has at least 2^{255/1.4405} = 2^177 leaves, which is more than the number of atoms on planet Earth. - ADDigest @@ (rootNode.label :+ rootNodeHeight.toByte) - } val rootNode = manifest.root val rootNodeHeight = manifest.rootHeight - val digestWrapper = digest(rootNode, rootNodeHeight) + val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) val indexes = Iterator(TopNodeKey -> nodeKey(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) val nodesIterator = visitedNodesSerializer(manifest, chunks) @@ -194,4 +185,18 @@ object VersionedLDBAVLStorage { l } } + + //todo: move to more appropriate place + def digest[D <: hash.Digest](rootNodeLabel:D, rootNodeHeight: Int): ADDigest = { + assert(rootNodeHeight >= 0 && rootNodeHeight < 256) + // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect + // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the + // verifier, by adding 256 if it's negative). + // The reason rootNodeHeight should never be more than 255 is that if height is more than 255, + // then the AVL tree has at least 2^{255/1.4405} = 2^177 leaves, which is more than the number of atoms on planet Earth. + ADDigest @@ (rootNodeLabel :+ rootNodeHeight.toByte) + } + + def digest[D <: hash.Digest](rootNode: Node[D], rootNodeHeight: Int): ADDigest = digest(rootNode.label, rootNodeHeight) } + diff --git a/papers/utxo.md b/papers/utxo.md index 7096066742..6eea677783 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -19,7 +19,8 @@ Implementation Details UTXO set is authenticated via AVL+ tree. -Time is broken into epochs, 1 epoch = 51,840 blocks (~72 days). +Time is broken into epochs, 1 epoch = 51,200 blocks (~72 days). +Snapshot is taken after last block of an epoch, so block with height h % 51200 == 51199 Chunk format ------------ diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 720d540ea7..53934e1aa6 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -883,7 +883,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release hr.registerDownloadedChunk(subtree.id, serializedChunk) if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { - viewHolderRef ! InitStateFromSnapshot() + val h = downloadPlan.get.snapshotHeight // todo: .get + val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get + viewHolderRef ! InitStateFromSnapshot(h, blockId) } else{ requestMoreChunksIfNeeded(hr, remote) } @@ -1538,7 +1540,7 @@ object ErgoNodeViewSynchronizer { */ case class RecheckMempool(state: UtxoStateReader, mempool: ErgoMemPoolReader) - case class InitStateFromSnapshot() + case class InitStateFromSnapshot(blockHeight: Height, blockId: ModifierId) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 1fbaa36e24..1d406a14ca 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -279,18 +279,14 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } def processStateSnapshot: Receive = { - case InitStateFromSnapshot() => + case InitStateFromSnapshot(height, blockId) => val store = minimalState().store - history().createPersistentProver(store) match { + history().createPersistentProver(store, blockId) match { //todo: pass metadata case Success(pp) => - /* - todo: restore state? - log.info(s"Restoring state from prover with digest ${prover.digest} reconstructed for height $height") - history().utxoSnapshotApplied(height) - */ - // todo: pass version - val newState = new UtxoState(pp, version = null, store, StateConstants(settings)) + log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") + history().utxoSnapshotApplied(height) + val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, StateConstants(settings)) updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(_) => ??? } 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 e126e9f85a..93172d9027 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 @@ -3,13 +3,15 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage +import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId -import org.ergoplatform.settings.{Algos, Constants} +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} +import scorex.core.VersionTag import scorex.crypto.authds.avltree.batch.NodeParameters import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore -import scorex.util.{ByteArrayBuilder, ScorexLogging} +import scorex.util.{ByteArrayBuilder, ModifierId, ScorexLogging} import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} import spire.syntax.all.cfor @@ -25,6 +27,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import org.ergoplatform.settings.ErgoAlgos.HF + protected val settings: ErgoSettings protected val historyStorage: HistoryStorage private[history] var minimalFullBlockHeightVar: Int @@ -32,7 +35,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private implicit val hf = Blake2b256 private val avlTreeSerializer = new BatchAVLProverSerializer[Digest32, Blake2b256.type]() - var _utxoSnapshotApplied = false + var _utxoSnapshotApplied = false //todo: persistence? def utxoSnapshotApplied(height: Height): Unit = { _utxoSnapshotApplied = true @@ -197,13 +200,16 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import scala.util.Try import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} - def createPersistentProver(stateStore: LDBVersionedStore): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { + def createPersistentProver(stateStore: LDBVersionedStore, + blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { val manifest = _manifest.get //todo: .get val np = NodeParameters(32, None, 32) // todo: use constants val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) - //todo: write metadata, see UtxoState.metadata log.info("Starting UTXO set snapshot transfer into state database") - ldbStorage.update(manifest, chunksIterator(), additionalData = Iterator.empty) + //todo: form state context correctly? + val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) + val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) + ldbStorage.update(manifest, chunksIterator(), additionalData = metadata.toIterator) log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => new PersistentBatchAVLProver[Digest32, HF] { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 63547e3058..3bb57b207f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -26,10 +26,10 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val SnapshotEvery = 25 // test value, switch to 51840 after testing + val SnapshotEvery = 1024 // test value, switch to 51200 after testing if (estimatedTip.nonEmpty && - (height % SnapshotEvery == 0) && + (height % SnapshotEvery == SnapshotEvery - 1) && estimatedTip.get - height <= SnapshotEvery) { val (manifest, subtrees) = slicedTree() diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 78a99ebb4e..b9b0597f9d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -259,10 +259,10 @@ object UtxoState extends ScorexLogging { * @param context * @return */ - private def metadata(modId: VersionTag, - stateRoot: ADDigest, - currentEmissionBoxOpt: Option[ErgoBox], - context: ErgoStateContext): Seq[(Array[Byte], Array[Byte])] = { + def metadata(modId: VersionTag, + stateRoot: ADDigest, + currentEmissionBoxOpt: Option[ErgoBox], + context: ErgoStateContext): Seq[(Array[Byte], Array[Byte])] = { val modIdBytes = versionToBytes(modId) val idStateDigestIdxElem: (Array[Byte], Array[Byte]) = modIdBytes -> stateRoot val stateDigestIdIdxElem = Algos.hash(stateRoot) -> modIdBytes From 9f31fab0e82636faae04402aac550f88f292815a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 23 Dec 2022 13:54:00 +0300 Subject: [PATCH 079/204] UtxoSetSnapshotProcessorSpecification, downloadedChunksIterator --- .../UtxoSetSnapshotProcessor.scala | 6 ++--- ...txoSetSnapshotProcessorSpecification.scala | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala 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 93172d9027..9a69452028 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 @@ -39,7 +39,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { def utxoSnapshotApplied(height: Height): Unit = { _utxoSnapshotApplied = true - minimalFullBlockHeightVar = height + 1 //todo: or height + 1? + minimalFullBlockHeightVar = height + 1 } private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) @@ -114,7 +114,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } - def chunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { + def downloadedChunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { getUtxoSetSnapshotDownloadPlan() match { case Some(plan) => Iterator.range(0, plan.totalChunks).flatMap{idx => @@ -209,7 +209,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { //todo: form state context correctly? val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - ldbStorage.update(manifest, chunksIterator(), additionalData = metadata.toIterator) + ldbStorage.update(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator) log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => new PersistentBatchAVLProver[Digest32, HF] { diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala new file mode 100644 index 0000000000..30aaf6e094 --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -0,0 +1,22 @@ +package org.ergoplatform.nodeView.history + +import org.ergoplatform.nodeView.history.storage.HistoryStorage +import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor +import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.utils.HistoryTestHelpers + +class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { + + private val s = settings + + val utxoSetSnapshotProcessor = new UtxoSetSnapshotProcessor { + override protected val settings: ErgoSettings = s + override protected val historyStorage: HistoryStorage = HistoryStorage(settings) + override private[history] var minimalFullBlockHeightVar = ErgoHistory.GenesisHeight + } + + property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { + ??? + } + +} From 5ea496951ec8488ba3a5eb572b68e95a31ee31d6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 23 Dec 2022 16:49:15 +0300 Subject: [PATCH 080/204] downloading from multiple peers --- .../network/ErgoNodeViewSynchronizer.scala | 39 ++++++++++--------- .../UtxoSetSnapshotProcessor.scala | 17 +++++++- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 53934e1aa6..25e2678690 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -821,12 +821,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, //todo: clear the table below private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() - private def heightOfManifest(manifestId: ManifestId): Option[Height] = { - availableManifests - .find(_._2.exists(_._2.sameElements(manifestId))) - .map(_._1) - } - protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => log.debug(s"Got manifest $manifestId for height $height from $remote") @@ -836,32 +830,41 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, checkUtxoSetManifests(hr) } - private def requestMoreChunksIfNeeded(hr: ErgoHistory, remote: ConnectedPeer): Unit = { + private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { hr.getUtxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => if (downloadPlan.downloadingChunks < 50) { - val toRequest = hr.getChunkIdsToDownload(50) - toRequest.foreach { - subtreeId => - requestUtxoSetChunk(subtreeId, remote) + (1 to 10).foreach { _ => + val toRequest = hr.getChunkIdsToDownload(5) + hr.getRandomPeerToDownloadChunks() match { + case Some(remote) => toRequest.foreach { + subtreeId => + requestUtxoSetChunk(subtreeId, remote) + } + case None => + log.warn(s"No peers to download chunks from") + } } } case None => - // todo: log + // todo: log } } protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer) = { - //todo : check that mnifestBytes were requested + //todo : check that manifestBytes were requested val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => - log.info(s"Got manifest ${Algos.encode(manifest.id)}") - heightOfManifest(manifest.id) match { + log.info(s"Got manifest ${Algos.encode(manifest.id)} from $remote") + val manifestRecords = availableManifests.filter(_._2.exists(_._2.sameElements(manifest.id))) + val heightOfManifest = manifestRecords.headOption.map(_._1) + val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq + heightOfManifest match { case Some(height) => - hr.registerManifestToDownload(manifest, height) + hr.registerManifestToDownload(manifest, height, peersToDownload) log.info(s"Going to download ${50} chunks for manifest ${Algos.encode(manifest.id)}") // todo: fix msg - requestMoreChunksIfNeeded(hr, remote) + requestMoreChunksIfNeeded(hr) case None => log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") } @@ -887,7 +890,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get viewHolderRef ! InitStateFromSnapshot(h, blockId) } else{ - requestMoreChunksIfNeeded(hr, remote) + requestMoreChunksIfNeeded(hr) } case Failure(e) => //todo: 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 9a69452028..518f622301 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 @@ -7,6 +7,7 @@ import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoSta import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} import scorex.core.VersionTag +import scorex.core.network.ConnectedPeer import scorex.crypto.authds.avltree.batch.NodeParameters import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} @@ -17,6 +18,7 @@ import spire.syntax.all.cfor import java.nio.ByteBuffer import scala.collection.mutable +import scala.util.Random /** * Stores: @@ -51,12 +53,16 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None + private var _peersToDownload: Seq[ConnectedPeer] = Seq.empty //todo: move to ErgoNodeViewSynchronizer? + def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? //todo: implement def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], - blockHeight: Height): UtxoSetSnapshotDownloadPlan = { + blockHeight: Height, + peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) _manifest = Some(manifest) + _peersToDownload = peersToDownload println(_manifest.get.id) updateUtxoSetSnashotDownloadPlan(plan) } @@ -74,6 +80,14 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + def getRandomPeerToDownloadChunks(): Option[ConnectedPeer] = { + if (_peersToDownload.nonEmpty) { + Some(_peersToDownload(Random.nextInt(_peersToDownload.size))) + } else { + None + } + } + def getChunkIdsToDownload(howMany: Int): Seq[SubtreeId] = { getUtxoSetSnapshotDownloadPlan() match { case Some(plan) => @@ -89,6 +103,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = newDownloaded, downloadingChunks = newDownloading) _cachedDownloadPlan = Some(plan) // we update only in-memory cache, so in case of node restart, chunks won't be missed toDownload + case None => log.warn(s"No download plan is found when requested to propose $howMany chunks to download") Seq.empty From 37537d49d0859e7c5e002f6ffd4ce969f4b6a24f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 24 Dec 2022 13:44:50 +0300 Subject: [PATCH 081/204] no persistence for downloading plan, logging improvs --- .../network/ErgoNodeViewSynchronizer.scala | 4 ++-- .../ToDownloadProcessor.scala | 2 +- .../UtxoSetSnapshotProcessor.scala | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 25e2678690..ffe3154273 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -803,7 +803,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def sendSnapshotsInfo(usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { val snapshotsInfo = usr.getSnapshotInfo() - log.debug(s"Sending snapshots info with ${snapshotsInfo.availableManifests.size} to $peer") + log.debug(s"Sending snapshots info with ${snapshotsInfo.availableManifests.size} snapshots to $peer") val msg = Message(SnapshotsInfoSpec, Right(snapshotsInfo), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } @@ -1159,7 +1159,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def checkUtxoSetManifests(historyReader: ErgoHistory) = { + protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { val MinSnapshots = 1 //todo: set to 3 after testing if (settings.nodeSettings.utxoBootstrap && 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 94e15d917c..a3fce9f11d 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 @@ -88,7 +88,7 @@ trait ToDownloadProcessor // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) - case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => + case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied && getUtxoSetSnapshotDownloadPlan().isEmpty) => // todo: can be requested multiple times, prevent it Map(SnapshotsInfo.modifierTypeId -> Seq.empty) case None => 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 518f622301..3e824b751b 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 @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId -import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} +import org.ergoplatform.settings.{Algos, ErgoSettings} import scorex.core.VersionTag import scorex.core.network.ConnectedPeer import scorex.crypto.authds.avltree.batch.NodeParameters @@ -13,11 +13,9 @@ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore import scorex.util.{ByteArrayBuilder, ModifierId, ScorexLogging} -import scorex.util.serialization.{VLQByteBufferReader, VLQByteBufferWriter} +import scorex.util.serialization.VLQByteBufferWriter import spire.syntax.all.cfor -import java.nio.ByteBuffer -import scala.collection.mutable import scala.util.Random /** @@ -70,13 +68,14 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { def getUtxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { _cachedDownloadPlan match { case s@Some(_) => s - case None => + case None => None + /* historyStorage.get(downloadPlanKey).flatMap { planId => val planOpt = readDownloadPlanFromDb(Digest32 @@ planId) if (planOpt.isEmpty) log.warn(s"No download plan with id ${Algos.encode(planId)} found") if (planOpt.nonEmpty) _cachedDownloadPlan = planOpt planOpt - } + }*/ } } @@ -98,10 +97,11 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } else { IndexedSeq.empty } + log.info(s"Downloaded or waiting ${plan.downloadedChunkIds.size} chunks out of ${expected.size}, downloading ${toDownload.size} more") val newDownloaded = plan.downloadedChunkIds ++ toDownload.map(_ => false) val newDownloading = plan.downloadingChunks + toDownload.size - plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = newDownloaded, downloadingChunks = newDownloading) - _cachedDownloadPlan = Some(plan) // we update only in-memory cache, so in case of node restart, chunks won't be missed + val updPlan = plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = newDownloaded, downloadingChunks = newDownloading) + _cachedDownloadPlan = Some(updPlan) toDownload case None => @@ -173,6 +173,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } + /* private def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { historyStorage.get(id).map { bytes => val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) @@ -209,7 +210,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { downloadingChunks = 0 ) } - } + }*/ import scala.util.Try From 7fd67c11bae1d8016ab94626502e30e605b82d94 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 26 Dec 2022 21:54:31 +0300 Subject: [PATCH 082/204] re-requesting non-delivered utxo set chunks via delivery tracker, do not ask for snapshots info when one is chosen --- .../network/ErgoNodeViewSynchronizer.scala | 11 +++++++++-- .../modifierprocessors/ToDownloadProcessor.scala | 8 ++++++-- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index ffe3154273..6efa31a616 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -9,6 +9,7 @@ import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader} @@ -567,6 +568,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { + deliveryTracker.setRequested(UTXOSnapshotChunk.modifierTypeId, ModifierId @@ Algos.encode(subtreeId), peer){ deliveryCheck => + context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) + } val msg = Message(GetUtxoSnapshotChunkSpec, Right(subtreeId), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } @@ -881,6 +885,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => + deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UTXOSnapshotChunk.modifierTypeId) val downloadPlan = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release @@ -1057,7 +1062,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * wait for delivery until the number of checks exceeds the maximum if the peer sent `Inv` for this modifier * re-request modifier from a different random peer, if our node does not know a peer who have it */ - protected def checkDelivery: Receive = { + protected def checkDelivery(hr: ErgoHistory): Receive = { case CheckDelivery(peer, modifierTypeId, modifierId) => if (deliveryTracker.status(modifierId, modifierTypeId, Seq.empty) == ModifiersStatus.Requested) { // If transaction not delivered on time, we just forget about it. @@ -1087,6 +1092,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // randomly choose a peer for another download attempt val newPeerCandidates: Seq[ConnectedPeer] = if (modifierTypeId == Header.modifierTypeId) { getPeersForDownloadingHeaders(peer).toSeq + } else if (modifierTypeId == UTXOSnapshotChunk.modifierTypeId) { + hr.getPeersToDownloadChunks() } else { getPeersForDownloadingBlocks.map(_.toSeq).getOrElse(Seq(peer)) } @@ -1364,7 +1371,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, sendLocalSyncInfo(hr) orElse viewHolderEvents(hr, mp, usr, blockAppliedTxsCache) orElse peerManagerEvents orElse - checkDelivery orElse { + checkDelivery(hr) orElse { case a: Any => log.error("Strange input: " + a) } } 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 a3fce9f11d..f9fd2b782d 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 @@ -88,9 +88,13 @@ trait ToDownloadProcessor // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) - case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied && getUtxoSetSnapshotDownloadPlan().isEmpty) => + case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => // todo: can be requested multiple times, prevent it - Map(SnapshotsInfo.modifierTypeId -> Seq.empty) + if (getUtxoSetSnapshotDownloadPlan().isEmpty) { + Map(SnapshotsInfo.modifierTypeId -> Seq.empty) + } else { + Map.empty + } case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from val res = continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) 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 3e824b751b..baaea1c1cf 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 @@ -79,6 +79,8 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + def getPeersToDownloadChunks(): Seq[ConnectedPeer] = _peersToDownload + def getRandomPeerToDownloadChunks(): Option[ConnectedPeer] = { if (_peersToDownload.nonEmpty) { Some(_peersToDownload(Random.nextInt(_peersToDownload.size))) From 48c0a561d85dea4277ecf34757f34469cfb01666 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 26 Dec 2022 23:02:35 +0300 Subject: [PATCH 083/204] first real test in UtxoSetSnapshotProcessorSpecification --- .../state/UtxoSetSnapshotPersistence.scala | 1 + .../UtxoSetSnapshotProcessorSpecification.scala | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 3bb57b207f..b2e9077686 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -17,6 +17,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? //todo: scaladoc + //todo: return Iterator? def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { persistentProver.synchronized { val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 30aaf6e094..15048879aa 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -2,8 +2,9 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers +import scorex.util.ModifierId class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { @@ -16,7 +17,19 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { } property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { - ??? + val bh = boxesHolderGen.sample.get + val us = createUtxoState(bh, parameters) + val (manifest, subtrees) = us.slicedTree() + println(s"Subtrees: ${subtrees.size}") + val snapshotHeight = 1 + utxoSetSnapshotProcessor.registerManifestToDownload(manifest, snapshotHeight, Seq.empty) + val dp = utxoSetSnapshotProcessor.getUtxoSetSnapshotDownloadPlan().get + dp.snapshotHeight shouldBe snapshotHeight + val subtreeIds = subtrees.map(s => ModifierId @@ Algos.encode(s.id)) + val expected = dp.expectedChunkIds.map(id => ModifierId @@ Algos.encode(id)) + expected shouldBe subtreeIds + val toDownload = utxoSetSnapshotProcessor.getChunkIdsToDownload(expected.size).map(id => ModifierId @@ Algos.encode(id)) + toDownload shouldBe expected } } From 51ece2686f8062cffe4c5a0b432653df2f1e879e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 26 Dec 2022 23:17:33 +0300 Subject: [PATCH 084/204] downloadedChunksIterator & createPersistentProver covered in test --- .../state/UtxoSetSnapshotPersistence.scala | 2 +- .../UtxoSetSnapshotProcessorSpecification.scala | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index b2e9077686..6cb275d138 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -21,7 +21,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { persistentProver.synchronized { val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) - serializer.slice(persistentProver.avlProver, subtreeDepth = 12) + serializer.slice(persistentProver.avlProver, subtreeDepth = 12) //todo: name constant } } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 15048879aa..c8d183f36d 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -2,10 +2,15 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor +import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.hash.Digest32 import scorex.util.ModifierId +import scala.util.Random + class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { private val s = settings @@ -22,6 +27,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val (manifest, subtrees) = us.slicedTree() println(s"Subtrees: ${subtrees.size}") val snapshotHeight = 1 + val blockId = ModifierId @@ Algos.encode(Array.fill(32)(Random.nextInt(100).toByte)) utxoSetSnapshotProcessor.registerManifestToDownload(manifest, snapshotHeight, Seq.empty) val dp = utxoSetSnapshotProcessor.getUtxoSetSnapshotDownloadPlan().get dp.snapshotHeight shouldBe snapshotHeight @@ -30,6 +36,17 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { expected shouldBe subtreeIds val toDownload = utxoSetSnapshotProcessor.getChunkIdsToDownload(expected.size).map(id => ModifierId @@ Algos.encode(id)) toDownload shouldBe expected + + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) + subtrees.foreach { subtree => + utxoSetSnapshotProcessor.registerDownloadedChunk(subtree.id, serializer.subtreeToBytes(subtree)) + } + utxoSetSnapshotProcessor.downloadedChunksIterator().toSeq.map(s => ModifierId @@ Algos.encode(s.id)) shouldBe subtreeIds + val restoredState = utxoSetSnapshotProcessor.createPersistentProver(us.store, blockId).get + restoredState.checkTree() + bh.sortedBoxes.foreach { box => + restoredState.unauthenticatedLookup(box.id).isDefined shouldBe true + } } } From 6bb08d840f71f19cbc586a80dc6f64c8cb763d3c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 27 Dec 2022 13:03:11 +0300 Subject: [PATCH 085/204] bigger state size in test --- .../history/UtxoSetSnapshotProcessorSpecification.scala | 6 +++--- .../utils/generators/ErgoTransactionGenerators.scala | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index c8d183f36d..c13169ac65 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -22,10 +22,10 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { } property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { - val bh = boxesHolderGen.sample.get + val bh = boxesHolderGenOfSize(65536).sample.get val us = createUtxoState(bh, parameters) val (manifest, subtrees) = us.slicedTree() - println(s"Subtrees: ${subtrees.size}") + println("Subtrees: " + subtrees.size) val snapshotHeight = 1 val blockId = ModifierId @@ Algos.encode(Array.fill(32)(Random.nextInt(100).toByte)) utxoSetSnapshotProcessor.registerManifestToDownload(manifest, snapshotHeight, Seq.empty) @@ -43,10 +43,10 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { } utxoSetSnapshotProcessor.downloadedChunksIterator().toSeq.map(s => ModifierId @@ Algos.encode(s.id)) shouldBe subtreeIds val restoredState = utxoSetSnapshotProcessor.createPersistentProver(us.store, blockId).get - restoredState.checkTree() bh.sortedBoxes.foreach { box => restoredState.unauthenticatedLookup(box.id).isDefined shouldBe true } + restoredState.checkTree(postProof = false) } } diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 6a0293dd78..dccb17b9cc 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -277,6 +277,9 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { lazy val validErgoTransactionWithAssetsGen: Gen[(IndexedSeq[ErgoBox], ErgoTransaction)] = validErgoTransactionGenTemplate(minAssets = 1) + def boxesHolderGenOfSize(numBoxes:Int): Gen[BoxHolder] = Gen.listOfN(numBoxes, ergoBoxGenForTokens(Seq(), trueLeafGen)) + .map(l => BoxHolder(l)) + lazy val boxesHolderGen: Gen[BoxHolder] = Gen.listOfN(2000, ergoBoxGenForTokens(Seq(), trueLeafGen)) .map(l => BoxHolder(l)) From dc69ee231fdeb3dba3b929038b6e4988256425b0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 28 Dec 2022 23:31:05 +0300 Subject: [PATCH 086/204] fixing proxy nodes serialization --- .../avltree/batch/VersionedLDBAVLStorage.scala | 17 +++++++++-------- .../scala/scorex/db/LDBVersionedStore.scala | 5 +---- .../UtxoSetSnapshotProcessor.scala | 1 + ...UtxoSetSnapshotProcessorSpecification.scala | 18 ++++++++++++++---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 642ccd1869..3c57c56c34 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -33,7 +33,7 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB private val fixedSizeValueMode = nodeParameters.valueSize.isDefined def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { - restorePrunedTopNode().map {recoveredTop => + restorePrunedTopNode().map { recoveredTop => new BatchAVLProver(nodeParameters.keySize, nodeParameters.valueSize, Some(recoveredTop))(hf) } } @@ -82,21 +82,20 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB val rootNode = manifest.root val rootNodeHeight = manifest.rootHeight val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) - val indexes = Iterator(TopNodeKey -> nodeKey(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) - val nodesIterator = visitedNodesSerializer(manifest, chunks) - - store.update(digestWrapper, Nil, toUpdate = indexes ++ nodesIterator ++ additionalData) + val indices = Iterator(TopNodeKey -> nodeKey(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) + val nodesIterator = serializedAllNodes(manifest, chunks) + store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData) } - private def visitedNodesSerializer(manifest: BatchAVLProverManifest[D], - chunks: Iterator[BatchAVLProverSubtree[D]]) = { + private def serializedAllNodes(manifest: BatchAVLProverManifest[D], + chunks: Iterator[BatchAVLProverSubtree[D]]) = { def idCollector(node: ProverNodes[D], acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { val pair: (Array[Byte], Array[Byte]) = (nodeKey(node), toBytes(node)) node match { case n: ProxyInternalNode[D] if n.isEmpty => acc ++ Iterator(pair) - case i : InternalProverNode[D] => + case i: InternalProverNode[D] => acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) case _: ProverLeaf[D] => acc ++ Iterator(pair) @@ -129,6 +128,8 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB private def toBytes(node: ProverNodes[D]): Array[Byte] = { val builder = new mutable.ArrayBuilder.ofByte; node match { + case n: ProxyInternalNode[D] => + builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel case n: InternalProverNode[D] => builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label case n: ProverLeaf[D] => diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 230966e0e1..aa9d56d735 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -1,16 +1,13 @@ package scorex.db import java.io.File - import scorex.db.LDBFactory.factory import org.iq80.leveldb._ -import java.nio.ByteBuffer +import java.nio.ByteBuffer import scala.collection.mutable.ArrayBuffer import java.util.concurrent.locks.ReentrantReadWriteLock - import scorex.crypto.hash.Blake2b256 - import scala.util.Try 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 baaea1c1cf..5603e5fea9 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 @@ -222,6 +222,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { val manifest = _manifest.get //todo: .get val np = NodeParameters(32, None, 32) // todo: use constants + // todo: recreate database? val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) log.info("Starting UTXO set snapshot transfer into state database") //todo: form state context correctly? diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index c13169ac65..fb32453e9c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -2,11 +2,14 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor +import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers +import scorex.core.VersionTag import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 +import scorex.db.LDBVersionedStore import scorex.util.ModifierId import scala.util.Random @@ -22,7 +25,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { } property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { - val bh = boxesHolderGenOfSize(65536).sample.get + val bh = boxesHolderGenOfSize(32 * 1024).sample.get val us = createUtxoState(bh, parameters) val (manifest, subtrees) = us.slicedTree() println("Subtrees: " + subtrees.size) @@ -42,11 +45,18 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { utxoSetSnapshotProcessor.registerDownloadedChunk(subtree.id, serializer.subtreeToBytes(subtree)) } utxoSetSnapshotProcessor.downloadedChunksIterator().toSeq.map(s => ModifierId @@ Algos.encode(s.id)) shouldBe subtreeIds - val restoredState = utxoSetSnapshotProcessor.createPersistentProver(us.store, blockId).get + + val dir = createTempDir + val store = new LDBVersionedStore(dir, initialKeepVersions = 100) + val restoredProver = utxoSetSnapshotProcessor.createPersistentProver(store, blockId).get + bh.sortedBoxes.foreach { box => + restoredProver.unauthenticatedLookup(box.id).isDefined shouldBe true + } + restoredProver.checkTree(postProof = false) + val restoredState = new UtxoState(restoredProver, version = VersionTag @@ blockId, store, StateConstants(settings)) bh.sortedBoxes.foreach { box => - restoredState.unauthenticatedLookup(box.id).isDefined shouldBe true + restoredState.boxById(box.id).isDefined shouldBe true } - restoredState.checkTree(postProof = false) } } From bd9f6972a95b0e8e1c0299235e33244df5c8c16a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 30 Dec 2022 18:52:55 +0300 Subject: [PATCH 087/204] redownloading chunk fixed --- .../network/ErgoNodeViewSynchronizer.scala | 36 +++++++++++-------- .../nodeView/ErgoNodeViewHolder.scala | 20 ++++++----- .../UtxoSetSnapshotProcessor.scala | 3 +- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6efa31a616..8d7650cebb 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -837,8 +837,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { hr.getUtxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => - if (downloadPlan.downloadingChunks < 50) { - (1 to 10).foreach { _ => + if (downloadPlan.downloadingChunks < 25) { + (1 to 5).foreach { _ => val toRequest = hr.getChunkIdsToDownload(5) hr.getRandomPeerToDownloadChunks() match { case Some(remote) => toRequest.foreach { @@ -890,7 +890,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release hr.registerDownloadedChunk(subtree.id, serializedChunk) - if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { + if (downloadPlan.map(_.fullyDownloaded).getOrElse(false) && !hr.isUtxoSnapshotApplied) { val h = downloadPlan.get.snapshotHeight // todo: .get val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get viewHolderRef ! InitStateFromSnapshot(h, blockId) @@ -1089,19 +1089,27 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val maxDeliveryChecks = networkSettings.maxDeliveryChecks if (checksDone < maxDeliveryChecks) { - // randomly choose a peer for another download attempt - val newPeerCandidates: Seq[ConnectedPeer] = if (modifierTypeId == Header.modifierTypeId) { - getPeersForDownloadingHeaders(peer).toSeq - } else if (modifierTypeId == UTXOSnapshotChunk.modifierTypeId) { - hr.getPeersToDownloadChunks() + if(modifierTypeId == UTXOSnapshotChunk.modifierTypeId){ + val newPeerOpt = hr.getRandomPeerToDownloadChunks() + log.info(s"Rescheduling request for UTXO set chunk $modifierId , new peer $newPeerOpt") + deliveryTracker.setUnknown(modifierId, modifierTypeId) + newPeerOpt match { + case Some(newPeer) => requestUtxoSetChunk (Digest32 @@ Algos.decode (modifierId).get, newPeer) + case None => log.warn(s"No peer found to download UTXO set chunk $modifierId") + } } else { - getPeersForDownloadingBlocks.map(_.toSeq).getOrElse(Seq(peer)) + // randomly choose a peer for another download attempt + val newPeerCandidates: Seq[ConnectedPeer] = if (modifierTypeId == Header.modifierTypeId) { + getPeersForDownloadingHeaders(peer).toSeq + } else { + getPeersForDownloadingBlocks.map(_.toSeq).getOrElse(Seq(peer)) + } + val newPeerIndex = scala.util.Random.nextInt(newPeerCandidates.size) + val newPeer = newPeerCandidates(newPeerIndex) + log.info(s"Rescheduling request for $modifierId , new peer $newPeer") + deliveryTracker.setUnknown(modifierId, modifierTypeId) + requestBlockSection(modifierTypeId, Seq(modifierId), newPeer, checksDone) } - val newPeerIndex = scala.util.Random.nextInt(newPeerCandidates.size) - val newPeer = newPeerCandidates(newPeerIndex) - log.info(s"Rescheduling request for $modifierId , new peer $newPeer") - deliveryTracker.setUnknown(modifierId, modifierTypeId) - requestBlockSection(modifierTypeId, Seq(modifierId), newPeer, checksDone) } else { log.error(s"Exceeded max delivery attempts($maxDeliveryChecks) limit for $modifierId") if (modifierTypeId == Header.modifierTypeId) { diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 1d406a14ca..a58958751a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -280,15 +280,17 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti def processStateSnapshot: Receive = { case InitStateFromSnapshot(height, blockId) => - val store = minimalState().store - history().createPersistentProver(store, blockId) match { - //todo: pass metadata - case Success(pp) => - log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") - history().utxoSnapshotApplied(height) - val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, StateConstants(settings)) - updateNodeView(updatedState = Some(newState.asInstanceOf[State])) - case Failure(_) => ??? + if (!history().isUtxoSnapshotApplied) { + val store = minimalState().store + history().createPersistentProver(store, blockId) match { + //todo: pass metadata + case Success(pp) => + log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") + history().utxoSnapshotApplied(height) + val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, StateConstants(settings)) + updateNodeView(updatedState = Some(newState.asInstanceOf[State])) + case Failure(_) => ??? + } } } 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 5603e5fea9..846fd8b1e2 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 @@ -37,6 +37,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { var _utxoSnapshotApplied = false //todo: persistence? + def isUtxoSnapshotApplied = _utxoSnapshotApplied def utxoSnapshotApplied(height: Height): Unit = { _utxoSnapshotApplied = true minimalFullBlockHeightVar = height + 1 @@ -79,8 +80,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } - def getPeersToDownloadChunks(): Seq[ConnectedPeer] = _peersToDownload - def getRandomPeerToDownloadChunks(): Option[ConnectedPeer] = { if (_peersToDownload.nonEmpty) { Some(_peersToDownload(Random.nextInt(_peersToDownload.size))) From 23df49c27fe1b2c2e110afd498343363b5a0679e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 31 Dec 2022 00:26:45 +0300 Subject: [PATCH 088/204] warning on double state application attempt --- .../network/ErgoNodeViewSynchronizer.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 8d7650cebb..53914da5fc 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -882,6 +882,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], hr: ErgoHistory, remote: ConnectedPeer): Unit = { + //todo: check if subtree was requested, penalize remote is not so val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => @@ -890,10 +891,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release hr.registerDownloadedChunk(subtree.id, serializedChunk) - if (downloadPlan.map(_.fullyDownloaded).getOrElse(false) && !hr.isUtxoSnapshotApplied) { - val h = downloadPlan.get.snapshotHeight // todo: .get - val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get - viewHolderRef ! InitStateFromSnapshot(h, blockId) + if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { + if (!hr.isUtxoSnapshotApplied) { + val h = downloadPlan.get.snapshotHeight // todo: .get + val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get + viewHolderRef ! InitStateFromSnapshot(h, blockId) + } else { + log.warn("UTXO set snapshot already applied, double application attemt") + } } else{ requestMoreChunksIfNeeded(hr) } @@ -1089,12 +1094,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val maxDeliveryChecks = networkSettings.maxDeliveryChecks if (checksDone < maxDeliveryChecks) { - if(modifierTypeId == UTXOSnapshotChunk.modifierTypeId){ + if (modifierTypeId == UTXOSnapshotChunk.modifierTypeId) { val newPeerOpt = hr.getRandomPeerToDownloadChunks() log.info(s"Rescheduling request for UTXO set chunk $modifierId , new peer $newPeerOpt") deliveryTracker.setUnknown(modifierId, modifierTypeId) newPeerOpt match { - case Some(newPeer) => requestUtxoSetChunk (Digest32 @@ Algos.decode (modifierId).get, newPeer) + case Some(newPeer) => requestUtxoSetChunk(Digest32 @@ Algos.decode(modifierId).get, newPeer) case None => log.warn(s"No peer found to download UTXO set chunk $modifierId") } } else { From dbf8d49455ca4fc1fd2770192af42cdf24537717 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 31 Dec 2022 21:44:22 +0300 Subject: [PATCH 089/204] downloadPlanOpt, tweaking downloading params --- .../network/ErgoNodeViewSynchronizer.scala | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 53914da5fc..c15754f53d 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -837,9 +837,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { hr.getUtxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => - if (downloadPlan.downloadingChunks < 25) { - (1 to 5).foreach { _ => - val toRequest = hr.getChunkIdsToDownload(5) + if (downloadPlan.downloadingChunks < 16) { + (1 to 4).foreach { _ => + val toRequest = hr.getChunkIdsToDownload(4) hr.getRandomPeerToDownloadChunks() match { case Some(remote) => toRequest.foreach { subtreeId => @@ -887,13 +887,19 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UTXOSnapshotChunk.modifierTypeId) - val downloadPlan = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result - log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release - // log.info(s"Awaiting ${requestedSubtrees.size} chunks, in queue ${expectedSubtrees.size} chunks")//todo: change to debug on release + val downloadPlanOpt = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result + log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? hr.registerDownloadedChunk(subtree.id, serializedChunk) - if (downloadPlan.map(_.fullyDownloaded).getOrElse(false)) { + + //todo: remove after testing + if (downloadPlanOpt.map(p => p.downloadedChunkIds == p.expectedChunkIds).getOrElse(false)) { + val notDownloaded = downloadPlanOpt.get.downloadedChunkIds.filter(_ == true) + log.info("Not downloaded yet: " + notDownloaded.size) + } + + if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { if (!hr.isUtxoSnapshotApplied) { - val h = downloadPlan.get.snapshotHeight // todo: .get + val h = downloadPlanOpt.get.snapshotHeight // todo: .get val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get viewHolderRef ! InitStateFromSnapshot(h, blockId) } else { From 022855dbd06eafd448571602249be2776571a01e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 1 Jan 2023 20:15:06 +0300 Subject: [PATCH 090/204] MinSnapshots = 2, more debug logging --- .../network/ErgoNodeViewSynchronizer.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index c15754f53d..064d2752a3 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -867,7 +867,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, heightOfManifest match { case Some(height) => hr.registerManifestToDownload(manifest, height, peersToDownload) - log.info(s"Going to download ${50} chunks for manifest ${Algos.encode(manifest.id)}") // todo: fix msg + log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height") requestMoreChunksIfNeeded(hr) case None => log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") @@ -889,12 +889,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UTXOSnapshotChunk.modifierTypeId) val downloadPlanOpt = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? + log.info(s"Downloaded chunks: ${downloadPlanOpt.map(_.downloadedChunkIds.size)}, true: ${downloadPlanOpt.map(_.downloadedChunkIds.filter(_ == true).size)}") //todo: remove after tests hr.registerDownloadedChunk(subtree.id, serializedChunk) + log.info(s"After! Downloaded chunks: ${downloadPlanOpt.map(_.downloadedChunkIds.size)}, true: ${downloadPlanOpt.map(_.downloadedChunkIds.filter(_ == true).size)}") //todo: remove after tests //todo: remove after testing - if (downloadPlanOpt.map(p => p.downloadedChunkIds == p.expectedChunkIds).getOrElse(false)) { + if (downloadPlanOpt.map(p => p.downloadedChunkIds.size == p.expectedChunkIds.size).getOrElse(false)) { val notDownloaded = downloadPlanOpt.get.downloadedChunkIds.filter(_ == true) - log.info("Not downloaded yet: " + notDownloaded.size) + log.info("Not downloaded yet: " + notDownloaded.size, " plan: " + downloadPlanOpt.get) } if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { @@ -1186,7 +1188,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { - val MinSnapshots = 1 //todo: set to 3 after testing + val MinSnapshots = 2 //todo: set to 3 after testing if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && From d30be43b3643406df493e6bca0f5c90fb578d2b6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 1 Jan 2023 20:32:11 +0300 Subject: [PATCH 091/204] logging peers chunks will be downloaded from --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 064d2752a3..cb4b860dde 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -866,8 +866,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq heightOfManifest match { case Some(height) => + log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") hr.registerManifestToDownload(manifest, height, peersToDownload) - log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height") requestMoreChunksIfNeeded(hr) case None => log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") From 5c70a2c36cb40185d752feb897a2f8456b3f8f47 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 1 Jan 2023 20:39:49 +0300 Subject: [PATCH 092/204] clearing availableManifests, scaladoc for it --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index cb4b860dde..40aafdaa14 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -822,7 +822,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - //todo: clear the table below + /** + * UTXO set snapshot manifests found in the p2p are stored in this table. The table is cleared when manifest + * available for download from at least min number of peers required + */ private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { @@ -868,6 +871,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Some(height) => log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") hr.registerManifestToDownload(manifest, height, peersToDownload) + availableManifests.clear() requestMoreChunksIfNeeded(hr) case None => log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") From 9f104388ad506045390bced5cff075ff130b10f4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 1 Jan 2023 20:52:08 +0300 Subject: [PATCH 093/204] fixing duplicates in availableManifests --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 40aafdaa14..8ca6b901a1 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -832,7 +832,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => log.debug(s"Got manifest $manifestId for height $height from $remote") val existingOffers = availableManifests.getOrElse(height, Seq.empty) - availableManifests.put(height, existingOffers :+ (remote -> manifestId)) + if (!existingOffers.exists(_._1 == remote)) { + log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") + availableManifests.put(height, existingOffers :+ (remote -> manifestId)) + } } checkUtxoSetManifests(hr) } From f22380c776e4574f1d73483aeb176d9743faa683 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 2 Jan 2023 02:05:51 +0300 Subject: [PATCH 094/204] fixing duplicates in availableManifests --- .../network/ErgoNodeViewSynchronizer.scala | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 8ca6b901a1..a9ae9542b5 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -894,18 +894,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UTXOSnapshotChunk.modifierTypeId) - val downloadPlanOpt = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? - log.info(s"Downloaded chunks: ${downloadPlanOpt.map(_.downloadedChunkIds.size)}, true: ${downloadPlanOpt.map(_.downloadedChunkIds.filter(_ == true).size)}") //todo: remove after tests hr.registerDownloadedChunk(subtree.id, serializedChunk) - log.info(s"After! Downloaded chunks: ${downloadPlanOpt.map(_.downloadedChunkIds.size)}, true: ${downloadPlanOpt.map(_.downloadedChunkIds.filter(_ == true).size)}") //todo: remove after tests - - //todo: remove after testing - if (downloadPlanOpt.map(p => p.downloadedChunkIds.size == p.expectedChunkIds.size).getOrElse(false)) { - val notDownloaded = downloadPlanOpt.get.downloadedChunkIds.filter(_ == true) - log.info("Not downloaded yet: " + notDownloaded.size, " plan: " + downloadPlanOpt.get) - } + val downloadPlanOpt = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { if (!hr.isUtxoSnapshotApplied) { val h = downloadPlanOpt.get.snapshotHeight // todo: .get From a5b7cf9595444689641f43db357c88af7b318c6f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 2 Jan 2023 19:25:21 +0300 Subject: [PATCH 095/204] ProxyInternalProverNode parameter names fix, ScalaDoc --- .../authds/avltree/batch/NodeParameters.scala | 7 +++++- .../batch/ProxyInternalProverNode.scala | 10 ++++---- .../batch/VersionedLDBAVLStorage.scala | 25 +++++++++++++------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala index 3b04bd069f..40b3237ef5 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala @@ -1,4 +1,9 @@ package scorex.crypto.authds.avltree.batch -//todo: support arbitrary-size values +/** + * Parameters of AVL+ tree nodes (internal and leaves) + * @param keySize - size of a key (fixed) + * @param valueSize - size of a value in a leaf (fixed, if defined, arbitrary, if None) + * @param labelSize - size of a label (node hash), fixed + */ case class NodeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala index d45076190a..cc393d6911 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala @@ -7,8 +7,8 @@ import scorex.db.LDBVersionedStore import InternalNode.InternalNodePrefix class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, - val lkey: ADKey, - val rkey: ADKey, + val leftLabel: ADKey, + val rightLabel: ADKey, protected var pb: Balance = Balance @@ 0.toByte) (implicit val phf: CryptographicHash[D], store: LDBVersionedStore, @@ -16,16 +16,16 @@ class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, extends InternalProverNode(k = pk, l = null, r = null, b = pb)(phf) { override protected def computeLabel: D = { - hf.hash(Array(InternalNodePrefix, b), lkey, rkey) + hf.hash(Array(InternalNodePrefix, b), leftLabel, rightLabel) } override def left: ProverNodes[D] = { - if (l == null) l = VersionedLDBAVLStorage.fetch[D](lkey) + if (l == null) l = VersionedLDBAVLStorage.fetch[D](leftLabel) l } override def right: ProverNodes[D] = { - if (r == null) r = VersionedLDBAVLStorage.fetch[D](rkey) + if (r == null) r = VersionedLDBAVLStorage.fetch[D](rightLabel) r } } diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 3c57c56c34..00d0b7e1a4 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -148,13 +148,24 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB object VersionedLDBAVLStorage { - val InternalNodePrefix: Byte = 0: Byte - val LeafPrefix: Byte = 1: Byte - - def fetch[D <: hash.Digest](key: ADKey)(implicit hf: CryptographicHash[D], - store: LDBVersionedStore, - nodeParameters: NodeParameters): ProverNodes[D] = { - val bytes = store(key) + private[batch] val InternalNodePrefix: Byte = 0: Byte + private[batch] val LeafPrefix: Byte = 1: Byte + + /** + * Fetch tree node from database + * + * @param dbKey - database key node of interest is stored under (hash of the node) + * @param hf - hash function instance + * @param store - database + * @param nodeParameters - tree node parameters + * @tparam D - type of an output of a hash function used + * @return node read from the database (or throws exception if there is no such node), in case of internal node it + * returns pruned internal node, so a node where left and right children not stored, only their hashes + */ + def fetch[D <: hash.Digest](dbKey: ADKey)(implicit hf: CryptographicHash[D], + store: LDBVersionedStore, + nodeParameters: NodeParameters): ProverNodes[D] = { + val bytes = store(dbKey) lazy val keySize = nodeParameters.keySize lazy val labelSize = nodeParameters.labelSize From 41f7aa240d008e5030bc4c270b00bba2c82e92d6 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 2 Jan 2023 19:51:07 +0300 Subject: [PATCH 096/204] ProxyInternalProverNode scaladoc --- .../batch/ProxyInternalProverNode.scala | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala index cc393d6911..2f187368aa 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala @@ -1,11 +1,16 @@ package scorex.crypto.authds.avltree.batch - import scorex.crypto.authds.{ADKey, Balance} import scorex.crypto.hash.{CryptographicHash, Digest} import scorex.db.LDBVersionedStore import InternalNode.InternalNodePrefix +/** + * Internal node where children are not provided during node construction, only pointers to them, + * and then children nodes are read from database and constructed only when requested (and children internal nodes are + * of the same type). It allows for lazy loading of a tree. + * + */ class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, val leftLabel: ADKey, val rightLabel: ADKey, @@ -20,14 +25,17 @@ class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, } override def left: ProverNodes[D] = { - if (l == null) l = VersionedLDBAVLStorage.fetch[D](leftLabel) + if (l == null) { + l = VersionedLDBAVLStorage.fetch[D](leftLabel) + } l } override def right: ProverNodes[D] = { - if (r == null) r = VersionedLDBAVLStorage.fetch[D](rightLabel) + if (r == null) { + r = VersionedLDBAVLStorage.fetch[D](rightLabel) + } r } -} - +} From 695efec3580a94694ff158f6ca91c988c5c44d35 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 3 Jan 2023 23:37:19 +0300 Subject: [PATCH 097/204] nodeKey => nodeLabel, more scaladoc in VersionedLDBAVLStorage --- .../batch/VersionedLDBAVLStorage.scala | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 00d0b7e1a4..b087d4217d 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -22,8 +22,9 @@ import scala.util.{Failure, Try} * @tparam D - type of hash function digest */ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDBVersionedStore, - nodeParameters: NodeParameters) - (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { + nodeParameters: NodeParameters) + (implicit val hf: HF) + extends VersionedAVLStorage[D] with ScorexLogging { private lazy val labelSize = nodeParameters.labelSize @@ -65,7 +66,7 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest - val indexes = Seq(TopNodeKey -> nodeKey(prover.topNode), TopNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) + val indexes = Seq(TopNodeKey -> nodeLabel(prover.topNode), TopNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) val toInsert = serializedVisitedNodes(prover.topNode, isTop = true) val toRemove = prover.removedNodes().map(rn => rn.label) val toUpdate = indexes ++ toInsert @@ -82,16 +83,16 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB val rootNode = manifest.root val rootNodeHeight = manifest.rootHeight val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) - val indices = Iterator(TopNodeKey -> nodeKey(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) + val indices = Iterator(TopNodeKey -> nodeLabel(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) val nodesIterator = serializedAllNodes(manifest, chunks) store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData) } private def serializedAllNodes(manifest: BatchAVLProverManifest[D], - chunks: Iterator[BatchAVLProverSubtree[D]]) = { + chunks: Iterator[BatchAVLProverSubtree[D]]): Iterator[(Array[Byte], Array[Byte])] = { def idCollector(node: ProverNodes[D], acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { - val pair: (Array[Byte], Array[Byte]) = (nodeKey(node), toBytes(node)) + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node)) node match { case n: ProxyInternalNode[D] if n.isEmpty => acc ++ Iterator(pair) @@ -102,14 +103,15 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB } } - idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) + idCollector(manifest.root, Iterator.empty) ++ + chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) } private def serializedVisitedNodes(node: ProverNodes[D], isTop: Boolean): Array[(Array[Byte], Array[Byte])] = { // Should always serialize top node. It may not be new if it is the creation of the tree if (node.isNew || isTop) { - val pair: (Array[Byte], Array[Byte]) = (nodeKey(node), toBytes(node)) + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node)) node match { case n: InternalProverNode[D] => val leftSubtree = serializedVisitedNodes(n.left, isTop = false) @@ -122,8 +124,10 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB } } - //TODO label or key??? - private def nodeKey(node: ProverNodes[D]): Array[Byte] = node.label + /** + * Node label (hash). Used as database key for node contents + */ + private def nodeLabel(node: ProverNodes[D]): Array[Byte] = node.label private def toBytes(node: ProverNodes[D]): Array[Byte] = { val builder = new mutable.ArrayBuilder.ofByte; @@ -148,6 +152,7 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB object VersionedLDBAVLStorage { + // prefixes used to encode node type (internal or leaf) in database private[batch] val InternalNodePrefix: Byte = 0: Byte private[batch] val LeafPrefix: Byte = 1: Byte From cfc37fbfbcf4eec358aafa4e70f5d574a1edd32c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 8 Jan 2023 22:22:39 +0300 Subject: [PATCH 098/204] fromSnapshot removed --- .../network/PeerFilteringRule.scala | 2 +- .../nodeView/state/ErgoStateReader.scala | 3 ++ .../nodeView/state/UtxoState.scala | 42 +------------------ 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala index f1695d5a2a..e34e159094 100644 --- a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala @@ -87,7 +87,7 @@ object SyncV2Filter extends PeerFilteringRule { } object UtxoSetNetworkingFilter extends PeerFilteringRule { - val UtxoSnapsnotActivationVersion = Version(4, 0, 20) // todo: set proper version around release + val UtxoSnapsnotActivationVersion = Version(5, 0, 6) // todo: set proper version around release def condition(version: Version): Boolean = { // If neighbour version is >= `UtxoSnapsnotActivationVersion`, the neighbour supports utxo snapshots exchange diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index b7f3fc21e5..2030f85580 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -23,6 +23,9 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { protected lazy val votingSettings: VotingSettings = chainSettings.voting + /** + * If the state is in its genesis version (before genesis block) + */ def isGenesis: Boolean = { rootHash.sameElements(constants.settings.chainSettings.genesisStateDigest) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index b9b0597f9d..440f12aa25 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings, Parameters} +import org.ergoplatform.settings.{Algos, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -18,7 +18,7 @@ import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree} import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} @@ -315,42 +315,4 @@ object UtxoState extends ScorexLogging { new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) } - def fromSnapshot(prover: BatchAVLProver[Digest32, HF], - settings: ErgoSettings) = { - val stateDir = ErgoState.stateDir(settings) - stateDir.mkdirs() - val constants = StateConstants(settings) - - val store = new LDBVersionedStore(stateDir, initialKeepVersions = constants.keepVersions) - val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) - .getOrElse(ErgoState.genesisStateVersion) - val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { - val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) - PersistentBatchAVLProver.create(prover, storage).get - } - new UtxoState(persistentProver, version, store, constants) - } - - // todo: do we need to restore from disk? - def fromLatestSnapshot(settings: ErgoSettings): Try[UtxoState] = { - val snapshotsDb = SnapshotsDb.create(settings) - fromLatestSnapshot(settings, snapshotsDb) - } - - // todo: do we need to restore from disk? - def fromLatestSnapshot(settings: ErgoSettings, - snapshotDb: SnapshotsDb): Try[UtxoState] = Try { - val snapshotsInfo = snapshotDb.readSnapshotsInfo - val (h, manifestId) = snapshotsInfo.availableManifests.maxBy(_._1) - log.info(s"Reading snapshot from height $h") - val manifest = snapshotDb.readManifest(manifestId).get - val subtreeIds = manifest.subtreesIds - val subtrees = subtreeIds.map(sid => snapshotDb.readSubtree(sid).get) - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) - val prover = serializer.combine(manifest -> subtrees, Algos.hash.DigestSize, None).get - - fromSnapshot(prover, settings) - } - } From ebdb5136e303348dede3fdb0145a117c2caa1e1b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 8 Jan 2023 22:51:35 +0300 Subject: [PATCH 099/204] unused code for state recovering removed --- .../network/ErgoNodeViewSynchronizer.scala | 2 +- .../nodeView/ErgoNodeViewHolder.scala | 81 ++++++++----------- .../state/UtxoSetSnapshotPersistence.scala | 13 ++- 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index a9ae9542b5..0994ee4804 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -917,7 +917,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def sendUtxoSnapshotChunk(subtreeId: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { - usr.getUtxoSnapshotChunk(subtreeId) match { + usr.getUtxoSnapshotChunkBytes(subtreeId) match { case Some(snapChunk) => { log.debug(s"Sending utxo snapshot chunk (${Algos.encode(subtreeId)}) to $peer") val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index a58958751a..b660214997 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -152,36 +152,36 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti } /** + + Assume that history knows the following blocktree: + + G + / \ + * G + / \ + * G + + where path with G-s is about canonical chain (G means semantically valid modifier), path with * is sidechain (* means + that semantic validity is unknown). New modifier is coming to the sidechain, it sends rollback to the root + + application of the sidechain to the state. Assume that state is finding that some modifier in the sidechain is + incorrect: + + G + / \ + G G + / \ + B G + / * - * Assume that history knows the following blocktree: - * - * G - * / \ - * G - * / \ - * G - * - * where path with G-s is about canonical chain (G means semantically valid modifier), path with * is sidechain (* means - * that semantic validity is unknown). New modifier is coming to the sidechain, it sends rollback to the root + - * application of the sidechain to the state. Assume that state is finding that some modifier in the sidechain is - * incorrect: - * - * G - * / \ - * G G - * / \ - * B G - * / - * - * - * In this case history should be informed about the bad modifier and it should retarget state - * - * //todo: improve the comment below - * - * We assume that we apply modifiers sequentially (on a single modifier coming from the network or generated locally), - * and in case of failed application of some modifier in a progressInfo, rollback point in an alternative should be not - * earlier than a rollback point of an initial progressInfo. - * */ + + In this case history should be informed about the bad modifier and it should retarget state + + //todo: improve the comment below + + We assume that we apply modifiers sequentially (on a single modifier coming from the network or generated locally), + and in case of failed application of some modifier in a progressInfo, rollback point in an alternative should be not + earlier than a rollback point of an initial progressInfo. + **/ @tailrec protected final def updateState(history: ErgoHistory, @@ -423,24 +423,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti log.info("History database read") val memPool = ErgoMemPool.empty(settings) val constants = StateConstants(settings) - val stateRead = ErgoState.readOrGenerate(settings, constants).asInstanceOf[State] - val stateRestored: State = stateRead /* match { - case us: UtxoState => - // todo: check if wallet is initialized - if (us.isGenesis && us.snapshotsAvailable().nonEmpty) { - // start from UTXO set snapshot available - UtxoState.fromLatestSnapshot(settings) match { - case Success(s) => s.asInstanceOf[State] - case Failure(e) => - log.error("Can't restore UTXO set from snapshot ", e) - stateRead - } - } else { - stateRead - } - case _ => stateRead - } */ - restoreConsistentState(stateRestored, history) match { + restoreConsistentState(ErgoState.readOrGenerate(settings, constants).asInstanceOf[State], history) match { case Success(state) => log.info(s"State database read, state synchronized") val wallet = ErgoWallet.readOrGenerate( @@ -703,8 +686,8 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti getNodeViewChanges orElse processStateSnapshot orElse handleHealthCheck orElse { - case a: Any => log.error("Strange input: " + a) - } + case a: Any => log.error("Strange input: " + a) + } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 6cb275d138..3022771c20 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -11,6 +11,8 @@ import scorex.util.ScorexLogging trait UtxoSetSnapshotPersistence extends ScorexLogging { + val MakeSnapshotEvery = 1024 // test value, switch to 51200 after testing + def constants: StateConstants protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] @@ -27,16 +29,14 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - val SnapshotEvery = 1024 // test value, switch to 51200 after testing - if (estimatedTip.nonEmpty && - (height % SnapshotEvery == SnapshotEvery - 1) && - estimatedTip.get - height <= SnapshotEvery) { + (height % MakeSnapshotEvery == MakeSnapshotEvery - 1) && + estimatedTip.get - height <= MakeSnapshotEvery) { val (manifest, subtrees) = slicedTree() val ms0 = System.currentTimeMillis() - snapshotsDb.pruneSnapshots(height - SnapshotEvery * 2) + snapshotsDb.pruneSnapshots(height - MakeSnapshotEvery * 2) snapshotsDb.writeSnapshot(height, manifest, subtrees) val ms = System.currentTimeMillis() log.info("Time to dump utxo set snapshot: " + (ms - ms0)) @@ -47,7 +47,6 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { snapshotsDb.readSnapshotsInfo } - def getSnapshotInfo(): SnapshotsInfo = { snapshotsDb.readSnapshotsInfo } @@ -56,7 +55,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { snapshotsDb.readManifestBytes(id) } - def getUtxoSnapshotChunk(id: SubtreeId): Option[Array[Byte]] = { + def getUtxoSnapshotChunkBytes(id: SubtreeId): Option[Array[Byte]] = { snapshotsDb.readSubtreeBytes(id) } From 6b130da4e242984480ccb044527e707357617111 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 9 Jan 2023 17:15:36 +0300 Subject: [PATCH 100/204] UtxoSnapshotChunk removed --- .../modifiers/history/header/Header.scala | 16 ++++----- .../modifiers/state/UTXOSnapshotChunk.scala | 36 ------------------- .../state/UTXOSnapshotManifest.scala | 5 --- .../network/ErgoNodeViewSynchronizer.scala | 6 ++-- .../nodeView/history/ErgoHistory.scala | 3 -- .../nodeView/history/ErgoHistoryReader.scala | 4 --- .../UTXOSnapshotChunkProcessor.scala | 36 ------------------- .../scala/scorex/core/NodeViewModifier.scala | 5 ++- .../NonVerifyADHistorySpecification.scala | 11 ------ .../ErgoTransactionGenerators.scala | 8 +---- 10 files changed, 12 insertions(+), 118 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index db1d003ff3..58919ee96d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -72,16 +72,6 @@ case class Header(override val version: Header.Version, override def minerPk: EcPointType = powSolution.pk - /** - * Expected identifiers of the block sections corresponding to this header, - * except of state transformations proof section id - */ - lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = - Array( - (BlockTransactions.modifierTypeId, transactionsId), - (Extension.modifierTypeId, extensionId) - ) - /** * Expected identifiers of the block sections corresponding to this header */ @@ -92,6 +82,12 @@ case class Header(override val version: Header.Version, (Extension.modifierTypeId, extensionId) ) + /** + * Expected identifiers of the block sections corresponding to this header, + * except of state transformations proof section id + */ + lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = sectionIds.tail + override lazy val toString: String = s"Header(${this.asJson.noSpaces})" override lazy val serializer: ScorexSerializer[Header] = HeaderSerializer diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala index 73e03d6b29..778045858d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala @@ -1,43 +1,7 @@ package org.ergoplatform.modifiers.state -import org.ergoplatform.ErgoBox -import org.ergoplatform.modifiers.BlockSection import scorex.core.ModifierTypeId -import scorex.core.serialization.ScorexSerializer -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSubtree -import scorex.crypto.hash.Digest32 -import scorex.util.{ModifierId, bytesToId} - -/** -* Container for a chunk of sliced AVL+ tree -*/ -case class UTXOSnapshotChunk(subTree: Either[BatchAVLProverSubtree[Digest32], Array[Byte]]) - extends BlockSection { - - override val modifierTypeId: ModifierTypeId = UTXOSnapshotChunk.modifierTypeId - - lazy val subtreeDeserialized: BatchAVLProverSubtree[Digest32] = subTree match { - case Left(st) => st - case Right(_) => ??? //todo: exception may happen here if bytes - } - - override lazy val id: ModifierId = bytesToId(subtreeDeserialized.id) - - override def parentId: ModifierId = ??? - - //todo: provide id from outside - override def serializedId: Array[Byte] = subtreeDeserialized.subtreeTop.label - - override type M = UTXOSnapshotChunk - - override def serializer: ScorexSerializer[UTXOSnapshotChunk] = ??? - - override val sizeOpt: Option[Int] = None - -} object UTXOSnapshotChunk { - type StateElement = ErgoBox - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (107: Byte) } diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala index 75d51bdd47..71373d73ca 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala @@ -40,10 +40,5 @@ object UTXOSnapshotManifest { val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (106: Byte) - def validate(manifest: UTXOSnapshotManifest, header: Header): Try[Unit] = Try { - require(manifest.blockId == header.id) - ??? - } - } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 0994ee4804..ab5edbf71c 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -8,8 +8,6 @@ import org.ergoplatform.modifiers.BlockSection import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} -import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.nodeView.ErgoNodeViewHolder.BlockAppliedTransactions import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.{ErgoMemPool, ErgoMemPoolReader} @@ -23,7 +21,6 @@ import scorex.core.{ModifierTypeId, NodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoSetSnapshotPersistence, UtxoStateReader} -import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import scorex.core.network.message._ import org.ergoplatform.nodeView.wallet.ErgoWalletReader import org.ergoplatform.settings.Algos.HF @@ -50,6 +47,9 @@ import scala.collection.mutable import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.modifiers.state.UTXOSnapshotChunk /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index d2ff73083f..8be7566a68 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -6,7 +6,6 @@ import org.ergoplatform.ErgoLikeContext import org.ergoplatform.mining.AutolykosPowScheme import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors._ @@ -81,8 +80,6 @@ trait ErgoHistory process(section) case poPoWProof: NipopowProofModifier => process(poPoWProof) - case chunk: UTXOSnapshotChunk => - process(chunk) } }.map(this -> _).recoverWith { case e => if (!e.isInstanceOf[RecoverableModifierError]) { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index 0637f04cdd..2474bd3b04 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -4,7 +4,6 @@ import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, PoPowHeader, PoPowParams} -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.storage._ @@ -29,7 +28,6 @@ trait ErgoHistoryReader with ContainsModifiers[BlockSection] with HeadersProcessor with PoPoWProofsProcessor - with UTXOSnapshotChunkProcessor with BlockSectionProcessor with ScorexLogging with ScorexEncoding { @@ -427,8 +425,6 @@ trait ErgoHistoryReader validate(m) case m: NipopowProofModifier => validate(m) - case chunk: UTXOSnapshotChunk => - validate(chunk) case m: Any => Failure(new MalformedModifierError(s"Modifier $m has incorrect type", modifier.id, modifier.modifierTypeId)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala deleted file mode 100644 index 17f2b16e40..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UTXOSnapshotChunkProcessor.scala +++ /dev/null @@ -1,36 +0,0 @@ -package org.ergoplatform.nodeView.history.storage.modifierprocessors - -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk -import org.ergoplatform.nodeView.history.storage.HistoryStorage -import scorex.core.consensus.ProgressInfo -import scorex.core.utils.ScorexEncoding -import scorex.util.ScorexLogging - -import scala.util.{Failure, Success, Try} - -/** - * Contains all functions required by History to process UTXOSnapshotChunk - */ -trait UTXOSnapshotChunkProcessor extends ScorexLogging with ScorexEncoding { - - protected val historyStorage: HistoryStorage - - def process(m: UTXOSnapshotChunk): Try[ProgressInfo[BlockSection]] = ??? -/* - { - //TODO - val toInsert = ??? - historyStorage.insert(Seq.empty, toInsert).map { _ => - ProgressInfo(None, Seq.empty, Seq(m), Seq.empty) - } - } -*/ - - def validate(m: UTXOSnapshotChunk): Try[Unit] = if (historyStorage.contains(m.id)) { - Failure(new Error(s"UTXOSnapshotChunk with id ${m.encodedId} is already in history")) - } else { - Success(Unit) - } - -} diff --git a/src/main/scala/scorex/core/NodeViewModifier.scala b/src/main/scala/scorex/core/NodeViewModifier.scala index b2ce44fdb7..749c532aa2 100644 --- a/src/main/scala/scorex/core/NodeViewModifier.scala +++ b/src/main/scala/scorex/core/NodeViewModifier.scala @@ -4,7 +4,6 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.serialization.BytesSerializable import scorex.core.utils.ScorexEncoding - sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {self => val modifierTypeId: ModifierTypeId @@ -12,13 +11,13 @@ sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {sel //todo: check statically or dynamically output size def id: scorex.util.ModifierId - def encodedId: String = encoder.encodeId(id) - override def equals(obj: scala.Any): Boolean = obj match { case that: NodeViewModifier => (that.id == id) && (that.modifierTypeId == modifierTypeId) case _ => false } + def encodedId: String = id + } trait EphemerealNodeViewModifier extends NodeViewModifier diff --git a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala index 484313b875..15dd345048 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala @@ -5,7 +5,6 @@ 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.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.nodeView.state.StateType import org.ergoplatform.settings.Algos import org.ergoplatform.utils.HistoryTestHelpers @@ -22,16 +21,6 @@ class NonVerifyADHistorySpecification extends HistoryTestHelpers { private lazy val popowHistory = ensureMinimalHeight(genHistory(), 100) - //todo: make real test - ignore("Should apply UTXOSnapshotChunks") { - forAll(randomUTXOSnapshotChunkGen) { snapshot: UTXOSnapshotChunk => - popowHistory.applicable(snapshot) shouldBe true - val processInfo = popowHistory.append(snapshot).get._2 - processInfo.toApply shouldEqual Some(snapshot) - popowHistory.applicable(snapshot) shouldBe false - } - } - property("Should calculate difficulty correctly") { val epochLength = 3 val useLastEpochs = 3 diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index dccb17b9cc..3ea0b1f7b0 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -4,7 +4,6 @@ import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{BoxHolder, ErgoStateContext, VotingData} @@ -20,7 +19,7 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.wallet.interpreter.TransactionHintsBag import org.ergoplatform.wallet.utils.Generators import org.ergoplatform.{DataInput, ErgoAddress, ErgoAddressEncoder, ErgoBox, ErgoBoxCandidate, Input, P2PKAddress} -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.Gen import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.ByteArrayWrapper import scorex.util.encode.Base16 @@ -295,11 +294,6 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { } yield BlockTransactions(headerId, Header.InitialVersion, txs.foldLeft(Seq.empty[ErgoTransaction])((acc, tx) => if ((acc :+ tx).map(_.size).sum < (Parameters.MaxBlockSizeDefault - 150)) acc :+ tx else acc)) - // todo: fix generator - lazy val randomUTXOSnapshotChunkGen: Gen[UTXOSnapshotChunk] = for { - index: Int <- Arbitrary.arbitrary[Int] - } yield UTXOSnapshotChunk(null) - lazy val invalidErgoFullBlockGen: Gen[ErgoFullBlock] = for { header <- defaultHeaderGen txs <- invalidBlockTransactionsGen From 13068e313c92d732ac79eb7482398d70bb08ce6a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 9 Jan 2023 17:20:06 +0300 Subject: [PATCH 101/204] UTXOSnapshotManifest removed --- .../state/UTXOSnapshotManifest.scala | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala index 71373d73ca..76b6360243 100644 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala @@ -1,40 +1,6 @@ package org.ergoplatform.modifiers.state -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.history.ErgoHistory.Height -import org.ergoplatform.settings.Algos import scorex.core.ModifierTypeId -import scorex.core.serialization.ScorexSerializer -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest -import scorex.crypto.authds.ADDigest -import scorex.crypto.hash.Digest32 -import scorex.util.{ModifierId, bytesToId, idToBytes} - -import scala.util.Try - -case class UTXOSnapshotManifest( - height: Height, - blockId: ModifierId, - utxoSetDigest: ADDigest, //33 bytes! extra byte with tree height here! - manifest: BatchAVLProverManifest[Digest32] - ) extends BlockSection { - - override val modifierTypeId: ModifierTypeId = UTXOSnapshotManifest.modifierTypeId - - override lazy val serializedId: Array[Byte] = Algos.hash(idToBytes(blockId) ++ manifest.id) - - override lazy val id: ModifierId = bytesToId(serializedId) - - override type M = UTXOSnapshotManifest - - override def serializer: ScorexSerializer[UTXOSnapshotManifest] = ??? - - override def parentId: ModifierId = ??? - - override val sizeOpt: Option[Int] = None - -} object UTXOSnapshotManifest { From 77b774cf70afecdce0b4e3e1704825ecb2594c2a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 10 Jan 2023 16:42:40 +0300 Subject: [PATCH 102/204] PoPoWProof removal, ModifierTypeId trait --- .../bench/misc/ModifierWriter.scala | 11 ++-- .../ergoplatform/local/MempoolAuditor.scala | 3 +- .../modifiers/ErgoFullBlock.scala | 6 +- .../modifiers/ModifierTypeId.scala | 48 +++++++++++++++ .../modifiers/NonHeaderBlockSection.scala | 8 +-- .../modifiers/history/ADProofs.scala | 8 +-- .../modifiers/history/BlockTransactions.scala | 6 +- .../history/NipopowProofModifier.scala | 41 ------------- .../history/extension/Extension.scala | 7 +-- .../modifiers/history/header/Header.scala | 11 ++-- .../modifiers/mempool/ErgoTransaction.scala | 4 +- .../modifiers/state/UTXOSnapshotChunk.scala | 7 --- .../state/UTXOSnapshotManifest.scala | 10 ---- .../network/ErgoNodeViewSynchronizer.scala | 60 +++++++++---------- .../nodeView/ErgoNodeViewHolder.scala | 8 +-- .../nodeView/history/ErgoHistory.scala | 15 ++--- .../nodeView/history/ErgoHistoryReader.scala | 12 ++-- .../history/storage/HistoryStorage.scala | 9 +-- .../ToDownloadProcessor.scala | 16 +++-- .../popow/EmptyPoPoWProofsProcessor.scala | 19 ------ .../popow/FullPoPoWProofsProcessor.scala | 20 ------- .../popow/PoPoWProofsProcessor.scala | 21 ------- .../nodeView/state/SnapshotsInfo.scala | 2 - .../nodeView/state/UtxoState.scala | 5 +- .../org/ergoplatform/settings/Constants.scala | 10 ++-- .../settings/ValidationRules.scala | 9 ++- .../tools/ValidationRulesPrinter.scala | 6 +- .../scala/scorex/core/NodeViewModifier.scala | 3 +- .../scorex/core/consensus/ProgressInfo.scala | 5 +- src/main/scala/scorex/core/core.scala | 10 +--- .../scorex/core/network/DeliveryTracker.scala | 32 +++++----- .../network/message/BasicMessagesRepo.scala | 11 ++-- .../scorex/core/transaction/Transaction.scala | 10 +--- .../core/validation/ModifierError.scala | 10 ++-- .../core/validation/ModifierValidator.scala | 35 ++++++----- .../core/validation/ValidationSettings.scala | 4 +- .../mempool/ErgoTransactionSpec.scala | 9 ++- .../VerifyNonADHistorySpecification.scala | 5 +- .../core/network/DeliveryTrackerSpec.scala | 4 +- .../testkit/generators/ObjectGenerators.scala | 9 +-- 40 files changed, 215 insertions(+), 314 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala delete mode 100644 src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala delete mode 100644 src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala delete mode 100644 src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala delete mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala delete mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala delete mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala index 3f448d9125..d6195b52d2 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala @@ -1,18 +1,17 @@ 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 +import org.ergoplatform.modifiers.{BlockSection, ModifierTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import scorex.core.serialization.ScorexSerializer -import scorex.core.{ModifierTypeId, NodeViewModifier} +import scorex.core.NodeViewModifier object ModifierWriter { - val modifierSerializers: Map[ModifierTypeId, ScorexSerializer[_ <: BlockSection]] = + val modifierSerializers: Map[ModifierTypeId.Value, ScorexSerializer[_ <: BlockSection]] = Map(Header.modifierTypeId -> HeaderSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, ADProofs.modifierTypeId -> ADProofsSerializer) @@ -34,9 +33,9 @@ object ModifierWriter { mod <- modifierSerializers(typeId).parseBytesTry(bytes).toOption } yield mod - private def readModId(implicit fis: InputStream): Option[ModifierTypeId] = { + private def readModId(implicit fis: InputStream): Option[ModifierTypeId.Value] = { val int = fis.read() - if (int == -1) { None } else { Some(ModifierTypeId @@ int.toByte) } + if (int == -1) { None } else { Some(ModifierTypeId.fromByte(int.toByte)) } } } diff --git a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala index c2239037c2..1996d04cd6 100644 --- a/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala +++ b/src/main/scala/org/ergoplatform/local/MempoolAuditor.scala @@ -12,7 +12,6 @@ import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.RecheckMempool import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} import scorex.core.network.message.{InvData, InvSpec, Message} -import scorex.core.transaction.Transaction import scorex.util.ScorexLogging import scala.concurrent.duration._ @@ -87,7 +86,7 @@ class MempoolAuditor(nodeViewHolderRef: ActorRef, private def broadcastTx(unconfirmedTx: UnconfirmedTransaction): Unit = { val msg = Message( InvSpec, - Right(InvData(Transaction.ModifierTypeId, Seq(unconfirmedTx.id))), + Right(InvData(unconfirmedTx.transaction.modifierTypeId, Seq(unconfirmedTx.id))), None ) networkControllerRef ! SendToNetwork(msg, Broadcast) diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 80240799d0..3ea34c76f5 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -8,7 +8,7 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.serialization.ScorexSerializer -import scorex.core.{ModifierTypeId, TransactionsCarryingPersistentNodeViewModifier} +import scorex.core.TransactionsCarryingPersistentNodeViewModifier import scorex.util.ModifierId case class ErgoFullBlock(header: Header, @@ -20,7 +20,7 @@ case class ErgoFullBlock(header: Header, override type M = ErgoFullBlock - override val modifierTypeId: ModifierTypeId = ErgoFullBlock.modifierTypeId + override val modifierTypeId: ModifierTypeId.Value = ErgoFullBlock.modifierTypeId override def serializedId: Array[Byte] = header.serializedId @@ -49,7 +49,7 @@ case class ErgoFullBlock(header: Header, object ErgoFullBlock extends ApiCodecs { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (-127: Byte) + val modifierTypeId: ModifierTypeId.Value = FullBlockTypeId.value implicit val jsonEncoder: Encoder[ErgoFullBlock] = { b: ErgoFullBlock => Json.obj( diff --git a/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala new file mode 100644 index 0000000000..13d6d8c238 --- /dev/null +++ b/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala @@ -0,0 +1,48 @@ +package org.ergoplatform.modifiers + +import supertagged.TaggedType +import ModifierTypeId._ + +sealed trait ModifierTypeId { + val value: ModifierTypeId.Value +} + +object ModifierTypeId { + object Value extends TaggedType[Byte] + type Value = Value.Type + + @inline + def fromByte(value: Byte): Value = Value @@ value +} + +object TransactionTypeId extends ModifierTypeId { + override val value: Value = fromByte(2) +} + +object HeaderTypeId extends ModifierTypeId { + override val value: Value = fromByte(101) +} + +object BlockTransactionsTypeId extends ModifierTypeId { + override val value: Value = fromByte(102) +} + +object ProofsTypeId extends ModifierTypeId { + override val value: Value = fromByte(104) +} + +object ExtensionTypeId extends ModifierTypeId { + override val value: Value = fromByte(108) +} + +object FullBlockTypeId extends ModifierTypeId { + override val value: Value = fromByte(-127) +} + +object UtxoSnapshotChunkTypeId extends ModifierTypeId { + override val value: Value = fromByte(-126) +} + +object SnapshotsInfoTypeId extends ModifierTypeId { + override val value: Value = fromByte(-125) +} diff --git a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala index bbed96e1ff..c3041cb3c8 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala @@ -1,7 +1,6 @@ package org.ergoplatform.modifiers import org.ergoplatform.settings.Algos -import scorex.core.ModifierTypeId import scorex.crypto.hash.Digest32 import scorex.util.{ModifierId, bytesToId, idToBytes} @@ -10,7 +9,8 @@ import scorex.util.{ModifierId, bytesToId, idToBytes} */ trait NonHeaderBlockSection extends BlockSection { - override lazy val serializedId: Array[Byte] = NonHeaderBlockSection.computeIdBytes(modifierTypeId, headerId, digest) + override lazy val serializedId: Array[Byte] = + NonHeaderBlockSection.computeIdBytes(modifierTypeId, headerId, digest) override lazy val id: ModifierId = bytesToId(serializedId) @@ -22,9 +22,9 @@ trait NonHeaderBlockSection extends BlockSection { } object NonHeaderBlockSection { - def computeId(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): ModifierId = + def computeId(modifierType: ModifierTypeId.Value, headerId: ModifierId, digest: Array[Byte]): ModifierId = bytesToId(computeIdBytes(modifierType, headerId, digest)) - def computeIdBytes(modifierType: ModifierTypeId, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = + def computeIdBytes(modifierType: ModifierTypeId.Value, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = Algos.hash.prefixedHash(modifierType, idToBytes(headerId), digest) } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala index 4471c7da30..a5979398b2 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala @@ -3,11 +3,10 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.NonHeaderBlockSection +import org.ergoplatform.modifiers.{ModifierTypeId, NonHeaderBlockSection, ProofsTypeId} import org.ergoplatform.modifiers.state._ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, Constants} -import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer import scorex.crypto.authds.avltree.batch.{Lookup => _, _} import scorex.crypto.authds.{ADDigest, ADValue, SerializedAdProof} @@ -24,7 +23,7 @@ case class ADProofs(headerId: ModifierId, override def digest: Digest32 = ADProofs.proofDigest(proofBytes) - override val modifierTypeId: ModifierTypeId = ADProofs.modifierTypeId + override val modifierTypeId: ModifierTypeId.Value = ADProofs.modifierTypeId override type M = ADProofs @@ -69,7 +68,8 @@ case class ADProofs(headerId: ModifierId, } object ADProofs extends ApiCodecs { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (104: Byte) + + val modifierTypeId: ModifierTypeId.Value = ProofsTypeId.value val KL = 32 diff --git a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala index cb926f3894..e4a436e311 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.NonHeaderBlockSection +import org.ergoplatform.modifiers.{BlockTransactionsTypeId, ModifierTypeId, NonHeaderBlockSection} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.nodeView.mempool.TransactionMembershipProof @@ -37,7 +37,7 @@ case class BlockTransactions(headerId: ModifierId, assert(txs.nonEmpty, "Block should always contain at least 1 coinbase-like transaction") - override val modifierTypeId: ModifierTypeId = BlockTransactions.modifierTypeId + override val modifierTypeId: ModifierTypeId.Value = BlockTransactions.modifierTypeId /** * Ids of block transactions @@ -94,7 +94,7 @@ case class BlockTransactions(headerId: ModifierId, object BlockTransactions extends ApiCodecs { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (102: Byte) + val modifierTypeId: ModifierTypeId.Value = BlockTransactionsTypeId.value // Used in the miner when a BlockTransaction instance is not generated yet (because a header is not known) def transactionsRoot(txs: Seq[ErgoTransaction], blockVersion: Version): Digest32 = { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala b/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala deleted file mode 100644 index fddf3e5779..0000000000 --- a/src/main/scala/org/ergoplatform/modifiers/history/NipopowProofModifier.scala +++ /dev/null @@ -1,41 +0,0 @@ -package org.ergoplatform.modifiers.history - -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.popow.NipopowProof -import org.ergoplatform.settings.Algos -import scorex.core.ModifierTypeId -import scorex.core.serialization.ScorexSerializer -import scorex.util.{ModifierId, bytesToId} - -/** - * Network message carrying NiPoPoW proof for a block. - * - * Not really implemented yet. Will be used for bootstrapping. - * - * @param proof - NiPoPoW proof - * @param sizeOpt - optionally, serialized network message size - */ -case class NipopowProofModifier(proof: NipopowProof, override val sizeOpt: Option[Int] = None) - extends Comparable[NipopowProofModifier] with Ordered[NipopowProofModifier] with BlockSection { - - override val modifierTypeId: ModifierTypeId = NipopowProofModifier.modifierTypeId - - override def parentId: ModifierId = ??? - - override def serializedId: Array[Byte] = Algos.hash(bytes) - - override lazy val id: ModifierId = bytesToId(serializedId) - - override type M = NipopowProofModifier - - override def serializer: ScorexSerializer[NipopowProofModifier] = throw new Error("PoPow proofs serialization not supported") - - override def compare(that: NipopowProofModifier): Int = ??? - -} - -object NipopowProofModifier { - - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (105: Byte) - -} diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index 38667c2ebd..bb9e884cde 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala @@ -4,9 +4,8 @@ import com.google.common.primitives.Bytes import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.NonHeaderBlockSection +import org.ergoplatform.modifiers.{ExtensionTypeId, ModifierTypeId, NonHeaderBlockSection} import org.ergoplatform.settings.Algos -import scorex.core.ModifierTypeId import scorex.core.serialization.ScorexSerializer import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.MerkleTree @@ -25,7 +24,7 @@ case class Extension(headerId: ModifierId, override val sizeOpt: Option[Int] = None) extends ExtensionCandidate(fields) with NonHeaderBlockSection { - override val modifierTypeId: ModifierTypeId = Extension.modifierTypeId + override val modifierTypeId: ModifierTypeId.Value = Extension.modifierTypeId override type M = Extension @@ -55,7 +54,7 @@ object Extension extends ApiCodecs { Algos.merkleTree(LeafData @@ fields.map(kvToLeaf)) } - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (108: Byte) + val modifierTypeId: ModifierTypeId.Value = ExtensionTypeId.value implicit val jsonEncoder: Encoder[Extension] = { e: Extension => Map( diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 58919ee96d..16fed997ed 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -7,14 +7,13 @@ import org.ergoplatform.mining.difficulty.RequiredDifficulty import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions, PreHeader} -import org.ergoplatform.modifiers.{NonHeaderBlockSection, BlockSection} +import org.ergoplatform.modifiers.{BlockSection, HeaderTypeId, ModifierTypeId, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.wallet.interpreter.ErgoInterpreter import scorex.core.serialization.ScorexSerializer import scorex.core.utils.NetworkTimeProvider -import scorex.core.ModifierTypeId import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util._ @@ -60,7 +59,7 @@ case class Header(override val version: Header.Version, override type M = Header - override val modifierTypeId: ModifierTypeId = Header.modifierTypeId + override val modifierTypeId: ModifierTypeId.Value = Header.modifierTypeId lazy val requiredDifficulty: Difficulty = RequiredDifficulty.decodeCompactBits(nBits) @@ -75,7 +74,7 @@ case class Header(override val version: Header.Version, /** * Expected identifiers of the block sections corresponding to this header */ - lazy val sectionIds: Seq[(ModifierTypeId, ModifierId)] = + lazy val sectionIds: Seq[(ModifierTypeId.Value, ModifierId)] = Array( (ADProofs.modifierTypeId, ADProofsId), (BlockTransactions.modifierTypeId, transactionsId), @@ -86,7 +85,7 @@ case class Header(override val version: Header.Version, * Expected identifiers of the block sections corresponding to this header, * except of state transformations proof section id */ - lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId, ModifierId)] = sectionIds.tail + lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId.Value, ModifierId)] = sectionIds.tail override lazy val toString: String = s"Header(${this.asJson.noSpaces})" @@ -157,7 +156,7 @@ object Header extends ApiCodecs { votes = header.votes.toColl ) - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (101: Byte) + val modifierTypeId: ModifierTypeId.Value = HeaderTypeId.value lazy val GenesisParentId: ModifierId = bytesToId(Array.fill(Constants.HashLength)(0: Byte)) diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index cb868aa93a..07478412aa 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -5,7 +5,7 @@ import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} import org.ergoplatform._ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.modifiers.ErgoNodeViewModifier +import org.ergoplatform.modifiers.{ErgoNodeViewModifier, ModifierTypeId, TransactionTypeId} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.ErgoContext import org.ergoplatform.nodeView.state.ErgoStateContext @@ -455,6 +455,8 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], object ErgoTransaction extends ApiCodecs with ScorexLogging with ScorexEncoding { + val modifierTypeId: ModifierTypeId.Value = TransactionTypeId.value + def apply(inputs: IndexedSeq[Input], outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoTransaction = ErgoTransaction(inputs, IndexedSeq.empty, outputCandidates, None) diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala deleted file mode 100644 index 778045858d..0000000000 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotChunk.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.ergoplatform.modifiers.state - -import scorex.core.ModifierTypeId - -object UTXOSnapshotChunk { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (107: Byte) -} diff --git a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala b/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala deleted file mode 100644 index 76b6360243..0000000000 --- a/src/main/scala/org/ergoplatform/modifiers/state/UTXOSnapshotManifest.scala +++ /dev/null @@ -1,10 +0,0 @@ -package org.ergoplatform.modifiers.state - -import scorex.core.ModifierTypeId - -object UTXOSnapshotManifest { - - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (106: Byte) - -} - diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index ab5edbf71c..02039b492e 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -3,8 +3,8 @@ 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.mempool.{ErgoTransactionSerializer, UnconfirmedTransaction} -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} +import org.ergoplatform.modifiers.{BlockSection, ModifierTypeId, SnapshotsInfoTypeId, TransactionTypeId, UtxoSnapshotChunkTypeId} import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} @@ -17,7 +17,7 @@ import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{ChainIsH import org.ergoplatform.nodeView.ErgoNodeViewHolder._ import scorex.core.consensus.{Equal, Fork, Nonsense, Older, Unknown, Younger} import scorex.core.network.ModifiersStatus.Requested -import scorex.core.{ModifierTypeId, NodeViewModifier, idsToString} +import scorex.core.{NodeViewModifier, idsToString} import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoSetSnapshotPersistence, UtxoStateReader} @@ -30,7 +30,6 @@ import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPe import scorex.core.network.message.{InvData, Message, ModifiersData} import scorex.core.serialization.ScorexSerializer import scorex.core.settings.NetworkSettings -import scorex.core.transaction.Transaction import scorex.core.utils.{NetworkTimeProvider, ScorexEncoding} import scorex.core.validation.MalformedModifierError import scorex.util.{ModifierId, ScorexLogging} @@ -49,7 +48,6 @@ import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.state.UTXOSnapshotChunk /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the @@ -268,7 +266,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, context.system.scheduler.scheduleAtFixedRate(healthCheckDelay, healthCheckRate, viewHolderRef, IsChainHealthy)(ex, self) } - protected def broadcastModifierInv(modTypeId: ModifierTypeId, modId: ModifierId): Unit = { + protected def broadcastModifierInv(modTypeId: ModifierTypeId.Value, modId: ModifierId): Unit = { val msg = Message(InvSpec, Right(InvData(modTypeId, Seq(modId))), None) networkControllerRef ! SendToNetwork(msg, Broadcast) } @@ -282,7 +280,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * (in history database available via `historyReader` interface, or delivery tracker cache, thus * downloading of the modifier is needed. */ - private def downloadRequired(historyReader: ErgoHistory)(modifierTypeId: ModifierTypeId, id: ModifierId): Boolean = { + private def downloadRequired(historyReader: ErgoHistory)(modifierTypeId: ModifierTypeId.Value, id: ModifierId): Boolean = { deliveryTracker.status(id, modifierTypeId, Array(historyReader)) == ModifiersStatus.Unknown } @@ -352,7 +350,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // Send history extension to the (less developed) peer 'remote' which does not have it. def sendExtension(remote: ConnectedPeer, - ext: Seq[(ModifierTypeId, ModifierId)]): Unit = { + ext: Seq[(ModifierTypeId.Value, ModifierId)]): Unit = { ext.groupBy(_._1).mapValues(_.map(_._2)).foreach { case (mid, mods) => networkControllerRef ! SendToNetwork(Message(InvSpec, Right(InvData(mid, mods)), None), SendToPeer(remote)) @@ -537,7 +535,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * (non-zero if we're re-requesting the block section, in this case, there should be only * one id to request in `modifierIds` */ - def requestBlockSection(modifierTypeId: ModifierTypeId, + def requestBlockSection(modifierTypeId: ModifierTypeId.Value, modifierIds: Seq[ModifierId], peer: ConnectedPeer, checksDone: Int = 0): Unit = { @@ -568,7 +566,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { - deliveryTracker.setRequested(UTXOSnapshotChunk.modifierTypeId, ModifierId @@ Algos.encode(subtreeId), peer){ deliveryCheck => + deliveryTracker.setRequested(UtxoSnapshotChunkTypeId.value, ModifierId @@ Algos.encode(subtreeId), peer){ deliveryCheck => context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) } val msg = Message(GetUtxoSnapshotChunkSpec, Right(subtreeId), None) @@ -576,7 +574,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def onDownloadRequest(historyReader: ErgoHistory): Receive = { - case DownloadRequest(modifiersToFetch: Map[ModifierTypeId, Seq[ModifierId]]) => + case DownloadRequest(modifiersToFetch: Map[ModifierTypeId.Value, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") if(modifiersToFetch.nonEmpty) { requestDownload( @@ -611,7 +609,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ protected def requestDownload(maxModifiers: Int, minModifiersPerBucket: Int, maxModifiersPerBucket: Int) (getPeersOpt: => Option[Iterable[ConnectedPeer]]) - (fetchMax: Int => Map[ModifierTypeId, Seq[ModifierId]]): Unit = + (fetchMax: Int => Map[ModifierTypeId.Value, Seq[ModifierId]]): Unit = getPeersOpt .foreach { peers => val peersCount = peers.size @@ -621,7 +619,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } else { fetchMax(maxElementsToFetch) } - if (fetched.size == 1 && fetched.head._1 == SnapshotsInfo.modifierTypeId) { + if (fetched.size == 1 && fetched.head._1 == SnapshotsInfoTypeId.value) { requestSnapshotsInfo() } val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) @@ -664,7 +662,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private def blockSectionsFromRemote(hr: ErgoHistory, - typeId: ModifierTypeId, + typeId: ModifierTypeId.Value, requestedModifiers: Map[ModifierId, Array[Byte]], remote: ConnectedPeer): Unit = { Constants.modifierSerializers.get(typeId) match { @@ -714,7 +712,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // filter out non-requested modifiers val requestedModifiers = processSpam(remote, typeId, modifiers, blockAppliedTxsCache) - if (typeId == Transaction.ModifierTypeId) { + if (typeId == TransactionTypeId.value) { transactionsFromRemote(requestedModifiers, mp, remote) } else { blockSectionsFromRemote(hr, typeId, requestedModifiers, remote) @@ -727,7 +725,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ def parseAndProcessTransaction(id: ModifierId, bytes: Array[Byte], remote: ConnectedPeer): Unit = { if (bytes.length > settings.nodeSettings.maxTransactionSize) { - deliveryTracker.setInvalid(id, Transaction.ModifierTypeId) + deliveryTracker.setInvalid(id, TransactionTypeId.value) penalizeMisbehavingPeer(remote) log.warn(s"Transaction size ${bytes.length} from ${remote.toString} " + s"exceeds limit ${settings.nodeSettings.maxTransactionSize}") @@ -751,7 +749,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return collection of parsed modifiers */ def parseModifiers[M <: NodeViewModifier](modifiers: Map[ModifierId, Array[Byte]], - modifierTypeId: ModifierTypeId, + modifierTypeId: ModifierTypeId.Value, serializer: ScorexSerializer[M], remote: ConnectedPeer): Iterable[M] = { modifiers.flatMap { case (id, bytes) => @@ -776,7 +774,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return ids and bytes of modifiers that were requested by our node */ def processSpam(remote: ConnectedPeer, - typeId: ModifierTypeId, + typeId: ModifierTypeId.Value, modifiers: Map[ModifierId, Array[Byte]], blockAppliedTxsCache: FixedSizeApproximateCacheQueue): Map[ModifierId, Array[Byte]] = { val modifiersByStatus = @@ -787,7 +785,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val spam = modifiersByStatus.filterKeys(_ != Requested) if (spam.nonEmpty) { - if (typeId == Transaction.ModifierTypeId) { + if (typeId == TransactionTypeId.value) { // penalize a peer for sending TXs that have been already applied to a block val spammyTxs = modifiers.filterKeys(blockAppliedTxsCache.mightContain) if (spammyTxs.nonEmpty) { @@ -893,7 +891,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => - deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UTXOSnapshotChunk.modifierTypeId) + deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UtxoSnapshotChunkTypeId.value) log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? hr.registerDownloadedChunk(subtree.id, serializedChunk) @@ -954,7 +952,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val modifierTypeId = invData.typeId val newModifierIds = modifierTypeId match { - case Transaction.ModifierTypeId => + case ErgoTransaction.modifierTypeId => if (txAcceptanceFilter) { val unknownMods = @@ -1006,7 +1004,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, //other node asking for objects by their ids protected def modifiersReq(hr: ErgoHistory, mp: ErgoMemPool, invData: InvData, remote: ConnectedPeer): Unit = { val objs: Seq[(ModifierId, Array[Byte])] = invData.typeId match { - case typeId: ModifierTypeId if typeId == Transaction.ModifierTypeId => + case typeId: ModifierTypeId if typeId == ErgoTransaction.modifierTypeId => mp.getAll(invData.ids).map { unconfirmedTx => unconfirmedTx.transaction.id -> unconfirmedTx.transactionBytes.getOrElse(unconfirmedTx.transaction.bytes) } @@ -1079,7 +1077,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (deliveryTracker.status(modifierId, modifierTypeId, Seq.empty) == ModifiersStatus.Requested) { // If transaction not delivered on time, we just forget about it. // It could be removed from other peer's mempool, so no reason to penalize the peer. - if (modifierTypeId == Transaction.ModifierTypeId) { + if (modifierTypeId == TransactionTypeId.value) { deliveryTracker.clearStatusForModifier(modifierId, modifierTypeId, ModifiersStatus.Requested) } else { // A block section is not delivered on time. @@ -1101,7 +1099,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val maxDeliveryChecks = networkSettings.maxDeliveryChecks if (checksDone < maxDeliveryChecks) { - if (modifierTypeId == UTXOSnapshotChunk.modifierTypeId) { + if (modifierTypeId == UtxoSnapshotChunkTypeId.value) { val newPeerOpt = hr.getRandomPeerToDownloadChunks() log.info(s"Rescheduling request for UTXO set chunk $modifierId , new peer $newPeerOpt") deliveryTracker.setUnknown(modifierId, modifierTypeId) @@ -1252,7 +1250,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case st@SuccessfulTransaction(utx) => val tx = utx.transaction - deliveryTracker.setHeld(tx.id, Transaction.ModifierTypeId) + deliveryTracker.setHeld(tx.id, TransactionTypeId.value) processMempoolResult(st) broadcastModifierInv(tx) @@ -1486,7 +1484,7 @@ object ErgoNodeViewSynchronizer { * */ case class CheckDelivery(source: ConnectedPeer, - modifierTypeId: ModifierTypeId, + modifierTypeId: ModifierTypeId.Value, modifierId: ModifierId) trait PeerManagerEvent @@ -1533,19 +1531,19 @@ object ErgoNodeViewSynchronizer { */ case class FailedOnRecheckTransaction(id : ModifierId, error: Throwable) extends ModificationOutcome - case class RecoverableFailedModification(typeId: ModifierTypeId, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + case class RecoverableFailedModification(typeId: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome - case class SyntacticallyFailedModification(typeId: ModifierTypeId, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + case class SyntacticallyFailedModification(typeId: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome /** * Signal associated with stateful validation of a block section */ - case class SemanticallyFailedModification(typeId: ModifierTypeId, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + case class SemanticallyFailedModification(typeId: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome /** * Signal associated with stateless validation of a block section */ - case class SyntacticallySuccessfulModifier(typeId: ModifierTypeId, modifierId: ModifierId) extends ModificationOutcome + case class SyntacticallySuccessfulModifier(typeId: ModifierTypeId.Value, modifierId: ModifierId) extends ModificationOutcome /** * Signal sent by node view holder when a full block is applied to state @@ -1561,7 +1559,7 @@ object ErgoNodeViewSynchronizer { */ case class BlockSectionsProcessingCacheUpdate(headersCacheSize: Int, blockSectionsCacheSize: Int, - cleared: (ModifierTypeId, Seq[ModifierId])) + cleared: (ModifierTypeId.Value, Seq[ModifierId])) /** * Command to re-check mempool to clean transactions become invalid while sitting in the mempool up diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index b660214997..f3eed521cf 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -6,7 +6,7 @@ import org.ergoplatform.ErgoApp import org.ergoplatform.ErgoApp.CriticalSystemException import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ModifierTypeId} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome @@ -25,8 +25,8 @@ import scorex.core.utils.{NetworkTimeProvider, ScorexEncoding} import scorex.core.validation.RecoverableModifierError import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor -import java.io.File +import java.io.File import org.ergoplatform.modifiers.history.extension.Extension import scala.annotation.tailrec @@ -137,7 +137,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti //TODO: actually, pi.toDownload contains only 1 modifierid per type, //TODO: see the only case where toDownload is not empty during ProgressInfo construction //TODO: so the code below can be optimized - val toDownload = mutable.Map[ModifierTypeId, Seq[ModifierId]]() + val toDownload = mutable.Map[ModifierTypeId.Value, Seq[ModifierId]]() pi.toDownload.foreach { case (tid, mid) => toDownload.put(tid, toDownload.getOrElse(tid, Seq()) :+ mid) } @@ -738,7 +738,7 @@ object ErgoNodeViewHolder { case class BlockAppliedTransactions(txs: Seq[ModifierId]) extends NodeViewHolderEvent - case class DownloadRequest(modifiersToFetch: Map[ModifierTypeId, Seq[ModifierId]]) extends NodeViewHolderEvent + case class DownloadRequest(modifiersToFetch: Map[ModifierTypeId.Value, Seq[ModifierId]]) extends NodeViewHolderEvent case class CurrentView[State](history: ErgoHistory, state: State, vault: ErgoWallet, pool: ErgoMemPool) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index 8be7566a68..e9f3f151aa 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -9,7 +9,6 @@ import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.{NonHeaderBlockSection, ErgoFullBlock, BlockSection} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors._ -import org.ergoplatform.nodeView.history.storage.modifierprocessors.popow.{EmptyPoPoWProofsProcessor, FullPoPoWProofsProcessor} import org.ergoplatform.settings._ import org.ergoplatform.utils.LoggingUtil import scorex.core.consensus.ProgressInfo @@ -78,8 +77,6 @@ trait ErgoHistory process(header) case section: NonHeaderBlockSection => process(section) - case poPoWProof: NipopowProofModifier => - process(poPoWProof) } }.map(this -> _).recoverWith { case e => if (!e.isInstanceOf[RecoverableModifierError]) { @@ -278,8 +275,7 @@ object ErgoHistory extends ScorexLogging { val history: ErgoHistory = (nodeSettings.verifyTransactions, nodeSettings.poPoWBootstrap) match { case (true, true) => - new ErgoHistory with FullBlockSectionProcessor - with FullPoPoWProofsProcessor { + new ErgoHistory with FullBlockSectionProcessor { override protected val settings: ErgoSettings = ergoSettings override protected[history] val historyStorage: HistoryStorage = db override val powScheme: AutolykosPowScheme = chainSettings.powScheme @@ -287,8 +283,7 @@ object ErgoHistory extends ScorexLogging { } case (false, true) => - new ErgoHistory with EmptyBlockSectionProcessor - with FullPoPoWProofsProcessor { + new ErgoHistory with EmptyBlockSectionProcessor { override protected val settings: ErgoSettings = ergoSettings override protected[history] val historyStorage: HistoryStorage = db override val powScheme: AutolykosPowScheme = chainSettings.powScheme @@ -296,8 +291,7 @@ object ErgoHistory extends ScorexLogging { } case (true, false) => - new ErgoHistory with FullBlockSectionProcessor - with EmptyPoPoWProofsProcessor { + new ErgoHistory with FullBlockSectionProcessor { override protected val settings: ErgoSettings = ergoSettings override protected[history] val historyStorage: HistoryStorage = db override val powScheme: AutolykosPowScheme = chainSettings.powScheme @@ -305,8 +299,7 @@ object ErgoHistory extends ScorexLogging { } case (false, false) => - new ErgoHistory with EmptyBlockSectionProcessor - with EmptyPoPoWProofsProcessor { + new ErgoHistory with EmptyBlockSectionProcessor { override protected val settings: ErgoSettings = ergoSettings override protected[history] val historyStorage: HistoryStorage = db override val powScheme: AutolykosPowScheme = chainSettings.powScheme diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index 2474bd3b04..d2bf72c4db 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -4,13 +4,12 @@ import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, PoPowHeader, PoPowParams} -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NonHeaderBlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ModifierTypeId, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.storage._ import org.ergoplatform.nodeView.history.storage.modifierprocessors._ -import org.ergoplatform.nodeView.history.storage.modifierprocessors.popow.PoPoWProofsProcessor import org.ergoplatform.settings.ErgoSettings -import scorex.core.{ModifierTypeId, NodeViewComponent} +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 @@ -27,12 +26,11 @@ trait ErgoHistoryReader extends NodeViewComponent with ContainsModifiers[BlockSection] with HeadersProcessor - with PoPoWProofsProcessor with BlockSectionProcessor with ScorexLogging with ScorexEncoding { - type ModifierIds = Seq[(ModifierTypeId, ModifierId)] + type ModifierIds = Seq[(ModifierTypeId.Value, ModifierId)] protected[history] val historyStorage: HistoryStorage @@ -75,7 +73,7 @@ trait ErgoHistoryReader * @param id - modifier id * @return type and raw bytes of semantically valid ErgoPersistentModifier with the given id it is in history */ - def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId, Array[Byte])] = + def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId.Value, Array[Byte])] = if (isSemanticallyValid(id) != ModifierSemanticValidity.Invalid) { historyStorage.modifierTypeAndBytesById(id) } else { @@ -423,8 +421,6 @@ trait ErgoHistoryReader validate(header) case m: NonHeaderBlockSection => validate(m) - case m: NipopowProofModifier => - validate(m) case m: Any => Failure(new MalformedModifierError(s"Modifier $m has incorrect type", modifier.id, modifier.modifierTypeId)) } 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 e55626057a..29a1ed52f3 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -1,16 +1,13 @@ package org.ergoplatform.nodeView.history.storage import com.github.benmanes.caffeine.cache.Caffeine -import org.ergoplatform.modifiers.BlockSection +import org.ergoplatform.modifiers.{BlockSection, ModifierTypeId} import org.ergoplatform.modifiers.history.HistoryModifierSerializer import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.{Algos, CacheSettings, ErgoSettings} -import scorex.core.ModifierTypeId import scorex.core.utils.ScorexEncoding import scorex.db.{ByteArrayWrapper, LDBFactory, LDBKVStore} import scorex.util.{ModifierId, ScorexLogging, idToBytes} -import supertagged.PostfixSugar - import scala.util.{Failure, Success, Try} /** @@ -58,8 +55,8 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, c objectsStore.get(idToBytes(id)).map(_.tail) // removing modifier type byte with .tail } - def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId, Array[Byte])] = { - objectsStore.get(idToBytes(id)).map(bs => (bs.head @@ ModifierTypeId, bs.tail)) // first byte is type id, tail is modifier bytes + def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId.Value, Array[Byte])] = { + objectsStore.get(idToBytes(id)).map(bs => (ModifierTypeId.fromByte(bs.head), bs.tail)) // first byte is type id, tail is modifier bytes } def modifierById(id: ModifierId): Option[BlockSection] = 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 f9fd2b782d..aa62972fc0 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 @@ -1,12 +1,10 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId, SnapshotsInfoTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.nodeView.state.SnapshotsInfo import org.ergoplatform.settings.{ChainSettings, ErgoSettings, NodeConfigurationSettings} -import scorex.core.ModifierTypeId import scorex.core.utils.NetworkTimeProvider import scorex.util.{ModifierId, ScorexLogging} @@ -45,7 +43,7 @@ trait ToDownloadProcessor * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition */ def nextModifiersToDownload(howManyPerType: Int, - condition: (ModifierTypeId, ModifierId) => Boolean): Map[ModifierTypeId, Seq[ModifierId]] = { + condition: (ModifierTypeId.Value, ModifierId) => Boolean): Map[ModifierTypeId.Value, Seq[ModifierId]] = { val FullBlocksToDownloadAhead = 192 // how many full blocks to download forwards during active sync @@ -53,8 +51,8 @@ trait ToDownloadProcessor @tailrec def continuation(height: Int, - acc: Map[ModifierTypeId, Vector[ModifierId]], - maxHeight: Int): Map[ModifierTypeId, Vector[ModifierId]] = { + acc: Map[ModifierTypeId.Value, Vector[ModifierId]], + maxHeight: Int): Map[ModifierTypeId.Value, Vector[ModifierId]] = { if (height > maxHeight) { acc } else { @@ -91,7 +89,7 @@ trait ToDownloadProcessor case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => // todo: can be requested multiple times, prevent it if (getUtxoSetSnapshotDownloadPlan().isEmpty) { - Map(SnapshotsInfo.modifierTypeId -> Seq.empty) + Map(SnapshotsInfoTypeId.value -> Seq.empty) } else { Map.empty } @@ -106,7 +104,7 @@ trait ToDownloadProcessor /** * Checks whether it's time to download full chain, and returns toDownload modifiers */ - protected def toDownload(header: Header): Seq[(ModifierTypeId, ModifierId)] = { + protected def toDownload(header: Header): Seq[(ModifierTypeId.Value, ModifierId)] = { if (!nodeSettings.verifyTransactions) { // A regime that do not download and verify transaction Nil @@ -123,7 +121,7 @@ trait ToDownloadProcessor } } - def requiredModifiersForHeader(h: Header): Seq[(ModifierTypeId, ModifierId)] = { + def requiredModifiersForHeader(h: Header): Seq[(ModifierTypeId.Value, ModifierId)] = { if (!nodeSettings.verifyTransactions) { Nil } else if (nodeSettings.stateType.requireProofs) { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala deleted file mode 100644 index 193dbeb830..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/EmptyPoPoWProofsProcessor.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow - -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.NipopowProofModifier -import scorex.core.consensus.ProgressInfo - -import scala.util.{Failure, Success, Try} - -/** - * Contains all functions required by History to process PoPoWProofs for regime that do not accept them. - */ -trait EmptyPoPoWProofsProcessor extends PoPoWProofsProcessor { - - def validate(m: NipopowProofModifier): Try[Unit] = Failure(new Error("Regime that do not process PoPoWProof")) - - def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] = - Success(ProgressInfo(None, Seq.empty, Seq.empty, Seq.empty)) -} - diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala deleted file mode 100644 index 7763ea0fe5..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/FullPoPoWProofsProcessor.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow - -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.NipopowProofModifier -import org.ergoplatform.nodeView.history.storage.modifierprocessors.HeadersProcessor -import scorex.core.consensus.ProgressInfo - -import scala.util.{Success, Try} - -/** - * Contains all functions required by History to process PoPoWProofs for regime that accept them. - */ -trait FullPoPoWProofsProcessor extends PoPoWProofsProcessor with HeadersProcessor { - - def validate(m: NipopowProofModifier): Try[Unit] = throw new Error("PoPow not yet supported") - - def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] = - Success(ProgressInfo(None, Seq.empty, Seq.empty, Seq.empty)) -} - diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala deleted file mode 100644 index 839400fb48..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/popow/PoPoWProofsProcessor.scala +++ /dev/null @@ -1,21 +0,0 @@ -package org.ergoplatform.nodeView.history.storage.modifierprocessors.popow - -import org.ergoplatform.modifiers.BlockSection -import org.ergoplatform.modifiers.history.{HeaderChain, NipopowProofModifier} -import org.ergoplatform.nodeView.history.storage.modifierprocessors.HeadersProcessor -import scorex.core.consensus.ProgressInfo -import scorex.util.ScorexLogging - -import scala.util.Try - -/** - * Contains all functions required by History to process PoPowProofModifier and generate them. - */ -trait PoPoWProofsProcessor extends HeadersProcessor with ScorexLogging { - - def validate(m: NipopowProofModifier): Try[Unit] - - def process(m: NipopowProofModifier): Try[ProgressInfo[BlockSection]] - - def lastHeaders(count: Int, offset: Int = 0): HeaderChain -} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index b579001bc0..8a08d26c5b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -2,7 +2,6 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId -import scorex.core.ModifierTypeId /** * Container for available UTXO set snapshots @@ -17,7 +16,6 @@ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { } object SnapshotsInfo { - val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (127: Byte) def makeEmpty(): SnapshotsInfo = SnapshotsInfo(Map.empty) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 440f12aa25..16acdbc3fe 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -6,14 +6,13 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, TransactionTypeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} import org.ergoplatform.settings.{Algos, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ -import scorex.core.transaction.Transaction import scorex.core.transaction.state.TransactionValidation import scorex.core.utils.ScorexEncoding import scorex.core.validation.ModifierValidator @@ -103,7 +102,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 opsResult } ModifierValidator(stateContext.validationSettings) - .validateNoFailure(fbOperationFailed, blockOpsTry, Transaction.ModifierTypeId) + .validateNoFailure(fbOperationFailed, blockOpsTry, TransactionTypeId.value) .validateEquals(fbDigestIncorrect, expectedDigest, persistentProver.digest, headerId, Header.modifierTypeId) .result .toTry diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index b5cece06e5..cb54a6bff1 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -1,14 +1,14 @@ package org.ergoplatform.settings import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.modifiers.ModifierTypeId 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.ErgoTransactionSerializer +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer} import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import scorex.core.serialization.ScorexSerializer -import scorex.core.transaction.Transaction -import scorex.core.{ModifierTypeId, NodeViewModifier} +import scorex.core.NodeViewModifier import sigmastate.Values import sigmastate.Values.ErgoTree @@ -44,12 +44,12 @@ object Constants { // Number of last block headers available is scripts from ErgoStateContext val LastHeadersInContext = 10 - val modifierSerializers: Map[ModifierTypeId, ScorexSerializer[_ <: NodeViewModifier]] = + val modifierSerializers: Map[ModifierTypeId.Value, ScorexSerializer[_ <: NodeViewModifier]] = Map(Header.modifierTypeId -> HeaderSerializer, Extension.modifierTypeId -> ExtensionSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, ADProofs.modifierTypeId -> ADProofsSerializer, - Transaction.ModifierTypeId -> ErgoTransactionSerializer) + ErgoTransaction.modifierTypeId -> ErgoTransactionSerializer) val SoftForkEpochs = 32 //about 45.5 days diff --git a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala b/src/main/scala/org/ergoplatform/settings/ValidationRules.scala index 2be07be4c3..d499775db1 100644 --- a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala +++ b/src/main/scala/org/ergoplatform/settings/ValidationRules.scala @@ -1,14 +1,13 @@ package org.ergoplatform.settings import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} -import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId} 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.wallet.boxes.ErgoBoxAssetExtractor -import scorex.core.ModifierTypeId import scorex.core.validation.{InvalidModifier, ModifierValidator} import scorex.core.validation.ValidationResult.Invalid import scorex.util.ModifierId @@ -306,7 +305,7 @@ object ValidationRules { val fbDigestIncorrect: Short = 501 - def errorMessage(id: Short, details: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): String = { + def errorMessage(id: Short, details: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): String = { ValidationRules.rulesSpec(id) .invalidMod(InvalidModifier(details, modifierId, modifierTypeId)) .errors @@ -314,10 +313,10 @@ object ValidationRules { .message } - private def recoverable(errorMessage: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + private def recoverable(errorMessage: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = ModifierValidator.error(errorMessage, modifierId, modifierTypeId) - private def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + private def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = ModifierValidator.fatal(error, modifierId, modifierTypeId) } diff --git a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala index b78453f0b1..2685d84c9c 100644 --- a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala +++ b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala @@ -1,7 +1,7 @@ package org.ergoplatform.tools +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.settings.ValidationRules -import scorex.core.ModifierTypeId import scorex.core.validation.InvalidModifier import scorex.util.{ModifierId, ScorexLogging, bytesToId} @@ -14,7 +14,7 @@ object ValidationRulesPrinter extends App with ScorexLogging { printHeader() rules.toSeq.sortBy(_._1).foreach { r => - val rule = r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId @@ 0.toByte)).errors.head.message.trim + val rule = r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId.fromByte(0))).errors.head.message.trim val activated = r._2.isActive val mayBeDisabled = r._2.mayBeDisabled val modifiers = r._2.affectedClasses.map(_.getSimpleName).mkString(", ") @@ -37,7 +37,7 @@ object ValidationRulesPrinter extends App with ScorexLogging { printHeader() } - if (r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId @@ 0.toByte)).isFatal) { + if (r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId.fromByte(0))).isFatal) { // we only mention fatal errors here println(s" ${r._1} & $rule & ${boolToLatex(mayBeDisabled)} & ${boolToLatex(activated)} & $modifiers \\\\") diff --git a/src/main/scala/scorex/core/NodeViewModifier.scala b/src/main/scala/scorex/core/NodeViewModifier.scala index 749c532aa2..eb5622d58f 100644 --- a/src/main/scala/scorex/core/NodeViewModifier.scala +++ b/src/main/scala/scorex/core/NodeViewModifier.scala @@ -1,12 +1,13 @@ package scorex.core +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.serialization.BytesSerializable import scorex.core.utils.ScorexEncoding sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {self => - val modifierTypeId: ModifierTypeId + val modifierTypeId: ModifierTypeId.Value //todo: check statically or dynamically output size def id: scorex.util.ModifierId diff --git a/src/main/scala/scorex/core/consensus/ProgressInfo.scala b/src/main/scala/scorex/core/consensus/ProgressInfo.scala index d540d897ef..713abb06a5 100644 --- a/src/main/scala/scorex/core/consensus/ProgressInfo.scala +++ b/src/main/scala/scorex/core/consensus/ProgressInfo.scala @@ -1,7 +1,8 @@ package scorex.core.consensus +import org.ergoplatform.modifiers.ModifierTypeId import scorex.core.utils.ScorexEncoder -import scorex.core.{ModifierTypeId, PersistentNodeViewModifier} +import scorex.core.PersistentNodeViewModifier import scorex.util.ModifierId /** @@ -16,7 +17,7 @@ import scorex.util.ModifierId case class ProgressInfo[PM <: PersistentNodeViewModifier](branchPoint: Option[ModifierId], toRemove: Seq[PM], toApply: Seq[PM], - toDownload: Seq[(ModifierTypeId, ModifierId)]) + toDownload: Seq[(ModifierTypeId.Value, ModifierId)]) (implicit encoder: ScorexEncoder) { if (toRemove.nonEmpty) diff --git a/src/main/scala/scorex/core/core.scala b/src/main/scala/scorex/core/core.scala index bb02ad8669..7ed44a3f89 100644 --- a/src/main/scala/scorex/core/core.scala +++ b/src/main/scala/scorex/core/core.scala @@ -1,5 +1,6 @@ package scorex +import org.ergoplatform.modifiers.ModifierTypeId import scorex.core.network.message.InvData import scorex.core.utils.ScorexEncoder import scorex.util.encode.Base16 @@ -7,23 +8,18 @@ import supertagged.TaggedType package object core { - //TODO implement ModifierTypeId as a trait - object ModifierTypeId extends TaggedType[Byte] - object VersionTag extends TaggedType[String] - type ModifierTypeId = ModifierTypeId.Type - type VersionTag = VersionTag.Type - def idsToString(ids: Seq[(ModifierTypeId, util.ModifierId)])(implicit enc: ScorexEncoder): String = { + def idsToString(ids: Seq[(ModifierTypeId.Value, util.ModifierId)])(implicit enc: ScorexEncoder): String = { List(ids.headOption, ids.lastOption) .flatten .map { case (typeId, id) => s"($typeId,${enc.encodeId(id)})" } .mkString("[", "..", "]") } - def idsToString(modifierType: ModifierTypeId, ids: Seq[util.ModifierId])(implicit encoder: ScorexEncoder): String = { + def idsToString(modifierType: ModifierTypeId.Value, ids: Seq[util.ModifierId])(implicit encoder: ScorexEncoder): String = { idsToString(ids.map(id => (modifierType, id))) } diff --git a/src/main/scala/scorex/core/network/DeliveryTracker.scala b/src/main/scala/scorex/core/network/DeliveryTracker.scala index 01615d8345..d9b856baae 100644 --- a/src/main/scala/scorex/core/network/DeliveryTracker.scala +++ b/src/main/scala/scorex/core/network/DeliveryTracker.scala @@ -2,11 +2,11 @@ package scorex.core.network import akka.actor.Cancellable import io.circe.{Encoder, Json} +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.CheckDelivery import org.ergoplatform.nodeView.mempool.ExpiringApproximateCache import org.ergoplatform.settings.{ErgoSettings, NetworkCacheSettings} -import scorex.core.ModifierTypeId import scorex.core.consensus.ContainsModifiers import scorex.core.network.DeliveryTracker._ import scorex.core.network.ModifiersStatus._ @@ -45,10 +45,10 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, desiredSizeOfExpectingModifierQueue: Int) extends ScorexLogging with ScorexEncoding { // when a remote peer is asked for a modifier we add the requested data to `requested` - protected val requested: mutable.Map[ModifierTypeId, Map[ModifierId, RequestedInfo]] = mutable.Map() + protected val requested: mutable.Map[ModifierTypeId.Value, Map[ModifierId, RequestedInfo]] = mutable.Map() // when our node received a modifier we put it to `received` - protected val received: mutable.Map[ModifierTypeId, Map[ModifierId, ConnectedPeer]] = mutable.Map() + protected val received: mutable.Map[ModifierTypeId.Value, Map[ModifierId, ConnectedPeer]] = mutable.Map() private val desiredSizeOfExpectingHeaderQueue: Int = desiredSizeOfExpectingModifierQueue * 8 @@ -97,7 +97,9 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * Since this class do not keep statuses for modifiers that are already in NodeViewHolder, * `modifierKeepers` are required here to check that modifier is in `Held` status */ - def status(modifierId: ModifierId, modifierTypeId: ModifierTypeId, modifierKeepers: Seq[ContainsModifiers[_]]): ModifiersStatus = + def status(modifierId: ModifierId, + modifierTypeId: ModifierTypeId.Value, + modifierKeepers: Seq[ContainsModifiers[_]]): ModifiersStatus = if (received.get(modifierTypeId).exists(_.contains(modifierId))) Received else if (requested.get(modifierTypeId).exists(_.contains(modifierId))) Requested else if (invalidModifierCache.mightContain(modifierId)) Invalid @@ -114,7 +116,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Set status of modifier with id `id` to `Requested` */ - def setRequested(typeId: ModifierTypeId, + def setRequested(typeId: ModifierTypeId.Value, id: ModifierId, supplier: ConnectedPeer, checksDone: Int = 0) @@ -130,12 +132,12 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * @return - information on requested modifier delivery tracker potentially has */ - def getRequestedInfo(typeId: ModifierTypeId, id: ModifierId): Option[RequestedInfo] = { + def getRequestedInfo(typeId: ModifierTypeId.Value, id: ModifierId): Option[RequestedInfo] = { requested.get(typeId).flatMap(_.get(id)) } /** Get peer we're communicating with in regards with modifier `id` **/ - def getSource(id: ModifierId, modifierTypeId: ModifierTypeId): Option[ConnectedPeer] = { + def getSource(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Option[ConnectedPeer] = { status(id, modifierTypeId, Seq.empty) match { case Requested => requested.get(modifierTypeId).flatMap(_.get(id)).map(_.peer) case Received => received.get(modifierTypeId).flatMap(_.get(id)) @@ -147,7 +149,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * Modified with id `id` is permanently invalid - set its status to `Invalid` * and return [[ConnectedPeer]] which sent bad modifier. */ - def setInvalid(id: ModifierId, modifierTypeId: ModifierTypeId): Option[ConnectedPeer] = { + def setInvalid(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Option[ConnectedPeer] = { val oldStatus: ModifiersStatus = status(id, modifierTypeId, Seq.empty) val transitionCheck = tryWithLogging { checkStatusTransition(oldStatus, Invalid) @@ -190,7 +192,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Modifier with id `id` was successfully applied to history - set its status to `Held`. */ - def setHeld(id: ModifierId, modifierTypeId: ModifierTypeId): Unit = + def setHeld(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Held) @@ -205,7 +207,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * this modifier was removed from cache because cache is overfull or * we stop trying to download this modifiers due to exceeded number of retries */ - def setUnknown(id: ModifierId, modifierTypeId: ModifierTypeId): Unit = + def setUnknown(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Unknown) @@ -215,7 +217,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Modifier with id `id` was received from remote peer - set its status to `Received`. */ - def setReceived(id: ModifierId, modifierTypeId: ModifierTypeId, sender: ConnectedPeer): Unit = + def setReceived(id: ModifierId, modifierTypeId: ModifierTypeId.Value, sender: ConnectedPeer): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Received) @@ -253,7 +255,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, case _ => false } - def clearStatusForModifier(id: ModifierId, modifierTypeId: ModifierTypeId, oldStatus: ModifiersStatus): Unit = + def clearStatusForModifier(id: ModifierId, modifierTypeId: ModifierTypeId.Value, oldStatus: ModifiersStatus): Unit = oldStatus match { case Requested => requested.flatAdjust(modifierTypeId)(_.map { infoById => @@ -328,15 +330,15 @@ object DeliveryTracker { case class FullInfo( invalidModifierApproxSize: Long, - requested: Seq[(ModifierTypeId, Map[ModifierId, RequestedInfo])], - received: Seq[(ModifierTypeId, Map[ModifierId, ConnectedPeer])] + requested: Seq[(ModifierTypeId.Value, Map[ModifierId, RequestedInfo])], + received: Seq[(ModifierTypeId.Value, Map[ModifierId, ConnectedPeer])] ) object FullInfo { import io.circe.syntax._ implicit val encodeState: Encoder[FullInfo] = new Encoder[FullInfo] { - def nestedMapAsJson[T : Encoder](requested: Seq[(ModifierTypeId, Map[ModifierId, T])]): Json = + def nestedMapAsJson[T : Encoder](requested: Seq[(ModifierTypeId.Value, Map[ModifierId, T])]): Json = Json.obj( requested.map { case (k, v) => k.toString -> Json.obj(v.mapValues(_.asJson).toSeq:_*) diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index f2163a1fc0..af0aabf6a5 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -1,6 +1,7 @@ package scorex.core.network.message +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.nodeView.state.SnapshotsInfo import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.wallet.Constants @@ -8,7 +9,7 @@ import scorex.core.consensus.SyncInfo import scorex.core.network._ import scorex.core.network.message.Message.MessageCode import scorex.core.serialization.ScorexSerializer -import scorex.core.{ModifierTypeId, NodeViewModifier} +import scorex.core.NodeViewModifier import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} @@ -16,9 +17,9 @@ import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} import scala.collection.immutable -case class ModifiersData(typeId: ModifierTypeId, modifiers: Map[ModifierId, Array[Byte]]) +case class ModifiersData(typeId: ModifierTypeId.Value, modifiers: Map[ModifierId, Array[Byte]]) -case class InvData(typeId: ModifierTypeId, ids: Seq[ModifierId]) +case class InvData(typeId: ModifierTypeId.Value, ids: Seq[ModifierId]) /** * The `SyncInfo` message requests an `Inv` message that provides modifier ids @@ -67,7 +68,7 @@ object InvSpec extends MessageSpecV1[InvData] { } override def parse(r: Reader): InvData = { - val typeId = ModifierTypeId @@ r.getByte() + val typeId = ModifierTypeId.fromByte(r.getByte()) val count = r.getUInt().toIntExact require(count > 0, "empty inv list") require(count <= maxInvObjects, s"$count elements in a message while limit is $maxInvObjects") @@ -146,7 +147,7 @@ object ModifiersSpec extends MessageSpecV1[ModifiersData] with ScorexLogging { } override def parse(r: Reader): ModifiersData = { - val typeId = ModifierTypeId @@ r.getByte() // 1 byte + val typeId = ModifierTypeId.fromByte(r.getByte()) // 1 byte val count = r.getUInt().toIntExact // 8 bytes require(count > 0, s"Illegal message with 0 modifiers of type $typeId") val resMap = immutable.Map.newBuilder[ModifierId, Array[Byte]] diff --git a/src/main/scala/scorex/core/transaction/Transaction.scala b/src/main/scala/scorex/core/transaction/Transaction.scala index ddda38caa4..7a793a92c7 100644 --- a/src/main/scala/scorex/core/transaction/Transaction.scala +++ b/src/main/scala/scorex/core/transaction/Transaction.scala @@ -1,6 +1,7 @@ package scorex.core.transaction -import scorex.core.{EphemerealNodeViewModifier, ModifierTypeId} +import org.ergoplatform.modifiers.{ModifierTypeId, TransactionTypeId} +import scorex.core.EphemerealNodeViewModifier import scorex.crypto.hash.Blake2b256 import scorex.util.{ModifierId, bytesToId} @@ -9,14 +10,9 @@ import scorex.util.{ModifierId, bytesToId} * A transaction is an atomic state modifier */ trait Transaction extends EphemerealNodeViewModifier { - override val modifierTypeId: ModifierTypeId = Transaction.ModifierTypeId + override val modifierTypeId: ModifierTypeId.Value = TransactionTypeId.value val messageToSign: Array[Byte] override lazy val id: ModifierId = bytesToId(Blake2b256(messageToSign)) } - - -object Transaction { - val ModifierTypeId: scorex.core.ModifierTypeId = scorex.core.ModifierTypeId @@ 2.toByte -} diff --git a/src/main/scala/scorex/core/validation/ModifierError.scala b/src/main/scala/scorex/core/validation/ModifierError.scala index d8fce64b30..3f5a32ef90 100644 --- a/src/main/scala/scorex/core/validation/ModifierError.scala +++ b/src/main/scala/scorex/core/validation/ModifierError.scala @@ -1,11 +1,11 @@ package scorex.core.validation -import scorex.core.ModifierTypeId +import org.ergoplatform.modifiers.ModifierTypeId import scorex.util.ModifierId import scala.util.control.NoStackTrace -case class InvalidModifier(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId) +case class InvalidModifier(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value) /** Base trait for errors that were occurred during NodeView Modifier validation */ @@ -13,7 +13,7 @@ trait ModifierError { def message: String def isFatal: Boolean def modifierId: ModifierId - def modifierTypeId: ModifierTypeId + def modifierTypeId: ModifierTypeId.Value def toThrowable: Throwable def info: String = { @@ -25,7 +25,7 @@ trait ModifierError { /** Permanent modifier error that could not be recovered in future even after any history updates */ @SuppressWarnings(Array("org.wartremover.warts.Null")) -class MalformedModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId, cause: Option[Throwable] = None) +class MalformedModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId.Value, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with ModifierError { def isFatal: Boolean = true def toThrowable: Throwable = this @@ -35,7 +35,7 @@ class MalformedModifierError(val message: String, val modifierId: ModifierId, va * When an instance is created, the stack trace is not collected which makes this exception lightweight. */ @SuppressWarnings(Array("org.wartremover.warts.Null")) -class RecoverableModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId, cause: Option[Throwable] = None) +class RecoverableModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId.Value, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with ModifierError with NoStackTrace { def isFatal: Boolean = false def toThrowable: Throwable = this diff --git a/src/main/scala/scorex/core/validation/ModifierValidator.scala b/src/main/scala/scorex/core/validation/ModifierValidator.scala index ed5b4e7ec0..6715c53679 100644 --- a/src/main/scala/scorex/core/validation/ModifierValidator.scala +++ b/src/main/scala/scorex/core/validation/ModifierValidator.scala @@ -1,7 +1,6 @@ package scorex.core.validation - -import scorex.core.ModifierTypeId +import org.ergoplatform.modifiers.ModifierTypeId import scorex.core.consensus.ModifierSemanticValidity import scorex.core.utils.ScorexEncoder import scorex.core.validation.ValidationResult._ @@ -29,33 +28,31 @@ object ModifierValidator { } /** report recoverable modifier error that could be fixed by later retries */ - def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = invalid(new RecoverableModifierError(error, modifierId, modifierTypeId, None)) /** report recoverable modifier error that could be fixed by later retries */ - def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId, cause: Throwable): Invalid = + def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value, cause: Throwable): Invalid = invalid(new RecoverableModifierError(msg(error, cause), modifierId, modifierTypeId, Option(cause))) /** report recoverable modifier error that could be fixed by later retries */ - def error(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + def error(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = error(msg(description, detail), modifierId, modifierTypeId) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = invalid(new MalformedModifierError(error, modifierId, modifierTypeId, None)) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId, cause: Throwable): Invalid = + def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value, cause: Throwable): Invalid = invalid(new MalformedModifierError(msg(error, cause), modifierId, modifierTypeId, Option(cause))) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + def fatal(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = fatal(msg(description, detail), modifierId, modifierTypeId) /** unsuccessful validation with a given error; also logs the error as an exception */ - def invalid(error: ModifierError): Invalid = { - Invalid(Seq(error)) - } + def invalid(error: ModifierError): Invalid = Invalid(Seq(error)) /** successful validation without payload */ val success: Valid[Unit] = Valid(()) @@ -103,7 +100,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the first argument equals the second. This should not be used with `ModifierId` of type `Array[Byte]`. * The `error` callback will be provided with detail on argument values for better reporting */ - def validateEquals[A](id: Short, given: => A, expected: => A, modifierId: ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateEquals[A](id: Short, given: => A, expected: => A, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass((given, expected) match { case _ if !settings.isActive(id) => result case (a: Array[_], b: Array[_]) if a sameElements[Any] b => result @@ -115,7 +112,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `id`s are equal. The `error` callback will be provided with detail on argument values */ - def validateEqualIds(id: Short, given: => ModifierId, expected: => ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateEqualIds(id: Short, given: => ModifierId, expected: => ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass { if (!settings.isActive(id) || given == expected) result else settings.getError(id, InvalidModifier(s"Given: ${e.encodeId(given)}, expected ${e.encodeId(expected)}", given, modifierTypeId)) @@ -131,7 +128,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `condition` is `Success`. Otherwise the `error` callback will be provided with detail * on a failure exception */ - def validateNoFailure(id: Short, condition: => Try[_], modifierId: ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateNoFailure(id: Short, condition: => Try[_], modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass(if (!settings.isActive(id)) result else condition.fold(e => settings.getError(id, e, modifierId, modifierTypeId), _ => result)) } @@ -143,7 +140,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS * @param modifierTypeId provide for a case when it cannot be resolved from a ModifierError * @return validation state */ - def validateNoFailure(id: Short, condition: => Try[_], modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateNoFailure(id: Short, condition: => Try[_], modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass { if (!settings.isActive(id)) { result @@ -163,7 +160,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `block` doesn't throw an Exception. Otherwise the `error` callback will be provided with detail * on the exception */ - def validateNoThrow(id: Short, block: => Any, modifierId: ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateNoThrow(id: Short, block: => Any, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { validateNoFailure(id, Try(block), modifierId, modifierTypeId) } @@ -176,7 +173,8 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate `condition` against payload is `true` or else return the `error` */ - def validateTryFlatten(id: Short, operation: T => Try[T], condition: T => Boolean, modifierId: ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateTryFlatten(id: Short, operation: T => Try[T], condition: T => Boolean, + modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass(result.toTry.flatMap(r => operation(r)) match { case Failure(ex) => settings.getError(id, ex, modifierId, modifierTypeId) case Success(v) if settings.isActive(id) && !condition(v) => settings.getError(id, InvalidModifier(modifierId, modifierId, modifierTypeId)) @@ -200,7 +198,8 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS * If given option is `None` then pass the previous result as success. * Return `error` if option is `Some` amd condition is `false` */ - def validateOrSkipFlatten[A](id: Short, option: => Option[A], condition: A => Boolean, modifierId: ModifierId, modifierTypeId: ModifierTypeId): ValidationState[T] = { + def validateOrSkipFlatten[A](id: Short, option: => Option[A], condition: A => Boolean, + modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { pass(option match { case Some(v) if settings.isActive(id) && !condition(v) => settings.getError(id, InvalidModifier(modifierId, modifierId, modifierTypeId)) case _ => result diff --git a/src/main/scala/scorex/core/validation/ValidationSettings.scala b/src/main/scala/scorex/core/validation/ValidationSettings.scala index 657b4a005e..e083d50e79 100644 --- a/src/main/scala/scorex/core/validation/ValidationSettings.scala +++ b/src/main/scala/scorex/core/validation/ValidationSettings.scala @@ -1,6 +1,6 @@ package scorex.core.validation -import scorex.core.ModifierTypeId +import org.ergoplatform.modifiers.ModifierTypeId import scorex.core.validation.ValidationResult.Invalid import scorex.util.ModifierId @@ -11,7 +11,7 @@ import scorex.util.ModifierId abstract class ValidationSettings { val isFailFast: Boolean - def getError(id: Short, e: Throwable, modifierId: ModifierId, modifierTypeId: ModifierTypeId): Invalid = + def getError(id: Short, e: Throwable, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = getError(id, InvalidModifier(e.getMessage, modifierId, modifierTypeId)) def getError(id: Short, invalidMod: InvalidModifier): ValidationResult.Invalid diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index 05e002a330..325e6b01d8 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -14,7 +14,6 @@ import org.ergoplatform.wallet.protocol.context.{InputContext, TransactionContex import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} import org.scalacheck.Gen import scalan.util.BenchmarkUtil -import scorex.core.transaction.Transaction import scorex.crypto.authds.ADKey import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.ByteArrayWrapper @@ -240,7 +239,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { val validity = tx.statefulValidity(from, emptyDataBoxes, emptyStateContext) validity.isSuccess shouldBe false val e = validity.failed.get - e.getMessage should startWith(ValidationRules.errorMessage(ValidationRules.txScriptValidation, "", emptyModifierId, Transaction.ModifierTypeId)) + e.getMessage should startWith(ValidationRules.errorMessage(ValidationRules.txScriptValidation, "", emptyModifierId, ErgoTransaction.modifierTypeId)) } } @@ -317,7 +316,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { } val txMod = tx.copy(inputs = inputsPointers, outputCandidates = out) val validFailure = txMod.statefulValidity(in, emptyDataBoxes, emptyStateContext) - validFailure.failed.get.getMessage should startWith(ValidationRules.errorMessage(txAssetsInOneBox, "", emptyModifierId, Transaction.ModifierTypeId).take(30)) + validFailure.failed.get.getMessage should startWith(ValidationRules.errorMessage(txAssetsInOneBox, "", emptyModifierId, ErgoTransaction.modifierTypeId).take(30)) } property("transaction with too many inputs should be rejected") { @@ -347,7 +346,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { assert(time0 <= Timeout) val cause = validity.failed.get.getMessage - cause should startWith(ValidationRules.errorMessage(bsBlockTransactionsCost, "", emptyModifierId, Transaction.ModifierTypeId).take(30)) + cause should startWith(ValidationRules.errorMessage(bsBlockTransactionsCost, "", emptyModifierId, ErgoTransaction.modifierTypeId).take(30)) //check that spam transaction validation with no cost limit is indeed taking too much time import Parameters._ @@ -534,7 +533,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { val txFailure = tx.statefulValidity(boxes, IndexedSeq.empty, ctx3) txFailure.isSuccess shouldBe false val cause = txFailure.toEither.left.get.getMessage - val expectedMessage = ValidationRules.errorMessage(ValidationRules.txMonotonicHeight, "", emptyModifierId, Transaction.ModifierTypeId) + val expectedMessage = ValidationRules.errorMessage(ValidationRules.txMonotonicHeight, "", emptyModifierId, ErgoTransaction.modifierTypeId) cause should startWith(expectedMessage) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index 7bbcb5acc5..66eac19f6b 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history -import org.ergoplatform.modifiers.ErgoFullBlock +import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.HeaderSerializer @@ -8,7 +8,6 @@ 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.ModifierTypeId import scorex.core.consensus.ProgressInfo class VerifyNonADHistorySpecification extends HistoryTestHelpers { @@ -118,7 +117,7 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { val missedChain = chain.tail.toList val missedBS = missedChain.flatMap { fb => Seq((BlockTransactions.modifierTypeId, fb.blockTransactions.encodedId), (Extension.modifierTypeId, fb.extension.encodedId)) - }.foldLeft(Map.empty[ModifierTypeId, Seq[String]]) { case (newAcc, (mType, mId)) => + }.foldLeft(Map.empty[ModifierTypeId.Value, Seq[String]]) { case (newAcc, (mType, mId)) => newAcc.adjust(mType)(_.fold(Seq(mId))(_ :+ mId)) } diff --git a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala index a338df74b2..d89220b98a 100644 --- a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala +++ b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala @@ -3,8 +3,8 @@ package scorex.core.network import akka.actor.{ActorRef, Cancellable} import io.circe._ import io.circe.syntax._ +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.utils.ErgoPropertyTest -import scorex.core.ModifierTypeId import scorex.core.network.ModifiersStatus.Received import scorex.testkit.generators.ObjectGenerators import scorex.util.ModifierId @@ -15,7 +15,7 @@ class DeliveryTrackerSpec extends ErgoPropertyTest with ObjectGenerators { forAll(connectedPeerGen(ActorRef.noSender)) { peer => val tracker = DeliveryTracker.empty(settings) val mid: ModifierId = ModifierId @@ "foo" - val mTypeId: ModifierTypeId = ModifierTypeId @@ (104: Byte) + val mTypeId: ModifierTypeId.Value = ModifierTypeId.fromByte(104) tracker.setRequested(mTypeId, mid, peer) { _ => Cancellable.alreadyCancelled} val infoFields = Seq( diff --git a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala index e85cef0572..d4285a50ab 100644 --- a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala +++ b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala @@ -3,6 +3,7 @@ package scorex.testkit.generators import java.net.{InetAddress, InetSocketAddress, URL} import akka.actor.ActorRef import akka.util.ByteString +import org.ergoplatform.modifiers.ModifierTypeId import org.ergoplatform.network.ModePeerFeature import org.ergoplatform.nodeView.state.StateType import org.scalacheck.Gen.{const, some} @@ -11,7 +12,7 @@ import scorex.core.app.Version import scorex.core.network.message.{InvData, ModifiersData} import scorex.core.network._ import scorex.core.network.peer.{PeerInfo, RestApiUrlPeerFeature} -import scorex.core.{ModifierTypeId, NodeViewModifier} +import scorex.core.NodeViewModifier import scorex.util.{ModifierId, bytesToId} trait ObjectGenerators { @@ -49,10 +50,10 @@ trait ObjectGenerators { lazy val modifierIdGen: Gen[ModifierId] = Gen.listOfN(NodeViewModifier.ModifierIdSize, Arbitrary.arbitrary[Byte]) .map(id => bytesToId(id.toArray)) - lazy val modifierTypeIdGen: Gen[ModifierTypeId] = Arbitrary.arbitrary[Byte].map(t => ModifierTypeId @@ t) + lazy val modifierTypeIdGen: Gen[ModifierTypeId.Value] = Arbitrary.arbitrary[Byte].map(t => ModifierTypeId.fromByte(t)) lazy val invDataGen: Gen[InvData] = for { - modifierTypeId: ModifierTypeId <- modifierTypeIdGen + modifierTypeId: ModifierTypeId.Value <- modifierTypeIdGen modifierIds: Seq[ModifierId] <- Gen.nonEmptyListOf(modifierIdGen) if modifierIds.nonEmpty } yield InvData(modifierTypeId, modifierIds) @@ -62,7 +63,7 @@ trait ObjectGenerators { } yield id -> mod lazy val modifiersGen: Gen[ModifiersData] = for { - modifierTypeId: ModifierTypeId <- modifierTypeIdGen + modifierTypeId: ModifierTypeId.Value <- modifierTypeIdGen modifiers: Map[ModifierId, Array[Byte]] <- Gen.nonEmptyMap(modifierWithIdGen).suchThat(_.nonEmpty) } yield ModifiersData(modifierTypeId, modifiers) From f153c66e677209ebd71441f4056c0450f09a9a59 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 13 Jan 2023 01:48:13 +0300 Subject: [PATCH 103/204] ScalaDocs in VersionedLDBAVLStorage --- .../batch/VersionedLDBAVLStorage.scala | 40 +++++++++++-------- .../scala/scorex/db/LDBVersionedStore.scala | 3 ++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index b087d4217d..2d00d40692 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -26,19 +26,12 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { - private lazy val labelSize = nodeParameters.labelSize + private def labelSize: Int = nodeParameters.labelSize + private val fixedSizeValueMode = nodeParameters.valueSize.isDefined private val TopNodeKey: Array[Byte] = Array.fill(labelSize)(123: Byte) private val TopNodeHeight: Array[Byte] = Array.fill(labelSize)(124: Byte) - private val fixedSizeValueMode = nodeParameters.valueSize.isDefined - - def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { - restorePrunedTopNode().map { recoveredTop => - new BatchAVLProver(nodeParameters.keySize, nodeParameters.valueSize, Some(recoveredTop))(hf) - } - } - private def restorePrunedTopNode(): Try[(ProverNodes[D], Int)] = Try { val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(TopNodeKey).get)(hf, store, nodeParameters) val topHeight = Ints.fromByteArray(store.get(TopNodeHeight).get) @@ -46,6 +39,15 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB top -> topHeight } + /** + * Restore prover's tree with just pruned top node + */ + def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { + restorePrunedTopNode().map { recoveredTop => + new BatchAVLProver(nodeParameters.keySize, nodeParameters.valueSize, Some(recoveredTop))(hf) + } + } + override def rollback(version: ADDigest): Try[(ProverNodes[D], Int)] = Try { if (!this.version.contains(version)) { // do not rollback to self store.rollbackTo(version) @@ -60,9 +62,6 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB def rollbackVersions: Iterable[ADDigest] = store.rollbackVersions().map(d => ADDigest @@ d) - def leafsIterator(): Iterator[(Array[Byte], Array[Byte])] = - store.getWithFilter{ case (_, v) => v.head == LeafPrefix } - override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest @@ -129,6 +128,9 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB */ private def nodeLabel(node: ProverNodes[D]): Array[Byte] = node.label + /** + * Serialize tree node (only, without possible children) + */ private def toBytes(node: ProverNodes[D]): Array[Byte] = { val builder = new mutable.ArrayBuilder.ofByte; node match { @@ -157,7 +159,7 @@ object VersionedLDBAVLStorage { private[batch] val LeafPrefix: Byte = 1: Byte /** - * Fetch tree node from database + * Fetch tree node from database by its database id (hash of node contents) * * @param dbKey - database key node of interest is stored under (hash of the node) * @param hf - hash function instance @@ -203,8 +205,10 @@ object VersionedLDBAVLStorage { } } - //todo: move to more appropriate place - def digest[D <: hash.Digest](rootNodeLabel:D, rootNodeHeight: Int): ADDigest = { + /** + * Calculate tree digest, given root node label(hash) and root node height, by appending height to the hash + */ + def digest[D <: hash.Digest](rootNodeLabel: D, rootNodeHeight: Int): ADDigest = { assert(rootNodeHeight >= 0 && rootNodeHeight < 256) // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the @@ -214,6 +218,8 @@ object VersionedLDBAVLStorage { ADDigest @@ (rootNodeLabel :+ rootNodeHeight.toByte) } - def digest[D <: hash.Digest](rootNode: Node[D], rootNodeHeight: Int): ADDigest = digest(rootNode.label, rootNodeHeight) -} + def digest[D <: hash.Digest](rootNode: Node[D], rootNodeHeight: Int): ADDigest = { + digest(rootNode.label, rootNodeHeight) + } +} diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index aa9d56d735..2791f8f03c 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -228,6 +228,9 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e Undo(versionID, key, value) } + /** + * Write versioned batch update to the database, removing keys from the database and adding new key -> value pairs + */ def update(versionID: VersionID, toRemove: TraversableOnce[Array[Byte]], toUpdate: TraversableOnce[(Array[Byte], Array[Byte])]): Try[Unit] = Try { From 98e6c978896690e3f114badb644cf98502d3f7d7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sun, 15 Jan 2023 01:37:31 +0300 Subject: [PATCH 104/204] extracting recreate to VersionedLDBAVLStorage object --- .../authds/avltree/batch/NodeParameters.scala | 11 +- .../batch/VersionedLDBAVLStorage.scala | 135 +++++++++--------- .../UtxoSetSnapshotProcessor.scala | 27 ++-- .../org/ergoplatform/settings/Constants.scala | 6 + 4 files changed, 96 insertions(+), 83 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala index 40b3237ef5..58b394af6e 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala @@ -6,4 +6,13 @@ package scorex.crypto.authds.avltree.batch * @param valueSize - size of a value in a leaf (fixed, if defined, arbitrary, if None) * @param labelSize - size of a label (node hash), fixed */ -case class NodeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) +case class NodeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) { + /** + * @return whether value is fixed-size + */ + def fixedSizeValue: Boolean = valueSize.isDefined + + // todo: move out? not looking so good here + private[batch] val TopNodeKey: Array[Byte] = Array.fill(labelSize)(123: Byte) + private[batch] val TopNodeHeight: Array[Byte] = Array.fill(labelSize)(124: Byte) +} diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 2d00d40692..332f70e716 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -1,7 +1,6 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints -import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.{InternalNodePrefix, LeafPrefix} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} import scorex.crypto.authds.{ADDigest, ADKey, ADValue, Balance} import scorex.util.encode.Base58 @@ -26,21 +25,20 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { - private def labelSize: Int = nodeParameters.labelSize - private val fixedSizeValueMode = nodeParameters.valueSize.isDefined + import VersionedLDBAVLStorage.{nodeLabel, toBytes} - private val TopNodeKey: Array[Byte] = Array.fill(labelSize)(123: Byte) - private val TopNodeHeight: Array[Byte] = Array.fill(labelSize)(124: Byte) + private val topNodeKey = nodeParameters.TopNodeKey + private val topNodeHeight = nodeParameters.TopNodeHeight private def restorePrunedTopNode(): Try[(ProverNodes[D], Int)] = Try { - val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(TopNodeKey).get)(hf, store, nodeParameters) - val topHeight = Ints.fromByteArray(store.get(TopNodeHeight).get) + val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(topNodeKey).get)(hf, store, nodeParameters) + val topHeight = Ints.fromByteArray(store.get(topNodeHeight).get) top -> topHeight } /** - * Restore prover's tree with just pruned top node + * Restore prover's tree from database with just pruned top node (so only one node is fetched) */ def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { restorePrunedTopNode().map { recoveredTop => @@ -65,7 +63,7 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest - val indexes = Seq(TopNodeKey -> nodeLabel(prover.topNode), TopNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) + val indexes = Seq(topNodeKey -> nodeLabel(prover.topNode), topNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) val toInsert = serializedVisitedNodes(prover.topNode, isTop = true) val toRemove = prover.removedNodes().map(rn => rn.label) val toUpdate = indexes ++ toInsert @@ -74,43 +72,11 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB store.update(digestWrapper, toRemove, toUpdateWithWrapped) } - def update(manifest: BatchAVLProverManifest[D], - chunks: Iterator[BatchAVLProverSubtree[D]], - additionalData: Iterator[(Array[Byte], Array[Byte])]): Try[Unit] = { - //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate - - val rootNode = manifest.root - val rootNodeHeight = manifest.rootHeight - val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) - val indices = Iterator(TopNodeKey -> nodeLabel(rootNode), TopNodeHeight -> Ints.toByteArray(rootNodeHeight)) - val nodesIterator = serializedAllNodes(manifest, chunks) - store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData) - } - - private def serializedAllNodes(manifest: BatchAVLProverManifest[D], - chunks: Iterator[BatchAVLProverSubtree[D]]): Iterator[(Array[Byte], Array[Byte])] = { - def idCollector(node: ProverNodes[D], - acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { - val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node)) - node match { - case n: ProxyInternalNode[D] if n.isEmpty => - acc ++ Iterator(pair) - case i: InternalProverNode[D] => - acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) - case _: ProverLeaf[D] => - acc ++ Iterator(pair) - } - } - - idCollector(manifest.root, Iterator.empty) ++ - chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) - } - private def serializedVisitedNodes(node: ProverNodes[D], isTop: Boolean): Array[(Array[Byte], Array[Byte])] = { // Should always serialize top node. It may not be new if it is the creation of the tree if (node.isNew || isTop) { - val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node)) + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node, nodeParameters)) node match { case n: InternalProverNode[D] => val leftSubtree = serializedVisitedNodes(n.left, isTop = false) @@ -123,31 +89,6 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB } } - /** - * Node label (hash). Used as database key for node contents - */ - private def nodeLabel(node: ProverNodes[D]): Array[Byte] = node.label - - /** - * Serialize tree node (only, without possible children) - */ - private def toBytes(node: ProverNodes[D]): Array[Byte] = { - val builder = new mutable.ArrayBuilder.ofByte; - node match { - case n: ProxyInternalNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel - case n: InternalProverNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label - case n: ProverLeaf[D] => - if (fixedSizeValueMode) { - builder += LeafPrefix ++= n.key ++= n.value ++= n.nextLeafKey - } else { - builder += LeafPrefix ++= n.key ++= Ints.toByteArray(n.value.length) ++= n.value ++= n.nextLeafKey - } - } - builder.result() - } - //todo: this method is not used, should be removed on next scrypto update? override def update(prover: BatchAVLProver[D, _]): Try[Unit] = update(prover, Nil) } @@ -205,6 +146,66 @@ object VersionedLDBAVLStorage { } } + /** + * Node label (hash). Used as database key for node contents + */ + private[batch] def nodeLabel[D <: hash.Digest](node: ProverNodes[D]): Array[Byte] = node.label + + /** + * Serialize tree node (only, without possible children) + */ + private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D], nodeParameters: NodeParameters): Array[Byte] = { + val builder = new mutable.ArrayBuilder.ofByte; + node match { + case n: ProxyInternalNode[D] => + builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel + case n: InternalProverNode[D] => + builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label + case n: ProverLeaf[D] => + if (nodeParameters.fixedSizeValue) { + builder += LeafPrefix ++= n.key ++= n.value ++= n.nextLeafKey + } else { + builder += LeafPrefix ++= n.key ++= Ints.toByteArray(n.value.length) ++= n.value ++= n.nextLeafKey + } + } + builder.result() + } + + + def recreate[D <: hash.Digest, HF <: CryptographicHash[D]](manifest: BatchAVLProverManifest[D], + chunks: Iterator[BatchAVLProverSubtree[D]], + additionalData: Iterator[(Array[Byte], Array[Byte])], + store: LDBVersionedStore, + nodeParameters: NodeParameters)(implicit hf: HF): Try[VersionedLDBAVLStorage[D, HF]] = { + //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate + + val topNodeKey = nodeParameters.TopNodeKey + val topNodeHeight = nodeParameters.TopNodeHeight + + def idCollector(node: ProverNodes[D], + acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node, nodeParameters)) + node match { + case n: ProxyInternalNode[D] if n.isEmpty => + acc ++ Iterator(pair) + case i: InternalProverNode[D] => + acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) + case _: ProverLeaf[D] => + acc ++ Iterator(pair) + } + } + + val rootNode = manifest.root + val rootNodeHeight = manifest.rootHeight + val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) + val indices = Iterator(topNodeKey -> nodeLabel(rootNode), topNodeHeight -> Ints.toByteArray(rootNodeHeight)) + val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ + chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) + store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map{_ => + new VersionedLDBAVLStorage[D, HF](store, nodeParameters) + } + } + /** * Calculate tree digest, given root node label(hash) and root node height, by appending height to the hash */ 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 846fd8b1e2..3e4c0cfd16 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 @@ -5,10 +5,9 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId -import org.ergoplatform.settings.{Algos, ErgoSettings} +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} import scorex.core.VersionTag import scorex.core.network.ConnectedPeer -import scorex.crypto.authds.avltree.batch.NodeParameters import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore @@ -218,23 +217,21 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} def createPersistentProver(stateStore: LDBVersionedStore, - blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = Try { + blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { val manifest = _manifest.get //todo: .get - val np = NodeParameters(32, None, 32) // todo: use constants - // todo: recreate database? - val ldbStorage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) log.info("Starting UTXO set snapshot transfer into state database") - //todo: form state context correctly? val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - ldbStorage.update(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator) - log.info("Finished UTXO set snapshot transfer into state database") - ldbStorage.restorePrunedProver().map { prunedAvlProver => - new PersistentBatchAVLProver[Digest32, HF] { - override var avlProver = prunedAvlProver - override val storage = ldbStorage - } - }.get //todo: .get + VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore, Constants.ErgoNodeParameters).flatMap { + ldbStorage => + log.info("Finished UTXO set snapshot transfer into state database") + ldbStorage.restorePrunedProver().map { prunedAvlProver => + new PersistentBatchAVLProver[Digest32, HF] { + override var avlProver = prunedAvlProver + override val storage = ldbStorage + } + } + } } } diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index cb54a6bff1..e4ae34edc7 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -9,6 +9,7 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSeria import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import scorex.core.serialization.ScorexSerializer import scorex.core.NodeViewModifier +import scorex.crypto.authds.avltree.batch.NodeParameters import sigmastate.Values import sigmastate.Values.ErgoTree @@ -64,4 +65,9 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 + /** + * AVL+ tree node parameters. The tree is used to authenticate UTXO set. + * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. + */ + object ErgoNodeParameters extends NodeParameters(HashLength, None, HashLength) } From 0ba48ccc8705aa60ac68013b18a690cce794e74b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 16 Jan 2023 18:10:25 +0300 Subject: [PATCH 105/204] penalizing peer for sending wrong bytes --- .../network/ErgoNodeViewSynchronizer.scala | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 4d96c825fc..3737a23240 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -855,7 +855,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } case None => - // todo: log + log.warn("No download plan found in requestMoreChunksIfNeeded") } } @@ -878,9 +878,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") } case Failure(e) => - //todo: - log.info("Cant' restore manifest from bytes ", e) - ??? + log.info(s"Cant' restore manifest (got from $remote) from bytes ", e) + penalizeMisbehavingPeer(remote) } } @@ -908,9 +907,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, requestMoreChunksIfNeeded(hr) } case Failure(e) => - //todo: - log.info("Cant' restore manifest from bytes ", e) - ??? + log.info(s"Cant' restore snapshot chunk (got from $remote) from bytes ", e) + penalizeMisbehavingPeer(remote) } } From df13affb31645df0273b81f04a18f73e17555856 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 17 Jan 2023 19:53:32 +0300 Subject: [PATCH 106/204] minor styling improvements --- .../batch/VersionedLDBAVLStorage.scala | 3 ++- .../network/ErgoNodeViewSynchronizer.scala | 14 +++++++------- .../ToDownloadProcessor.scala | 2 +- .../UtxoSetSnapshotProcessor.scala | 19 +++++++------------ ...txoSetSnapshotProcessorSpecification.scala | 2 +- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 332f70e716..5ba4ecc2f1 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -63,7 +63,8 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest - val indexes = Seq(topNodeKey -> nodeLabel(prover.topNode), topNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) + val indexes = Seq(topNodeKey -> nodeLabel(prover.topNode), + topNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) val toInsert = serializedVisitedNodes(prover.topNode, isTop = true) val toRemove = prover.removedNodes().map(rn => rn.label) val toUpdate = indexes ++ toInsert diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 3737a23240..65b6160be3 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -839,12 +839,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { - hr.getUtxoSetSnapshotDownloadPlan() match { + hr.utxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => if (downloadPlan.downloadingChunks < 16) { (1 to 4).foreach { _ => val toRequest = hr.getChunkIdsToDownload(4) - hr.getRandomPeerToDownloadChunks() match { + hr.randomPeerToDownloadChunks() match { case Some(remote) => toRequest.foreach { subtreeId => requestUtxoSetChunk(subtreeId, remote) @@ -859,7 +859,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer) = { + protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { //todo : check that manifestBytes were requested val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { @@ -894,7 +894,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? hr.registerDownloadedChunk(subtree.id, serializedChunk) - val downloadPlanOpt = hr.getUtxoSetSnapshotDownloadPlan() // todo: match for optional result + val downloadPlanOpt = hr.utxoSetSnapshotDownloadPlan() // todo: match for optional result if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { if (!hr.isUtxoSnapshotApplied) { val h = downloadPlanOpt.get.snapshotHeight // todo: .get @@ -1101,7 +1101,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val maxDeliveryChecks = networkSettings.maxDeliveryChecks if (checksDone < maxDeliveryChecks) { if (modifierTypeId == UtxoSnapshotChunkTypeId.value) { - val newPeerOpt = hr.getRandomPeerToDownloadChunks() + val newPeerOpt = hr.randomPeerToDownloadChunks() log.info(s"Rescheduling request for UTXO set chunk $modifierId , new peer $newPeerOpt") deliveryTracker.setUnknown(modifierId, modifierTypeId) newPeerOpt match { @@ -1186,12 +1186,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { - val MinSnapshots = 2 //todo: set to 3 after testing + val MinSnapshots = 2 //todo: set to 3 after testing, or move to settings? if (settings.nodeSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && - historyReader.getUtxoSetSnapshotDownloadPlan().isEmpty) { + historyReader.utxoSetSnapshotDownloadPlan().isEmpty) { val res = availableManifests.filter { case (h, records) => if (records.length >= MinSnapshots) { val idsSet = records.map(_._2).map(Base16.encode).toSet 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 aa62972fc0..022572d2d2 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 @@ -88,7 +88,7 @@ trait ToDownloadProcessor continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => // todo: can be requested multiple times, prevent it - if (getUtxoSetSnapshotDownloadPlan().isEmpty) { + if (utxoSetSnapshotDownloadPlan().isEmpty) { Map(SnapshotsInfoTypeId.value -> Seq.empty) } else { Map.empty 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 3e4c0cfd16..24e2d49c7c 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 @@ -14,8 +14,8 @@ import scorex.db.LDBVersionedStore import scorex.util.{ByteArrayBuilder, ModifierId, ScorexLogging} import scorex.util.serialization.VLQByteBufferWriter import spire.syntax.all.cfor - -import scala.util.Random +import scala.util.{Try, Random} +import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} /** * Stores: @@ -61,11 +61,10 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) _manifest = Some(manifest) _peersToDownload = peersToDownload - println(_manifest.get.id) updateUtxoSetSnashotDownloadPlan(plan) } - def getUtxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { + def utxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { _cachedDownloadPlan match { case s@Some(_) => s case None => None @@ -79,7 +78,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } - def getRandomPeerToDownloadChunks(): Option[ConnectedPeer] = { + def randomPeerToDownloadChunks(): Option[ConnectedPeer] = { if (_peersToDownload.nonEmpty) { Some(_peersToDownload(Random.nextInt(_peersToDownload.size))) } else { @@ -88,7 +87,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } def getChunkIdsToDownload(howMany: Int): Seq[SubtreeId] = { - getUtxoSetSnapshotDownloadPlan() match { + utxoSetSnapshotDownloadPlan() match { case Some(plan) => val expected = plan.expectedChunkIds val downloadIndex = plan.downloadedChunkIds.size @@ -111,7 +110,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } def registerDownloadedChunk(chunkId: Array[Byte], chunkSerialized: Array[Byte]): Unit = { - getUtxoSetSnapshotDownloadPlan() match { + utxoSetSnapshotDownloadPlan() match { case Some(plan) => cfor(0)(_ < plan.downloadedChunkIds.size, _ + 1) { idx => if (!plan.downloadedChunkIds(idx) && plan.expectedChunkIds(idx).sameElements(chunkId)) { @@ -130,7 +129,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } def downloadedChunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { - getUtxoSetSnapshotDownloadPlan() match { + utxoSetSnapshotDownloadPlan() match { case Some(plan) => Iterator.range(0, plan.totalChunks).flatMap{idx => val idxBytes = Ints.toByteArray(idx) @@ -212,10 +211,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } }*/ - - import scala.util.Try - import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} - def createPersistentProver(stateStore: LDBVersionedStore, blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { val manifest = _manifest.get //todo: .get diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index fb32453e9c..6f79f751b4 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -32,7 +32,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val snapshotHeight = 1 val blockId = ModifierId @@ Algos.encode(Array.fill(32)(Random.nextInt(100).toByte)) utxoSetSnapshotProcessor.registerManifestToDownload(manifest, snapshotHeight, Seq.empty) - val dp = utxoSetSnapshotProcessor.getUtxoSetSnapshotDownloadPlan().get + val dp = utxoSetSnapshotProcessor.utxoSetSnapshotDownloadPlan().get dp.snapshotHeight shouldBe snapshotHeight val subtreeIds = subtrees.map(s => ModifierId @@ Algos.encode(s.id)) val expected = dp.expectedChunkIds.map(id => ModifierId @@ Algos.encode(id)) From bdac932f8bb7438fb8abf3459f95438161eb4e6c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 24 Jan 2023 19:51:58 +0300 Subject: [PATCH 107/204] NetworkObjectTypeId -> ModifierTypeId, ScalaDoc --- .../bench/misc/ModifierWriter.scala | 8 +- .../modifiers/ErgoFullBlock.scala | 4 +- .../modifiers/ModifierTypeId.scala | 48 ----------- .../modifiers/NetworkObjectTypeId.scala | 84 +++++++++++++++++++ .../modifiers/NonHeaderBlockSection.scala | 4 +- .../modifiers/history/ADProofs.scala | 6 +- .../modifiers/history/BlockTransactions.scala | 6 +- .../history/extension/Extension.scala | 6 +- .../modifiers/history/header/Header.scala | 10 +-- .../modifiers/mempool/ErgoTransaction.scala | 4 +- .../network/ErgoNodeViewSynchronizer.scala | 38 ++++----- .../nodeView/ErgoNodeViewHolder.scala | 6 +- .../nodeView/history/ErgoHistoryReader.scala | 6 +- .../history/storage/HistoryStorage.scala | 6 +- .../ToDownloadProcessor.scala | 12 +-- .../org/ergoplatform/settings/Constants.scala | 4 +- .../settings/ValidationRules.scala | 8 +- .../tools/ValidationRulesPrinter.scala | 6 +- .../scala/scorex/core/NodeViewModifier.scala | 4 +- .../scorex/core/consensus/ProgressInfo.scala | 4 +- src/main/scala/scorex/core/core.scala | 6 +- .../scorex/core/network/DeliveryTracker.scala | 32 +++---- .../network/message/BasicMessagesRepo.scala | 10 +-- .../scorex/core/transaction/Transaction.scala | 4 +- .../core/validation/ModifierError.scala | 10 +-- .../core/validation/ModifierValidator.scala | 28 +++---- .../core/validation/ValidationSettings.scala | 4 +- .../VerifyNonADHistorySpecification.scala | 4 +- .../core/network/DeliveryTrackerSpec.scala | 4 +- .../testkit/generators/ObjectGenerators.scala | 8 +- 30 files changed, 210 insertions(+), 174 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala create mode 100644 src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala index d6195b52d2..1372cab088 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala @@ -3,7 +3,7 @@ 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, ModifierTypeId} +import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import scorex.core.serialization.ScorexSerializer @@ -11,7 +11,7 @@ import scorex.core.NodeViewModifier object ModifierWriter { - val modifierSerializers: Map[ModifierTypeId.Value, ScorexSerializer[_ <: BlockSection]] = + val modifierSerializers: Map[NetworkObjectTypeId.Value, ScorexSerializer[_ <: BlockSection]] = Map(Header.modifierTypeId -> HeaderSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, ADProofs.modifierTypeId -> ADProofsSerializer) @@ -33,9 +33,9 @@ object ModifierWriter { mod <- modifierSerializers(typeId).parseBytesTry(bytes).toOption } yield mod - private def readModId(implicit fis: InputStream): Option[ModifierTypeId.Value] = { + private def readModId(implicit fis: InputStream): Option[NetworkObjectTypeId.Value] = { val int = fis.read() - if (int == -1) { None } else { Some(ModifierTypeId.fromByte(int.toByte)) } + if (int == -1) { None } else { Some(NetworkObjectTypeId.fromByte(int.toByte)) } } } diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 3ea34c76f5..6fb1a0ff43 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -20,7 +20,7 @@ case class ErgoFullBlock(header: Header, override type M = ErgoFullBlock - override val modifierTypeId: ModifierTypeId.Value = ErgoFullBlock.modifierTypeId + override val modifierTypeId: NetworkObjectTypeId.Value = ErgoFullBlock.modifierTypeId override def serializedId: Array[Byte] = header.serializedId @@ -49,7 +49,7 @@ case class ErgoFullBlock(header: Header, object ErgoFullBlock extends ApiCodecs { - val modifierTypeId: ModifierTypeId.Value = FullBlockTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = FullBlockTypeId.value implicit val jsonEncoder: Encoder[ErgoFullBlock] = { b: ErgoFullBlock => Json.obj( diff --git a/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala deleted file mode 100644 index 13d6d8c238..0000000000 --- a/src/main/scala/org/ergoplatform/modifiers/ModifierTypeId.scala +++ /dev/null @@ -1,48 +0,0 @@ -package org.ergoplatform.modifiers - -import supertagged.TaggedType -import ModifierTypeId._ - -sealed trait ModifierTypeId { - val value: ModifierTypeId.Value -} - -object ModifierTypeId { - object Value extends TaggedType[Byte] - type Value = Value.Type - - @inline - def fromByte(value: Byte): Value = Value @@ value -} - -object TransactionTypeId extends ModifierTypeId { - override val value: Value = fromByte(2) -} - -object HeaderTypeId extends ModifierTypeId { - override val value: Value = fromByte(101) -} - -object BlockTransactionsTypeId extends ModifierTypeId { - override val value: Value = fromByte(102) -} - -object ProofsTypeId extends ModifierTypeId { - override val value: Value = fromByte(104) -} - -object ExtensionTypeId extends ModifierTypeId { - override val value: Value = fromByte(108) -} - -object FullBlockTypeId extends ModifierTypeId { - override val value: Value = fromByte(-127) -} - -object UtxoSnapshotChunkTypeId extends ModifierTypeId { - override val value: Value = fromByte(-126) -} - -object SnapshotsInfoTypeId extends ModifierTypeId { - override val value: Value = fromByte(-125) -} diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala new file mode 100644 index 0000000000..c37b2544f5 --- /dev/null +++ b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala @@ -0,0 +1,84 @@ +package org.ergoplatform.modifiers + +import supertagged.TaggedType +import NetworkObjectTypeId._ + +/** + * Hierarchy encoding blockchain objects being sent over wire: block sections, chunks of UTXO set snapshot etc + */ +sealed trait NetworkObjectTypeId { + /** + * 1-byte ID of network object type + */ + val value: NetworkObjectTypeId.Value +} + +object NetworkObjectTypeId { + object Value extends TaggedType[Byte] + type Value = Value.Type + + @inline + def fromByte(value: Byte): Value = Value @@ value +} + +/** + * Unconfirmed transactions sent outside blocks + */ +object TransactionTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(2) +} + +/** + * Block header, section of a block PoW is done on top of. This section is committing to other sections + */ +object HeaderTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(101) +} + +/** + * Block transactions sections. Contains all the transactions for a block. + */ +object BlockTransactionsTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(102) +} + +/** + * Block section which contains proofs of correctness for UTXO set transformations. + * The section contains proofs for all the transformations (i.e. for all the block transactions) + */ +object ProofsTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(104) +} + + +/** + * Block section which contains key-value pairs with different additional data. + * Interlinks vector (for nipopow proofs) written there, as well as current network parameters + * (at the beginning of voting epoch), but miners can also put arbitrary data there. + */ +object ExtensionTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(108) +} + +/** + * Virtual object which is not being sent over the wire rather, constructed locally from different sections + * got over the wire (header, transactions, extension in the "utxo" mode, those three sections plus proofs in + * the "digest" mode). + */ +object FullBlockTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(-127) +} + +/** + * Not a block section, but a chunk of UTXO set + */ +object UtxoSnapshotChunkTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(-126) +} + +/** + * Not a block section, but registry of UTXO set snapshots available + */ +object SnapshotsInfoTypeId extends NetworkObjectTypeId { + override val value: Value = fromByte(-125) +} diff --git a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala index c3041cb3c8..5a18374056 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NonHeaderBlockSection.scala @@ -22,9 +22,9 @@ trait NonHeaderBlockSection extends BlockSection { } object NonHeaderBlockSection { - def computeId(modifierType: ModifierTypeId.Value, headerId: ModifierId, digest: Array[Byte]): ModifierId = + def computeId(modifierType: NetworkObjectTypeId.Value, headerId: ModifierId, digest: Array[Byte]): ModifierId = bytesToId(computeIdBytes(modifierType, headerId, digest)) - def computeIdBytes(modifierType: ModifierTypeId.Value, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = + def computeIdBytes(modifierType: NetworkObjectTypeId.Value, headerId: ModifierId, digest: Array[Byte]): Array[Byte] = Algos.hash.prefixedHash(modifierType, idToBytes(headerId), digest) } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala index a5979398b2..dd3dc69966 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.{ModifierTypeId, NonHeaderBlockSection, ProofsTypeId} +import org.ergoplatform.modifiers.{NetworkObjectTypeId, NonHeaderBlockSection, ProofsTypeId} import org.ergoplatform.modifiers.state._ import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, Constants} @@ -23,7 +23,7 @@ case class ADProofs(headerId: ModifierId, override def digest: Digest32 = ADProofs.proofDigest(proofBytes) - override val modifierTypeId: ModifierTypeId.Value = ADProofs.modifierTypeId + override val modifierTypeId: NetworkObjectTypeId.Value = ADProofs.modifierTypeId override type M = ADProofs @@ -69,7 +69,7 @@ case class ADProofs(headerId: ModifierId, object ADProofs extends ApiCodecs { - val modifierTypeId: ModifierTypeId.Value = ProofsTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = ProofsTypeId.value val KL = 32 diff --git a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala index e4a436e311..12ce33b24d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.{BlockTransactionsTypeId, ModifierTypeId, NonHeaderBlockSection} +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 @@ -37,7 +37,7 @@ case class BlockTransactions(headerId: ModifierId, assert(txs.nonEmpty, "Block should always contain at least 1 coinbase-like transaction") - override val modifierTypeId: ModifierTypeId.Value = BlockTransactions.modifierTypeId + override val modifierTypeId: NetworkObjectTypeId.Value = BlockTransactions.modifierTypeId /** * Ids of block transactions @@ -94,7 +94,7 @@ case class BlockTransactions(headerId: ModifierId, object BlockTransactions extends ApiCodecs { - val modifierTypeId: ModifierTypeId.Value = BlockTransactionsTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = BlockTransactionsTypeId.value // Used in the miner when a BlockTransaction instance is not generated yet (because a header is not known) def transactionsRoot(txs: Seq[ErgoTransaction], blockVersion: Version): Digest32 = { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index bb9e884cde..2b5e03b2f8 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala @@ -4,7 +4,7 @@ import com.google.common.primitives.Bytes import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.modifiers.{ExtensionTypeId, ModifierTypeId, NonHeaderBlockSection} +import org.ergoplatform.modifiers.{ExtensionTypeId, NetworkObjectTypeId, NonHeaderBlockSection} import org.ergoplatform.settings.Algos import scorex.core.serialization.ScorexSerializer import scorex.crypto.authds.LeafData @@ -24,7 +24,7 @@ case class Extension(headerId: ModifierId, override val sizeOpt: Option[Int] = None) extends ExtensionCandidate(fields) with NonHeaderBlockSection { - override val modifierTypeId: ModifierTypeId.Value = Extension.modifierTypeId + override val modifierTypeId: NetworkObjectTypeId.Value = Extension.modifierTypeId override type M = Extension @@ -54,7 +54,7 @@ object Extension extends ApiCodecs { Algos.merkleTree(LeafData @@ fields.map(kvToLeaf)) } - val modifierTypeId: ModifierTypeId.Value = ExtensionTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = ExtensionTypeId.value implicit val jsonEncoder: Encoder[Extension] = { e: Extension => Map( diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 16fed997ed..25a39f2f9b 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -7,7 +7,7 @@ import org.ergoplatform.mining.difficulty.RequiredDifficulty import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions, PreHeader} -import org.ergoplatform.modifiers.{BlockSection, HeaderTypeId, ModifierTypeId, NonHeaderBlockSection} +import org.ergoplatform.modifiers.{BlockSection, HeaderTypeId, NetworkObjectTypeId, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.{Algos, Constants} @@ -59,7 +59,7 @@ case class Header(override val version: Header.Version, override type M = Header - override val modifierTypeId: ModifierTypeId.Value = Header.modifierTypeId + override val modifierTypeId: NetworkObjectTypeId.Value = Header.modifierTypeId lazy val requiredDifficulty: Difficulty = RequiredDifficulty.decodeCompactBits(nBits) @@ -74,7 +74,7 @@ case class Header(override val version: Header.Version, /** * Expected identifiers of the block sections corresponding to this header */ - lazy val sectionIds: Seq[(ModifierTypeId.Value, ModifierId)] = + lazy val sectionIds: Seq[(NetworkObjectTypeId.Value, ModifierId)] = Array( (ADProofs.modifierTypeId, ADProofsId), (BlockTransactions.modifierTypeId, transactionsId), @@ -85,7 +85,7 @@ case class Header(override val version: Header.Version, * Expected identifiers of the block sections corresponding to this header, * except of state transformations proof section id */ - lazy val sectionIdsWithNoProof: Seq[(ModifierTypeId.Value, ModifierId)] = sectionIds.tail + lazy val sectionIdsWithNoProof: Seq[(NetworkObjectTypeId.Value, ModifierId)] = sectionIds.tail override lazy val toString: String = s"Header(${this.asJson.noSpaces})" @@ -156,7 +156,7 @@ object Header extends ApiCodecs { votes = header.votes.toColl ) - val modifierTypeId: ModifierTypeId.Value = HeaderTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = HeaderTypeId.value lazy val GenesisParentId: ModifierId = bytesToId(Array.fill(Constants.HashLength)(0: Byte)) diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 30d8e09802..983205c8ed 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -6,7 +6,7 @@ import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} import org.ergoplatform._ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.modifiers.{ErgoNodeViewModifier, ModifierTypeId, TransactionTypeId} +import org.ergoplatform.modifiers.{ErgoNodeViewModifier, NetworkObjectTypeId, TransactionTypeId} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction.unresolvedIndices import org.ergoplatform.nodeView.ErgoContext @@ -474,7 +474,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], object ErgoTransaction extends ApiCodecs with ScorexLogging with ScorexEncoding { - val modifierTypeId: ModifierTypeId.Value = TransactionTypeId.value + val modifierTypeId: NetworkObjectTypeId.Value = TransactionTypeId.value def apply(inputs: IndexedSeq[Input], outputCandidates: IndexedSeq[ErgoBoxCandidate]): ErgoTransaction = ErgoTransaction(inputs, IndexedSeq.empty, outputCandidates, None) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 65b6160be3..89ed17e1ae 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -4,7 +4,7 @@ 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.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} -import org.ergoplatform.modifiers.{BlockSection, ModifierTypeId, SnapshotsInfoTypeId, TransactionTypeId, UtxoSnapshotChunkTypeId} +import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId, SnapshotsInfoTypeId, TransactionTypeId, UtxoSnapshotChunkTypeId} import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.history._ import ErgoNodeViewSynchronizer.{CheckModifiersToDownload, IncomingTxInfo, TransactionProcessingCacheRecord} @@ -266,7 +266,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, context.system.scheduler.scheduleAtFixedRate(healthCheckDelay, healthCheckRate, viewHolderRef, IsChainHealthy)(ex, self) } - protected def broadcastModifierInv(modTypeId: ModifierTypeId.Value, modId: ModifierId): Unit = { + protected def broadcastModifierInv(modTypeId: NetworkObjectTypeId.Value, modId: ModifierId): Unit = { val msg = Message(InvSpec, Right(InvData(modTypeId, Seq(modId))), None) networkControllerRef ! SendToNetwork(msg, Broadcast) } @@ -280,7 +280,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * (in history database available via `historyReader` interface, or delivery tracker cache, thus * downloading of the modifier is needed. */ - private def downloadRequired(historyReader: ErgoHistory)(modifierTypeId: ModifierTypeId.Value, id: ModifierId): Boolean = { + private def downloadRequired(historyReader: ErgoHistory)(modifierTypeId: NetworkObjectTypeId.Value, id: ModifierId): Boolean = { deliveryTracker.status(id, modifierTypeId, Array(historyReader)) == ModifiersStatus.Unknown } @@ -350,7 +350,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // Send history extension to the (less developed) peer 'remote' which does not have it. def sendExtension(remote: ConnectedPeer, - ext: Seq[(ModifierTypeId.Value, ModifierId)]): Unit = { + ext: Seq[(NetworkObjectTypeId.Value, ModifierId)]): Unit = { ext.groupBy(_._1).mapValues(_.map(_._2)).foreach { case (mid, mods) => networkControllerRef ! SendToNetwork(Message(InvSpec, Right(InvData(mid, mods)), None), SendToPeer(remote)) @@ -535,7 +535,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * (non-zero if we're re-requesting the block section, in this case, there should be only * one id to request in `modifierIds` */ - def requestBlockSection(modifierTypeId: ModifierTypeId.Value, + def requestBlockSection(modifierTypeId: NetworkObjectTypeId.Value, modifierIds: Seq[ModifierId], peer: ConnectedPeer, checksDone: Int = 0): Unit = { @@ -574,7 +574,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def onDownloadRequest(historyReader: ErgoHistory): Receive = { - case DownloadRequest(modifiersToFetch: Map[ModifierTypeId.Value, Seq[ModifierId]]) => + case DownloadRequest(modifiersToFetch: Map[NetworkObjectTypeId.Value, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") if(modifiersToFetch.nonEmpty) { requestDownload( @@ -609,7 +609,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ protected def requestDownload(maxModifiers: Int, minModifiersPerBucket: Int, maxModifiersPerBucket: Int) (getPeersOpt: => Option[Iterable[ConnectedPeer]]) - (fetchMax: Int => Map[ModifierTypeId.Value, Seq[ModifierId]]): Unit = + (fetchMax: Int => Map[NetworkObjectTypeId.Value, Seq[ModifierId]]): Unit = getPeersOpt .foreach { peers => val peersCount = peers.size @@ -662,7 +662,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private def blockSectionsFromRemote(hr: ErgoHistory, - typeId: ModifierTypeId.Value, + typeId: NetworkObjectTypeId.Value, requestedModifiers: Map[ModifierId, Array[Byte]], remote: ConnectedPeer): Unit = { Constants.modifierSerializers.get(typeId) match { @@ -749,7 +749,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return collection of parsed modifiers */ def parseModifiers[M <: NodeViewModifier](modifiers: Map[ModifierId, Array[Byte]], - modifierTypeId: ModifierTypeId.Value, + modifierTypeId: NetworkObjectTypeId.Value, serializer: ScorexSerializer[M], remote: ConnectedPeer): Iterable[M] = { modifiers.flatMap { case (id, bytes) => @@ -774,7 +774,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * @return ids and bytes of modifiers that were requested by our node */ def processSpam(remote: ConnectedPeer, - typeId: ModifierTypeId.Value, + typeId: NetworkObjectTypeId.Value, modifiers: Map[ModifierId, Array[Byte]], blockAppliedTxsCache: FixedSizeApproximateCacheQueue): Map[ModifierId, Array[Byte]] = { val modifiersByStatus = @@ -1005,11 +1005,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, //other node asking for objects by their ids protected def modifiersReq(hr: ErgoHistory, mp: ErgoMemPool, invData: InvData, remote: ConnectedPeer): Unit = { val objs: Seq[(ModifierId, Array[Byte])] = invData.typeId match { - case typeId: ModifierTypeId if typeId == ErgoTransaction.modifierTypeId => + case typeId: NetworkObjectTypeId if typeId == ErgoTransaction.modifierTypeId => mp.getAll(invData.ids).map { unconfirmedTx => unconfirmedTx.transaction.id -> unconfirmedTx.transactionBytes.getOrElse(unconfirmedTx.transaction.bytes) } - case expectedTypeId: ModifierTypeId => + case expectedTypeId: NetworkObjectTypeId => invData.ids.flatMap { id => hr.modifierTypeAndBytesById(id).flatMap { case (mTypeId, bytes) => if (mTypeId == expectedTypeId) { @@ -1476,7 +1476,7 @@ object ErgoNodeViewSynchronizer { // getLocalSyncInfo messages case object SendLocalSyncInfo - case class ResponseFromLocal(source: ConnectedPeer, modifierTypeId: ModifierTypeId, localObjects: Seq[(ModifierId, Array[Byte])]) + case class ResponseFromLocal(source: ConnectedPeer, modifierTypeId: NetworkObjectTypeId, localObjects: Seq[(ModifierId, Array[Byte])]) /** * Check delivery of modifier with type `modifierTypeId` and id `modifierId`. @@ -1485,7 +1485,7 @@ object ErgoNodeViewSynchronizer { * */ case class CheckDelivery(source: ConnectedPeer, - modifierTypeId: ModifierTypeId.Value, + modifierTypeId: NetworkObjectTypeId.Value, modifierId: ModifierId) trait PeerManagerEvent @@ -1532,19 +1532,19 @@ object ErgoNodeViewSynchronizer { */ case class FailedOnRecheckTransaction(id : ModifierId, error: Throwable) extends ModificationOutcome - case class RecoverableFailedModification(typeId: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + case class RecoverableFailedModification(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome - case class SyntacticallyFailedModification(typeId: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + 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: ModifierTypeId.Value, modifierId: ModifierId, error: Throwable) extends ModificationOutcome + 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: ModifierTypeId.Value, modifierId: ModifierId) extends ModificationOutcome + case class SyntacticallySuccessfulModifier(typeId: NetworkObjectTypeId.Value, modifierId: ModifierId) extends ModificationOutcome /** * Signal sent by node view holder when a full block is applied to state @@ -1560,7 +1560,7 @@ object ErgoNodeViewSynchronizer { */ case class BlockSectionsProcessingCacheUpdate(headersCacheSize: Int, blockSectionsCacheSize: Int, - cleared: (ModifierTypeId.Value, Seq[ModifierId])) + cleared: (NetworkObjectTypeId.Value, Seq[ModifierId])) /** * Command to re-check mempool to clean transactions become invalid while sitting in the mempool up diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index f44b1f5391..9e7a87185a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -6,7 +6,7 @@ import org.ergoplatform.ErgoApp import org.ergoplatform.ErgoApp.CriticalSystemException import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ModifierTypeId} +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 @@ -137,7 +137,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti //TODO: actually, pi.toDownload contains only 1 modifierid per type, //TODO: see the only case where toDownload is not empty during ProgressInfo construction //TODO: so the code below can be optimized - val toDownload = mutable.Map[ModifierTypeId.Value, Seq[ModifierId]]() + val toDownload = mutable.Map[NetworkObjectTypeId.Value, Seq[ModifierId]]() pi.toDownload.foreach { case (tid, mid) => toDownload.put(tid, toDownload.getOrElse(tid, Seq()) :+ mid) } @@ -738,7 +738,7 @@ object ErgoNodeViewHolder { case class BlockAppliedTransactions(txs: Seq[ModifierId]) extends NodeViewHolderEvent - case class DownloadRequest(modifiersToFetch: Map[ModifierTypeId.Value, Seq[ModifierId]]) extends NodeViewHolderEvent + case class DownloadRequest(modifiersToFetch: Map[NetworkObjectTypeId.Value, Seq[ModifierId]]) extends NodeViewHolderEvent case class CurrentView[State](history: ErgoHistory, state: State, vault: ErgoWallet, pool: ErgoMemPool) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index d2bf72c4db..e4087216ad 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -4,7 +4,7 @@ import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.{Header, PreGenesisHeader} import org.ergoplatform.modifiers.history.popow.{NipopowAlgos, NipopowProof, PoPowHeader, PoPowParams} -import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, ModifierTypeId, NonHeaderBlockSection} +import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock, NetworkObjectTypeId, NonHeaderBlockSection} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.storage._ import org.ergoplatform.nodeView.history.storage.modifierprocessors._ @@ -30,7 +30,7 @@ trait ErgoHistoryReader with ScorexLogging with ScorexEncoding { - type ModifierIds = Seq[(ModifierTypeId.Value, ModifierId)] + type ModifierIds = Seq[(NetworkObjectTypeId.Value, ModifierId)] protected[history] val historyStorage: HistoryStorage @@ -73,7 +73,7 @@ trait ErgoHistoryReader * @param id - modifier id * @return type and raw bytes of semantically valid ErgoPersistentModifier with the given id it is in history */ - def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId.Value, Array[Byte])] = + def modifierTypeAndBytesById(id: ModifierId): Option[(NetworkObjectTypeId.Value, Array[Byte])] = if (isSemanticallyValid(id) != ModifierSemanticValidity.Invalid) { historyStorage.modifierTypeAndBytesById(id) } else { 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 29a1ed52f3..1bdde6cce4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.history.storage import com.github.benmanes.caffeine.cache.Caffeine -import org.ergoplatform.modifiers.{BlockSection, ModifierTypeId} +import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId} import org.ergoplatform.modifiers.history.HistoryModifierSerializer import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.{Algos, CacheSettings, ErgoSettings} @@ -55,8 +55,8 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, c objectsStore.get(idToBytes(id)).map(_.tail) // removing modifier type byte with .tail } - def modifierTypeAndBytesById(id: ModifierId): Option[(ModifierTypeId.Value, Array[Byte])] = { - objectsStore.get(idToBytes(id)).map(bs => (ModifierTypeId.fromByte(bs.head), bs.tail)) // first byte is type id, tail is modifier bytes + def modifierTypeAndBytesById(id: ModifierId): Option[(NetworkObjectTypeId.Value, Array[Byte])] = { + objectsStore.get(idToBytes(id)).map(bs => (NetworkObjectTypeId.fromByte(bs.head), bs.tail)) // first byte is type id, tail is modifier bytes } def modifierById(id: ModifierId): Option[BlockSection] = 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 022572d2d2..776cd80ea0 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 @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId, SnapshotsInfoTypeId} +import org.ergoplatform.modifiers.{ErgoFullBlock, NetworkObjectTypeId, SnapshotsInfoTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.{ChainSettings, ErgoSettings, NodeConfigurationSettings} @@ -43,7 +43,7 @@ trait ToDownloadProcessor * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition */ def nextModifiersToDownload(howManyPerType: Int, - condition: (ModifierTypeId.Value, ModifierId) => Boolean): Map[ModifierTypeId.Value, Seq[ModifierId]] = { + condition: (NetworkObjectTypeId.Value, ModifierId) => Boolean): Map[NetworkObjectTypeId.Value, Seq[ModifierId]] = { val FullBlocksToDownloadAhead = 192 // how many full blocks to download forwards during active sync @@ -51,8 +51,8 @@ trait ToDownloadProcessor @tailrec def continuation(height: Int, - acc: Map[ModifierTypeId.Value, Vector[ModifierId]], - maxHeight: Int): Map[ModifierTypeId.Value, Vector[ModifierId]] = { + acc: Map[NetworkObjectTypeId.Value, Vector[ModifierId]], + maxHeight: Int): Map[NetworkObjectTypeId.Value, Vector[ModifierId]] = { if (height > maxHeight) { acc } else { @@ -104,7 +104,7 @@ trait ToDownloadProcessor /** * Checks whether it's time to download full chain, and returns toDownload modifiers */ - protected def toDownload(header: Header): Seq[(ModifierTypeId.Value, ModifierId)] = { + protected def toDownload(header: Header): Seq[(NetworkObjectTypeId.Value, ModifierId)] = { if (!nodeSettings.verifyTransactions) { // A regime that do not download and verify transaction Nil @@ -121,7 +121,7 @@ trait ToDownloadProcessor } } - def requiredModifiersForHeader(h: Header): Seq[(ModifierTypeId.Value, ModifierId)] = { + def requiredModifiersForHeader(h: Header): Seq[(NetworkObjectTypeId.Value, ModifierId)] = { if (!nodeSettings.verifyTransactions) { Nil } else if (nodeSettings.stateType.requireProofs) { diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index e4ae34edc7..c62f8562f9 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -1,7 +1,7 @@ package org.ergoplatform.settings import org.ergoplatform.mining.difficulty.RequiredDifficulty -import org.ergoplatform.modifiers.ModifierTypeId +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} @@ -45,7 +45,7 @@ object Constants { // Number of last block headers available is scripts from ErgoStateContext val LastHeadersInContext = 10 - val modifierSerializers: Map[ModifierTypeId.Value, ScorexSerializer[_ <: NodeViewModifier]] = + val modifierSerializers: Map[NetworkObjectTypeId.Value, ScorexSerializer[_ <: NodeViewModifier]] = Map(Header.modifierTypeId -> HeaderSerializer, Extension.modifierTypeId -> ExtensionSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, diff --git a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala b/src/main/scala/org/ergoplatform/settings/ValidationRules.scala index d499775db1..49d516c65f 100644 --- a/src/main/scala/org/ergoplatform/settings/ValidationRules.scala +++ b/src/main/scala/org/ergoplatform/settings/ValidationRules.scala @@ -1,7 +1,7 @@ package org.ergoplatform.settings import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} -import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId} +import org.ergoplatform.modifiers.{ErgoFullBlock, NetworkObjectTypeId} import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} @@ -305,7 +305,7 @@ object ValidationRules { val fbDigestIncorrect: Short = 501 - def errorMessage(id: Short, details: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): String = { + def errorMessage(id: Short, details: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): String = { ValidationRules.rulesSpec(id) .invalidMod(InvalidModifier(details, modifierId, modifierTypeId)) .errors @@ -313,10 +313,10 @@ object ValidationRules { .message } - private def recoverable(errorMessage: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + private def recoverable(errorMessage: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = ModifierValidator.error(errorMessage, modifierId, modifierTypeId) - private def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + private def fatal(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = ModifierValidator.fatal(error, modifierId, modifierTypeId) } diff --git a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala index 2685d84c9c..ed668f4904 100644 --- a/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala +++ b/src/main/scala/org/ergoplatform/tools/ValidationRulesPrinter.scala @@ -1,6 +1,6 @@ package org.ergoplatform.tools -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.settings.ValidationRules import scorex.core.validation.InvalidModifier import scorex.util.{ModifierId, ScorexLogging, bytesToId} @@ -14,7 +14,7 @@ object ValidationRulesPrinter extends App with ScorexLogging { printHeader() rules.toSeq.sortBy(_._1).foreach { r => - val rule = r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId.fromByte(0))).errors.head.message.trim + val rule = r._2.invalidMod(InvalidModifier("", emptyModifierId, NetworkObjectTypeId.fromByte(0))).errors.head.message.trim val activated = r._2.isActive val mayBeDisabled = r._2.mayBeDisabled val modifiers = r._2.affectedClasses.map(_.getSimpleName).mkString(", ") @@ -37,7 +37,7 @@ object ValidationRulesPrinter extends App with ScorexLogging { printHeader() } - if (r._2.invalidMod(InvalidModifier("", emptyModifierId, ModifierTypeId.fromByte(0))).isFatal) { + if (r._2.invalidMod(InvalidModifier("", emptyModifierId, NetworkObjectTypeId.fromByte(0))).isFatal) { // we only mention fatal errors here println(s" ${r._1} & $rule & ${boolToLatex(mayBeDisabled)} & ${boolToLatex(activated)} & $modifiers \\\\") diff --git a/src/main/scala/scorex/core/NodeViewModifier.scala b/src/main/scala/scorex/core/NodeViewModifier.scala index eb5622d58f..8e3be7e142 100644 --- a/src/main/scala/scorex/core/NodeViewModifier.scala +++ b/src/main/scala/scorex/core/NodeViewModifier.scala @@ -1,13 +1,13 @@ package scorex.core -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.serialization.BytesSerializable import scorex.core.utils.ScorexEncoding sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {self => - val modifierTypeId: ModifierTypeId.Value + val modifierTypeId: NetworkObjectTypeId.Value //todo: check statically or dynamically output size def id: scorex.util.ModifierId diff --git a/src/main/scala/scorex/core/consensus/ProgressInfo.scala b/src/main/scala/scorex/core/consensus/ProgressInfo.scala index 713abb06a5..c292b3760f 100644 --- a/src/main/scala/scorex/core/consensus/ProgressInfo.scala +++ b/src/main/scala/scorex/core/consensus/ProgressInfo.scala @@ -1,6 +1,6 @@ package scorex.core.consensus -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.core.utils.ScorexEncoder import scorex.core.PersistentNodeViewModifier import scorex.util.ModifierId @@ -17,7 +17,7 @@ import scorex.util.ModifierId case class ProgressInfo[PM <: PersistentNodeViewModifier](branchPoint: Option[ModifierId], toRemove: Seq[PM], toApply: Seq[PM], - toDownload: Seq[(ModifierTypeId.Value, ModifierId)]) + toDownload: Seq[(NetworkObjectTypeId.Value, ModifierId)]) (implicit encoder: ScorexEncoder) { if (toRemove.nonEmpty) diff --git a/src/main/scala/scorex/core/core.scala b/src/main/scala/scorex/core/core.scala index 7ed44a3f89..8609bd0eb1 100644 --- a/src/main/scala/scorex/core/core.scala +++ b/src/main/scala/scorex/core/core.scala @@ -1,6 +1,6 @@ package scorex -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.core.network.message.InvData import scorex.core.utils.ScorexEncoder import scorex.util.encode.Base16 @@ -12,14 +12,14 @@ package object core { type VersionTag = VersionTag.Type - def idsToString(ids: Seq[(ModifierTypeId.Value, util.ModifierId)])(implicit enc: ScorexEncoder): String = { + def idsToString(ids: Seq[(NetworkObjectTypeId.Value, util.ModifierId)])(implicit enc: ScorexEncoder): String = { List(ids.headOption, ids.lastOption) .flatten .map { case (typeId, id) => s"($typeId,${enc.encodeId(id)})" } .mkString("[", "..", "]") } - def idsToString(modifierType: ModifierTypeId.Value, ids: Seq[util.ModifierId])(implicit encoder: ScorexEncoder): String = { + def idsToString(modifierType: NetworkObjectTypeId.Value, ids: Seq[util.ModifierId])(implicit encoder: ScorexEncoder): String = { idsToString(ids.map(id => (modifierType, id))) } diff --git a/src/main/scala/scorex/core/network/DeliveryTracker.scala b/src/main/scala/scorex/core/network/DeliveryTracker.scala index 65fb658647..b6231445a5 100644 --- a/src/main/scala/scorex/core/network/DeliveryTracker.scala +++ b/src/main/scala/scorex/core/network/DeliveryTracker.scala @@ -2,7 +2,7 @@ package scorex.core.network import akka.actor.Cancellable import io.circe.{Encoder, Json} -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.CheckDelivery import org.ergoplatform.nodeView.mempool.ExpiringApproximateCache @@ -45,10 +45,10 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, desiredSizeOfExpectingModifierQueue: Int) extends ScorexLogging with ScorexEncoding { // when a remote peer is asked for a modifier we add the requested data to `requested` - protected val requested: mutable.Map[ModifierTypeId.Value, Map[ModifierId, RequestedInfo]] = mutable.Map() + protected val requested: mutable.Map[NetworkObjectTypeId.Value, Map[ModifierId, RequestedInfo]] = mutable.Map() // when our node received a modifier we put it to `received` - protected val received: mutable.Map[ModifierTypeId.Value, Map[ModifierId, ConnectedPeer]] = mutable.Map() + protected val received: mutable.Map[NetworkObjectTypeId.Value, Map[ModifierId, ConnectedPeer]] = mutable.Map() private val desiredSizeOfExpectingHeaderQueue: Int = desiredSizeOfExpectingModifierQueue * 8 @@ -96,7 +96,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * `modifierKeepers` are required here to check that modifier is in `Held` status */ def status(modifierId: ModifierId, - modifierTypeId: ModifierTypeId.Value, + modifierTypeId: NetworkObjectTypeId.Value, modifierKeepers: Seq[ContainsModifiers[_]]): ModifiersStatus = if (received.get(modifierTypeId).exists(_.contains(modifierId))) Received else if (requested.get(modifierTypeId).exists(_.contains(modifierId))) Requested @@ -114,7 +114,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Set status of modifier with id `id` to `Requested` */ - def setRequested(typeId: ModifierTypeId.Value, + def setRequested(typeId: NetworkObjectTypeId.Value, id: ModifierId, supplier: ConnectedPeer, checksDone: Int = 0) @@ -130,12 +130,12 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * @return - information on requested modifier delivery tracker potentially has */ - def getRequestedInfo(typeId: ModifierTypeId.Value, id: ModifierId): Option[RequestedInfo] = { + def getRequestedInfo(typeId: NetworkObjectTypeId.Value, id: ModifierId): Option[RequestedInfo] = { requested.get(typeId).flatMap(_.get(id)) } /** Get peer we're communicating with in regards with modifier `id` **/ - def getSource(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Option[ConnectedPeer] = { + def getSource(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Option[ConnectedPeer] = { status(id, modifierTypeId, Seq.empty) match { case Requested => requested.get(modifierTypeId).flatMap(_.get(id)).map(_.peer) case Received => received.get(modifierTypeId).flatMap(_.get(id)) @@ -147,7 +147,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * Modified with id `id` is permanently invalid - set its status to `Invalid` * and return [[ConnectedPeer]] which sent bad modifier. */ - def setInvalid(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Option[ConnectedPeer] = { + def setInvalid(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Option[ConnectedPeer] = { val oldStatus: ModifiersStatus = status(id, modifierTypeId, Seq.empty) val transitionCheck = tryWithLogging { checkStatusTransition(oldStatus, Invalid) @@ -190,7 +190,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Modifier with id `id` was successfully applied to history - set its status to `Held`. */ - def setHeld(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Unit = + def setHeld(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Held) @@ -205,7 +205,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, * this modifier was removed from cache because cache is overfull or * we stop trying to download this modifiers due to exceeded number of retries */ - def setUnknown(id: ModifierId, modifierTypeId: ModifierTypeId.Value): Unit = + def setUnknown(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Unknown) @@ -215,7 +215,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, /** * Modifier with id `id` was received from remote peer - set its status to `Received`. */ - def setReceived(id: ModifierId, modifierTypeId: ModifierTypeId.Value, sender: ConnectedPeer): Unit = + def setReceived(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value, sender: ConnectedPeer): Unit = tryWithLogging { val oldStatus = status(id, modifierTypeId, Seq.empty) checkStatusTransition(oldStatus, Received) @@ -253,7 +253,7 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, case _ => false } - def clearStatusForModifier(id: ModifierId, modifierTypeId: ModifierTypeId.Value, oldStatus: ModifiersStatus): Unit = + def clearStatusForModifier(id: ModifierId, modifierTypeId: NetworkObjectTypeId.Value, oldStatus: ModifiersStatus): Unit = oldStatus match { case Requested => requested.flatAdjust(modifierTypeId)(_.map { infoById => @@ -327,16 +327,16 @@ object DeliveryTracker { } case class FullInfo( - invalidModifierApproxSize: Long, - requested: Seq[(ModifierTypeId.Value, Map[ModifierId, RequestedInfo])], - received: Seq[(ModifierTypeId.Value, Map[ModifierId, ConnectedPeer])] + invalidModifierApproxSize: Long, + requested: Seq[(NetworkObjectTypeId.Value, Map[ModifierId, RequestedInfo])], + received: Seq[(NetworkObjectTypeId.Value, Map[ModifierId, ConnectedPeer])] ) object FullInfo { import io.circe.syntax._ implicit val encodeState: Encoder[FullInfo] = new Encoder[FullInfo] { - def nestedMapAsJson[T : Encoder](requested: Seq[(ModifierTypeId.Value, Map[ModifierId, T])]): Json = + def nestedMapAsJson[T : Encoder](requested: Seq[(NetworkObjectTypeId.Value, Map[ModifierId, T])]): Json = Json.obj( requested.map { case (k, v) => k.toString -> Json.obj(v.mapValues(_.asJson).toSeq:_*) diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index af0aabf6a5..aa5e4115f7 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -1,7 +1,7 @@ package scorex.core.network.message -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.nodeView.state.SnapshotsInfo import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.wallet.Constants @@ -17,9 +17,9 @@ import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} import scala.collection.immutable -case class ModifiersData(typeId: ModifierTypeId.Value, modifiers: Map[ModifierId, Array[Byte]]) +case class ModifiersData(typeId: NetworkObjectTypeId.Value, modifiers: Map[ModifierId, Array[Byte]]) -case class InvData(typeId: ModifierTypeId.Value, ids: Seq[ModifierId]) +case class InvData(typeId: NetworkObjectTypeId.Value, ids: Seq[ModifierId]) /** * The `SyncInfo` message requests an `Inv` message that provides modifier ids @@ -68,7 +68,7 @@ object InvSpec extends MessageSpecV1[InvData] { } override def parse(r: Reader): InvData = { - val typeId = ModifierTypeId.fromByte(r.getByte()) + val typeId = NetworkObjectTypeId.fromByte(r.getByte()) val count = r.getUInt().toIntExact require(count > 0, "empty inv list") require(count <= maxInvObjects, s"$count elements in a message while limit is $maxInvObjects") @@ -147,7 +147,7 @@ object ModifiersSpec extends MessageSpecV1[ModifiersData] with ScorexLogging { } override def parse(r: Reader): ModifiersData = { - val typeId = ModifierTypeId.fromByte(r.getByte()) // 1 byte + val typeId = NetworkObjectTypeId.fromByte(r.getByte()) // 1 byte val count = r.getUInt().toIntExact // 8 bytes require(count > 0, s"Illegal message with 0 modifiers of type $typeId") val resMap = immutable.Map.newBuilder[ModifierId, Array[Byte]] diff --git a/src/main/scala/scorex/core/transaction/Transaction.scala b/src/main/scala/scorex/core/transaction/Transaction.scala index 7a793a92c7..c170967ad1 100644 --- a/src/main/scala/scorex/core/transaction/Transaction.scala +++ b/src/main/scala/scorex/core/transaction/Transaction.scala @@ -1,6 +1,6 @@ package scorex.core.transaction -import org.ergoplatform.modifiers.{ModifierTypeId, TransactionTypeId} +import org.ergoplatform.modifiers.{NetworkObjectTypeId, TransactionTypeId} import scorex.core.EphemerealNodeViewModifier import scorex.crypto.hash.Blake2b256 import scorex.util.{ModifierId, bytesToId} @@ -10,7 +10,7 @@ import scorex.util.{ModifierId, bytesToId} * A transaction is an atomic state modifier */ trait Transaction extends EphemerealNodeViewModifier { - override val modifierTypeId: ModifierTypeId.Value = TransactionTypeId.value + override val modifierTypeId: NetworkObjectTypeId.Value = TransactionTypeId.value val messageToSign: Array[Byte] diff --git a/src/main/scala/scorex/core/validation/ModifierError.scala b/src/main/scala/scorex/core/validation/ModifierError.scala index 3f5a32ef90..672c93db8f 100644 --- a/src/main/scala/scorex/core/validation/ModifierError.scala +++ b/src/main/scala/scorex/core/validation/ModifierError.scala @@ -1,11 +1,11 @@ package scorex.core.validation -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.util.ModifierId import scala.util.control.NoStackTrace -case class InvalidModifier(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value) +case class InvalidModifier(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value) /** Base trait for errors that were occurred during NodeView Modifier validation */ @@ -13,7 +13,7 @@ trait ModifierError { def message: String def isFatal: Boolean def modifierId: ModifierId - def modifierTypeId: ModifierTypeId.Value + def modifierTypeId: NetworkObjectTypeId.Value def toThrowable: Throwable def info: String = { @@ -25,7 +25,7 @@ trait ModifierError { /** Permanent modifier error that could not be recovered in future even after any history updates */ @SuppressWarnings(Array("org.wartremover.warts.Null")) -class MalformedModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId.Value, cause: Option[Throwable] = None) +class MalformedModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: NetworkObjectTypeId.Value, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with ModifierError { def isFatal: Boolean = true def toThrowable: Throwable = this @@ -35,7 +35,7 @@ class MalformedModifierError(val message: String, val modifierId: ModifierId, va * When an instance is created, the stack trace is not collected which makes this exception lightweight. */ @SuppressWarnings(Array("org.wartremover.warts.Null")) -class RecoverableModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: ModifierTypeId.Value, cause: Option[Throwable] = None) +class RecoverableModifierError(val message: String, val modifierId: ModifierId, val modifierTypeId: NetworkObjectTypeId.Value, cause: Option[Throwable] = None) extends Exception(message, cause.orNull) with ModifierError with NoStackTrace { def isFatal: Boolean = false def toThrowable: Throwable = this diff --git a/src/main/scala/scorex/core/validation/ModifierValidator.scala b/src/main/scala/scorex/core/validation/ModifierValidator.scala index 6715c53679..8e23763c69 100644 --- a/src/main/scala/scorex/core/validation/ModifierValidator.scala +++ b/src/main/scala/scorex/core/validation/ModifierValidator.scala @@ -1,6 +1,6 @@ package scorex.core.validation -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.core.consensus.ModifierSemanticValidity import scorex.core.utils.ScorexEncoder import scorex.core.validation.ValidationResult._ @@ -28,27 +28,27 @@ object ModifierValidator { } /** report recoverable modifier error that could be fixed by later retries */ - def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + def error(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = invalid(new RecoverableModifierError(error, modifierId, modifierTypeId, None)) /** report recoverable modifier error that could be fixed by later retries */ - def error(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value, cause: Throwable): Invalid = + def error(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value, cause: Throwable): Invalid = invalid(new RecoverableModifierError(msg(error, cause), modifierId, modifierTypeId, Option(cause))) /** report recoverable modifier error that could be fixed by later retries */ - def error(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + def error(description: String, detail: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = error(msg(description, detail), modifierId, modifierTypeId) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + def fatal(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = invalid(new MalformedModifierError(error, modifierId, modifierTypeId, None)) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(error: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value, cause: Throwable): Invalid = + def fatal(error: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value, cause: Throwable): Invalid = invalid(new MalformedModifierError(msg(error, cause), modifierId, modifierTypeId, Option(cause))) /** report non-recoverable modifier error that could not be fixed by retries and requires modifier change */ - def fatal(description: String, detail: String, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + def fatal(description: String, detail: String, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = fatal(msg(description, detail), modifierId, modifierTypeId) /** unsuccessful validation with a given error; also logs the error as an exception */ @@ -100,7 +100,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the first argument equals the second. This should not be used with `ModifierId` of type `Array[Byte]`. * The `error` callback will be provided with detail on argument values for better reporting */ - def validateEquals[A](id: Short, given: => A, expected: => A, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + def validateEquals[A](id: Short, given: => A, expected: => A, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass((given, expected) match { case _ if !settings.isActive(id) => result case (a: Array[_], b: Array[_]) if a sameElements[Any] b => result @@ -112,7 +112,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `id`s are equal. The `error` callback will be provided with detail on argument values */ - def validateEqualIds(id: Short, given: => ModifierId, expected: => ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + def validateEqualIds(id: Short, given: => ModifierId, expected: => ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass { if (!settings.isActive(id) || given == expected) result else settings.getError(id, InvalidModifier(s"Given: ${e.encodeId(given)}, expected ${e.encodeId(expected)}", given, modifierTypeId)) @@ -128,7 +128,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `condition` is `Success`. Otherwise the `error` callback will be provided with detail * on a failure exception */ - def validateNoFailure(id: Short, condition: => Try[_], modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + def validateNoFailure(id: Short, condition: => Try[_], modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass(if (!settings.isActive(id)) result else condition.fold(e => settings.getError(id, e, modifierId, modifierTypeId), _ => result)) } @@ -140,7 +140,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS * @param modifierTypeId provide for a case when it cannot be resolved from a ModifierError * @return validation state */ - def validateNoFailure(id: Short, condition: => Try[_], modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + def validateNoFailure(id: Short, condition: => Try[_], modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass { if (!settings.isActive(id)) { result @@ -160,7 +160,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate the `block` doesn't throw an Exception. Otherwise the `error` callback will be provided with detail * on the exception */ - def validateNoThrow(id: Short, block: => Any, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + def validateNoThrow(id: Short, block: => Any, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { validateNoFailure(id, Try(block), modifierId, modifierTypeId) } @@ -174,7 +174,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS /** Validate `condition` against payload is `true` or else return the `error` */ def validateTryFlatten(id: Short, operation: T => Try[T], condition: T => Boolean, - modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass(result.toTry.flatMap(r => operation(r)) match { case Failure(ex) => settings.getError(id, ex, modifierId, modifierTypeId) case Success(v) if settings.isActive(id) && !condition(v) => settings.getError(id, InvalidModifier(modifierId, modifierId, modifierTypeId)) @@ -199,7 +199,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS * Return `error` if option is `Some` amd condition is `false` */ def validateOrSkipFlatten[A](id: Short, option: => Option[A], condition: A => Boolean, - modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): ValidationState[T] = { + modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): ValidationState[T] = { pass(option match { case Some(v) if settings.isActive(id) && !condition(v) => settings.getError(id, InvalidModifier(modifierId, modifierId, modifierTypeId)) case _ => result diff --git a/src/main/scala/scorex/core/validation/ValidationSettings.scala b/src/main/scala/scorex/core/validation/ValidationSettings.scala index e083d50e79..4ed9bb6cf3 100644 --- a/src/main/scala/scorex/core/validation/ValidationSettings.scala +++ b/src/main/scala/scorex/core/validation/ValidationSettings.scala @@ -1,6 +1,6 @@ package scorex.core.validation -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import scorex.core.validation.ValidationResult.Invalid import scorex.util.ModifierId @@ -11,7 +11,7 @@ import scorex.util.ModifierId abstract class ValidationSettings { val isFailFast: Boolean - def getError(id: Short, e: Throwable, modifierId: ModifierId, modifierTypeId: ModifierTypeId.Value): Invalid = + def getError(id: Short, e: Throwable, modifierId: ModifierId, modifierTypeId: NetworkObjectTypeId.Value): Invalid = getError(id, InvalidModifier(e.getMessage, modifierId, modifierTypeId)) def getError(id: Short, invalidMod: InvalidModifier): ValidationResult.Invalid diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index 66eac19f6b..935b5713c5 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history -import org.ergoplatform.modifiers.{ErgoFullBlock, ModifierTypeId} +import org.ergoplatform.modifiers.{ErgoFullBlock, NetworkObjectTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.HeaderSerializer @@ -117,7 +117,7 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { val missedChain = chain.tail.toList val missedBS = missedChain.flatMap { fb => Seq((BlockTransactions.modifierTypeId, fb.blockTransactions.encodedId), (Extension.modifierTypeId, fb.extension.encodedId)) - }.foldLeft(Map.empty[ModifierTypeId.Value, Seq[String]]) { case (newAcc, (mType, mId)) => + }.foldLeft(Map.empty[NetworkObjectTypeId.Value, Seq[String]]) { case (newAcc, (mType, mId)) => newAcc.adjust(mType)(_.fold(Seq(mId))(_ :+ mId)) } diff --git a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala index d89220b98a..4c7e5ad532 100644 --- a/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala +++ b/src/test/scala/scorex/core/network/DeliveryTrackerSpec.scala @@ -3,7 +3,7 @@ package scorex.core.network import akka.actor.{ActorRef, Cancellable} import io.circe._ import io.circe.syntax._ -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.utils.ErgoPropertyTest import scorex.core.network.ModifiersStatus.Received import scorex.testkit.generators.ObjectGenerators @@ -15,7 +15,7 @@ class DeliveryTrackerSpec extends ErgoPropertyTest with ObjectGenerators { forAll(connectedPeerGen(ActorRef.noSender)) { peer => val tracker = DeliveryTracker.empty(settings) val mid: ModifierId = ModifierId @@ "foo" - val mTypeId: ModifierTypeId.Value = ModifierTypeId.fromByte(104) + val mTypeId: NetworkObjectTypeId.Value = NetworkObjectTypeId.fromByte(104) tracker.setRequested(mTypeId, mid, peer) { _ => Cancellable.alreadyCancelled} val infoFields = Seq( diff --git a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala index d4285a50ab..7c9dddf961 100644 --- a/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala +++ b/src/test/scala/scorex/testkit/generators/ObjectGenerators.scala @@ -3,7 +3,7 @@ package scorex.testkit.generators import java.net.{InetAddress, InetSocketAddress, URL} import akka.actor.ActorRef import akka.util.ByteString -import org.ergoplatform.modifiers.ModifierTypeId +import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.network.ModePeerFeature import org.ergoplatform.nodeView.state.StateType import org.scalacheck.Gen.{const, some} @@ -50,10 +50,10 @@ trait ObjectGenerators { lazy val modifierIdGen: Gen[ModifierId] = Gen.listOfN(NodeViewModifier.ModifierIdSize, Arbitrary.arbitrary[Byte]) .map(id => bytesToId(id.toArray)) - lazy val modifierTypeIdGen: Gen[ModifierTypeId.Value] = Arbitrary.arbitrary[Byte].map(t => ModifierTypeId.fromByte(t)) + lazy val modifierTypeIdGen: Gen[NetworkObjectTypeId.Value] = Arbitrary.arbitrary[Byte].map(t => NetworkObjectTypeId.fromByte(t)) lazy val invDataGen: Gen[InvData] = for { - modifierTypeId: ModifierTypeId.Value <- modifierTypeIdGen + modifierTypeId: NetworkObjectTypeId.Value <- modifierTypeIdGen modifierIds: Seq[ModifierId] <- Gen.nonEmptyListOf(modifierIdGen) if modifierIds.nonEmpty } yield InvData(modifierTypeId, modifierIds) @@ -63,7 +63,7 @@ trait ObjectGenerators { } yield id -> mod lazy val modifiersGen: Gen[ModifiersData] = for { - modifierTypeId: ModifierTypeId.Value <- modifierTypeIdGen + modifierTypeId: NetworkObjectTypeId.Value <- modifierTypeIdGen modifiers: Map[ModifierId, Array[Byte]] <- Gen.nonEmptyMap(modifierWithIdGen).suchThat(_.nonEmpty) } yield ModifiersData(modifierTypeId, modifiers) From 4ba17f2acc2ea4a01eac9abd96210cc963051ca7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 26 Jan 2023 22:45:59 +0300 Subject: [PATCH 108/204] ScalaDoc for HF in VersionedLDBAVLStorage --- .../crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 5ba4ecc2f1..3196afccfa 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -19,6 +19,7 @@ import scala.util.{Failure, Try} * @param nodeParameters - parameters of the tree node (key size, optional value size, label size) * @param hf - hash function used to construct the tree * @tparam D - type of hash function digest + * @tparam HF - type of hash function used in AVL+ tree */ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDBVersionedStore, nodeParameters: NodeParameters) From 24d9728ed4fde8f658ce6bc6d4be349acfab319a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 2 Feb 2023 22:49:33 +0300 Subject: [PATCH 109/204] StateTreeParameters replacing instantiations --- .../scala/scorex/crypto/authds/benchmarks/Helper.scala | 2 +- .../{NodeParameters.scala => AvlTreeParameters.scala} | 2 +- .../authds/avltree/batch/ProxyInternalProverNode.scala | 2 +- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 8 ++++---- .../batch/AVLStorageWithPersistentProverSpec.scala | 2 +- .../avltree/batch/benchmark/BatchingBenchmark.scala | 2 +- .../crypto/authds/avltree/batch/benchmark/OOMTest.scala | 2 +- .../crypto/authds/avltree/batch/helpers/TestHelper.scala | 2 +- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 2 +- .../org/ergoplatform/nodeView/state/UtxoState.scala | 9 ++++----- .../ergoplatform/nodeView/state/UtxoStateReader.scala | 8 ++++---- src/main/scala/org/ergoplatform/settings/Constants.scala | 4 ++-- 12 files changed, 22 insertions(+), 23 deletions(-) rename avldb/src/main/scala/scorex/crypto/authds/avltree/batch/{NodeParameters.scala => AvlTreeParameters.scala} (87%) diff --git a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala index 8d9bce4fdb..c3e86664d4 100644 --- a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala +++ b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala @@ -38,7 +38,7 @@ object Helper { val dir = java.nio.file.Files.createTempDirectory("bench_testing_" + scala.util.Random.alphanumeric.take(15)).toFile dir.deleteOnExit() val store = new LDBVersionedStore(dir, initialKeepVersions = initialKeepVersions) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, NodeParameters(kl, Some(vl), ll)) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, AvlTreeParameters(kl, Some(vl), ll)) require(storage.isEmpty) val prover = new BatchAVLProver[Digest32, HF](kl, Some(vl)) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala similarity index 87% rename from avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala rename to avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala index 58b394af6e..fe24564aaa 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/NodeParameters.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala @@ -6,7 +6,7 @@ package scorex.crypto.authds.avltree.batch * @param valueSize - size of a value in a leaf (fixed, if defined, arbitrary, if None) * @param labelSize - size of a label (node hash), fixed */ -case class NodeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) { +case class AvlTreeParameters(keySize: Int, valueSize: Option[Int], labelSize: Int) { /** * @return whether value is fixed-size */ diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala index 2f187368aa..8455a18908 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala @@ -17,7 +17,7 @@ class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, protected var pb: Balance = Balance @@ 0.toByte) (implicit val phf: CryptographicHash[D], store: LDBVersionedStore, - nodeParameters: NodeParameters) + nodeParameters: AvlTreeParameters) extends InternalProverNode(k = pk, l = null, r = null, b = pb)(phf) { override protected def computeLabel: D = { diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 3196afccfa..e6aa4ee467 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -22,7 +22,7 @@ import scala.util.{Failure, Try} * @tparam HF - type of hash function used in AVL+ tree */ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDBVersionedStore, - nodeParameters: NodeParameters) + nodeParameters: AvlTreeParameters) (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { @@ -114,7 +114,7 @@ object VersionedLDBAVLStorage { */ def fetch[D <: hash.Digest](dbKey: ADKey)(implicit hf: CryptographicHash[D], store: LDBVersionedStore, - nodeParameters: NodeParameters): ProverNodes[D] = { + nodeParameters: AvlTreeParameters): ProverNodes[D] = { val bytes = store(dbKey) lazy val keySize = nodeParameters.keySize lazy val labelSize = nodeParameters.labelSize @@ -156,7 +156,7 @@ object VersionedLDBAVLStorage { /** * Serialize tree node (only, without possible children) */ - private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D], nodeParameters: NodeParameters): Array[Byte] = { + private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D], nodeParameters: AvlTreeParameters): Array[Byte] = { val builder = new mutable.ArrayBuilder.ofByte; node match { case n: ProxyInternalNode[D] => @@ -178,7 +178,7 @@ object VersionedLDBAVLStorage { chunks: Iterator[BatchAVLProverSubtree[D]], additionalData: Iterator[(Array[Byte], Array[Byte])], store: LDBVersionedStore, - nodeParameters: NodeParameters)(implicit hf: HF): Try[VersionedLDBAVLStorage[D, HF]] = { + nodeParameters: AvlTreeParameters)(implicit hf: HF): Try[VersionedLDBAVLStorage[D, HF]] = { //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate val topNodeKey = nodeParameters.TopNodeKey diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala index e79716d01d..94afdcd659 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala @@ -18,7 +18,7 @@ class AVLStorageWithPersistentProverSpec extends AnyPropSpec with Matchers { val stateStore = new LDBVersionedStore(getRandomTempDir, 10) private lazy val np = - NodeParameters(keySize = 32, valueSize = None, labelSize = 32) + AvlTreeParameters(keySize = 32, valueSize = None, labelSize = 32) protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala index 3e12b45a9f..03b0f5790a 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala @@ -19,7 +19,7 @@ object BatchingBenchmark extends App with FileHelper { type HF = Blake2b256.type val store = new LDBVersionedStore(getRandomTempDir, initialKeepVersions = 10) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, NodeParameters(KeyLength, Some(ValueLength), LabelLength)) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, AvlTreeParameters(KeyLength, Some(ValueLength), LabelLength)) require(storage.isEmpty) val mods = generateModifications() var digest: ADDigest = ADDigest @@ Array[Byte]() diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala index 11ea2c052b..922f57fd5c 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala @@ -23,7 +23,7 @@ object OOMTest extends App { val store = new LDBVersionedStore(dir, initialKeepVersions = 200) val bestVersionKey = Blake2b256("best state version") - private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) + private lazy val np = AvlTreeParameters(keySize = 32, valueSize = None, labelSize = 32) protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, np) val afterGenesisStateDigestHex: String = "78b130095239561ecf5449a7794c0615326d1fd007cc79dcc286e46e4beb1d3f01" diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala index 756ed3fa4c..3ad5b171bc 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala @@ -29,7 +29,7 @@ trait TestHelper extends FileHelper { } def createVersionedStorage(store: LDBVersionedStore): STORAGE = - new VersionedLDBAVLStorage(store, NodeParameters(KL, Some(VL), LL)) + new VersionedLDBAVLStorage(store, AvlTreeParameters(KL, Some(VL), LL)) def createPersistentProver(storage: STORAGE): PERSISTENT_PROVER = { val prover = new BatchAVLProver[D, HF](KL, Some(VL)) 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 24e2d49c7c..04d8756819 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 @@ -217,7 +217,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { log.info("Starting UTXO set snapshot transfer into state database") val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore, Constants.ErgoNodeParameters).flatMap { + VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore, Constants.StateTreeParameters).flatMap { ldbStorage => log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 12b0fc47a2..defbe7c6ab 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, Parameters} +import org.ergoplatform.settings.{Algos, Constants, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -23,6 +23,7 @@ import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} import scorex.util.ModifierId import scorex.util.ScorexLogging +import Constants.StateTreeParameters import scala.util.{Failure, Success, Try} @@ -276,8 +277,7 @@ object UtxoState extends ScorexLogging { .getOrElse(ErgoState.genesisStateVersion) val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { val bp = new BatchAVLProver[Digest32, HF](keyLength = 32, valueLengthOpt = None) - val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters)(Algos.hash) PersistentBatchAVLProver.create(bp, storage).get } new UtxoState(persistentProver, version, store, constants) @@ -300,8 +300,7 @@ object UtxoState extends ScorexLogging { val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) val defaultStateContext = ErgoStateContext.empty(constants, parameters) - val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - val storage: VersionedLDBAVLStorage[Digest32, HF] = new VersionedLDBAVLStorage(store, np)(Algos.hash) + val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters)(Algos.hash) val persistentProver = PersistentBatchAVLProver.create( p, storage, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index cb2624187a..1580f07f27 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,16 +5,17 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter import scorex.core.transaction.state.TransactionValidation import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.validation.MalformedModifierError -import scorex.crypto.authds.avltree.batch.{Lookup, NodeParameters, PersistentBatchAVLProver, VersionedLDBAVLStorage} +import scorex.crypto.authds.avltree.batch.{Lookup, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 +import Constants.StateTreeParameters import scala.util.{Failure, Success, Try} @@ -24,8 +25,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi val constants: StateConstants - private lazy val np = NodeParameters(keySize = 32, valueSize = None, labelSize = 32) - protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, np) + protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters) protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index c62f8562f9..7ead38023b 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSeria import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import scorex.core.serialization.ScorexSerializer import scorex.core.NodeViewModifier -import scorex.crypto.authds.avltree.batch.NodeParameters +import scorex.crypto.authds.avltree.batch.AvlTreeParameters import sigmastate.Values import sigmastate.Values.ErgoTree @@ -69,5 +69,5 @@ object Constants { * AVL+ tree node parameters. The tree is used to authenticate UTXO set. * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. */ - object ErgoNodeParameters extends NodeParameters(HashLength, None, HashLength) + object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength) } From 96bd7c2255dc14cead1a588d6d6430843887a416 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 2 Feb 2023 23:18:03 +0300 Subject: [PATCH 110/204] importing some changes from nipopow-bootstrapping --- .../org/ergoplatform/nodeView/ErgoNodeViewHolder.scala | 1 + .../nodeView/state/UtxoSetSnapshotPersistence.scala | 10 ++++------ .../scala/org/ergoplatform/settings/Constants.scala | 6 ++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 286730877f..8dd4b27187 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -287,6 +287,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, StateConstants(settings)) + // todo: apply 10 headers before utxo set snapshot updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(_) => ??? } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 3022771c20..23f0e340b3 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -8,11 +8,10 @@ import scorex.crypto.authds.avltree.batch.PersistentBatchAVLProver import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging +import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapshot} trait UtxoSetSnapshotPersistence extends ScorexLogging { - val MakeSnapshotEvery = 1024 // test value, switch to 51200 after testing - def constants: StateConstants protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] @@ -28,10 +27,9 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { } protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - - if (estimatedTip.nonEmpty && - (height % MakeSnapshotEvery == MakeSnapshotEvery - 1) && - estimatedTip.get - height <= MakeSnapshotEvery) { + if (timeToTakeSnapshot(height) && + estimatedTip.nonEmpty && + estimatedTip.get - height <= MakeSnapshotEvery) { val (manifest, subtrees) = slicedTree() diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 7ead38023b..d49083b9d1 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -70,4 +70,10 @@ object Constants { * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. */ object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength) + + val MakeSnapshotEvery = 1024 // test value, switch to 51200 after testing + + def timeToTakeSnapshot(height: Int): Boolean = { + height % MakeSnapshotEvery == MakeSnapshotEvery - 1 + } } From 09bdb6703948b04604dbd0afe0cec4a9a706d976 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 3 Feb 2023 17:14:48 +0300 Subject: [PATCH 111/204] SnapshotsInfoSpecification --- .../nodeView/state/ErgoStateReader.scala | 9 +++++-- .../nodeView/state/SnapshotsInfo.scala | 9 ++++++- .../nodeView/state/UtxoState.scala | 15 +++++++++++ .../state/SnapshotsInfoSpecification.scala | 25 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/test/scala/org/ergoplatform/nodeView/state/SnapshotsInfoSpecification.scala diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 2030f85580..d86d3332f4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -10,10 +10,15 @@ import scorex.util.ScorexLogging trait ErgoStateReader extends NodeViewComponent with ScorexLogging { - // root hash and height of AVL+ tree authenticating UTXO set + /** + * Root hash and height of AVL+ tree authenticating UTXO set + */ def rootHash: ADDigest - // must be ID of last applied block + /** + * Current version of the state + * Must be ID of last block applied + */ def version: VersionTag val store: LDBVersionedStore diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 8a08d26c5b..d43ee2f73b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -4,14 +4,21 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.ManifestId /** - * Container for available UTXO set snapshots + * Container for UTXO set snapshots the node holds * @param availableManifests - available UTXO set snapshot manifests and corresponding heights */ case class SnapshotsInfo(availableManifests: Map[Height, ManifestId]) { + + /** + * @return new container instance with new snapshot added + */ def withNewManifest(height: Height, manifestId: ManifestId): SnapshotsInfo = { SnapshotsInfo(availableManifests.updated(height, manifestId)) } + /** + * @return whether snapshots available + */ def nonEmpty: Boolean = availableManifests.nonEmpty } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index defbe7c6ab..ea09cb7954 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -238,10 +238,25 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 object UtxoState extends ScorexLogging { + /** + * Short synonym for AVL+ tree type used in the node + */ type Manifest = BatchAVLProverManifest[Digest32] + + /** + * Short synonym for AVL subtree type used in the node + */ type Subtree = BatchAVLProverSubtree[Digest32] + + /** + * Manifest is associated with 32 bytes cryptographically strong unique id (root hash of the AVL tree under manifest) + */ type ManifestId = Digest32 + + /** + * Subtree is associated with 32 bytes cryptographically strong unique id (hash of subtree's root node) + */ type SubtreeId = Digest32 private lazy val bestVersionKey = Algos.hash("best state version") diff --git a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsInfoSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsInfoSpecification.scala new file mode 100644 index 0000000000..e2b589d993 --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsInfoSpecification.scala @@ -0,0 +1,25 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.utils.ErgoPropertyTest + +class SnapshotsInfoSpecification extends ErgoPropertyTest { + + property("makeEmpty / nonEmpty / withNewManifest") { + val empty = SnapshotsInfo.makeEmpty() + empty.nonEmpty shouldBe false + + val h = 10 + val d = digest32Gen.sample.get + val nonEmpty = empty.withNewManifest(h, d) + nonEmpty.nonEmpty shouldBe true + nonEmpty.availableManifests(h).sameElements(d) shouldBe true + + val h2 = 20 + val d2 = digest32Gen.sample.get + val ne2 = nonEmpty.withNewManifest(h2, d2) + nonEmpty.availableManifests.size shouldBe 1 + ne2.availableManifests(h).sameElements(d) shouldBe true + ne2.availableManifests(h2).sameElements(d2) shouldBe true + } + +} From 86123224e0f367732f7a755a5d2f8d8be9a9ff24 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 3 Feb 2023 18:35:33 +0300 Subject: [PATCH 112/204] writeSnapshot temporal rework, unused methods removed from SnapshotsDb --- .../src/main/scala/scorex/db/LDBKVStore.scala | 11 ++++-- .../nodeView/state/SnapshotsDb.scala | 39 ++++++++++--------- .../org/ergoplatform/db/LDBKVStoreSpec.scala | 4 +- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index edbc958a7e..7d4ab17a1b 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -14,10 +14,11 @@ import spire.syntax.all.cfor */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { - def update(toInsert: Array[(K, V)], toRemove: Array[K]): Try[Unit] = { + def update(toInsertKeys: Array[K], toInsertValues: Array[V], toRemove: Array[K]): Try[Unit] = { val batch = db.createWriteBatch() try { - cfor(0)(_ < toInsert.length, _ + 1) { i => batch.put(toInsert(i)._1, toInsert(i)._2)} + require(toInsertKeys.length == toInsertValues.length) + cfor(0)(_ < toInsertKeys.length, _ + 1) { i => batch.put(toInsertKeys(i), toInsertValues(i))} cfor(0)(_ < toRemove.length, _ + 1) { i => batch.delete(toRemove(i))} db.write(batch) Success(()) @@ -43,9 +44,11 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging } } - def insert(values: Array[(K, V)]): Try[Unit] = update(values, Array.empty) + def insert(values: Array[(K, V)]): Try[Unit] = update(values.map(_._1), values.map(_._2), Array.empty) - def remove(keys: Array[K]): Try[Unit] = update(Array.empty, keys) + def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + + def remove(keys: Array[K]): Try[Unit] = update(Array.empty, Array.empty, keys) /** * Get last key within some range (inclusive) by used comparator. diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 0bc8a02cb9..4362884d66 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -6,12 +6,13 @@ import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} import org.ergoplatform.wallet.Constants -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging import scorex.util.encode.Base16 +import scala.collection.mutable import scala.util.{Failure, Success, Try} class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { @@ -42,12 +43,9 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { } def readSnapshotsInfo: SnapshotsInfo = { - store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.makeEmpty) + store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.makeEmpty()) } - def notEmpty(): Boolean = { - store.get(snapshotInfoKey).isDefined - } def pruneSnapshots(before: Height): Unit = { log.info("Starting snapshots pruning") @@ -82,18 +80,26 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def writeSnapshot(height: Height, manifest: UtxoState.Manifest, subtrees: Seq[UtxoState.Subtree]): Unit = { - val manifestBytes = serializer.manifestToBytes(manifest) - val manifestId = manifest.id - //todo: RAM consumption doubles here, avoid it - val subTreesToWrite = subtrees.map(s => (s.id: Array[Byte]) -> serializer.subtreeToBytes(s)).toArray //todo: eliminate .toArray - store.insert(Array((manifestId: Array[Byte]) -> manifestBytes) ++ subTreesToWrite) - val si = readSnapshotsInfo.withNewManifest(height, manifestId) + + val keysBuilder = mutable.ArrayBuilder.make[Array[Byte]]() + keysBuilder.sizeHint(subtrees.length + 1) + + val valuesBuilder = mutable.ArrayBuilder.make[Array[Byte]]() + valuesBuilder.sizeHint(subtrees.length + 1) + + keysBuilder += manifest.id + valuesBuilder += serializer.manifestToBytes(manifest) + + // todo: not efficient to write tree to the memory ? + subtrees.foreach { s => + keysBuilder += s.id + valuesBuilder += serializer.subtreeToBytes(s) + } + store.insert(keysBuilder.result(), valuesBuilder.result()) + val si = readSnapshotsInfo.withNewManifest(height, manifest.id) writeSnapshotsInfo(si) } - def readManifest(id: ManifestId): Option[UtxoState.Manifest] = { - readManifestBytes(id).flatMap(bs => serializer.manifestFromBytes(bs, Constants.ModifierIdLength).toOption) - } def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { store.get(id) @@ -102,11 +108,6 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { store.get(id) } - - def readSubtree(id: SubtreeId): Option[BatchAVLProverSubtree[Digest32]] = { - readSubtreeBytes(id).flatMap(bs => serializer.subtreeFromBytes(bs, Constants.ModifierIdLength).toOption) - } - } object SnapshotsDb { diff --git a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala index cf675c916d..8182ac3724 100644 --- a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala +++ b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala @@ -10,14 +10,14 @@ class LDBKVStoreSpec extends AnyPropSpec with Matchers with DBSpec { val valueA = (byteString("A"), byteString("1")) val valueB = (byteString("B"), byteString("2")) - store.update(toInsert = Array(valueA, valueB), toRemove = Array.empty).get + store.update(Array(valueA._1, valueB._1), Array(valueA._2, valueB._2), toRemove = Array.empty).get store.get(valueA._1).toBs shouldBe Some(valueA._2).toBs store.get(valueB._1).toBs shouldBe Some(valueB._2).toBs store.getAll.toSeq.toBs shouldBe Seq(valueA, valueB).toBs - store.update(toInsert = Array.empty, toRemove = Array(valueA._1)).get + store.update(Array.empty, Array.empty, toRemove = Array(valueA._1)).get store.get(valueA._1) shouldBe None } } From 2da650a8f48f0809d95b63cbade61819eccc99b0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 28 Feb 2023 00:55:56 +0300 Subject: [PATCH 113/204] more elegant solution for topNodeKey / topNodeHeight --- .../avltree/batch/AvlTreeParameters.scala | 4 --- .../batch/VersionedLDBAVLStorage.scala | 26 ++++++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala index fe24564aaa..273e2412ef 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala @@ -11,8 +11,4 @@ case class AvlTreeParameters(keySize: Int, valueSize: Option[Int], labelSize: In * @return whether value is fixed-size */ def fixedSizeValue: Boolean = valueSize.isDefined - - // todo: move out? not looking so good here - private[batch] val TopNodeKey: Array[Byte] = Array.fill(labelSize)(123: Byte) - private[batch] val TopNodeHeight: Array[Byte] = Array.fill(labelSize)(124: Byte) } diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index e6aa4ee467..40cc722dfa 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -26,14 +26,15 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB (implicit val hf: HF) extends VersionedAVLStorage[D] with ScorexLogging { - import VersionedLDBAVLStorage.{nodeLabel, toBytes} + import VersionedLDBAVLStorage.{nodeLabel, toBytes, topNodeKeys} - private val topNodeKey = nodeParameters.TopNodeKey - private val topNodeHeight = nodeParameters.TopNodeHeight + private val topKeys = topNodeKeys(nodeParameters) + private val topNodeHashKey: Array[Byte] = topKeys._1 + private val topNodeHeightKey: Array[Byte] = topKeys._2 private def restorePrunedTopNode(): Try[(ProverNodes[D], Int)] = Try { - val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(topNodeKey).get)(hf, store, nodeParameters) - val topHeight = Ints.fromByteArray(store.get(topNodeHeight).get) + val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(topNodeHashKey).get)(hf, store, nodeParameters) + val topHeight = Ints.fromByteArray(store.get(topNodeHeightKey).get) top -> topHeight } @@ -64,8 +65,8 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest - val indexes = Seq(topNodeKey -> nodeLabel(prover.topNode), - topNodeHeight -> Ints.toByteArray(prover.rootNodeHeight)) + val indexes = Seq(topNodeHashKey -> nodeLabel(prover.topNode), + topNodeHeightKey -> Ints.toByteArray(prover.rootNodeHeight)) val toInsert = serializedVisitedNodes(prover.topNode, isTop = true) val toRemove = prover.removedNodes().map(rn => rn.label) val toUpdate = indexes ++ toInsert @@ -101,6 +102,12 @@ object VersionedLDBAVLStorage { private[batch] val InternalNodePrefix: Byte = 0: Byte private[batch] val LeafPrefix: Byte = 1: Byte + private[batch] def topNodeKeys(nodeParameters: AvlTreeParameters) = { + val topNodeKey: Array[Byte] = Array.fill(nodeParameters.labelSize)(123: Byte) + val topNodeHeightKey: Array[Byte] = Array.fill(nodeParameters.labelSize)(124: Byte) + (topNodeKey, topNodeHeightKey) + } + /** * Fetch tree node from database by its database id (hash of node contents) * @@ -181,8 +188,7 @@ object VersionedLDBAVLStorage { nodeParameters: AvlTreeParameters)(implicit hf: HF): Try[VersionedLDBAVLStorage[D, HF]] = { //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate - val topNodeKey = nodeParameters.TopNodeKey - val topNodeHeight = nodeParameters.TopNodeHeight + val (topNodeHashKey, topNodeHeightKey) = topNodeKeys(nodeParameters) def idCollector(node: ProverNodes[D], acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { @@ -200,7 +206,7 @@ object VersionedLDBAVLStorage { val rootNode = manifest.root val rootNodeHeight = manifest.rootHeight val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) - val indices = Iterator(topNodeKey -> nodeLabel(rootNode), topNodeHeight -> Ints.toByteArray(rootNodeHeight)) + val indices = Iterator(topNodeHashKey -> nodeLabel(rootNode), topNodeHeightKey -> Ints.toByteArray(rootNodeHeight)) val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map{_ => From 30591030004c3eec580dd48959e747e24808eafa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 1 Mar 2023 14:58:57 +0300 Subject: [PATCH 114/204] storingUtxoSnapshots setting --- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 8 ++------ src/main/resources/application.conf | 3 +++ .../nodeView/state/UtxoSetSnapshotPersistence.scala | 3 ++- .../ergoplatform/settings/NodeConfigurationSettings.scala | 4 ++++ .../history/extra/ExtraIndexerSpecification.scala | 2 +- .../ergoplatform/settings/ErgoSettingsSpecification.scala | 3 +++ .../scala/org/ergoplatform/tools/ChainGenerator.scala | 2 +- .../scala/org/ergoplatform/utils/HistoryTestHelpers.scala | 2 +- src/test/scala/org/ergoplatform/utils/Stubs.scala | 2 +- 9 files changed, 18 insertions(+), 11 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 40cc722dfa..a252c4b1f5 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -205,7 +205,7 @@ object VersionedLDBAVLStorage { val rootNode = manifest.root val rootNodeHeight = manifest.rootHeight - val digestWrapper = VersionedLDBAVLStorage.digest(rootNode, rootNodeHeight) + val digestWrapper = VersionedLDBAVLStorage.digest(rootNode.label, rootNodeHeight) val indices = Iterator(topNodeHashKey -> nodeLabel(rootNode), topNodeHeightKey -> Ints.toByteArray(rootNodeHeight)) val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) @@ -226,9 +226,5 @@ object VersionedLDBAVLStorage { // then the AVL tree has at least 2^{255/1.4405} = 2^177 leaves, which is more than the number of atoms on planet Earth. ADDigest @@ (rootNodeLabel :+ rootNodeHeight.toByte) } - - def digest[D <: hash.Digest](rootNode: Node[D], rootNodeHeight: Int): ADDigest = { - digest(rootNode.label, rootNodeHeight) - } - + } diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 95dd9d067d..3dd8ba1eda 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -29,6 +29,9 @@ ergo { # Minimal suffix size for PoPoW proof (may be pre-defined constant or settings parameter) minimalSuffix = 10 + # how many utxo set snapshots to store, 0 means that they are not stored at all + storingUtxoSnapshots = 0 + # Is the node is doing mining mining = false diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 23f0e340b3..c66b629a1b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -27,7 +27,8 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { } protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - if (timeToTakeSnapshot(height) && + if (constants.settings.nodeSettings.areSnapshotsStored && + timeToTakeSnapshot(height) && estimatedTip.nonEmpty && estimatedTip.get - height <= MakeSnapshotEvery) { diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index d5beb6530b..e30f385901 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -40,6 +40,7 @@ case class NodeConfigurationSettings(stateType: StateType, miningPubKeyHex: Option[String], offlineGeneration: Boolean, keepVersions: Int, + storingUtxoSnapshots: Int, acceptableChainUpdateDelay: FiniteDuration, mempoolCapacity: Int, mempoolCleanupDuration: FiniteDuration, @@ -56,6 +57,8 @@ case class NodeConfigurationSettings(stateType: StateType, * @return true if the blockchain is pruned, false if not */ val isFullBlocksPruned: Boolean = blocksToKeep >= 0 + + val areSnapshotsStored = storingUtxoSnapshots > 0 } trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettingsReader with ModifierIdReader { @@ -79,6 +82,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin cfg.as[Option[String]](s"$path.miningPubKeyHex"), cfg.as[Boolean](s"$path.offlineGeneration"), cfg.as[Int](s"$path.keepVersions"), + cfg.as[Int](s"$path.storingUtxoSnapshots"), cfg.as[FiniteDuration](s"$path.acceptableChainUpdateDelay"), cfg.as[Int](s"$path.mempoolCapacity"), cfg.as[FiniteDuration](s"$path.mempoolCleanupDuration"), 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 7f7ba44a65..2692a1c73d 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -33,7 +33,7 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, -1, utxoBootstrap = false, poPoWBootstrap = false, ChainGenerator.minimalSuffix, 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, + 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) val HEIGHT: Int = 30 diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index 33ec9d1f40..79bc79731d 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -36,6 +36,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -86,6 +87,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -129,6 +131,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 79ed94fe0a..854550656c 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -61,7 +61,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, -1, utxoBootstrap = false, poPoWBootstrap = false, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, - 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, + 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false) val ms = settings.chainSettings.monetary.copy( minerRewardDelay = RewardDelay diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index 14a9190e42..7d2ea4fcbe 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -48,7 +48,7 @@ trait HistoryTestHelpers extends ErgoPropertyTest { val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, - offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index e214f6a35e..ec887abd75 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -372,7 +372,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second,miningPubKeyHex = None, - offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null From 7803bec73bd30696a208eba0c9a1a9515fdba018 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 13 Mar 2023 20:59:21 +0300 Subject: [PATCH 115/204] storing from snapshot --- .../benchmarks/AVLTreeBatchPerformance.scala | 2 +- .../crypto/authds/benchmarks/Helper.scala | 4 +- .../serialization/ManifestSerializer.scala | 59 ++++ .../core/serialization/ScorexSerializer.scala | 17 -- .../serialization/SubtreeSerializer.scala | 43 +++ .../avltree/batch/AvlTreeParameters.scala | 9 + .../authds/avltree/batch/Constants.scala | 11 + .../batch/ProxyInternalProverNode.scala | 20 +- .../batch/VersionedLDBAVLStorage.scala | 273 ++++++++++++------ .../scala/scorex/db/LDBVersionedStore.scala | 19 ++ .../AVLStorageWithPersistentProverSpec.scala | 5 +- .../LDBVersionedStoreSpecification.scala | 1 - .../VersionedLDBAVLStorageSpecification.scala | 16 +- ...edLDBAVLStorageStatefulSpecification.scala | 1 - .../batch/benchmark/BatchingBenchmark.scala | 2 +- .../avltree/batch/benchmark/OOMTest.scala | 3 +- .../avltree/batch/helpers/TestHelper.scala | 5 +- .../bench/misc/ModifierWriter.scala | 2 +- .../difficulty/RequiredDifficulty.scala | 1 - .../modifiers/ErgoFullBlock.scala | 2 +- .../network/ErgoNodeViewSynchronizer.scala | 2 +- .../history/extra/ExtraIndexSerializer.scala | 2 +- .../UtxoSetSnapshotProcessor.scala | 15 +- .../nodeView/state/SnapshotsDb.scala | 30 +- .../state/UtxoSetSnapshotPersistence.scala | 19 +- .../nodeView/state/UtxoState.scala | 7 +- .../nodeView/state/UtxoStateReader.scala | 5 +- .../wallet/persistence/WalletDigest.scala | 1 + .../org/ergoplatform/settings/Constants.scala | 11 +- .../ergoplatform/settings/Parameters.scala | 2 +- .../scorex/core/network/PeerFeature.scala | 2 +- .../network/message/BasicMessagesRepo.scala | 2 +- .../core/network/message/MessageSpec.scala | 2 +- .../peer/LocalAddressPeerFeature.scala | 3 +- .../network/peer/RestApiUrlPeerFeature.scala | 4 +- .../network/peer/SessionIdPeerFeature.scala | 2 +- .../nodeView}/NodeViewSynchronizerTests.scala | 46 +-- ...txoSetSnapshotProcessorSpecification.scala | 34 ++- .../org/ergoplatform/sanity/ErgoSanity.scala | 1 + 39 files changed, 449 insertions(+), 236 deletions(-) create mode 100644 avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala rename {src => avldb/src}/main/scala/scorex/core/serialization/ScorexSerializer.scala (60%) create mode 100644 avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala create mode 100644 avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala rename src/test/scala/{scorex/testkit/properties => org/ergoplatform/nodeView}/NodeViewSynchronizerTests.scala (90%) diff --git a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala index 4cfed4f425..9742eaa13b 100644 --- a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala +++ b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/AVLTreeBatchPerformance.scala @@ -21,7 +21,7 @@ object AVLTreeBatchPerformance extends { val logger = LoggerFactory.getLogger("TEST") var prover: Prover = _ var store: LDBVersionedStore = _ - var storage: VersionedLDBAVLStorage[Digest32, HF] = _ + var storage: VersionedLDBAVLStorage = _ var operations: Array[Operation] = _ @Setup(Level.Iteration) diff --git a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala index c3e86664d4..ab87dc73a9 100644 --- a/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala +++ b/avldb/benchmarks/src/main/scala/scorex/crypto/authds/benchmarks/Helper.scala @@ -34,11 +34,11 @@ object Helper { } def persistentProverWithVersionedStore(initialKeepVersions: Int, - baseOperationsCount: Int = 0): (Prover, LDBVersionedStore, VersionedLDBAVLStorage[Digest32, HF]) = { + baseOperationsCount: Int = 0): (Prover, LDBVersionedStore, VersionedLDBAVLStorage) = { val dir = java.nio.file.Files.createTempDirectory("bench_testing_" + scala.util.Random.alphanumeric.take(15)).toFile dir.deleteOnExit() val store = new LDBVersionedStore(dir, initialKeepVersions = initialKeepVersions) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, AvlTreeParameters(kl, Some(vl), ll)) + val storage = new VersionedLDBAVLStorage(store) require(storage.isEmpty) val prover = new BatchAVLProver[Digest32, HF](kl, Some(vl)) diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala new file mode 100644 index 0000000000..811b121a9c --- /dev/null +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -0,0 +1,59 @@ +package scorex.core.serialization + +import com.google.common.primitives.Ints +import scorex.crypto.authds.avltree.batch.Constants.DigestType +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, ProxyInternalNode} +import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes, VersionedLDBAVLStorage} +import scorex.util.serialization.{Reader, Writer} + +class ManifestSerializer(manifestDepth: Byte) extends ScorexSerializer[BatchAVLProverManifest[DigestType]] { + private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer + + override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { + val height = manifest.rootHeight + w.putBytes(Ints.toByteArray(height)) + w.put(manifestDepth) + + def loop(node: ProverNodes[DigestType], level: Int): Unit = { + nodeSerializer.serialize(node, w) + node match { + case _: ProverLeaf[DigestType] => + case n: ProxyInternalNode[DigestType] if n.isEmpty => + case i: InternalProverNode[DigestType] if level < manifestDepth => + loop(i.left, level + 1) + loop(i.right, level + 1) + } + } + + loop(manifest.root, level = 1) + } + + override def parse(r: Reader): BatchAVLProverManifest[DigestType] = { + val rootHeight = Ints.fromByteArray(r.getBytes(4)) + val manifestDepth = r.getByte() + + require(manifestDepth == this.manifestDepth, "Wrong manifest depth") + + def loop(level: Int): ProverNodes[DigestType] = { + val node = nodeSerializer.parse(r) + node match { + case _: ProverLeaf[DigestType] => node + case i: InternalProverNode[DigestType] if level < manifestDepth => + val left = loop(level + 1) + val right = loop(level + 1) + i.getNew(newLeft = left, newRight = right) + case i: InternalProverNode[DigestType] if level == manifestDepth => + i + } + } + val tree = loop(level = 1) + new BatchAVLProverManifest[DigestType](tree, rootHeight) + } + +} + +object ManifestSerializer { + val ManifestDepth: Byte = 2 + lazy val defaultSerializer = new ManifestSerializer(ManifestDepth) +} + diff --git a/src/main/scala/scorex/core/serialization/ScorexSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala similarity index 60% rename from src/main/scala/scorex/core/serialization/ScorexSerializer.scala rename to avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala index e65027aa84..bffed7872d 100644 --- a/src/main/scala/scorex/core/serialization/ScorexSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala @@ -1,8 +1,6 @@ package scorex.core.serialization import java.nio.ByteBuffer - -import akka.util.ByteString import scorex.util.ByteArrayBuilder import scorex.util.serialization._ @@ -10,21 +8,6 @@ import scala.util.Try trait ScorexSerializer[T] extends Serializer[T, T, Reader, Writer] { - def toByteString(obj: T): ByteString = { - val writer = new VLQByteStringWriter() - serialize(obj, writer) - writer.result() - } - - def parseByteString(byteString: ByteString): T = { - val reader = new VLQByteStringReader(byteString) - parse(reader) - } - - def parseByteStringTry(byteString: ByteString): Try[T] = { - Try(parseByteString(byteString)) - } - def toBytes(obj: T): Array[Byte] = { val writer = new VLQByteBufferWriter(new ByteArrayBuilder()) serialize(obj, writer) diff --git a/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala new file mode 100644 index 0000000000..2fd854d6f6 --- /dev/null +++ b/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala @@ -0,0 +1,43 @@ +package scorex.core.serialization + +import scorex.crypto.authds.avltree.batch.Constants.DigestType +import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes, VersionedLDBAVLStorage} +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverSubtree, ProxyInternalNode} +import scorex.util.ScorexLogging +import scorex.util.serialization.{Reader, Writer} + +object SubtreeSerializer extends ScorexSerializer[BatchAVLProverSubtree[DigestType]] with ScorexLogging { + private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer + + override def serialize(subtree: BatchAVLProverSubtree[DigestType], w: Writer): Unit = { + def loop(node: ProverNodes[DigestType]): Unit = { + nodeSerializer.serialize(node, w) + node match { + case _: ProverLeaf[DigestType] => + case n: ProxyInternalNode[DigestType] if n.isEmpty => + log.warn("Proxy node in subtree serialization") + case i: InternalProverNode[DigestType] => + loop(i.left) + loop(i.right) + } + } + + loop(subtree.subtreeTop) + } + + override def parse(r: Reader): BatchAVLProverSubtree[DigestType] = { + def loop(): ProverNodes[DigestType] = { + val node = nodeSerializer.parse(r) + node match { + case _: ProverLeaf[DigestType] => node + case i: InternalProverNode[DigestType] => + val left = loop() + val right = loop() + i.getNew(newLeft = left, newRight = right) + } + } + val tree = loop() + new BatchAVLProverSubtree[DigestType](tree) + } + +} diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala index 273e2412ef..0736d4c5dd 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/AvlTreeParameters.scala @@ -1,5 +1,7 @@ package scorex.crypto.authds.avltree.batch +import Constants.HashLength + /** * Parameters of AVL+ tree nodes (internal and leaves) * @param keySize - size of a key (fixed) @@ -12,3 +14,10 @@ case class AvlTreeParameters(keySize: Int, valueSize: Option[Int], labelSize: In */ def fixedSizeValue: Boolean = valueSize.isDefined } + + +/** + * AVL+ tree node parameters. The tree is used to authenticate UTXO set. + * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. + */ +object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala new file mode 100644 index 0000000000..43b1a183c4 --- /dev/null +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala @@ -0,0 +1,11 @@ +package scorex.crypto.authds.avltree.batch + +import scorex.crypto.hash.{Blake2b256, Digest32} + +object Constants { + type HashFnType = Blake2b256.type + type DigestType = Digest32 + + val hashFn: HashFnType = Blake2b256 + val HashLength: Int = 32 +} diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala index 8455a18908..c82b52cf3e 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProxyInternalProverNode.scala @@ -1,9 +1,9 @@ package scorex.crypto.authds.avltree.batch import scorex.crypto.authds.{ADKey, Balance} -import scorex.crypto.hash.{CryptographicHash, Digest} import scorex.db.LDBVersionedStore import InternalNode.InternalNodePrefix +import scorex.crypto.authds.avltree.batch.Constants.{DigestType, hashFn} /** * Internal node where children are not provided during node construction, only pointers to them, @@ -11,29 +11,27 @@ import InternalNode.InternalNodePrefix * of the same type). It allows for lazy loading of a tree. * */ -class ProxyInternalProverNode[D <: Digest](protected var pk: ADKey, +class ProxyInternalProverNode(protected var pk: ADKey, val leftLabel: ADKey, val rightLabel: ADKey, protected var pb: Balance = Balance @@ 0.toByte) - (implicit val phf: CryptographicHash[D], - store: LDBVersionedStore, - nodeParameters: AvlTreeParameters) - extends InternalProverNode(k = pk, l = null, r = null, b = pb)(phf) { + (store: LDBVersionedStore) + extends InternalProverNode(k = pk, l = null, r = null, b = pb)(hashFn) { - override protected def computeLabel: D = { + override protected def computeLabel: DigestType = { hf.hash(Array(InternalNodePrefix, b), leftLabel, rightLabel) } - override def left: ProverNodes[D] = { + override def left: ProverNodes[DigestType] = { if (l == null) { - l = VersionedLDBAVLStorage.fetch[D](leftLabel) + l = VersionedLDBAVLStorage.fetch(leftLabel)(store) } l } - override def right: ProverNodes[D] = { + override def right: ProverNodes[DigestType] = { if (r == null) { - r = VersionedLDBAVLStorage.fetch[D](rightLabel) + r = VersionedLDBAVLStorage.fetch(rightLabel)(store) } r } diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index a252c4b1f5..cc80dea311 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -1,13 +1,16 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints +import scorex.core.serialization.{ManifestSerializer, ScorexSerializer} +import scorex.crypto.authds.avltree.batch.Constants.{DigestType, HashFnType, hashFn} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} import scorex.crypto.authds.{ADDigest, ADKey, ADValue, Balance} import scorex.util.encode.Base58 import scorex.crypto.hash -import scorex.crypto.hash.{CryptographicHash, Digest} -import scorex.db.LDBVersionedStore +import scorex.crypto.hash.Digest32 +import scorex.db.{LDBKVStore, LDBVersionedStore} import scorex.util.ScorexLogging +import scorex.util.serialization.{Reader, Writer} import scala.collection.mutable import scala.util.{Failure, Try} @@ -16,24 +19,18 @@ import scala.util.{Failure, Try} * Persistent versioned authenticated AVL+ tree implementation on top of versioned LevelDB storage * * @param store - level db storage to save the tree in - * @param nodeParameters - parameters of the tree node (key size, optional value size, label size) - * @param hf - hash function used to construct the tree - * @tparam D - type of hash function digest - * @tparam HF - type of hash function used in AVL+ tree */ -class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDBVersionedStore, - nodeParameters: AvlTreeParameters) - (implicit val hf: HF) - extends VersionedAVLStorage[D] with ScorexLogging { +class VersionedLDBAVLStorage(store: LDBVersionedStore) + extends VersionedAVLStorage[DigestType] with ScorexLogging { - import VersionedLDBAVLStorage.{nodeLabel, toBytes, topNodeKeys} + import VersionedLDBAVLStorage.{nodeLabel, topNodeKeys} - private val topKeys = topNodeKeys(nodeParameters) + private val topKeys = topNodeKeys() private val topNodeHashKey: Array[Byte] = topKeys._1 private val topNodeHeightKey: Array[Byte] = topKeys._2 - private def restorePrunedTopNode(): Try[(ProverNodes[D], Int)] = Try { - val top = VersionedLDBAVLStorage.fetch[D](ADKey @@ store.get(topNodeHashKey).get)(hf, store, nodeParameters) + private def restorePrunedTopNode(): Try[(ProverNodes[DigestType], Int)] = Try { + val top = VersionedLDBAVLStorage.fetch(ADKey @@ store.get(topNodeHashKey).get)(store) val topHeight = Ints.fromByteArray(store.get(topNodeHeightKey).get) top -> topHeight @@ -42,13 +39,13 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB /** * Restore prover's tree from database with just pruned top node (so only one node is fetched) */ - def restorePrunedProver(): Try[BatchAVLProver[D, HF]] = { + def restorePrunedProver(): Try[BatchAVLProver[DigestType, HashFnType]] = { restorePrunedTopNode().map { recoveredTop => - new BatchAVLProver(nodeParameters.keySize, nodeParameters.valueSize, Some(recoveredTop))(hf) + new BatchAVLProver(StateTreeParameters.keySize, StateTreeParameters.valueSize, Some(recoveredTop))(hashFn) } } - override def rollback(version: ADDigest): Try[(ProverNodes[D], Int)] = Try { + override def rollback(version: ADDigest): Try[(ProverNodes[DigestType], Int)] = Try { if (!this.version.contains(version)) { // do not rollback to self store.rollbackTo(version) } @@ -62,7 +59,7 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB def rollbackVersions: Iterable[ADDigest] = store.rollbackVersions().map(d => ADDigest @@ d) - override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[D, _], + override def update[K <: Array[Byte], V <: Array[Byte]](prover: BatchAVLProver[DigestType, _], additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest val indexes = Seq(topNodeHashKey -> nodeLabel(prover.topNode), @@ -75,17 +72,18 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB store.update(digestWrapper, toRemove, toUpdateWithWrapped) } - private def serializedVisitedNodes(node: ProverNodes[D], + //todo: optimize the method + private def serializedVisitedNodes(node: ProverNodes[DigestType], isTop: Boolean): Array[(Array[Byte], Array[Byte])] = { // Should always serialize top node. It may not be new if it is the creation of the tree if (node.isNew || isTop) { - val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node, nodeParameters)) + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), VersionedLDBAVLStorage.noStoreSerializer.toBytes(node)) node match { - case n: InternalProverNode[D] => + case n: InternalProverNode[DigestType] => val leftSubtree = serializedVisitedNodes(n.left, isTop = false) val rightSubtree = serializedVisitedNodes(n.right, isTop = false) pair +: (leftSubtree ++ rightSubtree) - case _: ProverLeaf[D] => Array(pair) + case _: ProverLeaf[DigestType] => Array(pair) } } else { Array.empty @@ -93,7 +91,61 @@ class VersionedLDBAVLStorage[D <: Digest, HF <: CryptographicHash[D]](store: LDB } //todo: this method is not used, should be removed on next scrypto update? - override def update(prover: BatchAVLProver[D, _]): Try[Unit] = update(prover, Nil) + override def update(prover: BatchAVLProver[DigestType, _]): Try[Unit] = update(prover, Nil) + + def dumpSnapshot(dumpStorage: LDBKVStore, + manifestDepth: Int): Array[Byte] = { + store.backup { ri => + val rootNodeLabel = ri.get(topNodeHashKey) + val rootNodeHeight = Ints.fromByteArray(ri.get(topNodeHeightKey)) + + val manifestBuilder = mutable.ArrayBuilder.make[Byte]() + manifestBuilder.sizeHint(200000) + manifestBuilder ++= Ints.toByteArray(rootNodeHeight) + manifestBuilder += ManifestSerializer.ManifestDepth + + def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { + val nodeBytes = ri.get(label) + builder ++= nodeBytes + val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) + node match { + case in: ProxyInternalNode[DigestType] => + subtreeLoop(Digest32 @@ in.leftLabel, builder) + subtreeLoop(Digest32 @@ in.rightLabel, builder) + case _ => + } + } + + def dumpSubtree(sid: DigestType): Try[Unit] = { + val builder = mutable.ArrayBuilder.make[Byte]() + builder.sizeHint(200000) + subtreeLoop(sid, builder) + dumpStorage.insert(sid, builder.result()) + } + + def manifestLoop(nodeDbKey: Array[Byte], level: Int): Unit = { + val nodeBytes = ri.get(nodeDbKey) + manifestBuilder ++= nodeBytes + val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) + node match { + case in: ProxyInternalNode[DigestType] if level == manifestDepth => + dumpSubtree(Digest32 @@ in.leftLabel) + dumpSubtree(Digest32 @@ in.rightLabel) + case in: ProxyInternalNode[DigestType] => + manifestLoop(in.leftLabel, level + 1) + manifestLoop(in.rightLabel, level + 1) + case _ => + //todo: support leafs + println("!!!") + } + } + + manifestLoop(rootNodeLabel, level = 1) + val manifestBytes = manifestBuilder.result() + dumpStorage.insert(rootNodeLabel, manifestBytes) + rootNodeLabel + } + } } @@ -102,30 +154,36 @@ object VersionedLDBAVLStorage { private[batch] val InternalNodePrefix: Byte = 0: Byte private[batch] val LeafPrefix: Byte = 1: Byte - private[batch] def topNodeKeys(nodeParameters: AvlTreeParameters) = { - val topNodeKey: Array[Byte] = Array.fill(nodeParameters.labelSize)(123: Byte) - val topNodeHeightKey: Array[Byte] = Array.fill(nodeParameters.labelSize)(124: Byte) + val noStoreSerializer = new ProverNodeSerializer(null) + + private[batch] def topNodeKeys(): (Array[Byte], Array[Byte]) = { + val topNodeKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(123: Byte) + val topNodeHeightKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(124: Byte) (topNodeKey, topNodeHeightKey) } + /* /** - * Fetch tree node from database by its database id (hash of node contents) - * - * @param dbKey - database key node of interest is stored under (hash of the node) - * @param hf - hash function instance - * @param store - database - * @param nodeParameters - tree node parameters - * @tparam D - type of an output of a hash function used - * @return node read from the database (or throws exception if there is no such node), in case of internal node it - * returns pruned internal node, so a node where left and right children not stored, only their hashes + * Serialize tree node (only, without possible children) */ - def fetch[D <: hash.Digest](dbKey: ADKey)(implicit hf: CryptographicHash[D], - store: LDBVersionedStore, - nodeParameters: AvlTreeParameters): ProverNodes[D] = { - val bytes = store(dbKey) - lazy val keySize = nodeParameters.keySize - lazy val labelSize = nodeParameters.labelSize + private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D]): Array[Byte] = { + val builder = new mutable.ArrayBuilder.ofByte + node match { + case n: ProxyInternalNode[D] => + builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel + case n: InternalProverNode[D] => + builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label + case n: ProverLeaf[D] => + builder += LeafPrefix ++= n.key ++= Ints.toByteArray(n.value.length) ++= n.value ++= n.nextLeafKey + } + builder.result() + } + // todo: replace store: LDBVersionedStore with some generic interface + def fromBytes[D <: hash.Digest](bytes: Array[Byte], + store: LDBVersionedStore): ProverNodes[DigestType] = { + lazy val keySize = StateTreeParameters.keySize + lazy val labelSize = StateTreeParameters.labelSize bytes.head match { case InternalNodePrefix => val balance = Balance @@ bytes.slice(1, 2).head @@ -133,72 +191,62 @@ object VersionedLDBAVLStorage { val leftKey = ADKey @@ bytes.slice(2 + keySize, 2 + keySize + labelSize) val rightKey = ADKey @@ bytes.slice(2 + keySize + labelSize, 2 + keySize + (2 * labelSize)) - val n = new ProxyInternalProverNode[D](key, leftKey, rightKey, balance) - n.isNew = false - n + if(store != null) { + new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) + } else { + new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) + } + case LeafPrefix => val key = ADKey @@ bytes.slice(1, 1 + keySize) - val (value, nextLeafKey) = if (nodeParameters.valueSize.isDefined) { - val valueSize = nodeParameters.valueSize.get - val value = ADValue @@ bytes.slice(1 + keySize, 1 + keySize + valueSize) - val nextLeafKey = ADKey @@ bytes.slice(1 + keySize + valueSize, 1 + (2 * keySize) + valueSize) - value -> nextLeafKey - } else { + val (value, nextLeafKey) = { val valueSize = Ints.fromByteArray(bytes.slice(1 + keySize, 1 + keySize + 4)) val value = ADValue @@ bytes.slice(1 + keySize + 4, 1 + keySize + 4 + valueSize) val nextLeafKey = ADKey @@ bytes.slice(1 + keySize + 4 + valueSize, 1 + (2 * keySize) + 4 + valueSize) value -> nextLeafKey } - val l = new ProverLeaf[D](key, value, nextLeafKey) - l.isNew = false - l + new ProverLeaf[DigestType](key, value, nextLeafKey)(hashFn) } } + */ /** - * Node label (hash). Used as database key for node contents + * Fetch tree node from database by its database id (hash of node contents) + * + * @param dbKey - database key node of interest is stored under (hash of the node) + * @param store - database + * @return node read from the database (or throws exception if there is no such node), in case of internal node it + * returns pruned internal node, so a node where left and right children not stored, only their hashes */ - private[batch] def nodeLabel[D <: hash.Digest](node: ProverNodes[D]): Array[Byte] = node.label + def fetch(dbKey: ADKey)(store: LDBVersionedStore): ProverNodes[DigestType] = { + val bytes = store(dbKey) + val node = new ProverNodeSerializer(store).parseBytes(bytes) + node.isNew = false + node + } /** - * Serialize tree node (only, without possible children) + * Node label (hash). Used as database key for node contents */ - private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D], nodeParameters: AvlTreeParameters): Array[Byte] = { - val builder = new mutable.ArrayBuilder.ofByte; - node match { - case n: ProxyInternalNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel - case n: InternalProverNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label - case n: ProverLeaf[D] => - if (nodeParameters.fixedSizeValue) { - builder += LeafPrefix ++= n.key ++= n.value ++= n.nextLeafKey - } else { - builder += LeafPrefix ++= n.key ++= Ints.toByteArray(n.value.length) ++= n.value ++= n.nextLeafKey - } - } - builder.result() - } - + private[batch] def nodeLabel(node: ProverNodes[DigestType]): Array[Byte] = node.label - def recreate[D <: hash.Digest, HF <: CryptographicHash[D]](manifest: BatchAVLProverManifest[D], - chunks: Iterator[BatchAVLProverSubtree[D]], + def recreate(manifest: BatchAVLProverManifest[DigestType], + chunks: Iterator[BatchAVLProverSubtree[DigestType]], additionalData: Iterator[(Array[Byte], Array[Byte])], - store: LDBVersionedStore, - nodeParameters: AvlTreeParameters)(implicit hf: HF): Try[VersionedLDBAVLStorage[D, HF]] = { + store: LDBVersionedStore): Try[VersionedLDBAVLStorage] = { //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate - val (topNodeHashKey, topNodeHeightKey) = topNodeKeys(nodeParameters) + val (topNodeHashKey, topNodeHeightKey) = topNodeKeys() - def idCollector(node: ProverNodes[D], + def idCollector(node: ProverNodes[DigestType], acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { - val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), toBytes(node, nodeParameters)) + val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), noStoreSerializer.toBytes(node)) node match { - case n: ProxyInternalNode[D] if n.isEmpty => + case n: ProxyInternalNode[DigestType] if n.isEmpty => acc ++ Iterator(pair) - case i: InternalProverNode[D] => + case i: InternalProverNode[DigestType] => acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) - case _: ProverLeaf[D] => + case _: ProverLeaf[DigestType] => acc ++ Iterator(pair) } } @@ -210,7 +258,7 @@ object VersionedLDBAVLStorage { val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map{_ => - new VersionedLDBAVLStorage[D, HF](store, nodeParameters) + new VersionedLDBAVLStorage(store) } } @@ -226,5 +274,60 @@ object VersionedLDBAVLStorage { // then the AVL tree has at least 2^{255/1.4405} = 2^177 leaves, which is more than the number of atoms on planet Earth. ADDigest @@ (rootNodeLabel :+ rootNodeHeight.toByte) } - + +} + +class ProverNodeSerializer(store: LDBVersionedStore) extends ScorexSerializer[ProverNodes[DigestType]] { + + import VersionedLDBAVLStorage.{InternalNodePrefix,LeafPrefix} + + override def serialize(node: ProverNodes[DigestType], w: Writer): Unit = { + node match { + case n: ProxyInternalNode[DigestType] => + w.put(InternalNodePrefix) + w.put(n.balance) + w.putBytes(n.key) + w.putBytes(n.leftLabel) + w.putBytes(n.rightLabel) + case n: InternalProverNode[DigestType] => + w.put(InternalNodePrefix) + w.put(n.balance) + w.putBytes(n.key) + w.putBytes(n.left.label) + w.putBytes(n.right.label) + case n: ProverLeaf[DigestType] => + w.put(LeafPrefix) + w.putBytes(n.key) + w.putBytes(Ints.toByteArray(n.value.length)) + w.putBytes(n.value) + w.putBytes(n.nextLeafKey) + } + } + + override def parse(r: Reader): ProverNodes[DigestType] = { + val prefix = r.getByte() + prefix match { + case InternalNodePrefix => + val balance = Balance @@ r.getByte() + val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) + val leftKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) + val rightKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) + + if (store != null) { + new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) + } else { + new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) + } + case LeafPrefix => + val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) + val (value, nextLeafKey) = { + val valueSize = Ints.fromByteArray(r.getBytes(4)) + val value = ADValue @@ r.getBytes(valueSize) + val nextLeafKey = ADKey @@ r.getBytes(StateTreeParameters.keySize) + value -> nextLeafKey + } + new ProverLeaf[DigestType](key, value, nextLeafKey)(hashFn) + } + } + } diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 2791f8f03c..2e5e6c2e35 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -407,4 +407,23 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e def rollbackVersions(): Iterable[VersionID] = { versions.reverse } + + trait ReadInterface { + def get(key: Array[Byte]): Array[Byte] + } + + def backup[T](logic: ReadInterface => T): T = { + val ro = new ReadOptions() + ro.snapshot(db.getSnapshot) + try { + val ri = new ReadInterface { + override def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) + } + logic(ri) + } finally { + // Make sure you close the snapshot to avoid resource leaks. + ro.snapshot().close() + } + } + } diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala index 94afdcd659..47588f0af5 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/AVLStorageWithPersistentProverSpec.scala @@ -17,10 +17,7 @@ class AVLStorageWithPersistentProverSpec extends AnyPropSpec with Matchers { val stateStore = new LDBVersionedStore(getRandomTempDir, 10) - private lazy val np = - AvlTreeParameters(keySize = 32, valueSize = None, labelSize = 32) - - protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](stateStore, np) + protected lazy val storage = new VersionedLDBAVLStorage(stateStore) protected lazy val persistentProver: PersistentBatchAVLProver[Digest32, HF] = PersistentBatchAVLProver.create( diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala index f3236cfff3..c1fe98d5df 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala @@ -17,7 +17,6 @@ class LDBVersionedStoreSpecification extends AnyPropSpec override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 val storeTest: LDBVersionedStore => Unit = { store => var version = store.lastVersionID 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 31e90fdecd..b702f5de7d 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 @@ -10,7 +10,7 @@ import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.db.LDBVersionedStore +import scorex.db.{LDBFactory, LDBVersionedStore} import scorex.utils.{Random => RandomBytes} import scala.concurrent.ExecutionContext.Implicits.global @@ -25,7 +25,6 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 def kvGen: Gen[(ADKey, ADValue)] = for { key <- Gen.listOfN(KL, Arbitrary.arbitrary[Byte]).map(_.toArray) suchThat @@ -340,4 +339,17 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec testAddInfoSaving(createVersionedStore _) } + property("dumping snapshot") { + val prover = createPersistentProver() + blockchainWorkflowTest(prover) + + val storage = prover.storage.asInstanceOf[VersionedLDBAVLStorage] + val store = LDBFactory.createKvDb("/tmp/aa") + + val ts0 = System.currentTimeMillis() + storage.dumpSnapshot(store, 4) + val ts = System.currentTimeMillis() + println("time: " + (ts-ts0)) + } + } diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala index a7b6288edd..3d393582c6 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala @@ -27,7 +27,6 @@ object WithLDB extends VersionedLDBAVLStorageStatefulCommands with TestHelper { override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 override protected def createStatefulProver: PersistentBatchAVLProver[Digest32, HF] = { createPersistentProver(keepVersions) diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala index 03b0f5790a..6179c933cc 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/BatchingBenchmark.scala @@ -19,7 +19,7 @@ object BatchingBenchmark extends App with FileHelper { type HF = Blake2b256.type val store = new LDBVersionedStore(getRandomTempDir, initialKeepVersions = 10) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, AvlTreeParameters(KeyLength, Some(ValueLength), LabelLength)) + val storage = new VersionedLDBAVLStorage(store) require(storage.isEmpty) val mods = generateModifications() var digest: ADDigest = ADDigest @@ Array[Byte]() diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala index 922f57fd5c..03e82ed774 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala @@ -23,8 +23,7 @@ object OOMTest extends App { val store = new LDBVersionedStore(dir, initialKeepVersions = 200) val bestVersionKey = Blake2b256("best state version") - private lazy val np = AvlTreeParameters(keySize = 32, valueSize = None, labelSize = 32) - protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, np) + protected lazy val storage = new VersionedLDBAVLStorage(store) val afterGenesisStateDigestHex: String = "78b130095239561ecf5449a7794c0615326d1fd007cc79dcc286e46e4beb1d3f01" val afterGenesisStateDigest: ADDigest = ADDigest @@ Base16.decode(afterGenesisStateDigestHex).get diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala index 3ad5b171bc..0b2c4d79a9 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala @@ -15,11 +15,10 @@ trait TestHelper extends FileHelper { type PROVER = BatchAVLProver[D, HF] type VERIFIER = BatchAVLVerifier[D, HF] type PERSISTENT_PROVER = PersistentBatchAVLProver[D, HF] - type STORAGE = VersionedLDBAVLStorage[D, HF] + type STORAGE = VersionedLDBAVLStorage protected val KL: Int protected val VL: Int - protected val LL: Int implicit val hf: HF = Blake2b256 @@ -29,7 +28,7 @@ trait TestHelper extends FileHelper { } def createVersionedStorage(store: LDBVersionedStore): STORAGE = - new VersionedLDBAVLStorage(store, AvlTreeParameters(KL, Some(VL), LL)) + new VersionedLDBAVLStorage(store) def createPersistentProver(storage: STORAGE): PERSISTENT_PROVER = { val prover = new BatchAVLProver[D, HF](KL, Some(VL)) diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala index 1372cab088..09e04bf478 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala @@ -6,8 +6,8 @@ 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.ScorexSerializer import scorex.core.NodeViewModifier +import scorex.core.serialization.ScorexSerializer object ModifierWriter { diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala index ed59b4fd80..f9aee27f31 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala +++ b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala @@ -1,7 +1,6 @@ package org.ergoplatform.mining.difficulty import java.math.BigInteger - import org.ergoplatform.nodeView.history.ErgoHistory._ import scorex.core.serialization.ScorexSerializer import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 6fb1a0ff43..b8ad28d1df 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -7,8 +7,8 @@ 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.ScorexSerializer import scorex.core.TransactionsCarryingPersistentNodeViewModifier +import scorex.core.serialization.ScorexSerializer import scorex.util.ModifierId case class ErgoFullBlock(header: Header, diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6de4f6e817..1b357aecc1 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -28,7 +28,6 @@ import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, Request import scorex.core.network._ import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPeers} import scorex.core.network.message.{InvData, Message, ModifiersData} -import scorex.core.serialization.ScorexSerializer import scorex.core.settings.NetworkSettings import scorex.core.utils.ScorexEncoding import scorex.core.validation.MalformedModifierError @@ -48,6 +47,7 @@ import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height +import scorex.core.serialization.ScorexSerializer /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the 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 0c22fe4956..3200a74570 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.extra import scorex.core.serialization.ScorexSerializer import scorex.util.serialization.{Reader, Writer} -object ExtraIndexSerializer extends ScorexSerializer[ExtraIndex]{ +object ExtraIndexSerializer extends ScorexSerializer[ExtraIndex]{ override def serialize(obj: ExtraIndex, w: Writer): Unit = { obj match { 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 04d8756819..70414f44c7 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 @@ -5,16 +5,18 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId -import org.ergoplatform.settings.{Algos, Constants, ErgoSettings} +import org.ergoplatform.settings.{Algos, ErgoSettings} import scorex.core.VersionTag import scorex.core.network.ConnectedPeer -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.core.serialization.SubtreeSerializer +import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore import scorex.util.{ByteArrayBuilder, ModifierId, ScorexLogging} import scorex.util.serialization.VLQByteBufferWriter import spire.syntax.all.cfor -import scala.util.{Try, Random} + +import scala.util.{Random, Try} import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} /** @@ -31,9 +33,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private[history] var minimalFullBlockHeightVar: Int - private implicit val hf = Blake2b256 - private val avlTreeSerializer = new BatchAVLProverSerializer[Digest32, Blake2b256.type]() - var _utxoSnapshotApplied = false //todo: persistence? def isUtxoSnapshotApplied = _utxoSnapshotApplied @@ -135,7 +134,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { val idxBytes = Ints.toByteArray(idx) historyStorage .get(downloadedChunksPrefix ++ idxBytes) - .flatMap(bs => avlTreeSerializer.subtreeFromBytes(bs, 32).toOption) + .flatMap(bs => SubtreeSerializer.parseBytesTry(bs).toOption) } case None => log.error("todo: msg") // todo: @@ -217,7 +216,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { log.info("Starting UTXO set snapshot transfer into state database") val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore, Constants.StateTreeParameters).flatMap { + VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { ldbStorage => log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 1ca132f4fc..8a3968b67f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -6,13 +6,14 @@ import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} import org.ergoplatform.wallet.Constants +import scorex.core.serialization.ManifestSerializer +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging import scorex.util.encode.Base16 -import scala.collection.mutable import scala.util.{Failure, Success, Try} class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { @@ -48,6 +49,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def pruneSnapshots(before: Height): Unit = { + //todo: consider storingUtxoSnapsots setting log.info("Starting snapshots pruning") val (toPrune, toLeave) = readSnapshotsInfo .availableManifests @@ -77,30 +79,13 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { log.info("Snapshots pruning finished") } - def writeSnapshot(height: Height, - manifest: UtxoState.Manifest, - subtrees: Seq[UtxoState.Subtree]): Unit = { - - val keysBuilder = mutable.ArrayBuilder.make[Array[Byte]]() - keysBuilder.sizeHint(subtrees.length + 1) - - val valuesBuilder = mutable.ArrayBuilder.make[Array[Byte]]() - valuesBuilder.sizeHint(subtrees.length + 1) - - keysBuilder += manifest.id - valuesBuilder += serializer.manifestToBytes(manifest) - - // todo: not efficient to write tree to the memory ? - subtrees.foreach { s => - keysBuilder += s.id - valuesBuilder += serializer.subtreeToBytes(s) - } - store.insert(keysBuilder.result(), valuesBuilder.result()) - val si = readSnapshotsInfo.withNewManifest(height, manifest.id) + def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Array[Byte] = { + val manifestId = pullFrom.dumpSnapshot(store, ManifestSerializer.ManifestDepth) + val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) writeSnapshotsInfo(si) + manifestId } - def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { store.get(id) } @@ -108,6 +93,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { store.get(id) } + } object SnapshotsDb { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index c66b629a1b..aeefa10dd5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -2,10 +2,8 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} -import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF -import scorex.crypto.authds.avltree.batch.PersistentBatchAVLProver -import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSerializer, BatchAVLProverSubtree} +import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapshot} @@ -15,15 +13,10 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { def constants: StateConstants protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] - private val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? + private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? - //todo: scaladoc - //todo: return Iterator? - def slicedTree(): (BatchAVLProverManifest[Digest32], Seq[BatchAVLProverSubtree[Digest32]]) = { - persistentProver.synchronized { - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) - serializer.slice(persistentProver.avlProver, subtreeDepth = 12) //todo: name constant - } + private[nodeView] def dumpSnapshot(height: Height): Array[Byte] = { + snapshotsDb.writeSnapshot(persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage], height) } protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { @@ -32,11 +25,9 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { estimatedTip.nonEmpty && estimatedTip.get - height <= MakeSnapshotEvery) { - val (manifest, subtrees) = slicedTree() - val ms0 = System.currentTimeMillis() snapshotsDb.pruneSnapshots(height - MakeSnapshotEvery * 2) - snapshotsDb.writeSnapshot(height, manifest, subtrees) + dumpSnapshot(height) val ms = System.currentTimeMillis() log.info("Time to dump utxo set snapshot: " + (ms - ms0)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index aca2d37a30..ca0c5cff5b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, Constants, Parameters} +import org.ergoplatform.settings.{Algos, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -22,7 +22,6 @@ import scorex.crypto.authds.{ADDigest, ADValue} import scorex.crypto.hash.Digest32 import scorex.db.{ByteArrayWrapper, LDBVersionedStore} import scorex.util.ModifierId -import Constants.StateTreeParameters import scala.util.{Failure, Success, Try} @@ -291,7 +290,7 @@ object UtxoState { .getOrElse(ErgoState.genesisStateVersion) val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { val bp = new BatchAVLProver[Digest32, HF](keyLength = 32, valueLengthOpt = None) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters)(Algos.hash) + val storage = new VersionedLDBAVLStorage(store) PersistentBatchAVLProver.create(bp, storage).get } new UtxoState(persistentProver, version, store, constants) @@ -314,7 +313,7 @@ object UtxoState { val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) val defaultStateContext = ErgoStateContext.empty(constants, parameters) - val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters)(Algos.hash) + val storage = new VersionedLDBAVLStorage(store) val persistentProver = PersistentBatchAVLProver.create( p, storage, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 1580f07f27..6b4d995866 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,7 +5,7 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.settings.{Algos, Constants} +import org.ergoplatform.settings.Algos import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter @@ -15,7 +15,6 @@ import scorex.core.validation.MalformedModifierError import scorex.crypto.authds.avltree.batch.{Lookup, PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.authds.{ADDigest, ADKey, SerializedAdProof} import scorex.crypto.hash.Digest32 -import Constants.StateTreeParameters import scala.util.{Failure, Success, Try} @@ -25,7 +24,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi val constants: StateConstants - protected lazy val storage = new VersionedLDBAVLStorage[Digest32, HF](store, StateTreeParameters) + protected lazy val storage = new VersionedLDBAVLStorage(store) protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] 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 706e6d8ea0..5facb8f6bd 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -6,6 +6,7 @@ import org.ergoplatform.settings.Constants import scorex.core.serialization.ScorexSerializer import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} + import scala.collection.mutable import scorex.util.Extensions._ diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index d49083b9d1..43adc2bcf9 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -7,15 +7,14 @@ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSeriali 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.serialization.ScorexSerializer import scorex.core.NodeViewModifier -import scorex.crypto.authds.avltree.batch.AvlTreeParameters +import scorex.core.serialization.ScorexSerializer import sigmastate.Values import sigmastate.Values.ErgoTree object Constants { - val HashLength: Int = 32 + val HashLength: Int = scorex.crypto.authds.avltree.batch.Constants.HashLength val CoinsInOneErgo: Long = 1000000000 @@ -65,12 +64,6 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 - /** - * AVL+ tree node parameters. The tree is used to authenticate UTXO set. - * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. - */ - object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength) - val MakeSnapshotEvery = 1024 // test value, switch to 51200 after testing def timeToTakeSnapshot(height: Int): Boolean = { diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/src/main/scala/org/ergoplatform/settings/Parameters.scala index 0d77faca7f..e561e817d2 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -4,7 +4,6 @@ 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.ScorexSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -13,6 +12,7 @@ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters import Extension.SystemParametersPrefix +import scorex.core.serialization.ScorexSerializer /** * System parameters which could be readjusted via collective miners decision. diff --git a/src/main/scala/scorex/core/network/PeerFeature.scala b/src/main/scala/scorex/core/network/PeerFeature.scala index 8dc31a156e..6ceac2d8a2 100644 --- a/src/main/scala/scorex/core/network/PeerFeature.scala +++ b/src/main/scala/scorex/core/network/PeerFeature.scala @@ -18,4 +18,4 @@ trait PeerFeature extends BytesSerializable { object PeerFeature { type Id = Byte type Serializers = Map[Id, ScorexSerializer[_ <: PeerFeature]] -} \ No newline at end of file +} diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 6e57abf93f..2320a2ccdf 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -8,8 +8,8 @@ import org.ergoplatform.wallet.Constants import scorex.core.consensus.SyncInfo import scorex.core.network._ import scorex.core.network.message.Message.MessageCode -import scorex.core.serialization.ScorexSerializer import scorex.core.NodeViewModifier +import scorex.core.serialization.ScorexSerializer import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/scorex/core/network/message/MessageSpec.scala b/src/main/scala/scorex/core/network/message/MessageSpec.scala index 730acc04bb..6f1e814ab3 100644 --- a/src/main/scala/scorex/core/network/message/MessageSpec.scala +++ b/src/main/scala/scorex/core/network/message/MessageSpec.scala @@ -34,4 +34,4 @@ trait MessageSpecV1[Content] extends MessageSpec[Content] { override val protocolVersion: Version = Version.initial -} \ No newline at end of file +} diff --git a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala b/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala index b24680e2f2..d6ee53aa46 100644 --- a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala @@ -1,12 +1,11 @@ package scorex.core.network.peer import java.net.{InetAddress, InetSocketAddress} - import org.ergoplatform.settings.PeerFeatureDescriptors import scorex.core.network.PeerFeature import scorex.core.network.PeerFeature.Id -import scorex.util.serialization._ import scorex.core.serialization.ScorexSerializer +import scorex.util.serialization._ import scorex.util.Extensions._ /** diff --git a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala index 6105da5fc3..ccca6c49f4 100644 --- a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala @@ -2,11 +2,11 @@ package scorex.core.network.peer import scorex.core.network.PeerFeature import scorex.core.network.PeerFeature.Id -import scorex.core.serialization.ScorexSerializer import scorex.util.serialization._ -import java.net.URL +import java.net.URL import org.ergoplatform.settings.PeerFeatureDescriptors +import scorex.core.serialization.ScorexSerializer /** * Peer may have rest-api URL enabled in which case it needs to be passed to/from other peers diff --git a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala b/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala index 403db142b4..99bff52822 100644 --- a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala @@ -4,8 +4,8 @@ import org.ergoplatform.settings.PeerFeatureDescriptors import scorex.core.network.PeerFeature import scorex.core.network.PeerFeature.Id import scorex.core.network.message.Message -import scorex.util.serialization._ import scorex.core.serialization.ScorexSerializer +import scorex.util.serialization._ /** * This peer feature allows to more reliably detect connections to self node and connections from other networks diff --git a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala similarity index 90% rename from src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala rename to src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index c34a848b54..78280c8084 100644 --- a/src/test/scala/scorex/testkit/properties/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -1,34 +1,33 @@ -package scorex.testkit.properties +package org.ergoplatform.nodeView -import akka.actor._ +import akka.actor.{ActorRef, ActorSystem} import akka.testkit.TestProbe 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.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeViewChanges, ModifiersFromRemote} import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.ErgoMemPool +import org.ergoplatform.nodeView.state.UtxoState.ManifestId +import org.ergoplatform.nodeView.state._ +import org.ergoplatform.settings.Algos import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{GetNodeViewChanges, ModifiersFromRemote} import scorex.core.consensus.SyncInfo +import scorex.core.network.ConnectedPeer import scorex.core.network.NetworkController.ReceivableMessages.{PenalizePeer, SendToNetwork} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ -import org.ergoplatform.nodeView.state.UtxoState.ManifestId -import org.ergoplatform.nodeView.state.{ErgoState, SnapshotsDb, SnapshotsInfo, UtxoState, UtxoStateReader} -import org.ergoplatform.settings.Algos -import scorex.core.network._ import scorex.core.network.message._ import scorex.core.network.peer.PenaltyType -import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ManifestSerializer, ScorexSerializer} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture import scorex.util.ScorexLogging -import scorex.util.serialization._ +import scorex.util.serialization.{Reader, Writer} import scala.concurrent.Await -import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.language.postfixOps import scala.util.Random @@ -40,13 +39,15 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec with SyntacticallyTargetedModifierProducer with TotallyValidModifierProducer[ST] { + implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global + val historyGen: Gen[ErgoHistory] val memPool: ErgoMemPool val stateGen: Gen[ST] def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[BlockSection]) + (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[BlockSection]) class SynchronizerFixture extends AkkaFixture { @SuppressWarnings(Array("org.wartremover.warts.PublicInference")) @@ -117,7 +118,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec withFixture { ctx => import ctx._ - val dummySyncInfoMessageSpec = new SyncInfoMessageSpec[SyncInfo](serializer = new ScorexSerializer[SyncInfo]{ + val dummySyncInfoMessageSpec = new SyncInfoMessageSpec[SyncInfo](serializer = new ScorexSerializer[SyncInfo] { override def parse(r: Reader): SyncInfo = { throw new Exception() } @@ -275,13 +276,11 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec // Generate some snapshot val height = 1 usr.applyModifier(mod, Some(height))(_ => ()) - val (manifest, subtrees) = usr.slicedTree() - val db = SnapshotsDb.create(s.constants.settings) - db.writeSnapshot(height, manifest, subtrees) + val manifestId = usr.dumpSnapshot(height) // Then send message to request it - node ! Message[ManifestId](GetManifestSpec, Left(manifest.id), Option(peer)) + node ! Message[ManifestId](GetManifestSpec, Left(manifestId), Option(peer)) ncProbe.fishForMessage(5 seconds) { case stn: SendToNetwork if stn.message.spec.isInstanceOf[ManifestSpec.type] => true case _: Any => false @@ -305,15 +304,20 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec node ! ChangedState(s) // Generate some snapshot + val height = 1 + usr.applyModifier(mod, Some(height))(_ => ()) - val (manifest, subtrees) = usr.slicedTree() - val db = SnapshotsDb.create(s.constants.settings) - db.writeSnapshot(height, manifest, subtrees) + val serializer = ManifestSerializer.defaultSerializer + usr.dumpSnapshot(height) + val manifestId = usr.snapshotsDb.readSnapshotsInfo.availableManifests.apply(height) + val manifestBytes = usr.snapshotsDb.readManifestBytes(manifestId).get + val manifest = serializer.parseBytes(manifestBytes) + val subtreeIds = manifest.subtreesIds // Then send message to request it - node ! Message[ManifestId](GetUtxoSnapshotChunkSpec, Left(subtrees.last.id), Option(peer)) + node ! Message[ManifestId](GetUtxoSnapshotChunkSpec, Left(subtreeIds.last), Option(peer)) ncProbe.fishForMessage(5 seconds) { case stn: SendToNetwork if stn.message.spec.isInstanceOf[UtxoSnapshotChunkSpec.type] => true case _: Any => false diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 6f79f751b4..9072f229cf 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -3,12 +3,10 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} -import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers import scorex.core.VersionTag -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer -import scorex.crypto.hash.Digest32 +import scorex.core.serialization.{ManifestSerializer, SubtreeSerializer} import scorex.db.LDBVersionedStore import scorex.util.ModifierId @@ -27,24 +25,38 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { val bh = boxesHolderGenOfSize(32 * 1024).sample.get val us = createUtxoState(bh, parameters) - val (manifest, subtrees) = us.slicedTree() - println("Subtrees: " + subtrees.size) + val snapshotHeight = 1 + val serializer = ManifestSerializer.defaultSerializer + + us.dumpSnapshot(snapshotHeight) + val manifestId = us.snapshotsDb.readSnapshotsInfo.availableManifests.apply(snapshotHeight) + val manifestBytes = us.snapshotsDb.readManifestBytes(manifestId).get + val manifest = serializer.parseBytes(manifestBytes) + val subtreeIds = manifest.subtreesIds + val subtreeIdsEncoded = subtreeIds.map(id => ModifierId @@ Algos.encode(id)) + + subtreeIds.foreach {sid => + val subtreeBytes = us.snapshotsDb.readSubtreeBytes(sid).get + val subtree = SubtreeSerializer.parseBytes(subtreeBytes) + subtree.verify(sid) shouldBe true + } + val blockId = ModifierId @@ Algos.encode(Array.fill(32)(Random.nextInt(100).toByte)) utxoSetSnapshotProcessor.registerManifestToDownload(manifest, snapshotHeight, Seq.empty) val dp = utxoSetSnapshotProcessor.utxoSetSnapshotDownloadPlan().get dp.snapshotHeight shouldBe snapshotHeight - val subtreeIds = subtrees.map(s => ModifierId @@ Algos.encode(s.id)) val expected = dp.expectedChunkIds.map(id => ModifierId @@ Algos.encode(id)) - expected shouldBe subtreeIds + expected shouldBe subtreeIdsEncoded val toDownload = utxoSetSnapshotProcessor.getChunkIdsToDownload(expected.size).map(id => ModifierId @@ Algos.encode(id)) toDownload shouldBe expected - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(Algos.hash) - subtrees.foreach { subtree => - utxoSetSnapshotProcessor.registerDownloadedChunk(subtree.id, serializer.subtreeToBytes(subtree)) + subtreeIds.foreach { subtreeId => + val subtreeBytes = us.snapshotsDb.readSubtreeBytes(subtreeId).get + utxoSetSnapshotProcessor.registerDownloadedChunk(subtreeId, subtreeBytes) } - utxoSetSnapshotProcessor.downloadedChunksIterator().toSeq.map(s => ModifierId @@ Algos.encode(s.id)) shouldBe subtreeIds + val s = utxoSetSnapshotProcessor.downloadedChunksIterator().map(s => ModifierId @@ Algos.encode(s.id)).toSeq + s shouldBe subtreeIdsEncoded val dir = createTempDir val store = new LDBVersionedStore(dir, initialKeepVersions = 100) diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala index e4d9de0836..d1a7518e16 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanity.scala @@ -7,6 +7,7 @@ import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.network.{ErgoNodeViewSynchronizer, ErgoSyncTracker} +import org.ergoplatform.nodeView.NodeViewSynchronizerTests import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoSyncInfo, ErgoSyncInfoMessageSpec} import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.{DigestState, ErgoState, UtxoState} From 33fe15964f246100e1b8db30af76b942aa217f55 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 14 Mar 2023 12:49:28 +0300 Subject: [PATCH 116/204] fixing test --- .../PeerFilteringRuleSpecification.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala index 9fddcfc194..2309316ad9 100644 --- a/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/PeerFilteringRuleSpecification.scala @@ -45,15 +45,17 @@ class PeerFilteringRuleSpecification extends ErgoPropertyTest { } property("utxo set snapshot filter") { - val inPeer0 = peerWithVersion(Version(4, 0, 17)) - val inPeer1 = peerWithVersion(Version(4, 0, 18)) - val outPeer0 = peerWithVersion(Version(4, 0, 16)) - val outPeer1 = peerWithVersion(Version(4, 0, 19)) - val outPeer2 = peerWithVersion(Version(5, 0, 0)) - val outPeer3 = peerWithVersion(Version(5, 0, 5)) - - UtxoSetNetworkingFilter.filter(Seq(inPeer0, inPeer1, outPeer0, outPeer1, outPeer2, outPeer3)) shouldBe - Seq(outPeer2, outPeer3) + val peer0 = peerWithVersion(Version(4, 0, 17)) + val peer1 = peerWithVersion(Version(4, 0, 18)) + val peer2 = peerWithVersion(Version(4, 0, 16)) + val peer3 = peerWithVersion(Version(4, 0, 19)) + val peer4 = peerWithVersion(Version(5, 0, 0)) + val peer5 = peerWithVersion(Version(5, 0, 5)) + val peer6 = peerWithVersion(Version(5, 0, 15)) + val peer7 = peerWithVersion(Version(5, 0, 25)) + + UtxoSetNetworkingFilter.filter(Seq(peer0, peer1, peer2, peer3, peer4, peer5, peer6, peer7)) shouldBe + Seq(peer6, peer7) } } From 3aa32811027a36774d73bef9643bcdb36336af48 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 21 Mar 2023 01:09:39 +0300 Subject: [PATCH 117/204] dump in Future, code cleaning --- .../batch/VersionedLDBAVLStorage.scala | 82 +++++-------------- .../scala/scorex/db/LDBVersionedStore.scala | 2 +- .../network/ErgoNodeViewSynchronizer.scala | 2 +- .../state/UtxoSetSnapshotPersistence.scala | 2 +- 4 files changed, 24 insertions(+), 64 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index cc80dea311..1a71cf1e39 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -13,6 +13,7 @@ import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} import scala.collection.mutable +import scala.concurrent.Future import scala.util.{Failure, Try} /** @@ -96,13 +97,6 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Array[Byte] = { store.backup { ri => - val rootNodeLabel = ri.get(topNodeHashKey) - val rootNodeHeight = Ints.fromByteArray(ri.get(topNodeHeightKey)) - - val manifestBuilder = mutable.ArrayBuilder.make[Byte]() - manifestBuilder.sizeHint(200000) - manifestBuilder ++= Ints.toByteArray(rootNodeHeight) - manifestBuilder += ManifestSerializer.ManifestDepth def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { val nodeBytes = ri.get(label) @@ -123,7 +117,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) dumpStorage.insert(sid, builder.result()) } - def manifestLoop(nodeDbKey: Array[Byte], level: Int): Unit = { + def manifestLoop(nodeDbKey: Array[Byte], level: Int, manifestBuilder: mutable.ArrayBuilder[Byte]): Unit = { val nodeBytes = ri.get(nodeDbKey) manifestBuilder ++= nodeBytes val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) @@ -132,17 +126,31 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) dumpSubtree(Digest32 @@ in.leftLabel) dumpSubtree(Digest32 @@ in.rightLabel) case in: ProxyInternalNode[DigestType] => - manifestLoop(in.leftLabel, level + 1) - manifestLoop(in.rightLabel, level + 1) + manifestLoop(in.leftLabel, level + 1, manifestBuilder) + manifestLoop(in.rightLabel, level + 1, manifestBuilder) case _ => //todo: support leafs println("!!!") } } - manifestLoop(rootNodeLabel, level = 1) - val manifestBytes = manifestBuilder.result() - dumpStorage.insert(rootNodeLabel, manifestBytes) + val rootNodeLabel = ri.get(topNodeHashKey) + val rootNodeHeight = Ints.fromByteArray(ri.get(topNodeHeightKey)) + + import scala.concurrent.ExecutionContext.Implicits.global + Future { + val ft0 = System.currentTimeMillis() + val manifestBuilder = mutable.ArrayBuilder.make[Byte]() + manifestBuilder.sizeHint(200000) + manifestBuilder ++= Ints.toByteArray(rootNodeHeight) + manifestBuilder += ManifestSerializer.ManifestDepth + + manifestLoop(rootNodeLabel, level = 1, manifestBuilder) + val manifestBytes = manifestBuilder.result() + dumpStorage.insert(rootNodeLabel, manifestBytes) + val ft = System.currentTimeMillis() + log.info("Work within future: " + (ft - ft0) + " ms.") + } rootNodeLabel } } @@ -162,54 +170,6 @@ object VersionedLDBAVLStorage { (topNodeKey, topNodeHeightKey) } - /* - /** - * Serialize tree node (only, without possible children) - */ - private[batch] def toBytes[D <: hash.Digest](node: ProverNodes[D]): Array[Byte] = { - val builder = new mutable.ArrayBuilder.ofByte - node match { - case n: ProxyInternalNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.leftLabel ++= n.rightLabel - case n: InternalProverNode[D] => - builder += InternalNodePrefix += n.balance ++= n.key ++= n.left.label ++= n.right.label - case n: ProverLeaf[D] => - builder += LeafPrefix ++= n.key ++= Ints.toByteArray(n.value.length) ++= n.value ++= n.nextLeafKey - } - builder.result() - } - - // todo: replace store: LDBVersionedStore with some generic interface - def fromBytes[D <: hash.Digest](bytes: Array[Byte], - store: LDBVersionedStore): ProverNodes[DigestType] = { - lazy val keySize = StateTreeParameters.keySize - lazy val labelSize = StateTreeParameters.labelSize - bytes.head match { - case InternalNodePrefix => - val balance = Balance @@ bytes.slice(1, 2).head - val key = ADKey @@ bytes.slice(2, 2 + keySize) - val leftKey = ADKey @@ bytes.slice(2 + keySize, 2 + keySize + labelSize) - val rightKey = ADKey @@ bytes.slice(2 + keySize + labelSize, 2 + keySize + (2 * labelSize)) - - if(store != null) { - new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) - } else { - new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) - } - - case LeafPrefix => - val key = ADKey @@ bytes.slice(1, 1 + keySize) - val (value, nextLeafKey) = { - val valueSize = Ints.fromByteArray(bytes.slice(1 + keySize, 1 + keySize + 4)) - val value = ADValue @@ bytes.slice(1 + keySize + 4, 1 + keySize + 4 + valueSize) - val nextLeafKey = ADKey @@ bytes.slice(1 + keySize + 4 + valueSize, 1 + (2 * keySize) + 4 + valueSize) - value -> nextLeafKey - } - new ProverLeaf[DigestType](key, value, nextLeafKey)(hashFn) - } - } - */ - /** * Fetch tree node from database by its database id (hash of node contents) * diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 2e5e6c2e35..f67852ef28 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -421,7 +421,7 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e } logic(ri) } finally { - // Make sure you close the snapshot to avoid resource leaks. + // Close the snapshot to avoid resource leaks ro.snapshot().close() } } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 1b357aecc1..7e621e6387 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -810,7 +810,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def sendManifest(id: ManifestId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { - usr.getManifest(id) match { + usr.getManifestBytes(id) match { case Some(manifest) => { val msg = Message(ManifestSpec, Right(manifest), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index aeefa10dd5..cda3cdabca 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -41,7 +41,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { snapshotsDb.readSnapshotsInfo } - def getManifest(id: ManifestId): Option[Array[Byte]] = { + def getManifestBytes(id: ManifestId): Option[Array[Byte]] = { snapshotsDb.readManifestBytes(id) } From 2c83233ce51fed8c59151c74d5ea6aac4ec93bf4 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 24 Mar 2023 13:54:37 +0300 Subject: [PATCH 118/204] moving Future call to saveSnapshotIfNeeded --- .../batch/VersionedLDBAVLStorage.scala | 24 +++---- .../state/UtxoSetSnapshotPersistence.scala | 14 +++- .../examples/UtxoSnapshotExample.scala | 70 ------------------- 3 files changed, 21 insertions(+), 87 deletions(-) delete mode 100644 src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 1a71cf1e39..239e3eebe1 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -13,7 +13,6 @@ import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} import scala.collection.mutable -import scala.concurrent.Future import scala.util.{Failure, Try} /** @@ -137,20 +136,15 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) val rootNodeLabel = ri.get(topNodeHashKey) val rootNodeHeight = Ints.fromByteArray(ri.get(topNodeHeightKey)) - import scala.concurrent.ExecutionContext.Implicits.global - Future { - val ft0 = System.currentTimeMillis() - val manifestBuilder = mutable.ArrayBuilder.make[Byte]() - manifestBuilder.sizeHint(200000) - manifestBuilder ++= Ints.toByteArray(rootNodeHeight) - manifestBuilder += ManifestSerializer.ManifestDepth - - manifestLoop(rootNodeLabel, level = 1, manifestBuilder) - val manifestBytes = manifestBuilder.result() - dumpStorage.insert(rootNodeLabel, manifestBytes) - val ft = System.currentTimeMillis() - log.info("Work within future: " + (ft - ft0) + " ms.") - } + val manifestBuilder = mutable.ArrayBuilder.make[Byte]() + manifestBuilder.sizeHint(200000) + manifestBuilder ++= Ints.toByteArray(rootNodeHeight) + manifestBuilder += ManifestSerializer.ManifestDepth + + manifestLoop(rootNodeLabel, level = 1, manifestBuilder) + val manifestBytes = manifestBuilder.result() + dumpStorage.insert(rootNodeLabel, manifestBytes) + rootNodeLabel } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index cda3cdabca..07f5dc0d8e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -8,6 +8,8 @@ import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapshot} +import scala.concurrent.Future + trait UtxoSetSnapshotPersistence extends ScorexLogging { def constants: StateConstants @@ -25,9 +27,17 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { estimatedTip.nonEmpty && estimatedTip.get - height <= MakeSnapshotEvery) { + import scala.concurrent.ExecutionContext.Implicits.global val ms0 = System.currentTimeMillis() - snapshotsDb.pruneSnapshots(height - MakeSnapshotEvery * 2) - dumpSnapshot(height) + // todo: check that future will work with the same tree + Future { + log.info("Started work within future") + val ft0 = System.currentTimeMillis() + dumpSnapshot(height) + snapshotsDb.pruneSnapshots(height - MakeSnapshotEvery * 2) //todo: async + val ft = System.currentTimeMillis() + log.info("Work within future: " + (ft - ft0) + " ms.") + } val ms = System.currentTimeMillis() log.info("Time to dump utxo set snapshot: " + (ms - ms0)) } diff --git a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala b/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala deleted file mode 100644 index cddc37b42c..0000000000 --- a/src/test/scala/org/ergoplatform/examples/UtxoSnapshotExample.scala +++ /dev/null @@ -1,70 +0,0 @@ -package scorex.crypto.authds.avltree.batch - -import java.io.File - -import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} -import org.ergoplatform.settings.{Algos, Args, ErgoSettings, NetworkType} -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer -import scorex.crypto.hash.Digest32 -import scorex.util.encode.Base16 - -object UtxoSnapshotExample extends App { - import Algos.HF - - val stateConstants = StateConstants(ErgoSettings.read(Args(Some("/home/kushti/ergo/mainnet/mainnet.conf"), Some(NetworkType.MainNet)))) - - println("Init started") - val ims0 = System.currentTimeMillis() - val state = UtxoState.create(new File("/home/kushti/ergo/mainnet/.ergo/state"), stateConstants) - val ims = System.currentTimeMillis() - println("Init took: " + (ims-ims0) + " ms.") - - implicit val hf: HF = Algos.hash - val serializer = new BatchAVLProverSerializer[Digest32, HF] - - - /** - * Count number of leaves and their cumulative size (so total weight of boxes) - * - * @param prover - * @return - */ - - private def leafDataStats(prover: BatchAVLProver[scorex.crypto.hash.Digest32, HF]): (Long, Long) = { - - - def step(node: ProverNodes[Digest32], acc: (Long, Long)): (Long, Long) = { - node match { - case in: InternalProverNode[Digest32] => - val leftRes = step(in.left, acc) - val rightRes = step(in.right, acc) - (leftRes._1 + rightRes._1, leftRes._2 + rightRes._2) - case l: ProverLeaf[Digest32] => - (1, l.value.size) - } - } - - step(prover.topNode, (0, 0)) - } - - - val ss0 = System.currentTimeMillis() - val (elems, valSize) = leafDataStats(state.persistentProver.prover()) - val ss = System.currentTimeMillis() - println("time to traverse the tree: " + (ss - ss0) + " ms.") - println("elems: " + elems) - println("boxes total bytes count: " + valSize) - - println("height: " + state.persistentProver.height) - - val avlProver = state.persistentProver - val ms0 = System.currentTimeMillis() - val sliced = serializer.slice(avlProver.avlProver, subtreeDepth = 16) - - println("sliced count: " + sliced._2.size) - println("sliced: " + sliced._2) - println("sliced labels: " + sliced._2.map(_.subtreeTop.label).map(Base16.encode)) - val ms = System.currentTimeMillis() - println("Time: " + (ms - ms0)) - -} From 5c1f82523f241e1980c55434907274132a287897 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 24 Mar 2023 15:21:33 +0300 Subject: [PATCH 119/204] ErgoSerializer, scaladoc --- .../{ScorexSerializer.scala => ErgoSerializer.scala} | 5 ++++- .../scorex/core/serialization/ManifestSerializer.scala | 5 ++++- .../scorex/core/serialization/SubtreeSerializer.scala | 5 ++++- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 4 ++-- .../org/ergoplatform/bench/misc/ModifierWriter.scala | 4 ++-- .../scala/org/ergoplatform/mining/AutolykosSolution.scala | 8 ++++---- .../mining/difficulty/RequiredDifficulty.scala | 4 ++-- .../scala/org/ergoplatform/modifiers/ErgoFullBlock.scala | 4 ++-- .../org/ergoplatform/modifiers/history/ADProofs.scala | 6 +++--- .../modifiers/history/BlockTransactions.scala | 6 +++--- .../modifiers/history/HistoryModifierSerializer.scala | 4 ++-- .../modifiers/history/extension/Extension.scala | 4 ++-- .../modifiers/history/extension/ExtensionSerializer.scala | 4 ++-- .../ergoplatform/modifiers/history/header/Header.scala | 4 ++-- .../modifiers/history/header/HeaderSerializer.scala | 4 ++-- .../modifiers/history/popow/NipopowProof.scala | 6 +++--- .../modifiers/history/popow/PoPowHeader.scala | 6 +++--- .../ergoplatform/modifiers/mempool/ErgoTransaction.scala | 6 +++--- .../ergoplatform/network/ErgoNodeViewSynchronizer.scala | 6 +++--- .../scala/org/ergoplatform/network/ModePeerFeature.scala | 6 +++--- .../org/ergoplatform/nodeView/history/ErgoSyncInfo.scala | 6 +++--- .../ergoplatform/nodeView/history/extra/BalanceInfo.scala | 4 ++-- .../nodeView/history/extra/ExtraIndexSerializer.scala | 4 ++-- .../nodeView/history/extra/IndexedErgoAddress.scala | 4 ++-- .../nodeView/history/extra/IndexedErgoBox.scala | 4 ++-- .../nodeView/history/extra/IndexedErgoTransaction.scala | 4 ++-- .../nodeView/history/extra/IndexedToken.scala | 4 ++-- .../nodeView/history/extra/NumericIndex.scala | 6 +++--- .../ergoplatform/nodeView/state/ErgoStateContext.scala | 6 +++--- .../org/ergoplatform/nodeView/state/VotingData.scala | 4 ++-- .../ergoplatform/nodeView/wallet/WalletTransaction.scala | 4 ++-- .../nodeView/wallet/persistence/WalletDigest.scala | 4 ++-- .../org/ergoplatform/nodeView/wallet/scanning/Scan.scala | 4 ++-- .../wallet/scanning/ScanningPredicateSerializer.scala | 4 ++-- src/main/scala/org/ergoplatform/settings/Constants.scala | 4 ++-- .../ergoplatform/settings/ErgoValidationSettings.scala | 6 +++--- .../settings/ErgoValidationSettingsUpdate.scala | 4 ++-- src/main/scala/org/ergoplatform/settings/Parameters.scala | 4 ++-- src/main/scala/scorex/core/app/Version.scala | 6 +++--- src/main/scala/scorex/core/network/PeerFeature.scala | 4 ++-- src/main/scala/scorex/core/network/PeerSpec.scala | 4 ++-- .../scorex/core/network/message/BasicMessagesRepo.scala | 4 ++-- .../scala/scorex/core/network/message/MessageSpec.scala | 4 ++-- .../core/network/peer/LocalAddressPeerFeature.scala | 4 ++-- src/main/scala/scorex/core/network/peer/PeerInfo.scala | 4 ++-- .../scorex/core/network/peer/RestApiUrlPeerFeature.scala | 4 ++-- .../scorex/core/network/peer/SessionIdPeerFeature.scala | 4 ++-- .../scorex/core/serialization/BytesSerializable.scala | 2 +- .../network/ErgoNodeViewSynchronizerSpecification.scala | 6 +++--- .../ergoplatform/nodeView/NodeViewSynchronizerTests.scala | 8 ++++---- .../scala/org/ergoplatform/sanity/ErgoSanityDigest.scala | 6 +++--- .../scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala | 6 +++--- .../ergoplatform/serialization/SerializationTests.scala | 4 ++-- src/test/scala/scorex/testkit/SerializationTests.scala | 4 ++-- 54 files changed, 132 insertions(+), 123 deletions(-) rename avldb/src/main/scala/scorex/core/serialization/{ScorexSerializer.scala => ErgoSerializer.scala} (74%) diff --git a/avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala similarity index 74% rename from avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala rename to avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala index bffed7872d..cfb9b834de 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ScorexSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala @@ -6,7 +6,10 @@ import scorex.util.serialization._ import scala.util.Try -trait ScorexSerializer[T] extends Serializer[T, T, Reader, Writer] { +/** + * Basic interface for serializer with additional method to work with bytes insted of Reader/Writer instances + */ +trait ErgoSerializer[T] extends Serializer[T, T, Reader, Writer] { def toBytes(obj: T): Array[Byte] = { val writer = new VLQByteBufferWriter(new ByteArrayBuilder()) diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index 811b121a9c..c024852557 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -6,7 +6,10 @@ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes, VersionedLDBAVLStorage} import scorex.util.serialization.{Reader, Writer} -class ManifestSerializer(manifestDepth: Byte) extends ScorexSerializer[BatchAVLProverManifest[DigestType]] { +/** + * Serializer of manifest, a tree which is cut at some `manifestDepth` from root + */ +class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLProverManifest[DigestType]] { private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { diff --git a/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala index 2fd854d6f6..0a1f6df970 100644 --- a/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/SubtreeSerializer.scala @@ -6,7 +6,10 @@ import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverSubtree, import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} -object SubtreeSerializer extends ScorexSerializer[BatchAVLProverSubtree[DigestType]] with ScorexLogging { +/** + * Serializer for subtree + */ +object SubtreeSerializer extends ErgoSerializer[BatchAVLProverSubtree[DigestType]] with ScorexLogging { private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer override def serialize(subtree: BatchAVLProverSubtree[DigestType], w: Writer): Unit = { diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 239e3eebe1..4522210b67 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -1,7 +1,7 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints -import scorex.core.serialization.{ManifestSerializer, ScorexSerializer} +import scorex.core.serialization.{ManifestSerializer, ErgoSerializer} import scorex.crypto.authds.avltree.batch.Constants.{DigestType, HashFnType, hashFn} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} import scorex.crypto.authds.{ADDigest, ADKey, ADValue, Balance} @@ -231,7 +231,7 @@ object VersionedLDBAVLStorage { } -class ProverNodeSerializer(store: LDBVersionedStore) extends ScorexSerializer[ProverNodes[DigestType]] { +class ProverNodeSerializer(store: LDBVersionedStore) extends ErgoSerializer[ProverNodes[DigestType]] { import VersionedLDBAVLStorage.{InternalNodePrefix,LeafPrefix} diff --git a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala index 09e04bf478..d452aa0c6e 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/bench/misc/ModifierWriter.scala @@ -7,11 +7,11 @@ import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId} import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import scorex.core.NodeViewModifier -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer object ModifierWriter { - val modifierSerializers: Map[NetworkObjectTypeId.Value, ScorexSerializer[_ <: BlockSection]] = + val modifierSerializers: Map[NetworkObjectTypeId.Value, ErgoSerializer[_ <: BlockSection]] = Map(Header.modifierTypeId -> HeaderSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, ADProofs.modifierTypeId -> ADProofsSerializer) diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala index 342c610b12..25d0e40498 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala +++ b/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.settings.Algos import org.ergoplatform.modifiers.history.header.Header.Version -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.interpreter.CryptoConstants import sigmastate.interpreter.CryptoConstants.EcPointType @@ -62,7 +62,7 @@ object AutolykosSolution extends ApiCodecs { * Binary serializer for Autolykos v1 solution, * serializing and parsing "pk", "w", "nonce", and "d" values */ -class AutolykosV1SolutionSerializer extends ScorexSerializer[AutolykosSolution] { +class AutolykosV1SolutionSerializer extends ErgoSerializer[AutolykosSolution] { override def serialize(obj: AutolykosSolution, w: Writer): Unit = { val dBytes = BigIntegers.asUnsignedByteArray(obj.d.bigInteger) @@ -88,7 +88,7 @@ class AutolykosV1SolutionSerializer extends ScorexSerializer[AutolykosSolution] /** * Binary serializer for Autolykos v2 solution, serializing and parsing "pk" and "nonce" values */ -class AutolykosV2SolutionSerializer extends ScorexSerializer[AutolykosSolution] { +class AutolykosV2SolutionSerializer extends ErgoSerializer[AutolykosSolution] { import AutolykosSolution.{wForV2, dForV2} @@ -114,7 +114,7 @@ object AutolykosSolutionSerializer { private val v1Serializer = new AutolykosV1SolutionSerializer private val v2Serializer = new AutolykosV2SolutionSerializer - private def serializer(blockVersion: Version): ScorexSerializer[AutolykosSolution] = { + private def serializer(blockVersion: Version): ErgoSerializer[AutolykosSolution] = { if (blockVersion == 1) { v1Serializer } else { diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala index f9aee27f31..c0623be44f 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala +++ b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala @@ -2,10 +2,10 @@ package org.ergoplatform.mining.difficulty import java.math.BigInteger import org.ergoplatform.nodeView.history.ErgoHistory._ -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -object RequiredDifficulty extends ScorexSerializer[NBits] { +object RequiredDifficulty extends ErgoSerializer[NBits] { override def serialize(obj: NBits, w: Writer): Unit = { w.putBytes(uint32ToByteArrayBE(obj)) diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index b8ad28d1df..0c50b391e8 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -8,7 +8,7 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions} import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.TransactionsCarryingPersistentNodeViewModifier -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.ModifierId case class ErgoFullBlock(header: Header, @@ -40,7 +40,7 @@ case class ErgoFullBlock(header: Header, override lazy val size: Int = header.size + blockTransactions.size + adProofs.map(_.size).getOrElse(0) - override def serializer: ScorexSerializer[ErgoFullBlock] = + override def serializer: ErgoSerializer[ErgoFullBlock] = throw new Error("Serialization for ErgoFullBlock is not (and will be not) implemented") def height: Int = header.height diff --git a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala b/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala index dd3dc69966..14a4f1de15 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/ADProofs.scala +++ b/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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.avltree.batch.{Lookup => _, _} import scorex.crypto.authds.{ADDigest, ADValue, SerializedAdProof} import scorex.crypto.hash.Digest32 @@ -27,7 +27,7 @@ case class ADProofs(headerId: ModifierId, override type M = ADProofs - override lazy val serializer: ScorexSerializer[ADProofs] = ADProofsSerializer + override lazy val serializer: ErgoSerializer[ADProofs] = ADProofsSerializer override def toString: String = s"ADProofs(Id:$id,HeaderId:$headerId)" @@ -93,7 +93,7 @@ object ADProofs extends ApiCodecs { } } -object ADProofsSerializer extends ScorexSerializer[ADProofs] { +object ADProofsSerializer extends ErgoSerializer[ADProofs] { override def serialize(obj: ADProofs, w: Writer): Unit = { w.putBytes(idToBytes(obj.headerId)) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala index 12ce33b24d..4717faf501 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/BlockTransactions.scala @@ -10,7 +10,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.{Leaf, MerkleProof, MerkleTree} import scorex.crypto.hash.Digest32 @@ -76,7 +76,7 @@ case class BlockTransactions(headerId: ModifierId, override type M = BlockTransactions - override lazy val serializer: ScorexSerializer[BlockTransactions] = BlockTransactionsSerializer + override lazy val serializer: ErgoSerializer[BlockTransactions] = BlockTransactionsSerializer override def toString: String = { val idStr = Algos.encode(id) @@ -130,7 +130,7 @@ object BlockTransactions extends ApiCodecs { } } -object BlockTransactionsSerializer extends ScorexSerializer[BlockTransactions] { +object BlockTransactionsSerializer extends ErgoSerializer[BlockTransactions] { // See a comment in the parse() function val MaxTransactionsInBlock = 10000000 diff --git a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala b/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala index d1ef4eb208..2d4e8ac6fd 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/HistoryModifierSerializer.scala @@ -3,10 +3,10 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -object HistoryModifierSerializer extends ScorexSerializer[BlockSection] { +object HistoryModifierSerializer extends ErgoSerializer[BlockSection] { override def serialize(obj: BlockSection, w: Writer): Unit = { obj match { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala index 2b5e03b2f8..27c53bc80b 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/Extension.scala +++ b/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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.LeafData import scorex.crypto.authds.merkle.MerkleTree import scorex.crypto.hash.Digest32 @@ -28,7 +28,7 @@ case class Extension(headerId: ModifierId, override type M = Extension - override def serializer: ScorexSerializer[Extension] = ExtensionSerializer + override def serializer: ErgoSerializer[Extension] = ExtensionSerializer override def toString: String = { s"Extension(id: $id, headerId: ${Algos.encode(headerId)}, " + diff --git a/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala b/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala index 75e9c50f87..7207b9c056 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/extension/ExtensionSerializer.scala @@ -1,11 +1,11 @@ package org.ergoplatform.modifiers.history.extension import org.ergoplatform.settings.Constants -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.{bytesToId, idToBytes} -object ExtensionSerializer extends ScorexSerializer[Extension] { +object ExtensionSerializer extends ErgoSerializer[Extension] { override def serialize(obj: Extension, w: Writer): Unit = { w.putBytes(idToBytes(obj.headerId)) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 5523fa4b85..4a93b29115 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -12,7 +12,7 @@ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util._ @@ -88,7 +88,7 @@ case class Header(override val version: Header.Version, override lazy val toString: String = s"Header(${this.asJson.noSpaces})" - override lazy val serializer: ScorexSerializer[Header] = HeaderSerializer + override lazy val serializer: ErgoSerializer[Header] = HeaderSerializer lazy val isGenesis: Boolean = height == ErgoHistory.GenesisHeight diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala index 759046c47c..3ea960f1bc 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala @@ -3,14 +3,14 @@ package org.ergoplatform.modifiers.history.header import org.ergoplatform.mining.AutolykosSolutionSerializer import org.ergoplatform.mining.difficulty.RequiredDifficulty import scorex.core.idToBytes -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, VLQByteBufferWriter, Writer} import scorex.util.{ByteArrayBuilder, bytesToId} import scorex.util.Extensions._ -object HeaderSerializer extends ScorexSerializer[Header] { +object HeaderSerializer extends ErgoSerializer[Header] { override def serialize(h: Header, w: Writer): Unit = { serializeWithoutPow(h, w) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala index ea244df839..68393417d6 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowProof.scala @@ -2,7 +2,7 @@ package org.ergoplatform.modifiers.history.popow import io.circe.{Decoder, Encoder} import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions.LongOps @@ -26,7 +26,7 @@ case class NipopowProof(popowAlgos: NipopowAlgos, suffixHead: PoPowHeader, suffixTail: Seq[Header]) { - def serializer: ScorexSerializer[NipopowProof] = new NipopowProofSerializer(popowAlgos) + def serializer: ErgoSerializer[NipopowProof] = new NipopowProofSerializer(popowAlgos) def headersChain: Seq[Header] = prefixHeaders ++ suffixHeaders @@ -124,7 +124,7 @@ object NipopowProof { } -class NipopowProofSerializer(poPowAlgos: NipopowAlgos) extends ScorexSerializer[NipopowProof] { +class NipopowProofSerializer(poPowAlgos: NipopowAlgos) extends ErgoSerializer[NipopowProof] { override def serialize(obj: NipopowProof, w: Writer): Unit = { w.putUInt(obj.m) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala index dc5b9d2a61..57da8fa6dc 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala @@ -8,7 +8,7 @@ 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, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} import scorex.crypto.authds.Side import scorex.crypto.authds.merkle.BatchMerkleProof import scorex.crypto.authds.merkle.serialization.BatchMerkleProofSerializer @@ -33,7 +33,7 @@ case class PoPowHeader(header: Header, override type M = PoPowHeader - override def serializer: ScorexSerializer[M] = PoPowHeaderSerializer + override def serializer: ErgoSerializer[M] = PoPowHeaderSerializer def id: ModifierId = header.id @@ -136,7 +136,7 @@ object PoPowHeader { /** * Binary serializer for PoPowHeader, */ -object PoPowHeaderSerializer extends ScorexSerializer[PoPowHeader] { +object PoPowHeaderSerializer extends ErgoSerializer[PoPowHeader] { import org.ergoplatform.wallet.Constants.ModifierIdLength implicit val hf: HF = Algos.hash diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 983205c8ed..99b74cf602 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -20,7 +20,7 @@ import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.wallet.protocol.context.{InputContext, TransactionContext} import org.ergoplatform.wallet.serialization.JsonCodecsWrapper import scorex.core.EphemerealNodeViewModifier -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.core.transaction.Transaction import scorex.core.utils.ScorexEncoding import scorex.core.validation.ValidationResult.fromValidationState @@ -452,7 +452,7 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], override type M = ErgoTransaction - override def serializer: ScorexSerializer[ErgoTransaction] = ErgoTransactionSerializer + override def serializer: ErgoSerializer[ErgoTransaction] = ErgoTransactionSerializer override def toString: String = { import ErgoTransaction._ @@ -486,7 +486,7 @@ object ErgoTransaction extends ApiCodecs with ScorexLogging with ScorexEncoding inputs.zipWithIndex.filterNot(i => resolvedInputs.exists(bx => util.Arrays.equals(bx.id, i._1))).map(_._2) } -object ErgoTransactionSerializer extends ScorexSerializer[ErgoTransaction] { +object ErgoTransactionSerializer extends ErgoSerializer[ErgoTransaction] { override def serialize(tx: ErgoTransaction, w: Writer): Unit = { val elt = new ErgoLikeTransaction(tx.inputs, tx.dataInputs, tx.outputCandidates) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 7e621e6387..e8266293f7 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -47,7 +47,7 @@ import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the @@ -665,7 +665,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, requestedModifiers: Map[ModifierId, Array[Byte]], remote: ConnectedPeer): Unit = { Constants.modifierSerializers.get(typeId) match { - case Some(serializer: ScorexSerializer[BlockSection]@unchecked) => + case Some(serializer: ErgoSerializer[BlockSection]@unchecked) => // parse all modifiers and put them to modifiers cache val parsed: Iterable[BlockSection] = parseModifiers(requestedModifiers, typeId, serializer, remote) @@ -749,7 +749,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ def parseModifiers[M <: NodeViewModifier](modifiers: Map[ModifierId, Array[Byte]], modifierTypeId: NetworkObjectTypeId.Value, - serializer: ScorexSerializer[M], + serializer: ErgoSerializer[M], remote: ConnectedPeer): Iterable[M] = { modifiers.flatMap { case (id, bytes) => serializer.parseBytesTry(bytes) match { diff --git a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala b/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala index 2f6166a9ae..1e4b0453f7 100644 --- a/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala +++ b/src/main/scala/org/ergoplatform/network/ModePeerFeature.scala @@ -5,7 +5,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} /** @@ -26,7 +26,7 @@ case class ModePeerFeature(stateType: StateType, override val featureId: Id = PeerFeatureDescriptors.ModeFeatureId - override def serializer: ScorexSerializer[ModePeerFeature] = ModeFeatureSerializer + override def serializer: ErgoSerializer[ModePeerFeature] = ModeFeatureSerializer } object ModePeerFeature { @@ -60,7 +60,7 @@ object ModePeerFeature { * handshake which contains mode information (along with other features supported by the peer) has separate length * limit provided in settings ("maxHandshakeSize" field in network settings). */ -object ModeFeatureSerializer extends ScorexSerializer[ModePeerFeature] { +object ModeFeatureSerializer extends ErgoSerializer[ModePeerFeature] { val MaxSize = 512 diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala index cb627e3a4b..ab6756c5a6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala @@ -4,7 +4,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} @@ -20,7 +20,7 @@ sealed trait ErgoSyncInfo extends SyncInfo { override type M = ErgoSyncInfo - override lazy val serializer: ScorexSerializer[ErgoSyncInfo] = ErgoSyncInfoSerializer + override lazy val serializer: ErgoSerializer[ErgoSyncInfo] = ErgoSyncInfoSerializer } /** @@ -46,7 +46,7 @@ object ErgoSyncInfo { val MaxBlockIds = 1000 } -object ErgoSyncInfoSerializer extends ScorexSerializer[ErgoSyncInfo] with ScorexLogging { +object ErgoSyncInfoSerializer extends ErgoSerializer[ErgoSyncInfo] with ScorexLogging { val v2HeaderMode: Byte = -1 // used to mark sync v2 messages 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 10c0cfd364..a2592b7a9c 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import spire.implicits.cfor @@ -62,7 +62,7 @@ class BalanceInfo(var nanoErgs: Long = 0L, } -object BalanceInfoSerializer extends ScorexSerializer[BalanceInfo] { +object BalanceInfoSerializer extends ErgoSerializer[BalanceInfo] { override def serialize(bI: BalanceInfo, w: Writer): Unit = { w.putLong(bI.nanoErgs) 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 3200a74570..976ed9fdbb 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.history.extra -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -object ExtraIndexSerializer extends ScorexSerializer[ExtraIndex]{ +object ExtraIndexSerializer extends ErgoSerializer[ExtraIndex]{ override def serialize(obj: ExtraIndex, w: Writer): Unit = { obj match { 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 4a9f0f1103..98f5c64d0e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala @@ -6,7 +6,7 @@ import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, f import org.ergoplatform.nodeView.history.extra.IndexedErgoAddress.{getBoxes, getSegmentsForRange, getTxs, segmentTreshold, slice} import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, txSegmentId} import org.ergoplatform.settings.Algos -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.ErgoTree @@ -206,7 +206,7 @@ case class IndexedErgoAddress(treeHash: ModifierId, } } -object IndexedErgoAddressSerializer extends ScorexSerializer[IndexedErgoAddress] { +object IndexedErgoAddressSerializer extends ErgoSerializer[IndexedErgoAddress] { def hashErgoTree(tree: ErgoTree): Array[Byte] = Algos.hash(tree.bytes) 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 7b23ee3380..fba2576262 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoBox.scala @@ -5,7 +5,7 @@ import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, f import org.ergoplatform.nodeView.wallet.WalletBox import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, TrackedBox} -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} @@ -46,7 +46,7 @@ class IndexedErgoBox(val inclusionHeight: Int, this } } -object IndexedErgoBoxSerializer extends ScorexSerializer[IndexedErgoBox] { +object IndexedErgoBoxSerializer extends ErgoSerializer[IndexedErgoBox] { override def serialize(iEb: IndexedErgoBox, w: Writer): Unit = { w.putInt(iEb.inclusionHeight) 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 c243652466..4de2decad6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoTransaction.scala @@ -5,7 +5,7 @@ import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.DataInput import org.ergoplatform.modifiers.history.BlockTransactions import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.{ModifierId, bytesToId} import spire.implicits.cfor @@ -69,7 +69,7 @@ case class IndexedErgoTransaction(txid: ModifierId, } } -object IndexedErgoTransactionSerializer extends ScorexSerializer[IndexedErgoTransaction] { +object IndexedErgoTransactionSerializer extends ErgoSerializer[IndexedErgoTransaction] { override def serialize(iTx: IndexedErgoTransaction, w: Writer): Unit = { w.putUByte(iTx.serializedId.length) 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 c0bc43d0a6..8a119f5b81 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala @@ -5,7 +5,7 @@ import org.ergoplatform.ErgoBox.{R4, R5, R6} import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes} import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.uniqueId import org.ergoplatform.settings.Algos -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.{CollectionConstant, EvaluatedValue} @@ -32,7 +32,7 @@ case class IndexedToken(tokenId: ModifierId, } -object IndexedTokenSerializer extends ScorexSerializer[IndexedToken] { +object IndexedTokenSerializer extends ErgoSerializer[IndexedToken] { /** * Calculate a unique identifier for this a token. * Necessary, because token ids are sometimes identical to box ids, which causes overwrites. 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 ab519316bf..191e6149c0 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} @@ -16,7 +16,7 @@ case class NumericTxIndex(n: Long, m: ModifierId) extends ExtraIndex { override def serializedId: Array[Byte] = NumericTxIndex.indexToBytes(n) } -object NumericTxIndexSerializer extends ScorexSerializer[NumericTxIndex] { +object NumericTxIndexSerializer extends ErgoSerializer[NumericTxIndex] { override def serialize(ni: NumericTxIndex, w: Writer): Unit = { w.putLong(ni.n) @@ -59,7 +59,7 @@ case class NumericBoxIndex(n: Long, m: ModifierId) extends ExtraIndex { override def serializedId: Array[Byte] = NumericBoxIndex.indexToBytes(n) } -object NumericBoxIndexSerializer extends ScorexSerializer[NumericBoxIndex] { +object NumericBoxIndexSerializer extends ErgoSerializer[NumericBoxIndex] { override def serialize(ni: NumericBoxIndex, w: Writer): Unit = { w.putLong(ni.n) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index e1612ba4be..37415bf9ec 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -11,7 +11,7 @@ import org.ergoplatform.nodeView.history.storage.modifierprocessors.ExtensionVal import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ import org.ergoplatform.wallet.protocol.context.ErgoLikeStateContext -import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} import scorex.core.utils.ScorexEncoding import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationState} import scorex.crypto.authds.ADDigest @@ -107,7 +107,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], def lastHeaderOpt: Option[Header] = lastHeaders.headOption - override def serializer: ScorexSerializer[M] = ErgoStateContextSerializer(ergoSettings) + override def serializer: ErgoSerializer[M] = ErgoStateContextSerializer(ergoSettings) def upcoming(minerPk: EcPointType, timestamp: Long, @@ -351,7 +351,7 @@ object ErgoStateContext { } -case class ErgoStateContextSerializer(ergoSettings: ErgoSettings) extends ScorexSerializer[ErgoStateContext] { +case class ErgoStateContextSerializer(ergoSettings: ErgoSettings) extends ErgoSerializer[ErgoStateContext] { private val Eip27SupportValue = 100 // see comment in serialize() diff --git a/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala b/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala index 32fb7b43cd..3b55bee3ec 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/VotingData.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.state -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -25,7 +25,7 @@ object VotingData { val empty = VotingData(Array.empty) } -object VotingDataSerializer extends ScorexSerializer[VotingData] { +object VotingDataSerializer extends ErgoSerializer[VotingData] { override def serialize(obj: VotingData, w: Writer): Unit = { w.putUShort(obj.epochVotes.length) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletTransaction.scala index 97616b34f5..c93660c0a6 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.ModifierId import scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -27,7 +27,7 @@ final case class WalletTransaction(tx: ErgoTransaction, inclusionHeight: Int, sc } -object WalletTransactionSerializer extends ScorexSerializer[WalletTransaction] { +object WalletTransactionSerializer extends ErgoSerializer[WalletTransaction] { override def serialize(wtx: WalletTransaction, w: Writer): Unit = { val txBytes = wtx.tx.bytes 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 5facb8f6bd..ca5ebbec3b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.wallet.persistence import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.settings.Constants -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} @@ -28,7 +28,7 @@ object WalletDigest { } -object WalletDigestSerializer extends ScorexSerializer[WalletDigest] { +object WalletDigestSerializer extends ErgoSerializer[WalletDigest] { override def serialize(obj: WalletDigest, w: Writer): Unit = { w.putUInt(obj.height) 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 ab5a73cedd..76aa9c3085 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import scala.util.{Failure, Success, Try} @@ -34,7 +34,7 @@ object Scan { } -object ScanSerializer extends ScorexSerializer[Scan] { +object ScanSerializer extends ErgoSerializer[Scan] { private val trueNeg: Byte = -1 private val falseNeg: Byte = -2 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 093e2aa10e..a8cb34ccbf 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} import sigmastate.SType @@ -11,7 +11,7 @@ import sigmastate.serialization.ValueSerializer import scorex.util.Extensions._ -object ScanningPredicateSerializer extends ScorexSerializer[ScanningPredicate] { +object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { val EqualsPrefix = 1: Byte val ContainsPrefix = 2: Byte diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 43adc2bcf9..fd673e6c54 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -8,7 +8,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import sigmastate.Values import sigmastate.Values.ErgoTree @@ -44,7 +44,7 @@ object Constants { // Number of last block headers available is scripts from ErgoStateContext val LastHeadersInContext = 10 - val modifierSerializers: Map[NetworkObjectTypeId.Value, ScorexSerializer[_ <: NodeViewModifier]] = + val modifierSerializers: Map[NetworkObjectTypeId.Value, ErgoSerializer[_ <: NodeViewModifier]] = Map(Header.modifierTypeId -> HeaderSerializer, Extension.modifierTypeId -> ExtensionSerializer, BlockTransactions.modifierTypeId -> BlockTransactionsSerializer, diff --git a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala b/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala index eb0ae9996b..97bfb0d3f1 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ErgoValidationSettings.scala @@ -3,7 +3,7 @@ package org.ergoplatform.settings import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import org.ergoplatform.validation.SigmaValidationSettings -import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationSettings} import scorex.util.serialization.{Reader, Writer} @@ -77,7 +77,7 @@ case class ErgoValidationSettings(rules: Map[Short, RuleStatus], def isInitial: Boolean = this == ErgoValidationSettings.initial - override def serializer: ScorexSerializer[ErgoValidationSettings] = ErgoValidationSettingsSerializer + override def serializer: ErgoSerializer[ErgoValidationSettings] = ErgoValidationSettingsSerializer /** * We only cares about `updateFromInitial`, as far as `rules` and `sigmaSettings` may be @@ -120,7 +120,7 @@ object ErgoValidationSettings { } -object ErgoValidationSettingsSerializer extends ScorexSerializer[ErgoValidationSettings] with ApiCodecs { +object ErgoValidationSettingsSerializer extends ErgoSerializer[ErgoValidationSettings] with ApiCodecs { override def serialize(obj: ErgoValidationSettings, w: Writer): Unit = { ErgoValidationSettingsUpdateSerializer.serialize(obj.updateFromInitial, w) } diff --git a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala b/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala index 07e46f2354..9dccb5d9fa 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoValidationSettingsUpdate.scala +++ b/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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.serialization.ConstantStore import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} @@ -21,7 +21,7 @@ object ErgoValidationSettingsUpdate { val empty: ErgoValidationSettingsUpdate = ErgoValidationSettingsUpdate(Seq(), Seq()) } -object ErgoValidationSettingsUpdateSerializer extends ScorexSerializer[ErgoValidationSettingsUpdate] { +object ErgoValidationSettingsUpdateSerializer extends ErgoSerializer[ErgoValidationSettingsUpdate] { private val FirstRule = org.ergoplatform.validation.ValidationRules.FirstRuleId diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/src/main/scala/org/ergoplatform/settings/Parameters.scala index e561e817d2..c490de28eb 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -12,7 +12,7 @@ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters import Extension.SystemParametersPrefix -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer /** * System parameters which could be readjusted via collective miners decision. @@ -382,7 +382,7 @@ object Parameters { } -object ParametersSerializer extends ScorexSerializer[Parameters] with ApiCodecs { +object ParametersSerializer extends ErgoSerializer[Parameters] with ApiCodecs { override def serialize(params: Parameters, w: Writer): Unit = { require(params.parametersTable.nonEmpty, s"$params is empty") diff --git a/src/main/scala/scorex/core/app/Version.scala b/src/main/scala/scorex/core/app/Version.scala index c896fc4649..33d70e1ca4 100644 --- a/src/main/scala/scorex/core/app/Version.scala +++ b/src/main/scala/scorex/core/app/Version.scala @@ -1,6 +1,6 @@ package scorex.core.app -import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} import scorex.util.serialization._ /** @@ -9,7 +9,7 @@ import scorex.util.serialization._ case class Version(firstDigit: Byte, secondDigit: Byte, thirdDigit: Byte) extends BytesSerializable with Ordered[Version] { override type M = Version - override def serializer: ScorexSerializer[Version] = ApplicationVersionSerializer + override def serializer: ErgoSerializer[Version] = ApplicationVersionSerializer override def compare(that: Version): Int = { if (this.firstDigit != that.firstDigit) { @@ -42,7 +42,7 @@ object Version { val v4043: Version = Version(4, 0, 43) } -object ApplicationVersionSerializer extends ScorexSerializer[Version] { +object ApplicationVersionSerializer extends ErgoSerializer[Version] { val SerializedVersionLength: Int = 3 diff --git a/src/main/scala/scorex/core/network/PeerFeature.scala b/src/main/scala/scorex/core/network/PeerFeature.scala index 6ceac2d8a2..0275053ca0 100644 --- a/src/main/scala/scorex/core/network/PeerFeature.scala +++ b/src/main/scala/scorex/core/network/PeerFeature.scala @@ -1,6 +1,6 @@ package scorex.core.network -import scorex.core.serialization.{BytesSerializable, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} /** * An abstract trait to describe peer capabilities. @@ -17,5 +17,5 @@ trait PeerFeature extends BytesSerializable { object PeerFeature { type Id = Byte - type Serializers = Map[Id, ScorexSerializer[_ <: PeerFeature]] + type Serializers = Map[Id, ErgoSerializer[_ <: PeerFeature]] } diff --git a/src/main/scala/scorex/core/network/PeerSpec.scala b/src/main/scala/scorex/core/network/PeerSpec.scala index e7e8766796..47b9181442 100644 --- a/src/main/scala/scorex/core/network/PeerSpec.scala +++ b/src/main/scala/scorex/core/network/PeerSpec.scala @@ -5,7 +5,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.Extensions._ import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} @@ -41,7 +41,7 @@ case class PeerSpec(agentName: String, } -object PeerSpecSerializer extends ScorexSerializer[PeerSpec] with ScorexLogging { +object PeerSpecSerializer extends ErgoSerializer[PeerSpec] with ScorexLogging { override def serialize(obj: PeerSpec, w: Writer): Unit = { w.putShortString(obj.agentName) diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 2320a2ccdf..6b43601880 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -9,7 +9,7 @@ import scorex.core.consensus.SyncInfo import scorex.core.network._ import scorex.core.network.message.Message.MessageCode import scorex.core.NodeViewModifier -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} @@ -32,7 +32,7 @@ case class InvData(typeId: NetworkObjectTypeId.Value, ids: Seq[ModifierId]) * * Payload of this message should be determined in underlying applications. */ -class SyncInfoMessageSpec[SI <: SyncInfo](serializer: ScorexSerializer[SI]) extends MessageSpecV1[SI] { +class SyncInfoMessageSpec[SI <: SyncInfo](serializer: ErgoSerializer[SI]) extends MessageSpecV1[SI] { override val messageCode: MessageCode = 65: Byte override val messageName: String = "Sync" diff --git a/src/main/scala/scorex/core/network/message/MessageSpec.scala b/src/main/scala/scorex/core/network/message/MessageSpec.scala index 6f1e814ab3..8681364741 100644 --- a/src/main/scala/scorex/core/network/message/MessageSpec.scala +++ b/src/main/scala/scorex/core/network/message/MessageSpec.scala @@ -1,12 +1,12 @@ package scorex.core.network.message import scorex.core.app.Version -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer /** * Base trait for app p2p messages in the network */ -trait MessageSpec[Content] extends ScorexSerializer[Content] { +trait MessageSpec[Content] extends ErgoSerializer[Content] { /** * The p2p protocol version in which this message type first appeared diff --git a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala b/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala index d6ee53aa46..6ef6a2f5f5 100644 --- a/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/LocalAddressPeerFeature.scala @@ -4,7 +4,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization._ import scorex.util.Extensions._ @@ -19,7 +19,7 @@ case class LocalAddressPeerFeature(address: InetSocketAddress) extends PeerFeatu override def serializer: LocalAddressPeerFeatureSerializer.type = LocalAddressPeerFeatureSerializer } -object LocalAddressPeerFeatureSerializer extends ScorexSerializer[LocalAddressPeerFeature] { +object LocalAddressPeerFeatureSerializer extends ErgoSerializer[LocalAddressPeerFeature] { private val AddressLength = 4 diff --git a/src/main/scala/scorex/core/network/peer/PeerInfo.scala b/src/main/scala/scorex/core/network/peer/PeerInfo.scala index f0cc8d4cc2..e9dbbb3218 100644 --- a/src/main/scala/scorex/core/network/peer/PeerInfo.scala +++ b/src/main/scala/scorex/core/network/peer/PeerInfo.scala @@ -3,7 +3,7 @@ package scorex.core.network.peer import java.net.InetSocketAddress import scorex.core.app.Version import scorex.core.network.{ConnectionDirection, Incoming, Outgoing, PeerSpec, PeerSpecSerializer} -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} /** @@ -43,7 +43,7 @@ object PeerInfo { /** * Serializer of [[scorex.core.network.peer.PeerInfo]] */ -object PeerInfoSerializer extends ScorexSerializer[PeerInfo] { +object PeerInfoSerializer extends ErgoSerializer[PeerInfo] { override def serialize(obj: PeerInfo, w: Writer): Unit = { w.putLong(obj.lastHandshake) diff --git a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala index ccca6c49f4..2de35f7d59 100644 --- a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala @@ -6,7 +6,7 @@ import scorex.util.serialization._ import java.net.URL import org.ergoplatform.settings.PeerFeatureDescriptors -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer /** * Peer may have rest-api URL enabled in which case it needs to be passed to/from other peers @@ -19,7 +19,7 @@ case class RestApiUrlPeerFeature(restApiUrl: URL) extends PeerFeature { override def serializer: RestApiUrlPeerFeatureSerializer.type = RestApiUrlPeerFeatureSerializer } -object RestApiUrlPeerFeatureSerializer extends ScorexSerializer[RestApiUrlPeerFeature] { +object RestApiUrlPeerFeatureSerializer extends ErgoSerializer[RestApiUrlPeerFeature] { override def serialize(obj: RestApiUrlPeerFeature, w: Writer): Unit = { val restApiUrl = obj.restApiUrl.toString diff --git a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala b/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala index 99bff52822..386f999a5f 100644 --- a/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/SessionIdPeerFeature.scala @@ -4,7 +4,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.util.serialization._ /** @@ -24,7 +24,7 @@ case class SessionIdPeerFeature(networkMagic: Array[Byte], } -object SessionIdPeerFeatureSerializer extends ScorexSerializer[SessionIdPeerFeature] { +object SessionIdPeerFeatureSerializer extends ErgoSerializer[SessionIdPeerFeature] { override def serialize(obj: SessionIdPeerFeature, w: Writer): Unit = { w.putBytes(obj.networkMagic) diff --git a/src/main/scala/scorex/core/serialization/BytesSerializable.scala b/src/main/scala/scorex/core/serialization/BytesSerializable.scala index 33e6306ba7..d9d23295dd 100644 --- a/src/main/scala/scorex/core/serialization/BytesSerializable.scala +++ b/src/main/scala/scorex/core/serialization/BytesSerializable.scala @@ -6,5 +6,5 @@ trait BytesSerializable extends Serializable { def bytes: Array[Byte] = serializer.toBytes(this) - def serializer: ScorexSerializer[M] + def serializer: ErgoSerializer[M] } diff --git a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala index 8fd26ff081..6c5055ed5e 100644 --- a/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/ErgoNodeViewSynchronizerSpecification.scala @@ -23,7 +23,7 @@ import scorex.core.network.NetworkController.ReceivableMessages.SendToNetwork import scorex.core.network.message._ import scorex.core.network.peer.PeerInfo import scorex.core.network.{ConnectedPeer, DeliveryTracker} -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scorex.testkit.utils.AkkaFixture import scala.concurrent.duration.{Duration, _} @@ -121,7 +121,7 @@ class ErgoNodeViewSynchronizerSpecification extends HistoryTestHelpers with Matc } def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, ScorexSerializer[PM], DeliveryTracker) = { + (ActorRef, ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, ErgoSerializer[PM], DeliveryTracker) = { @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val h = localHistoryGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) @@ -162,7 +162,7 @@ class ErgoNodeViewSynchronizerSpecification extends HistoryTestHelpers with Matc ) synchronizerMockRef ! ChangedHistory(history) synchronizerMockRef ! ChangedMempool(pool) - val serializer: ScorexSerializer[PM] = HeaderSerializer.asInstanceOf[ScorexSerializer[PM]] + val serializer: ErgoSerializer[PM] = HeaderSerializer.asInstanceOf[ErgoSerializer[PM]] (synchronizerMockRef, nodeViewHolderMockRef, h.syncInfoV1, m, tx, p, pchProbe, ncProbe, eventListener, serializer, deliveryTracker) } diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index 78280c8084..226fea525d 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -20,7 +20,7 @@ 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, ManifestSerializer, ScorexSerializer} +import scorex.core.serialization.{BytesSerializable, ManifestSerializer, ErgoSerializer} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture @@ -47,7 +47,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val stateGen: Gen[ST] def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[BlockSection]) + (ActorRef, ErgoSyncInfo, BlockSection, ErgoTransaction, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ErgoSerializer[BlockSection]) class SynchronizerFixture extends AkkaFixture { @SuppressWarnings(Array("org.wartremover.warts.PublicInference")) @@ -118,7 +118,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec withFixture { ctx => import ctx._ - val dummySyncInfoMessageSpec = new SyncInfoMessageSpec[SyncInfo](serializer = new ScorexSerializer[SyncInfo] { + val dummySyncInfoMessageSpec = new SyncInfoMessageSpec[SyncInfo](serializer = new ErgoSerializer[SyncInfo] { override def parse(r: Reader): SyncInfo = { throw new Exception() } @@ -129,7 +129,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val dummySyncInfo: SyncInfo = new SyncInfo { type M = BytesSerializable - def serializer: ScorexSerializer[M] = throw new Exception + def serializer: ErgoSerializer[M] = throw new Exception } val msgBytes = dummySyncInfoMessageSpec.toBytes(dummySyncInfo) diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala index 9616dd81cf..4b8b3685ac 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala @@ -17,7 +17,7 @@ import org.scalacheck.Gen import scorex.core.idToBytes import scorex.core.network.{ConnectedPeer, DeliveryTracker} import scorex.core.network.peer.PeerInfo -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scala.concurrent.ExecutionContextExecutor @@ -53,7 +53,7 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { } override def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[PM]) = { + (ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ErgoSerializer[PM]) = { @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val h = historyGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) @@ -95,7 +95,7 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { ) ref ! ChangedHistory(h) ref ! ChangedMempool(pool) - val serializer: ScorexSerializer[PM] = HeaderSerializer.asInstanceOf[ScorexSerializer[PM]] + val serializer: ErgoSerializer[PM] = HeaderSerializer.asInstanceOf[ErgoSerializer[PM]] (ref, h.syncInfoV1, m, tx, p, pchProbe, ncProbe, vhProbe, eventListener, serializer) } diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala index 25c5862f33..52cb2fbaab 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityUTXO.scala @@ -17,7 +17,7 @@ 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.ScorexSerializer +import scorex.core.serialization.ErgoSerializer import scala.concurrent.ExecutionContextExecutor @@ -50,7 +50,7 @@ class ErgoSanityUTXO extends ErgoSanity[UTXO_ST] with ErgoTestHelpers { } override def nodeViewSynchronizer(implicit system: ActorSystem): - (ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ScorexSerializer[PM]) = { + (ActorRef, SI, PM, TX, ConnectedPeer, TestProbe, TestProbe, TestProbe, TestProbe, ErgoSerializer[PM]) = { @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) val h = historyGen.sample.get @SuppressWarnings(Array("org.wartremover.warts.OptionPartial")) @@ -88,7 +88,7 @@ class ErgoSanityUTXO extends ErgoSanity[UTXO_ST] with ErgoTestHelpers { ) ref ! ChangedHistory(h) ref ! ChangedMempool(pool) - val serializer: ScorexSerializer[PM] = HeaderSerializer.asInstanceOf[ScorexSerializer[PM]] + val serializer: ErgoSerializer[PM] = HeaderSerializer.asInstanceOf[ErgoSerializer[PM]] (ref, h.syncInfoV1, m, tx, p, pchProbe, ncProbe, vhProbe, eventListener, serializer) } diff --git a/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala b/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala index 74774ae306..65ac78905d 100644 --- a/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala +++ b/src/test/scala/org/ergoplatform/serialization/SerializationTests.scala @@ -14,12 +14,12 @@ import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators import org.scalacheck.Gen import org.scalatest.Assertion -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer class SerializationTests extends ErgoPropertyTest with WalletGenerators with scorex.testkit.SerializationTests { def checkSerializationRoundtripAndSize[A <: ErgoNodeViewModifier](generator: Gen[A], - serializer: ScorexSerializer[A]): Assertion = { + serializer: ErgoSerializer[A]): Assertion = { forAll(generator) { b: A => val recovered = serializer.parseBytes(serializer.toBytes(b)) val bytes = serializer.toBytes(b) diff --git a/src/test/scala/scorex/testkit/SerializationTests.scala b/src/test/scala/scorex/testkit/SerializationTests.scala index 23034ada83..b4ed96389e 100644 --- a/src/test/scala/scorex/testkit/SerializationTests.scala +++ b/src/test/scala/scorex/testkit/SerializationTests.scala @@ -4,10 +4,10 @@ import org.scalacheck.Gen import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.core.serialization.ScorexSerializer +import scorex.core.serialization.ErgoSerializer trait SerializationTests extends ScalaCheckPropertyChecks with Matchers { - def checkSerializationRoundtrip[A](generator: Gen[A], serializer: ScorexSerializer[A]): Assertion = { + 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) From 3bfbca75a0d93cd0d809975c3671f1d8fc3159ed Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 24 Mar 2023 16:30:04 +0300 Subject: [PATCH 120/204] removing writeDownloadPlanToTheDb --- .../scala/scorex/core/serialization/ErgoSerializer.scala | 2 +- .../scorex/core/serialization/ManifestSerializer.scala | 2 +- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 6 ++---- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 9 ++++----- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala index cfb9b834de..271030a840 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala @@ -7,7 +7,7 @@ import scorex.util.serialization._ import scala.util.Try /** - * Basic interface for serializer with additional method to work with bytes insted of Reader/Writer instances + * Basic interface for serializer with additional methods to work with bytes, not only Reader/Writer instances */ trait ErgoSerializer[T] extends Serializer[T, T, Reader, Writer] { diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index c024852557..7817e9fef6 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -56,7 +56,7 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro } object ManifestSerializer { - val ManifestDepth: Byte = 2 + val ManifestDepth: Byte = 14 lazy val defaultSerializer = new ManifestSerializer(ManifestDepth) } diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 4522210b67..f554938ab5 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -93,8 +93,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) //todo: this method is not used, should be removed on next scrypto update? override def update(prover: BatchAVLProver[DigestType, _]): Try[Unit] = update(prover, Nil) - def dumpSnapshot(dumpStorage: LDBKVStore, - manifestDepth: Int): Array[Byte] = { + def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Array[Byte] = { store.backup { ri => def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { @@ -128,8 +127,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) manifestLoop(in.leftLabel, level + 1, manifestBuilder) manifestLoop(in.rightLabel, level + 1, manifestBuilder) case _ => - //todo: support leafs - println("!!!") + // do nothing for a leaf } } 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 70414f44c7..b18926aa6e 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 @@ -12,8 +12,7 @@ import scorex.core.serialization.SubtreeSerializer import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.LDBVersionedStore -import scorex.util.{ByteArrayBuilder, ModifierId, ScorexLogging} -import scorex.util.serialization.VLQByteBufferWriter +import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor import scala.util.{Random, Try} @@ -41,7 +40,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { minimalFullBlockHeightVar = height + 1 } - private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) + // private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) private val downloadPlanKey = Blake2b256.hash("download plan") @@ -150,7 +149,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } private def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { - val w = new VLQByteBufferWriter(new ByteArrayBuilder()) + /* val w = new VLQByteBufferWriter(new ByteArrayBuilder()) w.putULong(plan.startingTime) w.putULong(plan.latestUpdateTime) w.putUInt(plan.snapshotHeight) @@ -168,7 +167,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { historyStorage.insert(expectedChunksPrefix ++ idxBytes, chunkId) idx = idx + 1 } - +*/ } /* From e12bc76c3adf95ab6cee809f18f8bc49e10f089b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 17 Apr 2023 17:25:15 +0300 Subject: [PATCH 121/204] merging most of biggest changes from preparation branch --- .../core/serialization/ErgoSerializer.scala | 10 ++ .../serialization/ManifestSerializer.scala | 11 +- .../authds/avltree/batch/Constants.scala | 24 ++- .../avltree/batch/ProverNodeSerializer.scala | 71 +++++++++ .../batch/VersionedLDBAVLStorage.scala | 145 ++++++------------ .../scala/scorex/db/LDBVersionedStore.scala | 48 ++++-- .../nodeView/state/SnapshotsDb.scala | 11 +- .../state/UtxoSetSnapshotPersistence.scala | 3 +- .../nodeView/NodeViewSynchronizerTests.scala | 2 +- 9 files changed, 210 insertions(+), 115 deletions(-) create mode 100644 avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala diff --git a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala index 271030a840..972ac73792 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ErgoSerializer.scala @@ -11,17 +11,27 @@ import scala.util.Try */ trait ErgoSerializer[T] extends Serializer[T, T, Reader, Writer] { + /** + * Serialize object `obj` to byte array + */ def toBytes(obj: T): Array[Byte] = { val writer = new VLQByteBufferWriter(new ByteArrayBuilder()) serialize(obj, writer) writer.result().toBytes } + + /** + * Deserialize byte array into object of type `T` (or throw exception) + */ def parseBytes(bytes: Array[Byte]): T = { val reader = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) parse(reader) } + /** + * Deserialize byte array into object of type `T` (or return Failure) + */ def parseBytesTry(bytes: Array[Byte]): Try[T] = { Try(parseBytes(bytes)) } diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index 7817e9fef6..2c4bac6397 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -56,7 +56,14 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro } object ManifestSerializer { - val ManifestDepth: Byte = 14 - lazy val defaultSerializer = new ManifestSerializer(ManifestDepth) + /** + * Current manifest depth in the Ergo mainnet + */ + val MainnetManifestDepth: Byte = 14 + + /** + * Manifest serializer used in the Ergo mainnet + */ + val defaultSerializer = new ManifestSerializer(MainnetManifestDepth) } diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala index 43b1a183c4..5f1868bf98 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/Constants.scala @@ -2,10 +2,30 @@ package scorex.crypto.authds.avltree.batch import scorex.crypto.hash.{Blake2b256, Digest32} + +/** + * Commonly used constants + * + * //todo: move to core module once it is established + */ object Constants { - type HashFnType = Blake2b256.type + /** + * Type of hash function output + */ type DigestType = Digest32 - val hashFn: HashFnType = Blake2b256 + /** + * Length of hash function output + */ val HashLength: Int = 32 + + /** + * Type of hash function used in the protocol + */ + type HashFnType = Blake2b256.type + + /** + * Thread-safe instance of hash function used in the protocol + */ + val hashFn: HashFnType = Blake2b256 } 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 new file mode 100644 index 0000000000..7a89b50ffa --- /dev/null +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/ProverNodeSerializer.scala @@ -0,0 +1,71 @@ +package scorex.crypto.authds.avltree.batch + +import com.google.common.primitives.Ints +import scorex.core.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 +import scorex.crypto.hash.Digest32 +import scorex.db.LDBVersionedStore +import scorex.util.serialization.{Reader, Writer} + +/** + * ErgoSerializer based prover nodes serializer (alternative to one provided in scrypto which is doing much more + * allocations) + * + * @param store - store which could be provided to fetch children of a node when a child is requested + */ +class ProverNodeSerializer(store: LDBVersionedStore) extends ErgoSerializer[ProverNodes[DigestType]] { + + import VersionedLDBAVLStorage.{InternalNodePrefix,LeafPrefix} + + override def serialize(node: ProverNodes[DigestType], w: Writer): Unit = { + node match { + case n: ProxyInternalNode[DigestType] => + w.put(InternalNodePrefix) + w.put(n.balance) + w.putBytes(n.key) + w.putBytes(n.leftLabel) + w.putBytes(n.rightLabel) + case n: InternalProverNode[DigestType] => + w.put(InternalNodePrefix) + w.put(n.balance) + w.putBytes(n.key) + w.putBytes(n.left.label) + w.putBytes(n.right.label) + case n: ProverLeaf[DigestType] => + w.put(LeafPrefix) + w.putBytes(n.key) + w.putBytes(Ints.toByteArray(n.value.length)) + w.putBytes(n.value) + w.putBytes(n.nextLeafKey) + } + } + + override def parse(r: Reader): ProverNodes[DigestType] = { + val prefix = r.getByte() + prefix match { + case InternalNodePrefix => + val balance = Balance @@ r.getByte() + val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) + val leftKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) + val rightKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) + + if (store != null) { + new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) + } else { + new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) + } + case LeafPrefix => + val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) + val (value, nextLeafKey) = { + val valueSize = Ints.fromByteArray(r.getBytes(4)) + val value = ADValue @@ r.getBytes(valueSize) + val nextLeafKey = ADKey @@ r.getBytes(StateTreeParameters.keySize) + value -> nextLeafKey + } + new ProverLeaf[DigestType](key, value, nextLeafKey)(hashFn) + } + } + +} diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index f554938ab5..e06db8e5a1 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -1,16 +1,16 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints -import scorex.core.serialization.{ManifestSerializer, ErgoSerializer} +import scorex.core.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.Constants.{DigestType, HashFnType, hashFn} +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.{topNodeHashKey, topNodeHeightKey} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} -import scorex.crypto.authds.{ADDigest, ADKey, ADValue, Balance} +import scorex.crypto.authds.{ADDigest, ADKey} import scorex.util.encode.Base58 import scorex.crypto.hash import scorex.crypto.hash.Digest32 import scorex.db.{LDBKVStore, LDBVersionedStore} import scorex.util.ScorexLogging -import scorex.util.serialization.{Reader, Writer} import scala.collection.mutable import scala.util.{Failure, Try} @@ -23,24 +23,20 @@ import scala.util.{Failure, Try} class VersionedLDBAVLStorage(store: LDBVersionedStore) extends VersionedAVLStorage[DigestType] with ScorexLogging { - import VersionedLDBAVLStorage.{nodeLabel, topNodeKeys} + import VersionedLDBAVLStorage.nodeLabel - private val topKeys = topNodeKeys() - private val topNodeHashKey: Array[Byte] = topKeys._1 - private val topNodeHeightKey: Array[Byte] = topKeys._2 + private def restorePrunedRootNode(): Try[(ProverNodes[DigestType], Int)] = Try { + val rootNode = VersionedLDBAVLStorage.fetch(ADKey @@ store.get(topNodeHashKey).get)(store) + val rootHeight = Ints.fromByteArray(store.get(topNodeHeightKey).get) - private def restorePrunedTopNode(): Try[(ProverNodes[DigestType], Int)] = Try { - val top = VersionedLDBAVLStorage.fetch(ADKey @@ store.get(topNodeHashKey).get)(store) - val topHeight = Ints.fromByteArray(store.get(topNodeHeightKey).get) - - top -> topHeight + rootNode -> rootHeight } /** - * Restore prover's tree from database with just pruned top node (so only one node is fetched) - */ + * Restore prover's tree from database with just pruned top node (so only one node is fetched) + */ def restorePrunedProver(): Try[BatchAVLProver[DigestType, HashFnType]] = { - restorePrunedTopNode().map { recoveredTop => + restorePrunedRootNode().map { recoveredTop => new BatchAVLProver(StateTreeParameters.keySize, StateTreeParameters.valueSize, Some(recoveredTop))(hashFn) } } @@ -49,7 +45,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) if (!this.version.contains(version)) { // do not rollback to self store.rollbackTo(version) } - }.flatMap(_ => restorePrunedTopNode()) + }.flatMap(_ => restorePrunedRootNode()) .recoverWith { case e => log.warn(s"Failed to recover tree for digest ${Base58.encode(version)}:", e) Failure(e) @@ -63,7 +59,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) additionalData: Seq[(K, V)]): Try[Unit] = { val digestWrapper = prover.digest val indexes = Seq(topNodeHashKey -> nodeLabel(prover.topNode), - topNodeHeightKey -> Ints.toByteArray(prover.rootNodeHeight)) + topNodeHeightKey -> Ints.toByteArray(prover.rootNodeHeight)) val toInsert = serializedVisitedNodes(prover.topNode, isTop = true) val toRemove = prover.removedNodes().map(rn => rn.label) val toUpdate = indexes ++ toInsert @@ -93,11 +89,19 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) //todo: this method is not used, should be removed on next scrypto update? override def update(prover: BatchAVLProver[DigestType, _]): Try[Unit] = update(prover, Nil) - def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Array[Byte] = { - store.backup { ri => + /** + * Dump current state of AVL+ tree into another (non-versioned) storage. Tree is dumped in + * (manifest, subtrees) format + * + * @param dumpStorage - non-versioned storage to dump tree to + * @param manifestDepth - depth of manifest tree + * @return - hash of root node of tree, or failure if an error (e.g. in database) happened + */ + def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Try[Array[Byte]] = { + store.processSnapshot { dbReader => def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { - val nodeBytes = ri.get(label) + val nodeBytes = dbReader.get(label) builder ++= nodeBytes val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) node match { @@ -116,7 +120,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) } def manifestLoop(nodeDbKey: Array[Byte], level: Int, manifestBuilder: mutable.ArrayBuilder[Byte]): Unit = { - val nodeBytes = ri.get(nodeDbKey) + val nodeBytes = dbReader.get(nodeDbKey) manifestBuilder ++= nodeBytes val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) node match { @@ -127,17 +131,17 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) manifestLoop(in.leftLabel, level + 1, manifestBuilder) manifestLoop(in.rightLabel, level + 1, manifestBuilder) case _ => - // do nothing for a leaf + // do nothing for a leaf } } - val rootNodeLabel = ri.get(topNodeHashKey) - val rootNodeHeight = Ints.fromByteArray(ri.get(topNodeHeightKey)) + val rootNodeLabel = dbReader.get(topNodeHashKey) + val rootNodeHeight = Ints.fromByteArray(dbReader.get(topNodeHeightKey)) val manifestBuilder = mutable.ArrayBuilder.make[Byte]() manifestBuilder.sizeHint(200000) manifestBuilder ++= Ints.toByteArray(rootNodeHeight) - manifestBuilder += ManifestSerializer.ManifestDepth + manifestBuilder += ManifestSerializer.MainnetManifestDepth manifestLoop(rootNodeLabel, level = 1, manifestBuilder) val manifestBytes = manifestBuilder.result() @@ -150,17 +154,19 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) object VersionedLDBAVLStorage { + + private[batch] val topNodeHashKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(123: Byte) + private[batch] val topNodeHeightKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(124: Byte) + // prefixes used to encode node type (internal or leaf) in database private[batch] val InternalNodePrefix: Byte = 0: Byte private[batch] val LeafPrefix: Byte = 1: Byte - val noStoreSerializer = new ProverNodeSerializer(null) - - private[batch] def topNodeKeys(): (Array[Byte], Array[Byte]) = { - val topNodeKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(123: Byte) - val topNodeHeightKey: Array[Byte] = Array.fill(StateTreeParameters.labelSize)(124: Byte) - (topNodeKey, topNodeHeightKey) - } + /** + * Node serializer which is restoring nodes with only children labels stored in, without possibility to restore + * children automatically when requested + */ + val noStoreSerializer = new ProverNodeSerializer(store = null) /** * Fetch tree node from database by its database id (hash of node contents) @@ -182,13 +188,19 @@ object VersionedLDBAVLStorage { */ private[batch] def nodeLabel(node: ProverNodes[DigestType]): Array[Byte] = node.label + /** + * Reconstruct versioned AVL+ tree storage from dump + * @param manifest - tree started from root node cut at fixed depth + * @param chunks - iterator (lazy sequence) of subtrees under manifest tree + * @param additionalData - additional fields (metadata) to write into database + * @param store - db to store restored tree to + * @return db with reconstructed tree written to, or error + */ def recreate(manifest: BatchAVLProverManifest[DigestType], - chunks: Iterator[BatchAVLProverSubtree[DigestType]], - additionalData: Iterator[(Array[Byte], Array[Byte])], - store: LDBVersionedStore): Try[VersionedLDBAVLStorage] = { - //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate - - val (topNodeHashKey, topNodeHeightKey) = topNodeKeys() + chunks: Iterator[BatchAVLProverSubtree[DigestType]], + additionalData: Iterator[(Array[Byte], Array[Byte])], + store: LDBVersionedStore): Try[VersionedLDBAVLStorage] = { + //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate? def idCollector(node: ProverNodes[DigestType], acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { @@ -209,7 +221,7 @@ object VersionedLDBAVLStorage { val indices = Iterator(topNodeHashKey -> nodeLabel(rootNode), topNodeHeightKey -> Ints.toByteArray(rootNodeHeight)) val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) - store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map{_ => + store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map { _ => new VersionedLDBAVLStorage(store) } } @@ -228,58 +240,3 @@ object VersionedLDBAVLStorage { } } - -class ProverNodeSerializer(store: LDBVersionedStore) extends ErgoSerializer[ProverNodes[DigestType]] { - - import VersionedLDBAVLStorage.{InternalNodePrefix,LeafPrefix} - - override def serialize(node: ProverNodes[DigestType], w: Writer): Unit = { - node match { - case n: ProxyInternalNode[DigestType] => - w.put(InternalNodePrefix) - w.put(n.balance) - w.putBytes(n.key) - w.putBytes(n.leftLabel) - w.putBytes(n.rightLabel) - case n: InternalProverNode[DigestType] => - w.put(InternalNodePrefix) - w.put(n.balance) - w.putBytes(n.key) - w.putBytes(n.left.label) - w.putBytes(n.right.label) - case n: ProverLeaf[DigestType] => - w.put(LeafPrefix) - w.putBytes(n.key) - w.putBytes(Ints.toByteArray(n.value.length)) - w.putBytes(n.value) - w.putBytes(n.nextLeafKey) - } - } - - override def parse(r: Reader): ProverNodes[DigestType] = { - val prefix = r.getByte() - prefix match { - case InternalNodePrefix => - val balance = Balance @@ r.getByte() - val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) - val leftKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) - val rightKey = ADKey @@ r.getBytes(StateTreeParameters.labelSize) - - if (store != null) { - new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) - } else { - new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) - } - case LeafPrefix => - val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) - val (value, nextLeafKey) = { - val valueSize = Ints.fromByteArray(r.getBytes(4)) - val value = ADValue @@ r.getBytes(valueSize) - val nextLeafKey = ADKey @@ r.getBytes(StateTreeParameters.keySize) - value -> nextLeafKey - } - new ProverLeaf[DigestType](key, value, nextLeafKey)(hashFn) - } - } - -} diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index f67852ef28..ff57dad668 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -8,7 +8,10 @@ import java.nio.ByteBuffer import scala.collection.mutable.ArrayBuffer import java.util.concurrent.locks.ReentrantReadWriteLock import scorex.crypto.hash.Blake2b256 -import scala.util.Try +import scorex.db.LDBVersionedStore.SnapshotReadInterface +import scorex.util.ScorexLogging + +import scala.util.{Failure, Success, Try} /** @@ -23,7 +26,9 @@ import scala.util.Try * @param initialKeepVersions - number of versions to keep when the store is created. Can be changed after. * */ -class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) extends KVStoreReader { +class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) + extends KVStoreReader with ScorexLogging { + type VersionID = Array[Byte] type LSN = Long // logical serial number: type used to provide order of records in undo list @@ -408,18 +413,25 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e versions.reverse } - trait ReadInterface { - def get(key: Array[Byte]): Array[Byte] - } - - def backup[T](logic: ReadInterface => T): T = { + /** + * Take database snapshot, process it, and then close the snapshot. + * Could be useful when it is needed to process current state of database without blocking database (and so threads + * possibly working with it). + * + * @param logic - processing logic which is getting access to `get` function to read from database snapshot + */ + def processSnapshot[T](logic: SnapshotReadInterface => T): Try[T] = { val ro = new ReadOptions() ro.snapshot(db.getSnapshot) try { - val ri = new ReadInterface { - override def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) + object readInterface extends SnapshotReadInterface { + def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) } - logic(ri) + Success(logic(readInterface)) + } catch { + case t: Throwable => + log.info("Error during snapshot processing: ", t) + Failure(t) } finally { // Close the snapshot to avoid resource leaks ro.snapshot().close() @@ -427,3 +439,19 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) e } } + +object LDBVersionedStore { + + /** + * Interface to read from versioned database snapshot which can be provided to clients in order to serve them with + * snapshot. Contains only reader function. + */ + trait SnapshotReadInterface { + /** + * Read value by key. Client should care about key existence on its side. If key does not exist in database, + * an exception will be thrown. + */ + def get(key: Array[Byte]): Array[Byte] + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 8a3968b67f..330228ea43 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -79,11 +79,12 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { log.info("Snapshots pruning finished") } - def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Array[Byte] = { - val manifestId = pullFrom.dumpSnapshot(store, ManifestSerializer.ManifestDepth) - val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) - writeSnapshotsInfo(si) - manifestId + def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Try[Array[Byte]] = { + pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth).map { manifestId => + val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) + writeSnapshotsInfo(si) + manifestId + } } def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 07f5dc0d8e..ca9fcbcfb5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -9,6 +9,7 @@ import scorex.util.ScorexLogging import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapshot} import scala.concurrent.Future +import scala.util.Try trait UtxoSetSnapshotPersistence extends ScorexLogging { @@ -17,7 +18,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? - private[nodeView] def dumpSnapshot(height: Height): Array[Byte] = { + private[nodeView] def dumpSnapshot(height: Height): Try[Array[Byte]] = { snapshotsDb.writeSnapshot(persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage], height) } diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index 226fea525d..41f4dd2a6b 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -277,7 +277,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val height = 1 usr.applyModifier(mod, Some(height))(_ => ()) - val manifestId = usr.dumpSnapshot(height) + val manifestId = usr.dumpSnapshot(height).get // Then send message to request it node ! Message[ManifestId](GetManifestSpec, Left(manifestId), Option(peer)) From d4b0603bfe3a30f4b2d766c9a52215b31eadf895 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 18 Apr 2023 17:19:25 +0300 Subject: [PATCH 122/204] importing DiffSer and comments from preparation branch --- src/main/resources/application.conf | 1 + .../ergoplatform/mining/AutolykosPowScheme.scala | 4 ++-- .../ergoplatform/mining/CandidateGenerator.scala | 4 ++-- .../mining/difficulty/DifficultyAdjustment.scala | 8 ++++---- .../mining/difficulty/RequiredDifficulty.scala | 7 ++++++- .../ergoplatform/modifiers/ErgoFullBlock.scala | 2 +- .../modifiers/history/PreHeader.scala | 5 +++++ .../modifiers/history/header/Header.scala | 4 ++-- .../history/header/HeaderSerializer.scala | 6 +++--- .../modifiers/history/popow/NipopowAlgos.scala | 4 ++-- .../modifiers/history/popow/PoPowHeader.scala | 2 +- .../ergoplatform/settings/ChainSettings.scala | 7 +++++-- .../org/ergoplatform/settings/Constants.scala | 16 ++++++++++++++-- .../org/ergoplatform/settings/Parameters.scala | 2 +- .../core/network/message/BasicMessagesRepo.scala | 2 +- .../core/serialization/BytesSerializable.scala | 7 +++++++ .../http/routes/InfoApiRoutesSpec.scala | 4 ++-- .../mining/AutolykosPowSchemeSpec.scala | 4 ++-- .../DifficultyAdjustmentSpecification.scala | 2 +- ...a => DifficultySerializerSpecification.scala} | 5 ++--- .../HeaderSerializationSpecification.scala | 6 +++--- .../NonVerifyADHistorySpecification.scala | 4 ++-- .../extra/ExtraIndexerSpecification.scala | 4 ++-- .../org/ergoplatform/tools/ChainGenerator.scala | 4 ++-- .../tools/DifficultyControlSimulator.scala | 6 +++--- .../org/ergoplatform/tools/MinerBench.scala | 4 ++-- .../utils/generators/ErgoGenerators.scala | 4 ++-- 27 files changed, 80 insertions(+), 48 deletions(-) rename src/test/scala/org/ergoplatform/mining/difficulty/{RequiredDifficultySerializerSpecification.scala => DifficultySerializerSpecification.scala} (86%) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 433eeb9c52..58bae38460 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -21,6 +21,7 @@ ergo { # otherwise, it could be hard to find proofs around the peers blocksToKeep = -1 + # Download and apply UTXO set snapshot and full-blocks after that utxoBootstrap = false # Download PoPoW proof on node bootstrap diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala index 8e07df9224..27ec642ce4 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala @@ -3,7 +3,7 @@ package org.ergoplatform.mining import com.google.common.primitives.{Bytes, Ints, Longs} import org.bouncycastle.util.BigIntegers import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.ExtensionCandidate @@ -205,7 +205,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { * Get target `b` from encoded difficulty `nBits` */ private[mining] def getB(nBits: Long): BigInt = { - q / RequiredDifficulty.decodeCompactBits(nBits) + q / DifficultySerializer.decodeCompactBits(nBits) } /** diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index dbdab5b7f2..8c3af63814 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -5,7 +5,7 @@ import akka.pattern.StatusReply import com.google.common.primitives.Longs import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.mining.AutolykosPowScheme.derivedHeaderFields -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.Extension @@ -460,7 +460,7 @@ object CandidateGenerator extends ScorexLogging { // Calculate required difficulty for the new block val nBits: Long = bestHeaderOpt .map(parent => history.requiredDifficultyAfter(parent)) - .map(d => RequiredDifficulty.encodeCompactBits(d)) + .map(d => DifficultySerializer.encodeCompactBits(d)) .getOrElse(ergoSettings.chainSettings.initialNBits) // Obtain NiPoPoW interlinks vector to pack it into the extension section diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala b/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala index bf230198b6..ce5a9a1dd6 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala +++ b/src/main/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustment.scala @@ -76,8 +76,8 @@ class DifficultyAdjustment(val chainSettings: ChainSettings) extends ScorexLoggi log.debug(s"Difficulty for ${previousHeaders.last.height + 1}: predictive $predictiveDiff, limited predictive: $limitedPredictiveDiff, classic: $classicDiff, " + s"resulting uncompressed: $uncompressedDiff") // perform serialization cycle in order to normalize resulted difficulty - RequiredDifficulty.decodeCompactBits( - RequiredDifficulty.encodeCompactBits(uncompressedDiff) + DifficultySerializer.decodeCompactBits( + DifficultySerializer.encodeCompactBits(uncompressedDiff) ) } @@ -101,8 +101,8 @@ class DifficultyAdjustment(val chainSettings: ChainSettings) extends ScorexLoggi } } // perform serialization cycle in order to normalize resulted difficulty - RequiredDifficulty.decodeCompactBits( - RequiredDifficulty.encodeCompactBits(uncompressedDiff) + DifficultySerializer.decodeCompactBits( + DifficultySerializer.encodeCompactBits(uncompressedDiff) ) } diff --git a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala index c0623be44f..6d3a2407f7 100644 --- a/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala +++ b/src/main/scala/org/ergoplatform/mining/difficulty/RequiredDifficulty.scala @@ -1,11 +1,16 @@ package org.ergoplatform.mining.difficulty import java.math.BigInteger + import org.ergoplatform.nodeView.history.ErgoHistory._ import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -object RequiredDifficulty extends ErgoSerializer[NBits] { +/** + * Binary serializer and (en/de)coding utils for difficulty presented in different formats (as a big integer or nbits + * encoded 32 bits number) + */ +object DifficultySerializer extends ErgoSerializer[NBits] { override def serialize(obj: NBits, w: Writer): Unit = { w.putBytes(uint32ToByteArrayBE(obj)) diff --git a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala index 0c50b391e8..42fd2c286e 100644 --- a/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala +++ b/src/main/scala/org/ergoplatform/modifiers/ErgoFullBlock.scala @@ -7,8 +7,8 @@ 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.TransactionsCarryingPersistentNodeViewModifier import scorex.core.serialization.ErgoSerializer +import scorex.core.TransactionsCarryingPersistentNodeViewModifier import scorex.util.ModifierId case class ErgoFullBlock(header: Header, diff --git a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala index f05ea42f97..641160917d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala @@ -62,6 +62,11 @@ object PreHeader { minerPk = pk) } + /** + * fake pre-header, which is used in ErgoStateContext if last headers are empty + * because ErgoStateContext needs a PreHeader and it's not optional. + * See ErgoStateContext.currentHeight returns a height from the passed PreHeader + */ val fake: PreHeader = CPreHeader( version = 0.toByte, parentId = Header.GenesisParentId, diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 4a93b29115..13b5d0112b 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -3,7 +3,7 @@ package org.ergoplatform.modifiers.history.header import io.circe.syntax._ import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.{ADProofs, BlockTransactions, PreHeader} @@ -60,7 +60,7 @@ case class Header(override val version: Header.Version, override val modifierTypeId: NetworkObjectTypeId.Value = Header.modifierTypeId - lazy val requiredDifficulty: Difficulty = RequiredDifficulty.decodeCompactBits(nBits) + lazy val requiredDifficulty: Difficulty = DifficultySerializer.decodeCompactBits(nBits) lazy val ADProofsId: ModifierId = NonHeaderBlockSection.computeId(ADProofs.modifierTypeId, id, ADProofsRoot) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala index 3ea960f1bc..0e09bd7714 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/HeaderSerializer.scala @@ -1,7 +1,7 @@ package org.ergoplatform.modifiers.history.header import org.ergoplatform.mining.AutolykosSolutionSerializer -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import scorex.core.idToBytes import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest @@ -25,7 +25,7 @@ object HeaderSerializer extends ErgoSerializer[Header] { w.putBytes(h.stateRoot) w.putULong(h.timestamp) w.putBytes(h.extensionRoot) - RequiredDifficulty.serialize(h.nBits, w) + DifficultySerializer.serialize(h.nBits, w) w.putUInt(h.height) w.putBytes(h.votes) @@ -50,7 +50,7 @@ object HeaderSerializer extends ErgoSerializer[Header] { val stateRoot = ADDigest @@ r.getBytes(33) val timestamp = r.getULong() val extensionHash = Digest32 @@ r.getBytes(32) - val nBits = RequiredDifficulty.parse(r) + val nBits = DifficultySerializer.parse(r) val height = r.getUInt().toIntExact val votes = r.getBytes(3) diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala index 68a022cf09..0d85392673 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/NipopowAlgos.scala @@ -1,7 +1,7 @@ package org.ergoplatform.modifiers.history.popow import org.ergoplatform.mining.AutolykosPowScheme -import org.ergoplatform.mining.difficulty.RequiredDifficulty +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 @@ -77,7 +77,7 @@ class NipopowAlgos(powScheme: AutolykosPowScheme) { */ def maxLevelOf(header: Header): Int = if (!header.isGenesis) { - val requiredTarget = org.ergoplatform.mining.q / RequiredDifficulty.decodeCompactBits(header.nBits) + val requiredTarget = org.ergoplatform.mining.q / DifficultySerializer.decodeCompactBits(header.nBits) val realTarget = powScheme.powHit(header).doubleValue val level = log2(requiredTarget.doubleValue) - log2(realTarget.doubleValue) level.toInt diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala index 57da8fa6dc..c45fcdbfda 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala @@ -134,7 +134,7 @@ object PoPowHeader { } /** - * Binary serializer for PoPowHeader, + * Binary serializer for PoPowHeader */ object PoPowHeaderSerializer extends ErgoSerializer[PoPowHeader] { import org.ergoplatform.wallet.Constants.ModifierIdLength diff --git a/src/main/scala/org/ergoplatform/settings/ChainSettings.scala b/src/main/scala/org/ergoplatform/settings/ChainSettings.scala index b07eaf4130..d8d7c46ede 100644 --- a/src/main/scala/org/ergoplatform/settings/ChainSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ChainSettings.scala @@ -2,7 +2,7 @@ package org.ergoplatform.settings import org.ergoplatform.ErgoAddressEncoder import org.ergoplatform.mining.AutolykosPowScheme -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.emission.EmissionRules import scorex.crypto.authds.ADDigest import scorex.util.ModifierId @@ -42,7 +42,10 @@ case class ChainSettings(protocolVersion: Byte, val initialDifficulty: BigInt = Base16.decode(initialDifficultyHex) .fold(_ => throw new Error(s"Failed to parse initialDifficultyHex = $initialDifficultyHex"), BigInt(_)) - val initialNBits: Long = RequiredDifficulty.encodeCompactBits(initialDifficulty) + /** + * Initial (genesis block) difficulty encoded as nbits + */ + val initialNBits: Long = DifficultySerializer.encodeCompactBits(initialDifficulty) val initialDifficultyVersion2: BigInt = Base16.decode(voting.version2ActivationDifficultyHex) .fold(_ => throw new Error(s"Failed to parse initialDifficultyVersion2 = $initialDifficultyHex"), BigInt(_)) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index fd673e6c54..afaa82cef5 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -1,6 +1,6 @@ package org.ergoplatform.settings -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.NetworkObjectTypeId import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionSerializer} @@ -9,6 +9,7 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSeria import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import scorex.core.NodeViewModifier import scorex.core.serialization.ErgoSerializer +import scorex.crypto.authds.avltree.batch.AvlTreeParameters import sigmastate.Values import sigmastate.Values.ErgoTree @@ -20,7 +21,7 @@ object Constants { val MaxTarget: BigInt = BigInt(1, Array.fill(HashLength)((-1).toByte)) val InitialDifficulty: Difficulty = BigInt(1) - val InitialNBits: Long = RequiredDifficulty.encodeCompactBits(InitialDifficulty) + val InitialNBits: Long = DifficultySerializer.encodeCompactBits(InitialDifficulty) val ModifierIdSize: Int = HashLength val BlocksPerHour = 30 @@ -44,6 +45,11 @@ 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, @@ -69,4 +75,10 @@ object Constants { def timeToTakeSnapshot(height: Int): Boolean = { height % MakeSnapshotEvery == MakeSnapshotEvery - 1 } + + /** + * AVL+ tree node parameters. The tree is used to authenticate UTXO set. + * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. + */ + object StateTreeParameters extends AvlTreeParameters(keySize = HashLength, valueSize = None, labelSize = HashLength) } diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/src/main/scala/org/ergoplatform/settings/Parameters.scala index c490de28eb..90adba7592 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -4,6 +4,7 @@ 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 scorex.util.serialization.{Reader, Writer} import scorex.util.Extensions._ @@ -12,7 +13,6 @@ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters import Extension.SystemParametersPrefix -import scorex.core.serialization.ErgoSerializer /** * System parameters which could be readjusted via collective miners decision. diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 6b43601880..6e760938bb 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -8,8 +8,8 @@ import org.ergoplatform.wallet.Constants import scorex.core.consensus.SyncInfo import scorex.core.network._ import scorex.core.network.message.Message.MessageCode -import scorex.core.NodeViewModifier import scorex.core.serialization.ErgoSerializer +import scorex.core.NodeViewModifier import scorex.crypto.hash.Digest32 import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} diff --git a/src/main/scala/scorex/core/serialization/BytesSerializable.scala b/src/main/scala/scorex/core/serialization/BytesSerializable.scala index d9d23295dd..27f8f6a0d9 100644 --- a/src/main/scala/scorex/core/serialization/BytesSerializable.scala +++ b/src/main/scala/scorex/core/serialization/BytesSerializable.scala @@ -1,10 +1,17 @@ package scorex.core.serialization +/** + * Basic interface for objects which can be represented as bytes + */ trait BytesSerializable extends Serializable { type M >: this.type <: BytesSerializable def bytes: Array[Byte] = serializer.toBytes(this) + /** + * Serializer which can convert self to bytes + */ def serializer: ErgoSerializer[M] + } diff --git a/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala b/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala index ebe6f17110..874bf21bbc 100644 --- a/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/InfoApiRoutesSpec.scala @@ -14,7 +14,7 @@ import org.ergoplatform.http.api.InfoApiRoute import org.ergoplatform.local.ErgoStatsCollector.NodeInfo.difficultyEncoder import org.ergoplatform.local.ErgoStatsCollector.{GetNodeInfo, NodeInfo} import org.ergoplatform.local.ErgoStatsCollectorRef -import org.ergoplatform.mining.difficulty.RequiredDifficulty +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 @@ -79,7 +79,7 @@ class InfoApiRoutesSpec extends AnyFlatSpec PoPoWBootstrap = settings.nodeSettings.poPoWBootstrap, blocksToKeep = settings.nodeSettings.blocksToKeep ) - val nBits = RequiredDifficulty.encodeCompactBits(difficulty) + val nBits = DifficultySerializer.encodeCompactBits(difficulty) val chain = genChain(height = 5, emptyHistory, Header.InitialVersion, nBits) val history = applyChain(emptyHistory, chain) val generatedDifficulty = history.bestFullBlockOpt diff --git a/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala b/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala index e90c85906b..173623f510 100644 --- a/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala @@ -1,7 +1,7 @@ package org.ergoplatform.mining import com.google.common.primitives.Ints -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.utils.ErgoPropertyTest import org.scalacheck.Gen @@ -16,7 +16,7 @@ class AutolykosPowSchemeSpec extends ErgoPropertyTest with NoShrink { forAll(invalidHeaderGen, Gen.choose(100, 120), Gen.choose[Byte](1, 2)) { (inHeader, difficulty, ver) => - val nBits = RequiredDifficulty.encodeCompactBits(difficulty) + val nBits = DifficultySerializer.encodeCompactBits(difficulty) val h = inHeader.copy(nBits = nBits, version = ver) val sk = randomSecret() val x = randomSecret() diff --git a/src/test/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustmentSpecification.scala b/src/test/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustmentSpecification.scala index ec69d80bcd..0187c8747f 100644 --- a/src/test/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustmentSpecification.scala +++ b/src/test/scala/org/ergoplatform/mining/difficulty/DifficultyAdjustmentSpecification.scala @@ -141,7 +141,7 @@ class DifficultyAdjustmentSpecification extends ErgoPropertyTest { val previousHeaders = control.previousHeadersRequiredForRecalculation(epoch * useLastEpochs + 1, epoch).map { i => header.copy(timestamp = header.timestamp + i * interval, height = i, - nBits = RequiredDifficulty.encodeCompactBits(RequiredDifficulty.decodeCompactBits(header.nBits) + step)) + nBits = DifficultySerializer.encodeCompactBits(DifficultySerializer.decodeCompactBits(header.nBits) + step)) } previousHeaders.length shouldBe useLastEpochs + 1 diff --git a/src/test/scala/org/ergoplatform/mining/difficulty/RequiredDifficultySerializerSpecification.scala b/src/test/scala/org/ergoplatform/mining/difficulty/DifficultySerializerSpecification.scala similarity index 86% rename from src/test/scala/org/ergoplatform/mining/difficulty/RequiredDifficultySerializerSpecification.scala rename to src/test/scala/org/ergoplatform/mining/difficulty/DifficultySerializerSpecification.scala index 7489c32d20..2039994171 100644 --- a/src/test/scala/org/ergoplatform/mining/difficulty/RequiredDifficultySerializerSpecification.scala +++ b/src/test/scala/org/ergoplatform/mining/difficulty/DifficultySerializerSpecification.scala @@ -2,8 +2,7 @@ package org.ergoplatform.mining.difficulty import org.ergoplatform.utils.ErgoPropertyTest -class RequiredDifficultySerializerSpecification extends ErgoPropertyTest { - +class DifficultySerializerSpecification extends ErgoPropertyTest { property("external vectors from BitcoinJ") { val longs = Seq(0x180130e0, 0x18019eaf, 0x1802cc47, 0x1806f0a8, 0x187c3053, 0x1a05db8b, 0x1b0404cb, 0x1c20bca7, 0x1d00ffff, 0x181bc330, @@ -25,7 +24,7 @@ class RequiredDifficultySerializerSpecification extends ErgoPropertyTest { "92340000", "-12345600", "12345600") - val calculated: Seq[String] = longs.map(nBits => RequiredDifficulty.decodeCompactBits(nBits).toString(16)) + val calculated: Seq[String] = longs.map(nBits => DifficultySerializer.decodeCompactBits(nBits).toString(16)) calculated shouldEqual expected } diff --git a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala index e9349a6371..bb80727f8e 100644 --- a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala @@ -2,7 +2,7 @@ package org.ergoplatform.network import java.nio.ByteBuffer -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.{AutolykosSolution, groupElemFromBytes} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.Algos @@ -86,7 +86,7 @@ class HeaderSerializationSpecification extends ErgoPropertyTest with DecodingUti // read difficulty encoded in Bitcoin nBits format, https://bitco.in/en/developer-reference#target-nbits val nbits = getBytes(bb, 4) // 4 bytes - val difficulty = RequiredDifficulty.decodeCompactBits(RequiredDifficulty.readUint32BE(nbits)) + val difficulty = DifficultySerializer.decodeCompactBits(DifficultySerializer.readUint32BE(nbits)) difficulty shouldBe h.requiredDifficulty val heightParsed = getULong(bb) // up to 4 bytes @@ -177,7 +177,7 @@ class HeaderSerializationSpecification extends ErgoPropertyTest with DecodingUti // read difficulty encoded in Bitcoin nBits format, https://bitco.in/en/developer-reference#target-nbits val nbits = getBytes(bb, 4) // 4 bytes - val difficulty = RequiredDifficulty.decodeCompactBits(RequiredDifficulty.readUint32BE(nbits)) + val difficulty = DifficultySerializer.decodeCompactBits(DifficultySerializer.readUint32BE(nbits)) difficulty shouldBe h.requiredDifficulty val heightParsed = getULong(bb) // up to 4 bytes diff --git a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala index 15dd345048..b2cbb964ca 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/NonVerifyADHistorySpecification.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.history -import org.ergoplatform.mining.difficulty.RequiredDifficulty +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 @@ -26,7 +26,7 @@ class NonVerifyADHistorySpecification extends HistoryTestHelpers { val useLastEpochs = 3 val initDiff = BigInt(2) - val initDiffBits = RequiredDifficulty.encodeCompactBits(initDiff) + val initDiffBits = DifficultySerializer.encodeCompactBits(initDiff) var history = generateHistory( verifyTransactions = false, 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 324555b484..a23fd59416 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoBoxCandidate, ErgoScriptPredef, P2PKAddress, UnsignedInput} import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.mining.difficulty.RequiredDifficulty +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} @@ -326,7 +326,7 @@ object ChainGenerator extends ErgoTestHelpers { val stateContext = state.stateContext val nBits: Long = lastHeaderOpt .map(parent => history.requiredDifficultyAfter(parent)) - .map(d => RequiredDifficulty.encodeCompactBits(d)) + .map(d => DifficultySerializer.encodeCompactBits(d)) .getOrElse(settings.chainSettings.initialNBits) val interlinks = lastHeaderOpt diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 854550656c..71bcaeb3cc 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -1,7 +1,7 @@ package org.ergoplatform.tools import org.ergoplatform._ -import org.ergoplatform.mining.difficulty.RequiredDifficulty +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} @@ -157,7 +157,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val stateContext = state.stateContext val nBits: Long = lastHeaderOpt .map(parent => history.requiredDifficultyAfter(parent)) - .map(d => RequiredDifficulty.encodeCompactBits(d)) + .map(d => DifficultySerializer.encodeCompactBits(d)) .getOrElse(settings.chainSettings.initialNBits) val interlinks = lastHeaderOpt diff --git a/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala b/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala index dabd6dbcf9..9d14132aab 100644 --- a/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala +++ b/src/test/scala/org/ergoplatform/tools/DifficultyControlSimulator.scala @@ -1,6 +1,6 @@ package org.ergoplatform.tools -import org.ergoplatform.mining.difficulty.{DifficultyAdjustment, RequiredDifficulty} +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 @@ -70,7 +70,7 @@ object DifficultyControlSimulator extends App with ErgoGenerators { val timeDiff = simulateTimeDiff() val newHeader = lastHeader.copy(timestamp = lastHeader.timestamp + timeDiff, height = curHeight + 1, - nBits = RequiredDifficulty.encodeCompactBits(requiredDifficulty)) + nBits = DifficultySerializer.encodeCompactBits(requiredDifficulty)) curChain(newHeader.height) = newHeader genchain(curHeight + 1) @@ -94,7 +94,7 @@ object DifficultyControlSimulator extends App with ErgoGenerators { baseHeader.copy( height = l(0).toInt, timestamp = l(1).toLong, - nBits = RequiredDifficulty.encodeCompactBits(BigInt(l(2))) + nBits = DifficultySerializer.encodeCompactBits(BigInt(l(2))) ) } printEpochs(headers, difficultyControl) diff --git a/src/test/scala/org/ergoplatform/tools/MinerBench.scala b/src/test/scala/org/ergoplatform/tools/MinerBench.scala index 9cc48f6fe1..7a997bfb05 100644 --- a/src/test/scala/org/ergoplatform/tools/MinerBench.scala +++ b/src/test/scala/org/ergoplatform/tools/MinerBench.scala @@ -3,7 +3,7 @@ package org.ergoplatform.tools import com.google.common.primitives.Bytes import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining._ -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.extension.ExtensionCandidate import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.utils.ErgoTestHelpers @@ -65,7 +65,7 @@ object MinerBench extends App with ErgoTestHelpers { val difficulty = 1000 val fb = invalidErgoFullBlockGen.sample.get val inHeader = fb.header - val nBits = RequiredDifficulty.encodeCompactBits(difficulty) + val nBits = DifficultySerializer.encodeCompactBits(difficulty) val h = inHeader.copy(nBits = nBits) val candidate = new CandidateBlock(None, Header.InitialVersion, nBits: Long, h.stateRoot, diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala index 2c56a65c0b..f24d6e3718 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala @@ -2,7 +2,7 @@ package org.ergoplatform.utils.generators import com.google.common.primitives.Shorts import org.bouncycastle.util.BigIntegers -import org.ergoplatform.mining.difficulty.RequiredDifficulty +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.{AutolykosSolution, genPk, q} import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.extension.Extension @@ -145,7 +145,7 @@ trait ErgoGenerators extends CoreGenerators with ChainGenerator with Generators stateRoot, transactionsRoot, timestamp, - RequiredDifficulty.encodeCompactBits(requiredDifficulty), + DifficultySerializer.encodeCompactBits(requiredDifficulty), height, extensionHash, powSolution, From e62286de2a8a8b9097c7b9b1f353d2ef9d73a785 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 18 Apr 2023 17:51:31 +0300 Subject: [PATCH 123/204] importing db layer changes --- avldb/src/main/scala/scorex/db/LDBKVStore.scala | 15 ++++++++------- .../batch/LDBVersionedStoreSpecification.scala | 1 - .../VersionedLDBAVLStorageSpecification.scala | 16 ++++++++++++++-- ...ionedLDBAVLStorageStatefulSpecification.scala | 1 - .../avltree/batch/helpers/TestHelper.scala | 4 ++-- src/main/resources/api/openapi.yaml | 2 +- src/main/resources/application.conf | 3 +++ .../org/ergoplatform/db/LDBKVStoreSpec.scala | 4 ++-- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index ffd53609db..7d4ab17a1b 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -14,13 +14,12 @@ import spire.syntax.all.cfor */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { - def update(toInsert: Array[(K, V)], toRemove: Array[K]): Try[Unit] = { + def update(toInsertKeys: Array[K], toInsertValues: Array[V], toRemove: Array[K]): Try[Unit] = { val batch = db.createWriteBatch() - val insertLen = toInsert.length - val removeLen = toRemove.length try { - cfor(0)(_ < insertLen, _ + 1) { i => batch.put(toInsert(i)._1, toInsert(i)._2)} - cfor(0)(_ < removeLen, _ + 1) { i => batch.delete(toRemove(i))} + require(toInsertKeys.length == toInsertValues.length) + cfor(0)(_ < toInsertKeys.length, _ + 1) { i => batch.put(toInsertKeys(i), toInsertValues(i))} + cfor(0)(_ < toRemove.length, _ + 1) { i => batch.delete(toRemove(i))} db.write(batch) Success(()) } catch { @@ -45,9 +44,11 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging } } - def insert(values: Array[(K, V)]): Try[Unit] = update(values, Array.empty) + def insert(values: Array[(K, V)]): Try[Unit] = update(values.map(_._1), values.map(_._2), Array.empty) - def remove(keys: Array[K]): Try[Unit] = update(Array.empty, keys) + def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + + def remove(keys: Array[K]): Try[Unit] = update(Array.empty, Array.empty, keys) /** * Get last key within some range (inclusive) by used comparator. diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala index f3236cfff3..c1fe98d5df 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/LDBVersionedStoreSpecification.scala @@ -17,7 +17,6 @@ class LDBVersionedStoreSpecification extends AnyPropSpec override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 val storeTest: LDBVersionedStore => Unit = { store => var version = store.lastVersionID 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 31e90fdecd..b702f5de7d 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 @@ -10,7 +10,7 @@ import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.db.LDBVersionedStore +import scorex.db.{LDBFactory, LDBVersionedStore} import scorex.utils.{Random => RandomBytes} import scala.concurrent.ExecutionContext.Implicits.global @@ -25,7 +25,6 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 def kvGen: Gen[(ADKey, ADValue)] = for { key <- Gen.listOfN(KL, Arbitrary.arbitrary[Byte]).map(_.toArray) suchThat @@ -340,4 +339,17 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec testAddInfoSaving(createVersionedStore _) } + property("dumping snapshot") { + val prover = createPersistentProver() + blockchainWorkflowTest(prover) + + val storage = prover.storage.asInstanceOf[VersionedLDBAVLStorage] + val store = LDBFactory.createKvDb("/tmp/aa") + + val ts0 = System.currentTimeMillis() + storage.dumpSnapshot(store, 4) + val ts = System.currentTimeMillis() + println("time: " + (ts-ts0)) + } + } diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala index a7b6288edd..3d393582c6 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorageStatefulSpecification.scala @@ -27,7 +27,6 @@ object WithLDB extends VersionedLDBAVLStorageStatefulCommands with TestHelper { override protected val KL = 32 override protected val VL = 8 - override protected val LL = 32 override protected def createStatefulProver: PersistentBatchAVLProver[Digest32, HF] = { createPersistentProver(keepVersions) diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala index ad8e259f7b..0b2c4d79a9 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/helpers/TestHelper.scala @@ -19,7 +19,6 @@ trait TestHelper extends FileHelper { protected val KL: Int protected val VL: Int - protected val LL: Int implicit val hf: HF = Blake2b256 @@ -28,7 +27,8 @@ trait TestHelper extends FileHelper { new LDBVersionedStore(dir, initialKeepVersions = initialKeepVersions) } - def createVersionedStorage(store: LDBVersionedStore): STORAGE = new VersionedLDBAVLStorage(store) + def createVersionedStorage(store: LDBVersionedStore): STORAGE = + new VersionedLDBAVLStorage(store) def createPersistentProver(storage: STORAGE): PERSISTENT_PROVER = { val prover = new BatchAVLProver[D, HF](KL, Some(VL)) diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 141dc6d39c..85dfb9391a 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "5.0.8" + version: "5.0.9" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 14bf1dd2a5..cfb68b7bc7 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -30,6 +30,9 @@ ergo { # Minimal suffix size for PoPoW proof (may be pre-defined constant or settings parameter) minimalSuffix = 10 + # how many utxo set snapshots to store, 0 means that they are not stored at all + storingUtxoSnapshots = 0 + # Is the node is doing mining mining = false diff --git a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala index cf675c916d..8182ac3724 100644 --- a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala +++ b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala @@ -10,14 +10,14 @@ class LDBKVStoreSpec extends AnyPropSpec with Matchers with DBSpec { val valueA = (byteString("A"), byteString("1")) val valueB = (byteString("B"), byteString("2")) - store.update(toInsert = Array(valueA, valueB), toRemove = Array.empty).get + store.update(Array(valueA._1, valueB._1), Array(valueA._2, valueB._2), toRemove = Array.empty).get store.get(valueA._1).toBs shouldBe Some(valueA._2).toBs store.get(valueB._1).toBs shouldBe Some(valueB._2).toBs store.getAll.toSeq.toBs shouldBe Seq(valueA, valueB).toBs - store.update(toInsert = Array.empty, toRemove = Array(valueA._1)).get + store.update(Array.empty, Array.empty, toRemove = Array(valueA._1)).get store.get(valueA._1) shouldBe None } } From a5b65319273bb291586febfe14a74e1f42f43a50 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 19 Apr 2023 14:13:26 +0300 Subject: [PATCH 124/204] SnapshotsInfoSerializer --- .../nodeView/state/SnapshotsDb.scala | 25 ++++--------------- .../nodeView/state/SnapshotsInfo.scala | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 330228ea43..122ce7bf2d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -1,6 +1,5 @@ package org.ergoplatform.nodeView.state -import com.google.common.primitives.{Bytes, Ints} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF @@ -16,35 +15,21 @@ import scorex.util.encode.Base16 import scala.util.{Failure, Success, Try} +/** + * Interface for a (non-versioned) database storing UTXO set snapshots and metadata about them + */ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) - private def snapshotsInfoToBytes(snapshotsInfo: SnapshotsInfo): Array[Byte] = { - Bytes.concat( - snapshotsInfo.availableManifests.map { case (h, manifestId) => - Bytes.concat(Ints.toByteArray(h), manifestId) - }.toSeq: _* - ) - } - - private def snapshotsInfoFromBytes(bytes: Array[Byte]): SnapshotsInfo = { - val manifests = bytes.grouped(36).map { rowBytes => - val height = Ints.fromByteArray(rowBytes.take(4)) - val manifestId = Digest32 @@ rowBytes.drop(4) - height -> manifestId - }.toMap - new SnapshotsInfo(manifests) - } - def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { - store.insert(Array(snapshotInfoKey -> snapshotsInfoToBytes(snapshotsInfo))) + store.insert(Array(snapshotInfoKey -> SnapshotsInfoSerializer.toBytes(snapshotsInfo))) } def readSnapshotsInfo: SnapshotsInfo = { - store.get(snapshotInfoKey).map(snapshotsInfoFromBytes).getOrElse(SnapshotsInfo.empty) + store.get(snapshotInfoKey).map(SnapshotsInfoSerializer.parseBytes).getOrElse(SnapshotsInfo.empty) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 6a57e76c50..f93645eaa6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -2,6 +2,10 @@ 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 scorex.crypto.hash.Digest32 +import scorex.util.serialization.{Reader, Writer} /** * Container for UTXO set snapshots the node holds @@ -31,6 +35,27 @@ object SnapshotsInfo { } +object SnapshotsInfoSerializer extends ErgoSerializer[SnapshotsInfo] { + + override def serialize(snapshotsInfo: SnapshotsInfo, w: Writer): Unit = { + w.putUInt(snapshotsInfo.availableManifests.size) + snapshotsInfo.availableManifests.foreach{case (height, manifestId) => + w.putUInt(height) + w.putBytes(manifestId) + } + } + + override def parse(r: Reader): SnapshotsInfo = { + val manifestsCount = r.getUInt().toInt // we read from trusted source, no need for extra checks + val manifests = (1 to manifestsCount).map{_ => + val h = r.getUInt().toInt + val manifestId = Digest32 @@ r.getBytes(Constants.HashLength) + h -> manifestId + }.toMap + new SnapshotsInfo(manifests) + } + +} From f0678330ec6b4236922f4689bcaaaf1e90a8b96c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 19 Apr 2023 15:46:09 +0300 Subject: [PATCH 125/204] pruneSnapshots considers num of snapshots to store setting --- .../nodeView/state/SnapshotsDb.scala | 16 +++++++++++----- .../state/UtxoSetSnapshotPersistence.scala | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 122ce7bf2d..a215c178cc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -33,12 +33,18 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { } - def pruneSnapshots(before: Height): Unit = { - //todo: consider storingUtxoSnapsots setting + def pruneSnapshots(toStore: Int): Unit = { log.info("Starting snapshots pruning") - val (toPrune, toLeave) = readSnapshotsInfo - .availableManifests - .partition(_._1 < before) + val manifests = readSnapshotsInfo.availableManifests + + val (toPrune, toLeave) = if (manifests.size > toStore) { + val tp = manifests.dropRight(toStore) + val tl = manifests.takeRight(toStore) + tp -> tl + } else { + log.info("No snapshots to prune") + return + } toPrune.foreach { case (h, manifestId) => log.info(s"Pruning snapshot at height $h") diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index ca9fcbcfb5..f0355cff45 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -35,7 +35,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { log.info("Started work within future") val ft0 = System.currentTimeMillis() dumpSnapshot(height) - snapshotsDb.pruneSnapshots(height - MakeSnapshotEvery * 2) //todo: async + snapshotsDb.pruneSnapshots(constants.settings.nodeSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() log.info("Work within future: " + (ft - ft0) + " ms.") } From a1b44f7989c96cffd8f5c215b90b9b03de586591 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 20 Apr 2023 00:56:38 +0300 Subject: [PATCH 126/204] test for pruning --- .../nodeView/state/SnapshotsDb.scala | 6 ++- .../state/SnapshotsDbSpecification.scala | 47 ++++++++++++++----- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index a215c178cc..3896a75784 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -35,7 +35,9 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { def pruneSnapshots(toStore: Int): Unit = { log.info("Starting snapshots pruning") - val manifests = readSnapshotsInfo.availableManifests + + // sort manifests by height to prune oldest ones after + val manifests = readSnapshotsInfo.availableManifests.toSeq.sortBy(_._1) val (toPrune, toLeave) = if (manifests.size > toStore) { val tp = manifests.dropRight(toStore) @@ -64,7 +66,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { store.remove(keysToRemove) } - val updInfo = new SnapshotsInfo(toLeave) + val updInfo = new SnapshotsInfo(toLeave.toMap) writeSnapshotsInfo(updInfo) log.info("Snapshots pruning finished") diff --git a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala index 0d7e076c6f..f273749ca7 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala @@ -3,24 +3,49 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.utils.ErgoPropertyTest import org.scalacheck.Gen import scorex.crypto.hash.Digest32 -import scorex.util.{idToBytes, bytesToId} +import scorex.util.{ModifierId, bytesToId, idToBytes} + import scala.util.Random class SnapshotsDbSpecification extends ErgoPropertyTest { - property("snapshotsInfo round-trip") { - forAll(Gen.nonEmptyListOf(modifierIdGen)){manifestIds => - val m = manifestIds.map{mid => - Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mid)) - }.toMap - val si = new SnapshotsInfo(m) - val dir = createTempDir.getAbsolutePath - val db = SnapshotsDb.create(dir) - db.writeSnapshotsInfo(si) + def seededDatabase(manifestIds: Seq[ModifierId]): (SnapshotsInfo, SnapshotsDb) = { + val m = manifestIds.map { mid => + Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mid)) + }.toMap + val si = new SnapshotsInfo(m) + val dir = createTempDir.getAbsolutePath + val db = SnapshotsDb.create(dir) + db.writeSnapshotsInfo(si) + si -> db + } + + property("snapshotsInfo round-trip") { + forAll(Gen.nonEmptyListOf(modifierIdGen)) { manifestIds => + val (si, db) = seededDatabase(manifestIds) val read = db.readSnapshotsInfo.availableManifests.mapValues(bs => bytesToId(bs)) val siTocompare = si.availableManifests.mapValues(bs => bytesToId(bs)) - read shouldBe siTocompare } } + + property("pruneSnapshots choosing snapshots correctly") { + forAll(Gen.nonEmptyListOf(modifierIdGen)) { manifestIds => + val (si, db) = seededDatabase(manifestIds) + + val toStore = Random.nextInt(manifestIds.size + 3) + + db.pruneSnapshots(toStore) + + val after = db.readSnapshotsInfo + + if (toStore >= manifestIds.size) { + after.availableManifests.size == manifestIds.size + } else { + val storedKeys = si.availableManifests.keySet.toSeq.sorted.takeRight(toStore) + val stored = si.availableManifests.filterKeys(h => storedKeys.contains(h)) + after.availableManifests.mapValues(bytesToId) shouldBe stored.mapValues(bytesToId) + } + } + } } From 0d8631835bb970b3ff0d172e87733da0b842325c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 20 Apr 2023 13:38:49 +0300 Subject: [PATCH 127/204] scaladoc in SnapshotsDb --- .../nodeView/state/SnapshotsDb.scala | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 3896a75784..781efdfe22 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -24,15 +24,21 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) - def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { + // helper method to write information about store UTXO set snapshots into the database + /// private[nodeView] as used in some tests + private[nodeView] def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { store.insert(Array(snapshotInfoKey -> SnapshotsInfoSerializer.toBytes(snapshotsInfo))) } - def readSnapshotsInfo: SnapshotsInfo = { + // helper method to read information about store UTXO set snapshots from the database + /// private[nodeView] as used in some tests + private[nodeView] def readSnapshotsInfo: SnapshotsInfo = { store.get(snapshotInfoKey).map(SnapshotsInfoSerializer.parseBytes).getOrElse(SnapshotsInfo.empty) } - + /** + * Remove old snapshots in the database and metadata records about them, leaving only `toStore` most recent snapshots + */ def pruneSnapshots(toStore: Int): Unit = { log.info("Starting snapshots pruning") @@ -72,6 +78,14 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { log.info("Snapshots pruning finished") } + /** + * Lazily read current UTXO set snapshot from versioned AVL+ tree database and store it in this snapshots database + * + * @param pullFrom - versioned AVL+ tree database to pull snapshot from + * @param height - height of a block snapshot is corresponding to + * @return - id of the snapshot (root hash of its authenticating AVL+ tree), + * or error happened during read-write process + */ def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Try[Array[Byte]] = { pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth).map { manifestId => val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) @@ -80,10 +94,18 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { } } + /** + * Read manifest bytes without deserializing it. Useful when manifest is to be sent over the wir + * @param id - manifest id + */ def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { store.get(id) } + /** + * Read subtree bytes without deserializing it. Useful when subtree is to be sent over the wir + * @param id - subtree id + */ def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { store.get(id) } @@ -92,14 +114,19 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { object SnapshotsDb { - def create(ergoSettings: ErgoSettings): SnapshotsDb = { - val dir = s"${ergoSettings.directory}/snapshots" - create(dir) - } - + // internal method to open or init snapshots database in given folder + // private[state] to use it in tests also private[state] def create(dir: String): SnapshotsDb = { val store = LDBFactory.createKvDb(dir) new SnapshotsDb(store) } + /** + * Read or create snapshots database in a folder defined by provided settings + */ + def create(ergoSettings: ErgoSettings): SnapshotsDb = { + val dir = s"${ergoSettings.directory}/snapshots" + create(dir) + } + } From bf51af153dbe1bacf60b3e86519677824e75d482 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 20 Apr 2023 13:42:49 +0300 Subject: [PATCH 128/204] importing SnapshotsDb --- .../nodeView/state/SnapshotsDb.scala | 132 ++++++++++++++++++ .../nodeView/state/SnapshotsInfo.scala | 25 ++++ .../state/SnapshotsDbSpecification.scala | 51 +++++++ 3 files changed, 208 insertions(+) create mode 100644 src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala create mode 100644 src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala new file mode 100644 index 0000000000..781efdfe22 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -0,0 +1,132 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} +import org.ergoplatform.settings.Algos.HF +import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} +import org.ergoplatform.wallet.Constants +import scorex.core.serialization.ManifestSerializer +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer +import scorex.crypto.hash.Digest32 +import scorex.db.{LDBFactory, LDBKVStore} +import scorex.util.ScorexLogging +import scorex.util.encode.Base16 + +import scala.util.{Failure, Success, Try} + +/** + * Interface for a (non-versioned) database storing UTXO set snapshots and metadata about them + */ +class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { + + private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + + private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) + + // helper method to write information about store UTXO set snapshots into the database + /// private[nodeView] as used in some tests + private[nodeView] def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { + store.insert(Array(snapshotInfoKey -> SnapshotsInfoSerializer.toBytes(snapshotsInfo))) + } + + // helper method to read information about store UTXO set snapshots from the database + /// private[nodeView] as used in some tests + private[nodeView] def readSnapshotsInfo: SnapshotsInfo = { + store.get(snapshotInfoKey).map(SnapshotsInfoSerializer.parseBytes).getOrElse(SnapshotsInfo.empty) + } + + /** + * Remove old snapshots in the database and metadata records about them, leaving only `toStore` most recent snapshots + */ + def pruneSnapshots(toStore: Int): Unit = { + log.info("Starting snapshots pruning") + + // sort manifests by height to prune oldest ones after + val manifests = readSnapshotsInfo.availableManifests.toSeq.sortBy(_._1) + + val (toPrune, toLeave) = if (manifests.size > toStore) { + val tp = manifests.dropRight(toStore) + val tl = manifests.takeRight(toStore) + tp -> tl + } else { + log.info("No snapshots to prune") + return + } + + toPrune.foreach { case (h, manifestId) => + log.info(s"Pruning snapshot at height $h") + val keysToRemove: Array[Array[Byte]] = store.get(manifestId) match { + case Some(manifestBytes) => + serializer.manifestFromBytes(manifestBytes, Constants.ModifierIdLength) match { + case Success(m) => + (m.subtreesIds += manifestId).toArray // todo: more efficient construction + case Failure(e) => + log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) + Array.empty + } + case None => + log.error(s"Manifest ${Base16.encode(manifestId)} not found:") + Array.empty + } + store.remove(keysToRemove) + } + + val updInfo = new SnapshotsInfo(toLeave.toMap) + writeSnapshotsInfo(updInfo) + + log.info("Snapshots pruning finished") + } + + /** + * Lazily read current UTXO set snapshot from versioned AVL+ tree database and store it in this snapshots database + * + * @param pullFrom - versioned AVL+ tree database to pull snapshot from + * @param height - height of a block snapshot is corresponding to + * @return - id of the snapshot (root hash of its authenticating AVL+ tree), + * or error happened during read-write process + */ + def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Try[Array[Byte]] = { + pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth).map { manifestId => + val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) + writeSnapshotsInfo(si) + manifestId + } + } + + /** + * Read manifest bytes without deserializing it. Useful when manifest is to be sent over the wir + * @param id - manifest id + */ + def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { + store.get(id) + } + + /** + * Read subtree bytes without deserializing it. Useful when subtree is to be sent over the wir + * @param id - subtree id + */ + def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { + store.get(id) + } + +} + +object SnapshotsDb { + + // internal method to open or init snapshots database in given folder + // private[state] to use it in tests also + private[state] def create(dir: String): SnapshotsDb = { + val store = LDBFactory.createKvDb(dir) + new SnapshotsDb(store) + } + + /** + * Read or create snapshots database in a folder defined by provided settings + */ + def create(ergoSettings: ErgoSettings): SnapshotsDb = { + val dir = s"${ergoSettings.directory}/snapshots" + create(dir) + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index 6a57e76c50..f93645eaa6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -2,6 +2,10 @@ 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 scorex.crypto.hash.Digest32 +import scorex.util.serialization.{Reader, Writer} /** * Container for UTXO set snapshots the node holds @@ -31,6 +35,27 @@ object SnapshotsInfo { } +object SnapshotsInfoSerializer extends ErgoSerializer[SnapshotsInfo] { + + override def serialize(snapshotsInfo: SnapshotsInfo, w: Writer): Unit = { + w.putUInt(snapshotsInfo.availableManifests.size) + snapshotsInfo.availableManifests.foreach{case (height, manifestId) => + w.putUInt(height) + w.putBytes(manifestId) + } + } + + override def parse(r: Reader): SnapshotsInfo = { + val manifestsCount = r.getUInt().toInt // we read from trusted source, no need for extra checks + val manifests = (1 to manifestsCount).map{_ => + val h = r.getUInt().toInt + val manifestId = Digest32 @@ r.getBytes(Constants.HashLength) + h -> manifestId + }.toMap + new SnapshotsInfo(manifests) + } + +} diff --git a/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala new file mode 100644 index 0000000000..f273749ca7 --- /dev/null +++ b/src/test/scala/org/ergoplatform/nodeView/state/SnapshotsDbSpecification.scala @@ -0,0 +1,51 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.utils.ErgoPropertyTest +import org.scalacheck.Gen +import scorex.crypto.hash.Digest32 +import scorex.util.{ModifierId, bytesToId, idToBytes} + +import scala.util.Random + +class SnapshotsDbSpecification extends ErgoPropertyTest { + + def seededDatabase(manifestIds: Seq[ModifierId]): (SnapshotsInfo, SnapshotsDb) = { + val m = manifestIds.map { mid => + Random.nextInt(1000000) -> (Digest32 @@ idToBytes(mid)) + }.toMap + val si = new SnapshotsInfo(m) + val dir = createTempDir.getAbsolutePath + val db = SnapshotsDb.create(dir) + db.writeSnapshotsInfo(si) + si -> db + } + + property("snapshotsInfo round-trip") { + forAll(Gen.nonEmptyListOf(modifierIdGen)) { manifestIds => + val (si, db) = seededDatabase(manifestIds) + val read = db.readSnapshotsInfo.availableManifests.mapValues(bs => bytesToId(bs)) + val siTocompare = si.availableManifests.mapValues(bs => bytesToId(bs)) + read shouldBe siTocompare + } + } + + property("pruneSnapshots choosing snapshots correctly") { + forAll(Gen.nonEmptyListOf(modifierIdGen)) { manifestIds => + val (si, db) = seededDatabase(manifestIds) + + val toStore = Random.nextInt(manifestIds.size + 3) + + db.pruneSnapshots(toStore) + + val after = db.readSnapshotsInfo + + if (toStore >= manifestIds.size) { + after.availableManifests.size == manifestIds.size + } else { + val storedKeys = si.availableManifests.keySet.toSeq.sorted.takeRight(toStore) + val stored = si.availableManifests.filterKeys(h => storedKeys.contains(h)) + after.availableManifests.mapValues(bytesToId) shouldBe stored.mapValues(bytesToId) + } + } + } +} From 2f539cc9db7e9dedca7cb0e4170a174d9f023a7b Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 20 Apr 2023 14:08:39 +0300 Subject: [PATCH 129/204] constants in state made protected, removing unused vals in ErgoStateReader --- .../ergoplatform/nodeView/state/ErgoStateReader.scala | 9 +++------ .../org/ergoplatform/nodeView/state/SnapshotsDb.scala | 7 ++++--- .../nodeView/state/UtxoSetSnapshotPersistence.scala | 2 +- .../org/ergoplatform/nodeView/state/UtxoState.scala | 4 ++-- .../ergoplatform/nodeView/state/UtxoStateReader.scala | 2 +- .../nodeView/NodeViewSynchronizerTests.scala | 8 +++++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 9479fe4cf5..09319c3c35 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters, VotingSettings} +import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters} import scorex.core.{NodeViewComponent, VersionTag} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 @@ -26,11 +26,8 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { def version: VersionTag val store: LDBVersionedStore - val constants: StateConstants - private lazy val chainSettings = constants.settings.chainSettings - - protected lazy val votingSettings: VotingSettings = chainSettings.voting + protected def constants: StateConstants /** * If the state is in its genesis version (before genesis block) @@ -46,7 +43,7 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { */ def parameters: Parameters = stateContext.currentParameters - def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(chainSettings) + def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(constants.settings.chainSettings) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 781efdfe22..c499bc38f3 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -114,9 +114,10 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { object SnapshotsDb { - // internal method to open or init snapshots database in given folder - // private[state] to use it in tests also - private[state] def create(dir: String): SnapshotsDb = { + /** + * Open or init snapshots database in given folder + */ + def create(dir: String): SnapshotsDb = { val store = LDBFactory.createKvDb(dir) new SnapshotsDb(store) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index f0355cff45..51855511f6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -13,7 +13,7 @@ import scala.util.Try trait UtxoSetSnapshotPersistence extends ScorexLogging { - def constants: StateConstants + protected def constants: StateConstants protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index ca0c5cff5b..6ec953de20 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -28,7 +28,7 @@ import scala.util.{Failure, Success, Try} /** * Utxo set implementation * - * @param persistentProver - persistent prover that build authenticated AVL+ tree on top of utxo set + * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata * @param version - current state version * @param constants - constants, that do not change with state version changes @@ -36,7 +36,7 @@ import scala.util.{Failure, Success, Try} class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, override val store: LDBVersionedStore, - override val constants: StateConstants) + override protected val constants: StateConstants) extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 6b4d995866..75bc36d54a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -22,7 +22,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi protected implicit val hf: HF = Algos.hash - val constants: StateConstants + protected def constants: StateConstants protected lazy val storage = new VersionedLDBAVLStorage(store) diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index 41f4dd2a6b..679afce19b 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -12,6 +12,7 @@ import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.UtxoState.ManifestId import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.Algos +import org.ergoplatform.wallet.utils.TestFileUtils import org.scalacheck.Gen import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec @@ -20,7 +21,7 @@ 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, ManifestSerializer, ErgoSerializer} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer, ManifestSerializer} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.{SyntacticallyTargetedModifierProducer, TotallyValidModifierProducer} import scorex.testkit.utils.AkkaFixture @@ -37,7 +38,8 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec with Matchers with ScorexLogging with SyntacticallyTargetedModifierProducer - with TotallyValidModifierProducer[ST] { + with TotallyValidModifierProducer[ST] + with TestFileUtils { implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global @@ -247,7 +249,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec Random.nextInt(1000000) -> (Digest32 @@ Algos.decode(mod.id).get) }.toMap val si = new SnapshotsInfo(m) - val db = SnapshotsDb.create(s.constants.settings) + val db = SnapshotsDb.create(createTempDir.getPath) db.writeSnapshotsInfo(si) // Then send message to request it From c1c0922f3ff0b48c3d596449ae1ddd1d3bce5fc1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 21 Apr 2023 00:15:50 +0300 Subject: [PATCH 130/204] UtxoSetSnapshotPersistence scaladoc --- .../state/UtxoSetSnapshotPersistence.scala | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 51855511f6..f094933a46 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -11,17 +11,33 @@ import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapsho import scala.concurrent.Future import scala.util.Try +/** + * Functions needed for storing UTXO set snapshots and working with them + */ trait UtxoSetSnapshotPersistence extends ScorexLogging { protected def constants: StateConstants protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] - private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) //todo: move to some other place ? + private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) + // Dump current UTXO set snapshot to persistent snapshots database + // private[nodeView] as used in tests also private[nodeView] def dumpSnapshot(height: Height): Try[Array[Byte]] = { snapshotsDb.writeSnapshot(persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage], height) } + /** + * Check if it is time to store UTXO set snapshot, if so, store it asynchronously, + * to avoid locking the thread currently working with UTXO set, and then prune snapshots which become + * obsolete (also in a separate thread). + * + * Thus this method is doing everything which is needed to store snapshot when needed. Client logic then + * may simply call it on every block applied to the state. + * + * @param height - height of a block just processed (so we work with UTXO set after block with this height applied) + * @param estimatedTip - estimated height of best blockchain in the network + */ protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { if (constants.settings.nodeSettings.areSnapshotsStored && timeToTakeSnapshot(height) && @@ -44,18 +60,25 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { } } - def snapshotsAvailable(): SnapshotsInfo = { - snapshotsDb.readSnapshotsInfo - } - + /** + * @return list of stored UTXO set snapshots + */ def getSnapshotInfo(): SnapshotsInfo = { snapshotsDb.readSnapshotsInfo } + /** + * Read snapshot manifest bytes from database without decoding. Used to serve clients over the wire. + * @param id - manifest id + */ def getManifestBytes(id: ManifestId): Option[Array[Byte]] = { snapshotsDb.readManifestBytes(id) } + /** + * Read snapshot chunk (subtree) bytes from database without decoding. Used to serve clients over the wire. + * @param id - subtree id + */ def getUtxoSnapshotChunkBytes(id: SubtreeId): Option[Array[Byte]] = { snapshotsDb.readSubtreeBytes(id) } From 583ecc02cc3d73b4b211ea7c757bcb570a59769c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 21 Apr 2023 14:52:32 +0300 Subject: [PATCH 131/204] expectedRootHash --- .../avltree/batch/VersionedLDBAVLStorage.scala | 5 ++++- .../scala/scorex/db/LDBVersionedStore.scala | 4 ++-- .../nodeView/state/SnapshotsDb.scala | 7 +++++-- .../state/UtxoSetSnapshotPersistence.scala | 17 +++++++++++------ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index e06db8e5a1..c606289c3d 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -95,9 +95,10 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) * * @param dumpStorage - non-versioned storage to dump tree to * @param manifestDepth - depth of manifest tree + * @param expectedRootHash - expected UTXO set aunthenticating tree root hash * @return - hash of root node of tree, or failure if an error (e.g. in database) happened */ - def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Try[Array[Byte]] = { + def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { store.processSnapshot { dbReader => def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { @@ -138,6 +139,8 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) val rootNodeLabel = dbReader.get(topNodeHashKey) val rootNodeHeight = Ints.fromByteArray(dbReader.get(topNodeHeightKey)) + require(rootNodeLabel.sameElements(expectedRootHash), "Root node hash changed") + val manifestBuilder = mutable.ArrayBuilder.make[Byte]() manifestBuilder.sizeHint(200000) manifestBuilder ++= Ints.toByteArray(rootNodeHeight) diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index ff57dad668..7530300467 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -421,9 +421,9 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) * @param logic - processing logic which is getting access to `get` function to read from database snapshot */ def processSnapshot[T](logic: SnapshotReadInterface => T): Try[T] = { - val ro = new ReadOptions() - ro.snapshot(db.getSnapshot) try { + val ro = new ReadOptions() + ro.snapshot(db.getSnapshot) object readInterface extends SnapshotReadInterface { def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index c499bc38f3..a67bd1c345 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -83,11 +83,14 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { * * @param pullFrom - versioned AVL+ tree database to pull snapshot from * @param height - height of a block snapshot is corresponding to + * @param expectedRootHash - expected tree root hash in `pullFrom` database * @return - id of the snapshot (root hash of its authenticating AVL+ tree), * or error happened during read-write process */ - def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Try[Array[Byte]] = { - pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth).map { manifestId => + def writeSnapshot(pullFrom: VersionedLDBAVLStorage, + height: Height, + expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth, expectedRootHash).map { manifestId => val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) writeSnapshotsInfo(si) manifestId diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index f094933a46..a9b9777b85 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -23,8 +23,9 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { // Dump current UTXO set snapshot to persistent snapshots database // private[nodeView] as used in tests also - private[nodeView] def dumpSnapshot(height: Height): Try[Array[Byte]] = { - snapshotsDb.writeSnapshot(persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage], height) + private[nodeView] def dumpSnapshot(height: Height, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + val storage = persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage] + snapshotsDb.writeSnapshot(storage, height, expectedRootHash) } /** @@ -46,17 +47,21 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { import scala.concurrent.ExecutionContext.Implicits.global val ms0 = System.currentTimeMillis() - // todo: check that future will work with the same tree + + // drop tree height byte from digest to get current root hash of AVL+ tree + // we then pass the hash and check in another thread whether it is the same after taking db snapshot + // there is small probability of failure, but that is ok, and better than any locking likely + val expectedRootHash = persistentProver.digest.dropRight(1) Future { log.info("Started work within future") val ft0 = System.currentTimeMillis() - dumpSnapshot(height) + dumpSnapshot(height, expectedRootHash) snapshotsDb.pruneSnapshots(constants.settings.nodeSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() - log.info("Work within future: " + (ft - ft0) + " ms.") + log.info("Work within future finished in: " + (ft - ft0) + " ms.") } val ms = System.currentTimeMillis() - log.info("Time to dump utxo set snapshot: " + (ms - ms0)) + log.info("Main thread time to dump utxo set snapshot: " + (ms - ms0)) } } From 743fc6dbfad3134ff830fdef1708c7bbffb52e60 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 21 Apr 2023 22:49:10 +0300 Subject: [PATCH 132/204] UtxoSetSnapshotPersistence --- .../batch/VersionedLDBAVLStorage.scala | 5 +- .../scala/scorex/db/LDBVersionedStore.scala | 2 +- .../VersionedLDBAVLStorageSpecification.scala | 2 +- .../nodeView/state/ErgoStateReader.scala | 9 +- .../nodeView/state/SnapshotsDb.scala | 7 +- .../state/UtxoSetSnapshotPersistence.scala | 95 +++++++++++++++++++ .../nodeView/state/UtxoState.scala | 23 +++-- .../nodeView/state/UtxoStateReader.scala | 4 +- .../org/ergoplatform/settings/Constants.scala | 3 + .../settings/NodeConfigurationSettings.scala | 6 ++ .../extra/ExtraIndexerSpecification.scala | 4 +- .../settings/ErgoSettingsSpecification.scala | 6 ++ .../ergoplatform/tools/ChainGenerator.scala | 4 +- .../utils/HistoryTestHelpers.scala | 4 +- .../scala/org/ergoplatform/utils/Stubs.scala | 4 +- 15 files changed, 150 insertions(+), 28 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 3e3205cff7..ed04b7b0e9 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -95,9 +95,10 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) * * @param dumpStorage - non-versioned storage to dump tree to * @param manifestDepth - depth of manifest tree + * @param expectedRootHash - expected UTXO set aunthenticating tree root hash * @return - hash of root node of tree, or failure if an error (e.g. in database) happened */ - def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int): Try[Array[Byte]] = { + def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { store.processSnapshot { dbReader => def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { @@ -138,6 +139,8 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) val rootNodeLabel = dbReader.get(topNodeHashKey) val rootNodeHeight = Ints.fromByteArray(dbReader.get(topNodeHeightKey)) + require(rootNodeLabel.sameElements(expectedRootHash), "Root node hash changed") + val manifestBuilder = mutable.ArrayBuilder.make[Byte]() manifestBuilder.sizeHint(200000) manifestBuilder ++= Ints.toByteArray(rootNodeHeight) diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index ff57dad668..7cd107a9ad 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -422,8 +422,8 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) */ def processSnapshot[T](logic: SnapshotReadInterface => T): Try[T] = { val ro = new ReadOptions() - ro.snapshot(db.getSnapshot) try { + ro.snapshot(db.getSnapshot) object readInterface extends SnapshotReadInterface { def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) } 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 b702f5de7d..056380a441 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 @@ -347,7 +347,7 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec val store = LDBFactory.createKvDb("/tmp/aa") val ts0 = System.currentTimeMillis() - storage.dumpSnapshot(store, 4) + storage.dumpSnapshot(store, 4, prover.digest.dropRight(1)) val ts = System.currentTimeMillis() println("time: " + (ts-ts0)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 9479fe4cf5..09319c3c35 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters, VotingSettings} +import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters} import scorex.core.{NodeViewComponent, VersionTag} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 @@ -26,11 +26,8 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { def version: VersionTag val store: LDBVersionedStore - val constants: StateConstants - private lazy val chainSettings = constants.settings.chainSettings - - protected lazy val votingSettings: VotingSettings = chainSettings.voting + protected def constants: StateConstants /** * If the state is in its genesis version (before genesis block) @@ -46,7 +43,7 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { */ def parameters: Parameters = stateContext.currentParameters - def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(chainSettings) + def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(constants.settings.chainSettings) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 781efdfe22..3e65ef3eb9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -83,11 +83,14 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { * * @param pullFrom - versioned AVL+ tree database to pull snapshot from * @param height - height of a block snapshot is corresponding to + * @param expectedRootHash - expected tree root hash in `pullFrom` database * @return - id of the snapshot (root hash of its authenticating AVL+ tree), * or error happened during read-write process */ - def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height): Try[Array[Byte]] = { - pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth).map { manifestId => + def writeSnapshot(pullFrom: VersionedLDBAVLStorage, + height: Height, + expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth, expectedRootHash).map { manifestId => val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) writeSnapshotsInfo(si) manifestId diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala new file mode 100644 index 0000000000..f9e202d7db --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -0,0 +1,95 @@ +package org.ergoplatform.nodeView.state + +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} +import org.ergoplatform.settings.Algos.HF +import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} +import scorex.crypto.hash.Digest32 +import scorex.util.ScorexLogging +import org.ergoplatform.settings.Constants.MakeSnapshotEvery + +import scala.concurrent.Future +import scala.util.Try + +/** + * Functions needed for storing UTXO set snapshots and working with them + */ +trait UtxoSetSnapshotPersistence extends ScorexLogging { + + protected def constants: StateConstants + protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] + + private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) + + // Dump current UTXO set snapshot to persistent snapshots database + // private[nodeView] as used in tests also + private[nodeView] def dumpSnapshot(height: Height, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + val storage = persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage] + snapshotsDb.writeSnapshot(storage, height, expectedRootHash) + } + + /** + * Check if it is time to store UTXO set snapshot, if so, store it asynchronously, + * to avoid locking the thread currently working with UTXO set, and then prune snapshots which become + * obsolete (also in a separate thread). + * + * Thus this method is doing everything which is needed to store snapshot when needed. Client logic then + * may simply call it on every block applied to the state. + * + * @param height - height of a block just processed (so we work with UTXO set after block with this height applied) + * @param estimatedTip - estimated height of best blockchain in the network + */ + protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { + def timeToTakeSnapshot(height: Int): Boolean = { + height % MakeSnapshotEvery == MakeSnapshotEvery - 1 + } + + if (constants.settings.nodeSettings.areSnapshotsStored && + timeToTakeSnapshot(height) && + estimatedTip.nonEmpty && + estimatedTip.get - height <= MakeSnapshotEvery) { + + import scala.concurrent.ExecutionContext.Implicits.global + val ms0 = System.currentTimeMillis() + + // drop tree height byte from digest to get current root hash of AVL+ tree + // we then pass the hash and check in another thread whether it is the same after taking db snapshot + // there is small probability of failure, but that is ok, and better than any locking likely + val expectedRootHash = persistentProver.digest.dropRight(1) + Future { + log.info("Started work within future") + val ft0 = System.currentTimeMillis() + dumpSnapshot(height, expectedRootHash) + snapshotsDb.pruneSnapshots(constants.settings.nodeSettings.storingUtxoSnapshots) + val ft = System.currentTimeMillis() + log.info("Work within future finished in: " + (ft - ft0) + " ms.") + } + val ms = System.currentTimeMillis() + log.info("Main thread time to dump utxo set snapshot: " + (ms - ms0)) + } + } + + /** + * @return list of stored UTXO set snapshots + */ + def getSnapshotInfo(): SnapshotsInfo = { + snapshotsDb.readSnapshotsInfo + } + + /** + * Read snapshot manifest bytes from database without decoding. Used to serve clients over the wire. + * @param id - manifest id + */ + def getManifestBytes(id: ManifestId): Option[Array[Byte]] = { + snapshotsDb.readManifestBytes(id) + } + + /** + * Read snapshot chunk (subtree) bytes from database without decoding. Used to serve clients over the wire. + * @param id - subtree id + */ + def getUtxoSnapshotChunkBytes(id: SubtreeId): Option[Array[Byte]] = { + snapshotsDb.readSubtreeBytes(id) + } + +} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 5ea575903e..6ec953de20 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -28,7 +28,7 @@ import scala.util.{Failure, Success, Try} /** * Utxo set implementation * - * @param persistentProver - persistent prover that build authenticated AVL+ tree on top of utxo set + * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata * @param version - current state version * @param constants - constants, that do not change with state version changes @@ -36,7 +36,7 @@ import scala.util.{Failure, Success, Try} class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, override val store: LDBVersionedStore, - override val constants: StateConstants) + override protected val constants: StateConstants) extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader @@ -200,6 +200,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 log.info(s"Valid modifier with header ${fb.header.encodedId} and emission box " + s"${emissionBox.map(e => Algos.encode(e.id))} applied to UtxoState at height ${fb.header.height}") + saveSnapshotIfNeeded(fb.height, estimatedTip) new UtxoState(persistentProver, idToVersion(fb.id), store, constants) } } @@ -210,7 +211,6 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 .ensuring(java.util.Arrays.equals(persistentProver.digest, inRoot)) Failure(e) } - } case h: Header => @@ -261,10 +261,19 @@ object UtxoState { val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") // block-specific metadata to write into database (in addition to AVL+ tree) - private def metadata(modId: VersionTag, - stateRoot: ADDigest, - currentEmissionBoxOpt: Option[ErgoBox], - context: ErgoStateContext): Seq[(Array[Byte], Array[Byte])] = { + + /** + * + * @param modId - ID of a block (header) corresponding to UTXO set + * @param stateRoot - UTXO set digest (hash and tree height) AFTER applying block `modId` + * @param currentEmissionBoxOpt + * @param context + * @return + */ + def metadata(modId: VersionTag, + stateRoot: ADDigest, + currentEmissionBoxOpt: Option[ErgoBox], + context: ErgoStateContext): Seq[(Array[Byte], Array[Byte])] = { val modIdBytes = versionToBytes(modId) val idStateDigestIdxElem: (Array[Byte], Array[Byte]) = modIdBytes -> stateRoot val stateDigestIdIdxElem = Algos.hash(stateRoot) -> modIdBytes diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index bff54366d9..5198afea20 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -18,11 +18,11 @@ import scorex.crypto.hash.Digest32 import scala.util.{Failure, Success, Try} -trait UtxoStateReader extends ErgoStateReader with TransactionValidation { +trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { protected implicit val hf: HF = Algos.hash - val constants: StateConstants + protected def constants: StateConstants /** * Versioned database where UTXO set and its authenticating AVL+ tree are stored diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index ac4f323137..bfafdd5719 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -70,6 +70,9 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 + // UTXO set snapshot to be taken every this number of blocks + val MakeSnapshotEvery = 51200 + /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. * Keys and hashes are 256-bits long, values are boxes, so value size is dynamic. diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index 0234eec6b4..e30f385901 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -28,6 +28,7 @@ trait CheckpointingSettingsReader extends ModifierIdReader { case class NodeConfigurationSettings(stateType: StateType, verifyTransactions: Boolean, blocksToKeep: Int, + utxoBootstrap: Boolean, poPoWBootstrap: Boolean, minimalSuffix: Int, mining: Boolean, @@ -39,6 +40,7 @@ case class NodeConfigurationSettings(stateType: StateType, miningPubKeyHex: Option[String], offlineGeneration: Boolean, keepVersions: Int, + storingUtxoSnapshots: Int, acceptableChainUpdateDelay: FiniteDuration, mempoolCapacity: Int, mempoolCleanupDuration: FiniteDuration, @@ -55,6 +57,8 @@ case class NodeConfigurationSettings(stateType: StateType, * @return true if the blockchain is pruned, false if not */ val isFullBlocksPruned: Boolean = blocksToKeep >= 0 + + val areSnapshotsStored = storingUtxoSnapshots > 0 } trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettingsReader with ModifierIdReader { @@ -66,6 +70,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin stateType, cfg.as[Boolean](s"$path.verifyTransactions"), cfg.as[Int](s"$path.blocksToKeep"), + cfg.as[Boolean](s"$path.utxoBootstrap"), cfg.as[Boolean](s"$path.PoPoWBootstrap"), cfg.as[Int](s"$path.minimalSuffix"), cfg.as[Boolean](s"$path.mining"), @@ -77,6 +82,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin cfg.as[Option[String]](s"$path.miningPubKeyHex"), cfg.as[Boolean](s"$path.offlineGeneration"), cfg.as[Int](s"$path.keepVersions"), + cfg.as[Int](s"$path.storingUtxoSnapshots"), cfg.as[FiniteDuration](s"$path.acceptableChainUpdateDelay"), cfg.as[Int](s"$path.mempoolCapacity"), cfg.as[FiniteDuration](s"$path.mempoolCleanupDuration"), 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 b81a6b14f9..1cc3e8e437 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -38,9 +38,9 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w override protected implicit val addressEncoder: ErgoAddressEncoder = initSettings.chainSettings.addressEncoder val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, poPoWBootstrap = false, ChainGenerator.minimalSuffix, mining = false, ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, + -1, utxoBootstrap = false, poPoWBootstrap = false, ChainGenerator.minimalSuffix, 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, + 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) val HEIGHT: Int = 50 diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index 89d394d17b..db14ea932e 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -24,6 +24,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 1000, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, @@ -35,6 +36,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -73,6 +75,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 12, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, @@ -84,6 +87,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -115,6 +119,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 13, + utxoBootstrap = false, poPoWBootstrap = false, 10, mining = true, @@ -126,6 +131,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, + storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index c234dab7e8..71bcaeb3cc 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -59,9 +59,9 @@ object ChainGenerator extends App with ErgoTestHelpers { val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, poPoWBootstrap = false, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + -1, utxoBootstrap = false, poPoWBootstrap = false, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, - 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, + 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false) val ms = settings.chainSettings.monetary.copy( minerRewardDelay = RewardDelay diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index 5937979e48..7d2ea4fcbe 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -46,9 +46,9 @@ trait HistoryTestHelpers extends ErgoPropertyTest { val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, - offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 66ffffeea1..ec887abd75 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -370,9 +370,9 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second,miningPubKeyHex = None, - offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null From 8749662e5779f91e9c14d50eb6af97f0ef74b879 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 22 Apr 2023 23:27:07 +0300 Subject: [PATCH 133/204] StateConstants eliminated, more scaladocs --- .../src/main/scala/scorex/db/LDBKVStore.scala | 17 +++++- .../VersionedLDBAVLStorageSpecification.scala | 5 +- .../nodeView/state/UtxoStateBenchmark.scala | 2 +- .../nodeView/ErgoNodeViewHolder.scala | 14 ++--- .../nodeView/state/DigestState.scala | 30 +++++------ .../nodeView/state/ErgoState.scala | 37 ++++++++----- .../nodeView/state/ErgoStateContext.scala | 4 -- .../nodeView/state/ErgoStateReader.scala | 25 ++++++--- .../nodeView/state/StateConstants.scala | 17 ------ .../state/UtxoSetSnapshotPersistence.scala | 9 ++-- .../nodeView/state/UtxoState.scala | 34 ++++++------ .../nodeView/state/UtxoStateReader.scala | 14 ++--- .../http/routes/BlocksApiRouteSpec.scala | 2 +- .../local/MempoolAuditorSpec.scala | 6 +-- .../mining/CandidateGeneratorPropSpec.scala | 4 +- .../VerifyNonADHistorySpecification.scala | 2 +- .../extra/ExtraIndexerSpecification.scala | 4 +- .../nodeView/mempool/ErgoMemPoolSpec.scala | 44 ++++++++-------- .../nodeView/mempool/ScriptsSpec.scala | 2 +- .../state/DigestStateSpecification.scala | 18 +++---- .../state/ErgoStateSpecification.scala | 14 ++--- .../state/UtxoStateSpecification.scala | 26 +++++----- .../state/wrapped/WrappedUtxoState.scala | 21 ++++---- .../viewholder/ErgoNodeViewHolderSpec.scala | 52 +++++++++---------- .../viewholder/PrunedNodeViewHolderSpec.scala | 4 +- .../sanity/ErgoSanityDigest.scala | 2 +- .../serialization/JsonSerializationSpec.scala | 2 +- .../ergoplatform/tools/ChainGenerator.scala | 2 +- .../utils/ErgoTestConstants.scala | 3 +- .../scala/org/ergoplatform/utils/Stubs.scala | 4 +- .../generators/ValidBlocksGenerators.scala | 16 +++--- 31 files changed, 220 insertions(+), 216 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index 7d4ab17a1b..a8ead25e37 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -14,6 +14,14 @@ import spire.syntax.all.cfor */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { + /** + * Update this database atomically with a batch of insertion and removal operations + * + * @param toInsertKeys - keys pf key-value pairs to insert into database + * @param toInsertValues - values pf key-value pairs to insert into database + * @param toRemove - keys of key-value pairs to remove from the database + * @return - error if it happens, or success status + */ def update(toInsertKeys: Array[K], toInsertValues: Array[V], toRemove: Array[K]): Try[Unit] = { val batch = db.createWriteBatch() try { @@ -44,10 +52,17 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging } } + /** + * `update` variant where we only insert values into this database + */ + def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + def insert(values: Array[(K, V)]): Try[Unit] = update(values.map(_._1), values.map(_._2), Array.empty) - def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + /** + * `update` variant where we only remove values from this database + */ def remove(keys: Array[K]): Try[Unit] = update(Array.empty, Array.empty, keys) /** 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 056380a441..5a7db6cffe 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,6 +6,7 @@ import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.topNodeHashKey import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 @@ -346,10 +347,8 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec val storage = prover.storage.asInstanceOf[VersionedLDBAVLStorage] val store = LDBFactory.createKvDb("/tmp/aa") - val ts0 = System.currentTimeMillis() storage.dumpSnapshot(store, 4, prover.digest.dropRight(1)) - val ts = System.currentTimeMillis() - println("time: " + (ts-ts0)) + store.get(topNodeHashKey).sameElements(prover.digest.dropRight(1)) shouldBe true } } diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala index b8d8ffa094..5a257b0420 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala @@ -22,7 +22,7 @@ object UtxoStateBenchmark extends HistoryTestHelpers with NVBenchmark { val transactionsQty = blocks.flatMap(_.transactions).size def bench(mods: Seq[BlockSection]): Long = { - val state = ErgoState.generateGenesisUtxoState(createTempDir, StateConstants(realNetworkSetting))._1 + val state = ErgoState.generateGenesisUtxoState(createTempDir, realNetworkSetting)._1 Utils.time { mods.foldLeft(state) { case (st, mod) => st.applyModifier(mod, None)(_ => ()).get diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 5e13ab0d61..e00f0ff096 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -406,8 +406,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val history = ErgoHistory.readOrGenerate(settings) log.info("History database read") val memPool = ErgoMemPool.empty(settings) - val constants = StateConstants(settings) - restoreConsistentState(ErgoState.readOrGenerate(settings, constants).asInstanceOf[State], history) match { + restoreConsistentState(ErgoState.readOrGenerate(settings).asInstanceOf[State], history) match { case Success(state) => log.info(s"State database read, state synchronized") val wallet = ErgoWallet.readOrGenerate( @@ -533,8 +532,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val dir = stateDir(settings) deleteRecursive(dir) - val constants = StateConstants(settings) - ErgoState.readOrGenerate(settings, constants) + ErgoState.readOrGenerate(settings) .asInstanceOf[State] .ensuring( state => java.util.Arrays.equals(state.rootDigest, settings.chainSettings.genesisStateDigest), @@ -580,7 +578,6 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti * Recovers digest state from history. */ private def recoverDigestState(bestFullBlock: ErgoFullBlock, history: ErgoHistory): Try[DigestState] = { - val constants = StateConstants(settings) val votingLength = settings.chainSettings.voting.votingLength val bestHeight = bestFullBlock.header.height val newEpochHeadersQty = bestHeight % votingLength // how many blocks current epoch lasts @@ -592,12 +589,11 @@ 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(constants.genesisStateDigest, ext, lastHeaders)(settings)) + )(ext => ErgoStateContext.recover(settings.chainSettings.genesisStateDigest, ext, lastHeaders)(settings)) .flatMap { ctx => val recoverVersion = idToVersion(lastHeaders.last.id) val recoverRoot = bestFullBlock.header.stateRoot - val parameters = ctx.currentParameters - DigestState.recover(recoverVersion, recoverRoot, ctx, stateDir(settings), constants, parameters) + DigestState.recover(recoverVersion, recoverRoot, ctx, stateDir(settings), settings) } recoveredStateTry match { @@ -609,7 +605,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case Failure(exception) => // recover using whole headers chain log.warn(s"Failed to recover state from current epoch, using whole chain: ${exception.getMessage}") val wholeChain = history.headerChainBack(Int.MaxValue, bestFullBlock.header, _.isGenesis).headers - val genesisState = DigestState.create(None, None, stateDir(settings), constants) + val genesisState = DigestState.create(None, None, stateDir(settings), settings) wholeChain.foldLeft[Try[DigestState]](Success(genesisState)) { case (acc, m) => acc.flatMap(_.applyModifier(m, history.estimatedTip())(lm => self ! lm)) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala index d4e1f27595..0711bc1a3c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala @@ -28,7 +28,7 @@ import scala.util.{Failure, Success, Try} class DigestState protected(override val version: VersionTag, override val rootDigest: ADDigest, override val store: LDBVersionedStore, - ergoSettings: ErgoSettings) + override val ergoSettings: ErgoSettings) extends ErgoState[DigestState] with ScorexLogging with ScorexEncoding { @@ -36,8 +36,6 @@ class DigestState protected(override val version: VersionTag, store.lastVersionID .foreach(id => require(version == bytesToVersion(id), "version should always be equal to store.lastVersionID")) - override val constants: StateConstants = StateConstants(ergoSettings) - private lazy val nodeSettings = ergoSettings.nodeSettings private[state] def validateTransactions(transactions: Seq[ErgoTransaction], @@ -163,46 +161,48 @@ object DigestState extends ScorexLogging with ScorexEncoding { rootHash: ADDigest, stateContext: ErgoStateContext, dir: File, - constants: StateConstants, - parameters: Parameters): Try[DigestState] = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + settings: ErgoSettings): Try[DigestState] = { + 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 { _ => - new DigestState(version, rootHash, store, constants.settings) + new DigestState(version, rootHash, store, settings) } } + /** + * Read digest state from disk, or generate it from genesis data if nothing on the disk + */ def create(versionOpt: Option[VersionTag], rootHashOpt: Option[ADDigest], dir: File, - constants: StateConstants): DigestState = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + settings: ErgoSettings): DigestState = { + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) Try { - val context = ErgoStateReader.storageStateContext(store, constants) + val context = ErgoStateReader.storageStateContext(store, settings) (versionOpt, rootHashOpt) match { case (Some(version), Some(rootHash)) => val state = if (store.lastVersionID.map(w => bytesToVersion(w)).contains(version)) { - new DigestState(version, rootHash, store, constants.settings) + new DigestState(version, rootHash, store, settings) } else { val inVersion = store.lastVersionID.map(w => bytesToVersion(w)).getOrElse(version) - new DigestState(inVersion, rootHash, store, constants.settings) + new DigestState(inVersion, rootHash, store, settings) .update(version, rootHash, context).get //sync store } state.ensuring(bytesToVersion(store.lastVersionID.get) == version) case (None, None) if store.lastVersionID.isEmpty => - ErgoState.generateGenesisDigestState(dir, constants.settings) + ErgoState.generateGenesisDigestState(dir, settings) case _ => val version = store.lastVersionID.get val rootHash = store.get(version).get - new DigestState(bytesToVersion(version), ADDigest @@ rootHash, store, constants.settings) + new DigestState(bytesToVersion(version), ADDigest @@ rootHash, store, settings) } } match { case Success(state) => state case Failure(e) => store.close() log.warn(s"Failed to create state with ${versionOpt.map(encoder.encode)} and ${rootHashOpt.map(encoder.encode)}", e) - ErgoState.generateGenesisDigestState(dir, constants.settings) + ErgoState.generateGenesisDigestState(dir, settings) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index cfc95bba4b..4ef21d3f2f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -255,44 +255,53 @@ object ErgoState extends ScorexLogging { } /** - * All boxes of genesis state. - * Emission box is always the first. + * Genesis state boxes generator. + * Genesis state is corresponding to the state before the very first block processed. + * For Ergo mainnet, contains emission contract box, proof-of-no--premine box, and treasury contract box */ def genesisBoxes(chainSettings: ChainSettings): Seq[ErgoBox] = { Seq(genesisEmissionBox(chainSettings), noPremineBox(chainSettings), genesisFoundersBox(chainSettings)) } - def generateGenesisUtxoState(stateDir: File, - constants: StateConstants): (UtxoState, BoxHolder) = { + /** + * Generate genesis full (UTXO-set) state by inserting genesis boxes into empty UTXO set. + * Assign `genesisStateDigest` from config as its version. + */ + def generateGenesisUtxoState(stateDir: File, settings: ErgoSettings): (UtxoState, BoxHolder) = { log.info("Generating genesis UTXO state") - val boxes = genesisBoxes(constants.settings.chainSettings) + val boxes = genesisBoxes(settings.chainSettings) val bh = BoxHolder(boxes) - UtxoState.fromBoxHolder(bh, boxes.headOption, stateDir, constants, LaunchParameters).ensuring(us => { + UtxoState.fromBoxHolder(bh, boxes.headOption, stateDir, settings, LaunchParameters).ensuring(us => { log.info(s"Genesis UTXO state generated with hex digest ${Base16.encode(us.rootDigest)}") - java.util.Arrays.equals(us.rootDigest, constants.settings.chainSettings.genesisStateDigest) && us.version == genesisStateVersion + java.util.Arrays.equals(us.rootDigest, settings.chainSettings.genesisStateDigest) && us.version == genesisStateVersion }) -> bh } + /** + * Generate genesis digest state similarly to `generateGenesisUtxoState`, but without really storing boxes + */ def generateGenesisDigestState(stateDir: File, settings: ErgoSettings): DigestState = { - DigestState.create(Some(genesisStateVersion), Some(settings.chainSettings.genesisStateDigest), - stateDir, StateConstants(settings)) + DigestState.create(Some(genesisStateVersion), Some(settings.chainSettings.genesisStateDigest), stateDir, settings) } val preGenesisStateDigest: ADDigest = ADDigest @@ Array.fill(32)(0: Byte) lazy val genesisStateVersion: VersionTag = idToVersion(Header.GenesisParentId) - def readOrGenerate(settings: ErgoSettings, - constants: StateConstants): ErgoState[_] = { + /** + * Read from disk or generate genesis UTXO-set or digest based state + * @param settings - config used to find state database or extract genesis boxes data + */ + def readOrGenerate(settings: ErgoSettings): ErgoState[_] = { val dir = stateDir(settings) dir.mkdirs() settings.nodeSettings.stateType match { - case StateType.Digest => DigestState.create(None, None, dir, constants) - case StateType.Utxo if dir.listFiles().nonEmpty => UtxoState.create(dir, constants) - case _ => ErgoState.generateGenesisUtxoState(dir, constants)._1 + case StateType.Digest => DigestState.create(None, None, dir, settings) + case StateType.Utxo if dir.listFiles().nonEmpty => UtxoState.create(dir, settings) + case _ => ErgoState.generateGenesisUtxoState(dir, settings)._1 } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index 37415bf9ec..92ee24f828 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -310,10 +310,6 @@ object ErgoStateContext { */ val eip27Vote: Byte = 8 - def empty(constants: StateConstants, parameters: Parameters): ErgoStateContext = { - empty(constants.settings.chainSettings.genesisStateDigest, constants.settings, parameters) - } - def empty(settings: ErgoSettings, parameters: Parameters): ErgoStateContext = { empty(settings.chainSettings.genesisStateDigest, settings, parameters) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 09319c3c35..74e303d970 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters} +import org.ergoplatform.settings.{Algos, ErgoSettings, LaunchParameters, Parameters} import scorex.core.{NodeViewComponent, VersionTag} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 @@ -27,23 +27,29 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { val store: LDBVersionedStore - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings /** * If the state is in its genesis version (before genesis block) */ def isGenesis: Boolean = { - rootDigest.sameElements(constants.settings.chainSettings.genesisStateDigest) + rootDigest.sameElements(ergoSettings.chainSettings.genesisStateDigest) } - def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, constants) + /** + * Blockchain-derived context used in scripts validation. It changes from block to block. + */ + def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, ergoSettings) /** * @return current network parameters used in transaction and block validation (block cost and size limits etc) */ def parameters: Parameters = stateContext.currentParameters - def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(constants.settings.chainSettings) + /** + * Genesis state boxes, see `ErgoState.genesisBoxes` for details + */ + def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(ergoSettings.chainSettings) } @@ -51,12 +57,15 @@ object ErgoStateReader extends ScorexLogging { val ContextKey: Digest32 = Algos.hash("current state context") - def storageStateContext(store: LDBVersionedStore, constants: StateConstants): ErgoStateContext = { + /** + * Read blockchain-related state context from `store` database + */ + def storageStateContext(store: LDBVersionedStore, settings: ErgoSettings): ErgoStateContext = { store.get(ErgoStateReader.ContextKey) - .flatMap(b => ErgoStateContextSerializer(constants.settings).parseBytesTry(b).toOption) + .flatMap(b => ErgoStateContextSerializer(settings).parseBytesTry(b).toOption) .getOrElse { log.warn("Can't read blockchain parameters from database") - ErgoStateContext.empty(constants, LaunchParameters) + ErgoStateContext.empty(settings, LaunchParameters) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala b/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala deleted file mode 100644 index ba606c5efb..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.ergoplatform.nodeView.state - -import org.ergoplatform.settings.{ErgoSettings, VotingSettings} -import scorex.crypto.authds.ADDigest - -/** - * Constants that do not change when state version changes - * - * @param settings - node settings - */ -case class StateConstants(settings: ErgoSettings) { - - lazy val keepVersions: Int = settings.nodeSettings.keepVersions - lazy val votingSettings: VotingSettings = settings.chainSettings.voting - - lazy val genesisStateDigest: ADDigest = settings.chainSettings.genesisStateDigest -} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index f9e202d7db..ff94f40a7b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -7,6 +7,7 @@ import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLD import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import org.ergoplatform.settings.Constants.MakeSnapshotEvery +import org.ergoplatform.settings.ErgoSettings import scala.concurrent.Future import scala.util.Try @@ -16,10 +17,10 @@ import scala.util.Try */ trait UtxoSetSnapshotPersistence extends ScorexLogging { - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] - private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) + private[nodeView] val snapshotsDb = SnapshotsDb.create(ergoSettings) // Dump current UTXO set snapshot to persistent snapshots database // private[nodeView] as used in tests also @@ -44,7 +45,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { height % MakeSnapshotEvery == MakeSnapshotEvery - 1 } - if (constants.settings.nodeSettings.areSnapshotsStored && + if (ergoSettings.nodeSettings.areSnapshotsStored && timeToTakeSnapshot(height) && estimatedTip.nonEmpty && estimatedTip.get - height <= MakeSnapshotEvery) { @@ -60,7 +61,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { log.info("Started work within future") val ft0 = System.currentTimeMillis() dumpSnapshot(height, expectedRootHash) - snapshotsDb.pruneSnapshots(constants.settings.nodeSettings.storingUtxoSnapshots) + snapshotsDb.pruneSnapshots(ergoSettings.nodeSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() log.info("Work within future finished in: " + (ft - ft0) + " ms.") } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 6ec953de20..0e99b323d2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, Parameters} +import org.ergoplatform.settings.{Algos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -31,12 +31,12 @@ import scala.util.{Failure, Success, Try} * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata * @param version - current state version - * @param constants - constants, that do not change with state version changes + * @param ergoSettings - protocol and client config to to get state-related settings from */ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, override val store: LDBVersionedStore, - override protected val constants: StateConstants) + override protected val ergoSettings: ErgoSettings) extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader @@ -55,7 +55,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 case Some(hash) => val rootHash: ADDigest = ADDigest @@ hash val rollbackResult = p.rollback(rootHash).map { _ => - new UtxoState(p, version, store, constants) + new UtxoState(p, version, store, ergoSettings) } rollbackResult case None => @@ -114,12 +114,14 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 (generate: LocallyGeneratedModifier => Unit): Try[UtxoState] = mod match { case fb: ErgoFullBlock => + val keepVersions = ergoSettings.nodeSettings.keepVersions + // avoid storing versioned information in the database when block being processed is behind // blockchain tip by `keepVersions` blocks at least // we store `keepVersions` diffs in the database if chain tip is not known yet - if (fb.height >= estimatedTip.getOrElse(0) - constants.keepVersions) { - if (store.getKeepVersions < constants.keepVersions) { - store.setKeepVersions(constants.keepVersions) + if (fb.height >= estimatedTip.getOrElse(0) - keepVersions) { + if (store.getKeepVersions < keepVersions) { + store.setKeepVersions(keepVersions) } } else { if (store.getKeepVersions > 0) { @@ -201,7 +203,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 log.info(s"Valid modifier with header ${fb.header.encodedId} and emission box " + s"${emissionBox.map(e => Algos.encode(e.id))} applied to UtxoState at height ${fb.header.height}") saveSnapshotIfNeeded(fb.height, estimatedTip) - new UtxoState(persistentProver, idToVersion(fb.id), store, constants) + new UtxoState(persistentProver, idToVersion(fb.id), store, ergoSettings) } } stateTry.recoverWith[UtxoState] { case e => @@ -218,7 +220,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 //todo: update state context with headers (when snapshot downloading is done), so //todo: application of the first full block after the snapshot should have correct state context //todo: (in particular, "lastHeaders" field of it) - Success(new UtxoState(persistentProver, idToVersion(h.id), this.store, constants)) + Success(new UtxoState(persistentProver, idToVersion(h.id), this.store, ergoSettings)) case a: Any => log.error(s"Unhandled unknown modifier: $a") @@ -284,8 +286,8 @@ object UtxoState { Array(idStateDigestIdxElem, stateDigestIdIdxElem, bestVersion, eb, cb) } - def create(dir: File, constants: StateConstants): UtxoState = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + def create(dir: File, settings: ErgoSettings): UtxoState = { + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) .getOrElse(ErgoState.genesisStateVersion) val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { @@ -293,7 +295,7 @@ object UtxoState { val storage = new VersionedLDBAVLStorage(store) PersistentBatchAVLProver.create(bp, storage).get } - new UtxoState(persistentProver, version, store, constants) + new UtxoState(persistentProver, version, store, settings) } /** @@ -303,16 +305,16 @@ object UtxoState { def fromBoxHolder(bh: BoxHolder, currentEmissionBoxOpt: Option[ErgoBox], dir: File, - constants: StateConstants, + settings: ErgoSettings, parameters: Parameters): UtxoState = { val p = new BatchAVLProver[Digest32, HF](keyLength = 32, valueLengthOpt = None) bh.sortedBoxes.foreach { b => p.performOneOperation(Insert(b.id, ADValue @@ b.bytes)).ensuring(_.isSuccess) } - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) - val defaultStateContext = ErgoStateContext.empty(constants, parameters) + val defaultStateContext = ErgoStateContext.empty(settings, parameters) val storage = new VersionedLDBAVLStorage(store) val persistentProver = PersistentBatchAVLProver.create( p, @@ -321,7 +323,7 @@ object UtxoState { paranoidChecks = true ).get - new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) + new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, settings) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 5198afea20..09a7d67838 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,7 +5,7 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter @@ -22,7 +22,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi protected implicit val hf: HF = Algos.hash - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings /** * Versioned database where UTXO set and its authenticating AVL+ tree are stored @@ -84,13 +84,13 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi */ protected[state] def extractEmissionBox(fb: ErgoFullBlock): Option[ErgoBox] = { def hasEmissionBox(tx: ErgoTransaction): Boolean = - if(fb.height > constants.settings.chainSettings.reemission.activationHeight) { + if(fb.height > ergoSettings.chainSettings.reemission.activationHeight) { // after EIP-27 we search for emission box NFT for efficiency's sake tx.outputs.size == 2 && !tx.outputs.head.additionalTokens.isEmpty && - java.util.Arrays.equals(tx.outputs.head.additionalTokens(0)._1, constants.settings.chainSettings.reemission.emissionNftIdBytes) + java.util.Arrays.equals(tx.outputs.head.additionalTokens(0)._1, ergoSettings.chainSettings.reemission.emissionNftIdBytes) } else { - tx.outputs.head.ergoTree == constants.settings.chainSettings.monetary.emissionBoxProposition + tx.outputs.head.ergoTree == ergoSettings.chainSettings.monetary.emissionBoxProposition } def fullSearch(fb: ErgoFullBlock): Option[ErgoBox] = { @@ -169,7 +169,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * Useful when checking mempool transactions. */ def withUnconfirmedTransactions(unconfirmedTxs: Seq[UnconfirmedTransaction]): UtxoState = { - new UtxoState(persistentProver, version, store, constants) { + new UtxoState(persistentProver, version, store, ergoSettings) { lazy val createdBoxes: Seq[ErgoBox] = unconfirmedTxs.map(_.transaction).flatMap(_.outputs) override def boxById(id: ADKey): Option[ErgoBox] = { @@ -183,7 +183,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * Useful when checking mempool transactions. */ def withTransactions(transactions: Seq[ErgoTransaction]): UtxoState = { - new UtxoState(persistentProver, version, store, constants) { + new UtxoState(persistentProver, version, store, ergoSettings) { lazy val createdBoxes: Seq[ErgoBox] = transactions.flatMap(_.outputs) override def boxById(id: ADKey): Option[ErgoBox] = { diff --git a/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala index 4b5e50c6b9..ed0e328ed4 100644 --- a/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala @@ -33,7 +33,7 @@ class BlocksApiRouteSpec extends AnyFlatSpec } it should "post block correctly" in { - val (st, bh) = createUtxoState(parameters) + val (st, bh) = createUtxoState(settings) val block: ErgoFullBlock = validFullBlock(parentOpt = None, st, bh) val blockJson: UniversalEntity = HttpEntity(block.asJson.toString).withContentType(ContentTypes.`application/json`) Post(prefix, blockJson) ~> route ~> check { diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index 09974da381..5096dec09d 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -41,10 +41,10 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH val testProbe = new TestProbe(actorSystem) actorSystem.eventStream.subscribe(testProbe.ref, newTx) - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settingsToTest) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, settingsToTest, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod } .get @@ -95,7 +95,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH it should "rebroadcast transactions correctly" in { - val (us0, bh0) = createUtxoState(parameters) + val (us0, bh0) = createUtxoState(settingsToTest) val (txs0, bh1) = validTransactionsFromBoxHolder(bh0) val b1 = validFullBlock(None, us0, txs0) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index a8c6db6a3f..74e16b396a 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -51,7 +51,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { } property("collect reward from emission box only") { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 us.emissionBoxOpt should not be None val expectedReward = emission.minersRewardAtHeight(us.stateContext.currentHeight) @@ -240,7 +240,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { } property("collect reward from both emission box and fees") { - val (us, _) = createUtxoState(parameters) + val (us, _) = createUtxoState(settings) us.emissionBoxOpt should not be None val expectedReward = emission.minersRewardAtHeight(us.stateContext.currentHeight) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index 30908ceb1a..a507bcd4c4 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -156,7 +156,7 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { } property("append header to genesis - 2") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val block = validFullBlock(None, us, bh) 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 1cc3e8e437..d97d4f7384 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -13,7 +13,7 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransact import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UtxoState, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoState, UtxoStateReader} import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} import scorex.crypto.hash.Digest32 @@ -251,7 +251,7 @@ object ChainGenerator extends ErgoTestHelpers { def generate(length: Int, dir: File)(history: ErgoHistory): Unit = { val stateDir = new File(s"${dir.getAbsolutePath}/state") stateDir.mkdirs() - val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, StateConstants(initSettings)) + 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) diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index c68d279f50..d08408e7cf 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -19,9 +19,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec with ScalaCheckPropertyChecks { it should "accept valid transaction" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) val pool0 = ErgoMemPool.empty(settings) val poolAfter = txs.foldLeft(pool0) { case (pool, tx) => @@ -42,9 +42,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "respect given sorting order" in { implicit val ms = settings.chainSettings.monetary - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val inputBox = wus.takeBoxes(1).head val feeOut = new ErgoBoxCandidate(inputBox.value, feeProp, creationHeight = 0) val tx = ErgoTransaction( @@ -77,9 +77,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline already contained transaction" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -93,9 +93,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "reject double-spending transaction if it is paying no more than one already sitting in the pool" in { forAll(smallPositiveInt, smallPositiveInt) { case (n1, n2) => whenever(n1 != n2) { - val (us, bh) = createUtxoState(extendedParameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, extendedParameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, extendedParameters).applyModifier(genesis)(_ => ()).get val feeProp = settings.chainSettings.monetary.feeProposition val inputBox = wus.takeBoxes(100).collectFirst{ @@ -141,7 +141,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline transactions invalidated earlier" in { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 var pool = ErgoMemPool.empty(settings) forAll(invalidBlockTransactionsGen) { blockTransactions => val unconfirmedTxs = blockTransactions.txs.map(tx => UnconfirmedTransaction(tx, None)) @@ -152,9 +152,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline transactions not meeting min fee" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) val unconfirmedTxs = txs.map(tx => UnconfirmedTransaction(tx, None)) @@ -176,7 +176,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "invalidate or reject invalid transaction" in { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 val pool = ErgoMemPool.empty(settings) forAll(invalidBlockTransactionsGen) { blockTransactions => blockTransactions.txs.forall{tx => @@ -214,9 +214,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "Accept output of pooled transactions" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -234,9 +234,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "consider families for replacement policy" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -269,9 +269,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "correctly remove transaction from pool and rebuild families" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var allTxs = txs val family_depth = 10 @@ -302,9 +302,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "return results take / getAll / getAllPrioritized sorted by priority" in { val feeProp = settings.chainSettings.monetary.feeProposition - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -344,9 +344,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "add removed transaction to mempool statistics" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var allTxs = txs val family_depth = 10 diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index 96c8aad776..36940f420f 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -67,7 +67,7 @@ class ScriptsSpec extends ErgoPropertyTest { private def applyBlockSpendingScript(script: ErgoTree): Try[UtxoState] = { val scriptBox = ergoBoxGen(script, heightGen = 0).sample.get val bh = BoxHolder(Seq(fixedBox, scriptBox)) - val us = UtxoState.fromBoxHolder(bh, None, createTempDir, stateConstants, parameters) + val us = UtxoState.fromBoxHolder(bh, None, createTempDir, settings, parameters) bh.boxes.map(b => us.boxById(b._2.id) shouldBe Some(b._2)) val tx = validTransactionsFromBoxHolder(bh, new RandomWrapper(Some(1)), 201)._1 tx.size shouldBe 1 diff --git a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala index 4d670365d1..7cf71def1a 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala @@ -21,19 +21,19 @@ class DigestStateSpecification extends ErgoPropertyTest { val fb = validFullBlock(parentOpt = None, us, bh) val dir2 = createTempDir - val ds = DigestState.create(Some(us.version), Some(us.rootDigest), dir2, stateConstants) + val ds = DigestState.create(Some(us.version), Some(us.rootDigest), dir2, settings) ds.applyModifier(fb, None)(_ => ()) shouldBe 'success ds.close() - val state = DigestState.create(None, None, dir2, stateConstants) + val state = DigestState.create(None, None, dir2, settings) state.version shouldEqual fb.header.id state.rootDigest shouldEqual fb.header.stateRoot } } property("validate() - valid block") { - var (us, bh) = createUtxoState(parameters) - var ds = createDigestState(us.version, us.rootDigest, parameters) + var (us, bh) = createUtxoState(settings) + var ds = createDigestState(us.version, us.rootDigest) var parentOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -48,7 +48,7 @@ class DigestStateSpecification extends ErgoPropertyTest { property("validate() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createDigestState(emptyVersion, emptyAdDigest, parameters) + val state = createDigestState(emptyVersion, emptyAdDigest) state.validate(b).isFailure shouldBe true } } @@ -61,14 +61,14 @@ class DigestStateSpecification extends ErgoPropertyTest { val block = validFullBlock(parentOpt = None, us, bh) block.blockTransactions.transactions.exists(_.dataInputs.nonEmpty) shouldBe true - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) ds.applyModifier(block, None)(_ => ()) shouldBe 'success } } property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createDigestState(emptyVersion, emptyAdDigest, parameters) + val state = createDigestState(emptyVersion, emptyAdDigest) state.applyModifier(b, None)(_ => ()).isFailure shouldBe true } } @@ -80,7 +80,7 @@ class DigestStateSpecification extends ErgoPropertyTest { val block = validFullBlock(parentOpt = None, us, bh) - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) ds.rollbackVersions.size shouldEqual 1 @@ -104,7 +104,7 @@ class DigestStateSpecification extends ErgoPropertyTest { property("validateTransactions() - dataInputs") { forAll(boxesHolderGen) { bh => val us = createUtxoState(bh, parameters) - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) // generate 2 independent transactions spending state boxes only val headTx = validTransactionsFromBoxes(1, bh.boxes.take(10).values.toSeq, new RandomWrapper())._1.head diff --git a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala index 60d49ff68a..059b4e0d1c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala @@ -20,7 +20,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { property("applyModifier() - double spending") { forAll(boxesHolderGen, Gen.choose(1: Byte, 2: Byte)) { case (bh, version) => val us = createUtxoState(bh, parameters) - val ds = createDigestState(bytesToVersion(Array.fill(32)(100: Byte)), us.rootDigest, parameters) + val ds = createDigestState(bytesToVersion(Array.fill(32)(100: Byte)), us.rootDigest) val validBlock = validFullBlock(None, us, bh) val dsTxs = validBlock.transactions ++ validBlock.transactions @@ -51,8 +51,8 @@ class ErgoStateSpecification extends ErgoPropertyTest { s1.genesisStateDigest shouldBe s2.genesisStateDigest } - var (us, bh) = createUtxoState(parameters) - var ds = createDigestState(us.version, us.rootDigest, parameters) + var (us, bh) = createUtxoState(settings) + var ds = createDigestState(us.version, us.rootDigest) var lastBlocks: Seq[ErgoFullBlock] = Seq() forAll { seed: Int => val blBh = validFullBlockWithBoxHolder(lastBlocks.headOption, us, bh, new RandomWrapper(Some(seed))) @@ -68,13 +68,13 @@ class ErgoStateSpecification extends ErgoPropertyTest { property("generateGenesisUtxoState & generateGenesisDigestState are compliant") { val settings = ErgoSettings.read(Args.empty) val dir = createTempDir - val rootHash = createUtxoState(parameters)._1.rootDigest + val rootHash = createUtxoState(settings)._1.rootDigest val expectedRootHash = ErgoState.generateGenesisDigestState(dir, settings).rootDigest rootHash shouldBe expectedRootHash } property("ErgoState.boxChanges() should generate operations in the same order") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) var parentOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -92,7 +92,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { } property("ErgoState.boxChanges() double spend attempt") { - val (_, bh) = createUtxoState(parameters) + val (_, bh) = createUtxoState(settings) val emissionBox = genesisBoxes.head forAll { seed: Int => @@ -116,7 +116,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { } property("ErgoState.stateChanges()") { - val bh = createUtxoState(parameters)._2 + val bh = createUtxoState(settings)._2 val emissionBox = genesisBoxes.head forAll { seed: Int => diff --git a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala index 205caa634a..a0cd911b10 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala @@ -35,7 +35,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera private val emptyModifierId: ModifierId = bytesToId(Array.fill(32)(0.toByte)) property("Founders box workflow") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) var foundersBox = genesisBoxes.last var lastBlock = validFullBlock(parentOpt = None, us, bh) us = us.applyModifier(lastBlock, None)(_ => ()).get @@ -60,7 +60,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("Founders should be able to spend genesis founders box") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) val foundersBox = genesisBoxes.last var height: Int = ErgoHistory.GenesisHeight @@ -111,7 +111,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("Correct genesis state") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val boxes = bh.boxes.values.toList boxes.size shouldBe 3 @@ -135,7 +135,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("extractEmissionBox() should extract correct box") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) us.emissionBoxOpt should not be None var lastBlockOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -158,7 +158,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("proofsForTransactions") { - var (us: UtxoState, bh) = createUtxoState(parameters) + var (us: UtxoState, bh) = createUtxoState(settings) var height: Int = ErgoHistory.GenesisHeight forAll(defaultHeaderGen) { header => val t = validTransactionsFromBoxHolder(bh, new RandomWrapper(Some(height))) @@ -435,13 +435,13 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createUtxoState(parameters)._1 + val state = createUtxoState(settings)._1 state.applyModifier(b, None)(_ => ()).isFailure shouldBe true } } property("applyModifier() - valid full block after invalid one") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val validBlock = validFullBlock(parentOpt = None, us, bh) //Different state @@ -460,20 +460,20 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("2 forks switching") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wusAfterGenesis = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val chain1block1 = validFullBlock(Some(genesis), wusAfterGenesis) val wusChain1Block1 = wusAfterGenesis.applyModifier(chain1block1)(_ => ()).get val chain1block2 = validFullBlock(Some(chain1block1), wusChain1Block1) - val (us2, bh2) = createUtxoState(parameters) - val wus2AfterGenesis = WrappedUtxoState(us2, bh2, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val (us2, bh2) = createUtxoState(settings) + val wus2AfterGenesis = WrappedUtxoState(us2, bh2, settings, parameters).applyModifier(genesis)(_ => ()).get val chain2block1 = validFullBlock(Some(genesis), wus2AfterGenesis) val wusChain2Block1 = wus2AfterGenesis.applyModifier(chain2block1)(_ => ()).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) - var (state, _) = createUtxoState(parameters) + var (state, _) = createUtxoState(settings) state = state.applyModifier(genesis, None)(_ => ()).get state = state.applyModifier(chain1block1, None)(_ => ()).get @@ -494,7 +494,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val us = createUtxoState(bh, parameters) bh.sortedBoxes.foreach(box => us.boxById(box.id) should not be None) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wusAfterGenesis = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get wusAfterGenesis.rootDigest shouldEqual genesis.header.stateRoot val (finalState: WrappedUtxoState, chain: Seq[ErgoFullBlock]) = (0 until depth) 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 fbb2cf883b..d747a4b5be 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala @@ -22,8 +22,8 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, store: LDBVersionedStore, val versionedBoxHolder: VersionedInMemoryBoxHolder, - constants: StateConstants) - extends UtxoState(prover, version, store, constants) { + settings: ErgoSettings) + extends UtxoState(prover, version, store, settings) { def size: Int = versionedBoxHolder.size @@ -32,7 +32,7 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], override def rollbackTo(version: VersionTag): Try[WrappedUtxoState] = super.rollbackTo(version) match { case Success(us) => val updHolder = versionedBoxHolder.rollback(us.version) - Success(new WrappedUtxoState(us.persistentProver, version, us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, version, us.store, updHolder, settings)) case Failure(e) => Failure(e) } @@ -49,10 +49,10 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], us.version, changes.toRemove.map(_.key).map(ByteArrayWrapper.apply), changes.toAppend.map(_.value).map(ErgoBoxSerializer.parseBytes)) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, settings)) case _ => val updHolder = versionedBoxHolder.applyChanges(us.version, Seq(), Seq()) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, settings)) } case Failure(e) => Failure(e) } @@ -65,13 +65,12 @@ object WrappedUtxoState { nodeViewHolderRef: Option[ActorRef], parameters: Parameters, settings: ErgoSettings): WrappedUtxoState = { - val constants = StateConstants(settings) - val emissionBox = ErgoState.genesisBoxes(constants.settings.chainSettings).headOption - val us = UtxoState.fromBoxHolder(boxHolder, emissionBox, dir, constants, parameters) - WrappedUtxoState(us, boxHolder, constants, parameters) + val emissionBox = ErgoState.genesisBoxes(settings.chainSettings).headOption + val us = UtxoState.fromBoxHolder(boxHolder, emissionBox, dir, settings, parameters) + WrappedUtxoState(us, boxHolder, settings, parameters) } - def apply(us: UtxoState, boxHolder: BoxHolder, constants: StateConstants, parameters: Parameters): WrappedUtxoState = { + def apply(us: UtxoState, boxHolder: BoxHolder, settings: ErgoSettings, parameters: Parameters): WrappedUtxoState = { val boxes = boxHolder.boxes val version = us.version @@ -81,6 +80,6 @@ object WrappedUtxoState { Map(version -> (Seq() -> boxHolder.sortedBoxes.toSeq)) ) - new WrappedUtxoState(us.persistentProver, ErgoState.genesisStateVersion, us.store, vbh, constants) + new WrappedUtxoState(us.persistentProver, ErgoState.genesisStateVersion, us.store, vbh, settings) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala index f3b89434a5..83e9243ad9 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala @@ -22,7 +22,7 @@ import scorex.util.{ModifierId, bytesToId} class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers with NodeViewTestOps with NoShrink { private val t0 = TestCase("check chain is healthy") { fixture => - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val block = validFullBlock(None, us, bh) val history = generateHistory(true, StateType.Utxo, false, 2) @@ -51,7 +51,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t3 = TestCase("apply valid block header") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -71,7 +71,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t3a = TestCase("do not apply block headers in invalid order") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val parentBlock = validFullBlock(None, us, bh) val block = validFullBlock(Some(parentBlock), us, bh) @@ -96,7 +96,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t4 = TestCase("apply valid block as genesis") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -116,10 +116,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t5 = TestCase("apply full blocks after genesis") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get applyBlock(genesis) shouldBe 'success @@ -138,7 +138,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t6 = TestCase("add transaction to memory pool") { fixture => import fixture._ if (stateType == Utxo) { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) applyBlock(genesis) shouldBe 'success @@ -155,10 +155,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t7 = TestCase("apply statefully invalid full block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get // TODO looks like another bug is still present here, see https://github.com/ergoplatform/ergo/issues/309 @@ -210,10 +210,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t8 = TestCase("switching for a better chain") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get @@ -247,7 +247,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t9 = TestCase("UTXO state should generate adProofs and put them in history") { fixture => import fixture._ if (stateType == StateType.Utxo) { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) nodeViewHolderRef ! LocallyGeneratedModifier(genesis.header) @@ -261,10 +261,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t10 = TestCase("NodeViewHolder start from inconsistent state") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get applyBlock(genesis) shouldBe 'success @@ -284,10 +284,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t11 = TestCase("apply payload in incorrect order (excluding extension)") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get @@ -314,7 +314,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t12 = TestCase("Do not apply txs with wrong header id") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight @@ -367,7 +367,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t13 = TestCase("Do not apply wrong adProofs") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -400,7 +400,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi "it's not equal to genesisId from config") { fixture => import fixture._ updateConfig(genesisIdConfig(modifierIdGen.sample)) - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -419,7 +419,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t15 = TestCase("apply genesis block header if it's equal to genesisId from config") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) updateConfig(genesisIdConfig(Some(block.header.id))) @@ -439,8 +439,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t16 = TestCase("apply forks that include genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) - val wusGenesis = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wusGenesis = WrappedUtxoState(us, bh, fixture.settings, parameters) val chain1block1 = validFullBlock(parentOpt = None, us, bh) @@ -469,7 +469,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t17 = TestCase("apply invalid genesis header") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val header = validFullBlock(None, us, bh).header.copy(parentId = bytesToId(Array.fill(32)(9: Byte))) getBestHeaderOpt shouldBe None @@ -488,7 +488,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t18 = TestCase("apply syntactically invalid genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val validBlock = validFullBlock(parentOpt = None, us, bh) val invalidBlock = validBlock.copy(header = validBlock.header.copy(parentId = bytesToId(Array.fill(32)(9: Byte)))) @@ -501,8 +501,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t19 = TestCase("apply semantically invalid genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) - val wusGenesis = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wusGenesis = WrappedUtxoState(us, bh, fixture.settings, parameters) val invalidBlock = generateInvalidFullBlock(None, wusGenesis) diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala index 7087481567..0dd39c88c8 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala @@ -48,8 +48,8 @@ class PrunedNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps wit private def testCode(fixture: NodeViewFixture, toSkip: Int, totalBlocks: Int = 20) = { import fixture._ - val (us, bh) = createUtxoState(stateConstants, parameters) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wus = WrappedUtxoState(us, bh, fixture.settings, parameters) val fullChain = genFullChain(wus, totalBlocks, nodeViewHolderRef) diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala index 4b8b3685ac..74955421b2 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala @@ -28,7 +28,7 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { override val stateGen: Gen[WrappedDigestState] = { boxesHolderGen.map(WrappedUtxoState(_, createTempDir, None, parameters, settings)).map { wus => - val digestState = DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, stateConstants) + val digestState = DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, settings) new WrappedDigestState(digestState, wus, settings) } } diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 48e9cb2732..ff5c142c72 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -28,7 +28,7 @@ class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with property("ErgoFullBlock should be encoded into JSON and decoded back correctly") { - val (st, bh) = createUtxoState(parameters) + val (st, bh) = createUtxoState(settings) val block: ErgoFullBlock = validFullBlock(parentOpt = None, st, bh) val blockJson: Json = block.asJson diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 71bcaeb3cc..5be22f7827 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -78,7 +78,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val history = ErgoHistory.readOrGenerate(fullHistorySettings)(null) HistoryTestHelpers.allowToApplyOldBlocks(history) - val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, StateConstants(fullHistorySettings)) + val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, fullHistorySettings) log.info(s"Going to generate a chain at ${dir.getAbsoluteFile} starting from ${history.bestFullBlockOpt}") val chain = loop(state, None, None, Seq()) diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 7dabb08224..775190285f 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -6,7 +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.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UpcomingStateContext} +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UpcomingStateContext} import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.Parameters.{MaxBlockCostIncrease, MinValuePerByteIncrease} import org.ergoplatform.settings.ValidationRules._ @@ -58,7 +58,6 @@ trait ErgoTestConstants extends ScorexLogging { val emission: EmissionRules = settings.chainSettings.emissionRules val coinsTotal: Long = emission.coinsTotal - val stateConstants: StateConstants = StateConstants(settings) val genesisStateDigest: ADDigest = settings.chainSettings.genesisStateDigest val feeProp: ErgoTree = ErgoScriptPredef.feeProposition(emission.settings.minerRewardDelay) diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index ec887abd75..00540a1e69 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -58,7 +58,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val digestState: DigestState = { boxesHolderGen.map(WrappedUtxoState(_, createTempDir, None, parameters, settings)).map { wus => - DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, stateConstants) + DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, settings) } }.sample.value @@ -257,7 +257,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with sender() ! Success(tx) case SignTransaction(tx, secrets, hints, boxesToSpendOpt, dataBoxesOpt) => - val sc = ErgoStateContext.empty(stateConstants, parameters) + val sc = ErgoStateContext.empty(settings, 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/generators/ValidBlocksGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala index 31f60d02cb..b4ef8203c2 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState -import org.ergoplatform.settings.{Algos, Constants, Parameters} +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, Parameters} import org.ergoplatform.utils.{LoggingUtil, RandomLike, RandomWrapper} import org.ergoplatform.wallet.utils.TestFileUtils import org.scalatest.matchers.should.Matchers @@ -26,19 +26,15 @@ import scala.util.{Failure, Random, Success} trait ValidBlocksGenerators extends TestkitHelpers with TestFileUtils with Matchers with ChainGenerator with ErgoTransactionGenerators { - def createUtxoState(parameters: Parameters): (UtxoState, BoxHolder) = { - createUtxoState(StateConstants(settings), parameters) - } - - def createUtxoState(constants: StateConstants, parameters: Parameters): (UtxoState, BoxHolder) = { - ErgoState.generateGenesisUtxoState(createTempDir, constants) + def createUtxoState(settings: ErgoSettings): (UtxoState, BoxHolder) = { + ErgoState.generateGenesisUtxoState(createTempDir, settings) } def createUtxoState(bh: BoxHolder, parameters: Parameters): UtxoState = - UtxoState.fromBoxHolder(bh, None, createTempDir, stateConstants, parameters) + UtxoState.fromBoxHolder(bh, None, createTempDir, settings, parameters) - def createDigestState(version: VersionTag, digest: ADDigest, parameters: Parameters): DigestState = - DigestState.create(Some(version), Some(digest), createTempDir, stateConstants) + def createDigestState(version: VersionTag, digest: ADDigest): DigestState = + DigestState.create(Some(version), Some(digest), createTempDir, settings) def validTransactionsFromBoxHolder(boxHolder: BoxHolder): (Seq[ErgoTransaction], BoxHolder) = validTransactionsFromBoxHolder(boxHolder, new RandomWrapper) From ccfa678350aba0be572c037282d7731fad7b83ac Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 24 Apr 2023 17:54:33 +0300 Subject: [PATCH 134/204] pulling changes from preparation --- .../serialization/ManifestSerializer.scala | 10 ++-- .../batch/VersionedLDBAVLStorage.scala | 22 ++++---- .../src/main/scala/scorex/db/LDBKVStore.scala | 17 +++++- .../scala/scorex/db/LDBVersionedStore.scala | 2 +- .../VersionedLDBAVLStorageSpecification.scala | 7 ++- .../nodeView/state/UtxoStateBenchmark.scala | 2 +- papers/utxo.md | 38 ++++++++------ src/main/resources/api/openapi.yaml | 36 ++++++++++++- src/main/resources/application.conf | 2 +- .../ergoplatform/http/api/UtxoApiRoute.scala | 5 ++ .../http/api/WalletApiRoute.scala | 16 +++++- .../modifiers/history/PreHeader.scala | 2 +- .../nodeView/ErgoNodeViewHolder.scala | 25 ++++----- .../UtxoSetSnapshotProcessor.scala | 4 +- .../nodeView/state/DigestState.scala | 30 +++++------ .../nodeView/state/ErgoState.scala | 37 ++++++++----- .../nodeView/state/ErgoStateContext.scala | 4 -- .../nodeView/state/ErgoStateReader.scala | 25 ++++++--- .../nodeView/state/SnapshotsDb.scala | 12 ++--- .../nodeView/state/StateConstants.scala | 17 ------ .../state/UtxoSetSnapshotPersistence.scala | 22 +++++--- .../nodeView/state/UtxoState.scala | 34 ++++++------ .../nodeView/state/UtxoStateReader.scala | 18 ++++--- .../nodeView/wallet/ErgoWalletActor.scala | 17 ++++++ .../nodeView/wallet/ErgoWalletReader.scala | 8 +++ .../nodeView/wallet/ErgoWalletService.scala | 21 ++++++++ .../wallet/persistence/WalletDigest.scala | 1 - .../org/ergoplatform/settings/Constants.scala | 7 +-- .../scala/scorex/core/NodeViewModifier.scala | 1 - .../http/routes/BlocksApiRouteSpec.scala | 2 +- .../local/MempoolAuditorSpec.scala | 6 +-- .../mining/CandidateGeneratorPropSpec.scala | 4 +- .../nodeView/NodeViewSynchronizerTests.scala | 7 +-- ...txoSetSnapshotProcessorSpecification.scala | 6 +-- .../VerifyNonADHistorySpecification.scala | 2 +- .../extra/ExtraIndexerSpecification.scala | 4 +- .../nodeView/mempool/ErgoMemPoolSpec.scala | 44 ++++++++-------- .../nodeView/mempool/ScriptsSpec.scala | 2 +- .../state/DigestStateSpecification.scala | 18 +++---- .../state/ErgoStateSpecification.scala | 14 ++--- .../state/UtxoStateSpecification.scala | 26 +++++----- .../state/wrapped/WrappedUtxoState.scala | 21 ++++---- .../viewholder/ErgoNodeViewHolderSpec.scala | 52 +++++++++---------- .../viewholder/PrunedNodeViewHolderSpec.scala | 4 +- .../wallet/ErgoWalletServiceSpec.scala | 23 +++++++- .../sanity/ErgoSanityDigest.scala | 2 +- .../serialization/JsonSerializationSpec.scala | 2 +- .../ergoplatform/tools/ChainGenerator.scala | 2 +- .../utils/ErgoTestConstants.scala | 3 +- .../scala/org/ergoplatform/utils/Stubs.scala | 4 +- .../generators/ValidBlocksGenerators.scala | 16 +++--- 51 files changed, 420 insertions(+), 286 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index 2c4bac6397..eb451e16b2 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -1,6 +1,5 @@ package scorex.core.serialization -import com.google.common.primitives.Ints import scorex.crypto.authds.avltree.batch.Constants.DigestType import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, ProxyInternalNode} import scorex.crypto.authds.avltree.batch.{InternalProverNode, ProverLeaf, ProverNodes, VersionedLDBAVLStorage} @@ -13,8 +12,8 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { - val height = manifest.rootHeight - w.putBytes(Ints.toByteArray(height)) + val rootNodeHeight = manifest.rootHeight.toByte + w.put(rootNodeHeight) w.put(manifestDepth) def loop(node: ProverNodes[DigestType], level: Int): Unit = { @@ -32,10 +31,11 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro } override def parse(r: Reader): BatchAVLProverManifest[DigestType] = { - val rootHeight = Ints.fromByteArray(r.getBytes(4)) + val rootHeight = r.getByte() val manifestDepth = r.getByte() - require(manifestDepth == this.manifestDepth, "Wrong manifest depth") + require(manifestDepth == this.manifestDepth, + s"Wrong manifest depth, found: $manifestDepth, expected: ${this.manifestDepth}") def loop(level: Int): ProverNodes[DigestType] = { val node = nodeSerializer.parse(r) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index c606289c3d..c1b50a7621 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -1,7 +1,6 @@ package scorex.crypto.authds.avltree.batch import com.google.common.primitives.Ints -import scorex.core.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.Constants.{DigestType, HashFnType, hashFn} import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.{topNodeHashKey, topNodeHeightKey} import scorex.crypto.authds.avltree.batch.serialization.{BatchAVLProverManifest, BatchAVLProverSubtree, ProxyInternalNode} @@ -98,7 +97,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) * @param expectedRootHash - expected UTXO set aunthenticating tree root hash * @return - hash of root node of tree, or failure if an error (e.g. in database) happened */ - def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Byte, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { store.processSnapshot { dbReader => def subtreeLoop(label: DigestType, builder: mutable.ArrayBuilder[Byte]): Unit = { @@ -137,14 +136,14 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) } val rootNodeLabel = dbReader.get(topNodeHashKey) - val rootNodeHeight = Ints.fromByteArray(dbReader.get(topNodeHeightKey)) + val rootNodeHeight = Ints.fromByteArray(dbReader.get(topNodeHeightKey)).toByte require(rootNodeLabel.sameElements(expectedRootHash), "Root node hash changed") val manifestBuilder = mutable.ArrayBuilder.make[Byte]() manifestBuilder.sizeHint(200000) - manifestBuilder ++= Ints.toByteArray(rootNodeHeight) - manifestBuilder += ManifestSerializer.MainnetManifestDepth + manifestBuilder += rootNodeHeight + manifestBuilder += manifestDepth manifestLoop(rootNodeLabel, level = 1, manifestBuilder) val manifestBytes = manifestBuilder.result() @@ -205,16 +204,15 @@ object VersionedLDBAVLStorage { store: LDBVersionedStore): Try[VersionedLDBAVLStorage] = { //todo: the function below copy-pasted from BatchAVLProver, eliminate boilerplate? - def idCollector(node: ProverNodes[DigestType], - acc: Iterator[(Array[Byte], Array[Byte])]): Iterator[(Array[Byte], Array[Byte])] = { + def idCollector(node: ProverNodes[DigestType]): Iterator[(Array[Byte], Array[Byte])] = { val pair: (Array[Byte], Array[Byte]) = (nodeLabel(node), noStoreSerializer.toBytes(node)) node match { case n: ProxyInternalNode[DigestType] if n.isEmpty => - acc ++ Iterator(pair) + Iterator(pair) case i: InternalProverNode[DigestType] => - acc ++ Iterator(pair) ++ idCollector(i.left, acc) ++ idCollector(i.right, acc) + Iterator(pair) ++ idCollector(i.left) ++ idCollector(i.right) case _: ProverLeaf[DigestType] => - acc ++ Iterator(pair) + Iterator(pair) } } @@ -222,8 +220,8 @@ object VersionedLDBAVLStorage { val rootNodeHeight = manifest.rootHeight val digestWrapper = VersionedLDBAVLStorage.digest(rootNode.label, rootNodeHeight) val indices = Iterator(topNodeHashKey -> nodeLabel(rootNode), topNodeHeightKey -> Ints.toByteArray(rootNodeHeight)) - val nodesIterator = idCollector(manifest.root, Iterator.empty) ++ - chunks.flatMap(subtree => idCollector(subtree.subtreeTop, Iterator.empty)) + val nodesIterator = idCollector(manifest.root) ++ + chunks.flatMap(subtree => idCollector(subtree.subtreeTop)) store.update(digestWrapper, toRemove = Nil, toUpdate = indices ++ nodesIterator ++ additionalData).map { _ => new VersionedLDBAVLStorage(store) } diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index 7d4ab17a1b..a8ead25e37 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -14,6 +14,14 @@ import spire.syntax.all.cfor */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { + /** + * Update this database atomically with a batch of insertion and removal operations + * + * @param toInsertKeys - keys pf key-value pairs to insert into database + * @param toInsertValues - values pf key-value pairs to insert into database + * @param toRemove - keys of key-value pairs to remove from the database + * @return - error if it happens, or success status + */ def update(toInsertKeys: Array[K], toInsertValues: Array[V], toRemove: Array[K]): Try[Unit] = { val batch = db.createWriteBatch() try { @@ -44,10 +52,17 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging } } + /** + * `update` variant where we only insert values into this database + */ + def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + def insert(values: Array[(K, V)]): Try[Unit] = update(values.map(_._1), values.map(_._2), Array.empty) - def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + /** + * `update` variant where we only remove values from this database + */ def remove(keys: Array[K]): Try[Unit] = update(Array.empty, Array.empty, keys) /** diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 7530300467..7cd107a9ad 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -421,8 +421,8 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) * @param logic - processing logic which is getting access to `get` function to read from database snapshot */ def processSnapshot[T](logic: SnapshotReadInterface => T): Try[T] = { + val ro = new ReadOptions() try { - val ro = new ReadOptions() ro.snapshot(db.getSnapshot) object readInterface extends SnapshotReadInterface { def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) 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 b702f5de7d..5a7db6cffe 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,6 +6,7 @@ import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.topNodeHashKey import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 @@ -346,10 +347,8 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec val storage = prover.storage.asInstanceOf[VersionedLDBAVLStorage] val store = LDBFactory.createKvDb("/tmp/aa") - val ts0 = System.currentTimeMillis() - storage.dumpSnapshot(store, 4) - val ts = System.currentTimeMillis() - println("time: " + (ts-ts0)) + storage.dumpSnapshot(store, 4, prover.digest.dropRight(1)) + store.get(topNodeHashKey).sameElements(prover.digest.dropRight(1)) shouldBe true } } diff --git a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala index b8d8ffa094..5a257b0420 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateBenchmark.scala @@ -22,7 +22,7 @@ object UtxoStateBenchmark extends HistoryTestHelpers with NVBenchmark { val transactionsQty = blocks.flatMap(_.transactions).size def bench(mods: Seq[BlockSection]): Long = { - val state = ErgoState.generateGenesisUtxoState(createTempDir, StateConstants(realNetworkSetting))._1 + val state = ErgoState.generateGenesisUtxoState(createTempDir, realNetworkSetting)._1 Utils.time { mods.foldLeft(state) { case (st, mod) => st.applyModifier(mod, None)(_ => ()).get diff --git a/papers/utxo.md b/papers/utxo.md index 6eea677783..dc8cbbc6cc 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -1,30 +1,37 @@ Bootstrapping with UTXO set snapshot ==================================== -Motivation +Motivation ---------- -With time it consumes more and more resources to download, store and process the -whole blockchain with all the fullblocks. In blockchain where UTXO set snapshot is -authenticated (e.g. Ergo or Ethereum), it is possible to avoid that, downloading only some -historical UTXO set snapshot (accounts snapshot in Ethereum) and full-blocks after it. +With time, more and more resources are required to download, store and process the +whole blockchain with all the full blocks. In blockchain where UTXO set snapshot is +authenticated (such as Ergo or Ethereum), it is possible to avoid that, by downloading and applying +only a historical UTXO set snapshot (accounts snapshot in Ethereum) and full-blocks after it. It is shown (in [https://eprint.iacr.org/2018/129](https://eprint.iacr.org/2018/129)) that this -mode can be as secure as processing all the blocks (under the same assumptions). +mode can be as secure as processing all the blocks (under the same assumptions, with overwhelming probability +as a function of full-blocks suffix length). -This specification defines how to implement bootstrapping with UTXO set snapshot in Ergo -protocol reference client. +This specification defines how bootstrapping with UTXO set snapshot is implemented in Ergo +protocol reference client. The specification may be useful in order to understand how implementation +is done, and also to do alternative clients compatible with the reference client. -Implementation Details +Implementation Details ---------------------- -UTXO set is authenticated via AVL+ tree. +UTXO set is authenticated via AVL+ tree. Design principles for tree construction are provided in +[https://eprint.iacr.org/2016/994.pdf](https://eprint.iacr.org/2016/994.pdf), the implementation of the +tree is available in [the Scrypto framework](https://github.com/input-output-hk/scrypto). Time is broken into epochs, 1 epoch = 51,200 blocks (~72 days). -Snapshot is taken after last block of an epoch, so block with height h % 51200 == 51199 +Snapshot is taken after last block of an epoch, namely, after processing a block with +height *h % 51200 == 51199*. Chunk format ------------ + + Manifest format --------------- @@ -38,12 +45,9 @@ Bootstrapping Node Config ----------- -Sync Info V3 ------------- +The node is using bootstrapping with UTXO set snapshot if *ergo.node.utxoBootstrap = true*. -Testing -------- - -1. No nodes with UTXO set snapshots around - stuck with headers synced - tested \ No newline at end of file +Sync Info V3 +------------ diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 1436707dcb..748ea3c741 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "5.0.9" + version: "5.0.10" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: @@ -4761,6 +4761,40 @@ paths: schema: $ref: '#/components/schemas/ApiError' + /wallet/getPrivateKey: + post: + security: + - ApiKeyAuth: [api_key] + summary: Get the private key corresponding to a known address + operationId: walletGetPrivateKey + tags: + - wallet + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ErgoAddress" + responses: + '200': + description: Successfully retrieved secret key + content: + application/json: + schema: + $ref: '#/components/schemas/DlogSecret' + '404': + description: Address not found in wallet database + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + default: + description: Error + content: + application/json: + schema: + $ref: '#/components/schemas/ApiError' + /mining/candidate: get: security: diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 58bae38460..6af5a8fa95 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -408,7 +408,7 @@ scorex { nodeName = "ergo-node" # Network protocol version to be sent in handshakes - appVersion = 5.0.9 + appVersion = 5.0.10 # Network agent name. May contain information about client code # stack, starting from core code-base up to the end graphical interface. diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index 850b12b4c7..d2bf3661b2 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -83,6 +83,11 @@ case class UtxoApiRoute(readersHolder: ActorRef, case _ => None }) } + + /** + * Handler for /utxo/getSnapshotsInfo API call which is providing list of + * UTXO set snapshots stored locally + */ def getSnapshotsInfo: Route = (get & path("getSnapshotsInfo")) { ApiResponse(getState.map { case usr: UtxoSetSnapshotPersistence => diff --git a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala index c4ddea6377..7669192f19 100644 --- a/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala @@ -67,7 +67,8 @@ case class WalletApiRoute(readersHolder: ActorRef, signTransactionR ~ checkSeedR ~ rescanWalletR ~ - extractHintsR + extractHintsR ~ + getPrivateKeyR } } @@ -483,4 +484,17 @@ case class WalletApiRoute(readersHolder: ActorRef, } } + def getPrivateKeyR: Route = (path("getPrivateKey") & post & p2pkAddress) { p2pk => + withWalletOp(_.allExtendedPublicKeys()) { extKeys => + extKeys.find(_.key.value.equals(p2pk.pubkey.value)).map(_.path) match { + case Some(path) => + withWalletOp(_.getPrivateKeyFromPath(path)) { + case Success(secret) => ApiResponse(secret.w) + case Failure(f) => BadRequest(f.getMessage) + } + case None => NotExists("Address not found in wallet database.") + } + } + } + } diff --git a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala index 641160917d..bb5962a3d6 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala @@ -63,7 +63,7 @@ object PreHeader { } /** - * fake pre-header, which is used in ErgoStateContext if last headers are empty + * Fake pre-header, which is used in ErgoStateContext if last headers are empty * because ErgoStateContext needs a PreHeader and it's not optional. * See ErgoStateContext.currentHeight returns a height from the passed PreHeader */ diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 8dd4b27187..30c8e0532f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -286,7 +286,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case Success(pp) => log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) - val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, StateConstants(settings)) + val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, settings) // todo: apply 10 headers before utxo set snapshot updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(_) => ??? @@ -424,8 +424,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val history = ErgoHistory.readOrGenerate(settings) log.info("History database read") val memPool = ErgoMemPool.empty(settings) - val constants = StateConstants(settings) - restoreConsistentState(ErgoState.readOrGenerate(settings, constants).asInstanceOf[State], history) match { + restoreConsistentState(ErgoState.readOrGenerate(settings).asInstanceOf[State], history) match { case Success(state) => log.info(s"State database read, state synchronized") val wallet = ErgoWallet.readOrGenerate( @@ -551,10 +550,12 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti val dir = stateDir(settings) deleteRecursive(dir) - val constants = StateConstants(settings) - ErgoState.readOrGenerate(settings, constants) + ErgoState.readOrGenerate(settings) .asInstanceOf[State] - .ensuring(_.isGenesis, "State root is incorrect") + .ensuring( + state => java.util.Arrays.equals(state.rootDigest, settings.chainSettings.genesisStateDigest), + "State root is incorrect" + ) } private def restoreConsistentState(stateIn: State, history: ErgoHistory): Try[State] = { @@ -595,7 +596,6 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti * Recovers digest state from history. */ private def recoverDigestState(bestFullBlock: ErgoFullBlock, history: ErgoHistory): Try[DigestState] = { - val constants = StateConstants(settings) val votingLength = settings.chainSettings.voting.votingLength val bestHeight = bestFullBlock.header.height val newEpochHeadersQty = bestHeight % votingLength // how many blocks current epoch lasts @@ -607,12 +607,11 @@ 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(constants.genesisStateDigest, ext, lastHeaders)(settings)) + )(ext => ErgoStateContext.recover(settings.chainSettings.genesisStateDigest, ext, lastHeaders)(settings)) .flatMap { ctx => val recoverVersion = idToVersion(lastHeaders.last.id) val recoverRoot = bestFullBlock.header.stateRoot - val parameters = ctx.currentParameters - DigestState.recover(recoverVersion, recoverRoot, ctx, stateDir(settings), constants, parameters) + DigestState.recover(recoverVersion, recoverRoot, ctx, stateDir(settings), settings) } recoveredStateTry match { @@ -624,7 +623,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case Failure(exception) => // recover using whole headers chain log.warn(s"Failed to recover state from current epoch, using whole chain: ${exception.getMessage}") val wholeChain = history.headerChainBack(Int.MaxValue, bestFullBlock.header, _.isGenesis).headers - val genesisState = DigestState.create(None, None, stateDir(settings), constants) + val genesisState = DigestState.create(None, None, stateDir(settings), settings) wholeChain.foldLeft[Try[DigestState]](Success(genesisState)) { case (acc, m) => acc.flatMap(_.applyModifier(m, history.estimatedTip())(lm => self ! lm)) } @@ -732,11 +731,8 @@ object ErgoNodeViewHolder { case class EliminateTransactions(ids: Seq[ModifierId]) case object IsChainHealthy - sealed trait HealthCheckResult - case object ChainIsHealthy extends HealthCheckResult - case class ChainIsStuck(reason: String) extends HealthCheckResult } @@ -792,6 +788,7 @@ private[nodeView] class UtxoNodeViewHolder(settings: ErgoSettings) extends ErgoNodeViewHolder[UtxoState](settings) + object ErgoNodeViewRef { private def digestProps(settings: ErgoSettings): Props = 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 b18926aa6e..f05eaac3ad 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 @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.history.storage.HistoryStorage -import org.ergoplatform.nodeView.state.{ErgoStateReader, StateConstants, UtxoState} +import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId import org.ergoplatform.settings.{Algos, ErgoSettings} import scorex.core.VersionTag @@ -213,7 +213,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { val manifest = _manifest.get //todo: .get log.info("Starting UTXO set snapshot transfer into state database") - val esc = ErgoStateReader.storageStateContext(stateStore, StateConstants(settings)) + val esc = ErgoStateReader.storageStateContext(stateStore, settings) val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { ldbStorage => diff --git a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala index d4e1f27595..0711bc1a3c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/DigestState.scala @@ -28,7 +28,7 @@ import scala.util.{Failure, Success, Try} class DigestState protected(override val version: VersionTag, override val rootDigest: ADDigest, override val store: LDBVersionedStore, - ergoSettings: ErgoSettings) + override val ergoSettings: ErgoSettings) extends ErgoState[DigestState] with ScorexLogging with ScorexEncoding { @@ -36,8 +36,6 @@ class DigestState protected(override val version: VersionTag, store.lastVersionID .foreach(id => require(version == bytesToVersion(id), "version should always be equal to store.lastVersionID")) - override val constants: StateConstants = StateConstants(ergoSettings) - private lazy val nodeSettings = ergoSettings.nodeSettings private[state] def validateTransactions(transactions: Seq[ErgoTransaction], @@ -163,46 +161,48 @@ object DigestState extends ScorexLogging with ScorexEncoding { rootHash: ADDigest, stateContext: ErgoStateContext, dir: File, - constants: StateConstants, - parameters: Parameters): Try[DigestState] = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + settings: ErgoSettings): Try[DigestState] = { + 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 { _ => - new DigestState(version, rootHash, store, constants.settings) + new DigestState(version, rootHash, store, settings) } } + /** + * Read digest state from disk, or generate it from genesis data if nothing on the disk + */ def create(versionOpt: Option[VersionTag], rootHashOpt: Option[ADDigest], dir: File, - constants: StateConstants): DigestState = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + settings: ErgoSettings): DigestState = { + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) Try { - val context = ErgoStateReader.storageStateContext(store, constants) + val context = ErgoStateReader.storageStateContext(store, settings) (versionOpt, rootHashOpt) match { case (Some(version), Some(rootHash)) => val state = if (store.lastVersionID.map(w => bytesToVersion(w)).contains(version)) { - new DigestState(version, rootHash, store, constants.settings) + new DigestState(version, rootHash, store, settings) } else { val inVersion = store.lastVersionID.map(w => bytesToVersion(w)).getOrElse(version) - new DigestState(inVersion, rootHash, store, constants.settings) + new DigestState(inVersion, rootHash, store, settings) .update(version, rootHash, context).get //sync store } state.ensuring(bytesToVersion(store.lastVersionID.get) == version) case (None, None) if store.lastVersionID.isEmpty => - ErgoState.generateGenesisDigestState(dir, constants.settings) + ErgoState.generateGenesisDigestState(dir, settings) case _ => val version = store.lastVersionID.get val rootHash = store.get(version).get - new DigestState(bytesToVersion(version), ADDigest @@ rootHash, store, constants.settings) + new DigestState(bytesToVersion(version), ADDigest @@ rootHash, store, settings) } } match { case Success(state) => state case Failure(e) => store.close() log.warn(s"Failed to create state with ${versionOpt.map(encoder.encode)} and ${rootHashOpt.map(encoder.encode)}", e) - ErgoState.generateGenesisDigestState(dir, constants.settings) + ErgoState.generateGenesisDigestState(dir, settings) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 6af4c0cd2e..8f88940d6f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -257,44 +257,53 @@ object ErgoState extends ScorexLogging { } /** - * All boxes of genesis state. - * Emission box is always the first. + * Genesis state boxes generator. + * Genesis state is corresponding to the state before the very first block processed. + * For Ergo mainnet, contains emission contract box, proof-of-no--premine box, and treasury contract box */ def genesisBoxes(chainSettings: ChainSettings): Seq[ErgoBox] = { Seq(genesisEmissionBox(chainSettings), noPremineBox(chainSettings), genesisFoundersBox(chainSettings)) } - def generateGenesisUtxoState(stateDir: File, - constants: StateConstants): (UtxoState, BoxHolder) = { + /** + * Generate genesis full (UTXO-set) state by inserting genesis boxes into empty UTXO set. + * Assign `genesisStateDigest` from config as its version. + */ + def generateGenesisUtxoState(stateDir: File, settings: ErgoSettings): (UtxoState, BoxHolder) = { log.info("Generating genesis UTXO state") - val boxes = genesisBoxes(constants.settings.chainSettings) + val boxes = genesisBoxes(settings.chainSettings) val bh = BoxHolder(boxes) - UtxoState.fromBoxHolder(bh, boxes.headOption, stateDir, constants, LaunchParameters).ensuring(us => { + UtxoState.fromBoxHolder(bh, boxes.headOption, stateDir, settings, LaunchParameters).ensuring(us => { log.info(s"Genesis UTXO state generated with hex digest ${Base16.encode(us.rootDigest)}") - java.util.Arrays.equals(us.rootDigest, constants.settings.chainSettings.genesisStateDigest) && us.version == genesisStateVersion + java.util.Arrays.equals(us.rootDigest, settings.chainSettings.genesisStateDigest) && us.version == genesisStateVersion }) -> bh } + /** + * Generate genesis digest state similarly to `generateGenesisUtxoState`, but without really storing boxes + */ def generateGenesisDigestState(stateDir: File, settings: ErgoSettings): DigestState = { - DigestState.create(Some(genesisStateVersion), Some(settings.chainSettings.genesisStateDigest), - stateDir, StateConstants(settings)) + DigestState.create(Some(genesisStateVersion), Some(settings.chainSettings.genesisStateDigest), stateDir, settings) } val preGenesisStateDigest: ADDigest = ADDigest @@ Array.fill(32)(0: Byte) lazy val genesisStateVersion: VersionTag = idToVersion(Header.GenesisParentId) - def readOrGenerate(settings: ErgoSettings, - constants: StateConstants): ErgoState[_] = { + /** + * Read from disk or generate genesis UTXO-set or digest based state + * @param settings - config used to find state database or extract genesis boxes data + */ + def readOrGenerate(settings: ErgoSettings): ErgoState[_] = { val dir = stateDir(settings) dir.mkdirs() settings.nodeSettings.stateType match { - case StateType.Digest => DigestState.create(None, None, dir, constants) - case StateType.Utxo if dir.listFiles().nonEmpty => UtxoState.create(dir, constants) - case _ => ErgoState.generateGenesisUtxoState(dir, constants)._1 + case StateType.Digest => DigestState.create(None, None, dir, settings) + case StateType.Utxo if dir.listFiles().nonEmpty => UtxoState.create(dir, settings) + case _ => ErgoState.generateGenesisUtxoState(dir, settings)._1 } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index 37415bf9ec..92ee24f828 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -310,10 +310,6 @@ object ErgoStateContext { */ val eip27Vote: Byte = 8 - def empty(constants: StateConstants, parameters: Parameters): ErgoStateContext = { - empty(constants.settings.chainSettings.genesisStateDigest, constants.settings, parameters) - } - def empty(settings: ErgoSettings, parameters: Parameters): ErgoStateContext = { empty(settings.chainSettings.genesisStateDigest, settings, parameters) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 09319c3c35..74e303d970 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import org.ergoplatform.settings.{Algos, LaunchParameters, Parameters} +import org.ergoplatform.settings.{Algos, ErgoSettings, LaunchParameters, Parameters} import scorex.core.{NodeViewComponent, VersionTag} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 @@ -27,23 +27,29 @@ trait ErgoStateReader extends NodeViewComponent with ScorexLogging { val store: LDBVersionedStore - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings /** * If the state is in its genesis version (before genesis block) */ def isGenesis: Boolean = { - rootDigest.sameElements(constants.settings.chainSettings.genesisStateDigest) + rootDigest.sameElements(ergoSettings.chainSettings.genesisStateDigest) } - def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, constants) + /** + * Blockchain-derived context used in scripts validation. It changes from block to block. + */ + def stateContext: ErgoStateContext = ErgoStateReader.storageStateContext(store, ergoSettings) /** * @return current network parameters used in transaction and block validation (block cost and size limits etc) */ def parameters: Parameters = stateContext.currentParameters - def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(constants.settings.chainSettings) + /** + * Genesis state boxes, see `ErgoState.genesisBoxes` for details + */ + def genesisBoxes: Seq[ErgoBox] = ErgoState.genesisBoxes(ergoSettings.chainSettings) } @@ -51,12 +57,15 @@ object ErgoStateReader extends ScorexLogging { val ContextKey: Digest32 = Algos.hash("current state context") - def storageStateContext(store: LDBVersionedStore, constants: StateConstants): ErgoStateContext = { + /** + * Read blockchain-related state context from `store` database + */ + def storageStateContext(store: LDBVersionedStore, settings: ErgoSettings): ErgoStateContext = { store.get(ErgoStateReader.ContextKey) - .flatMap(b => ErgoStateContextSerializer(constants.settings).parseBytesTry(b).toOption) + .flatMap(b => ErgoStateContextSerializer(settings).parseBytesTry(b).toOption) .getOrElse { log.warn("Can't read blockchain parameters from database") - ErgoStateContext.empty(constants, LaunchParameters) + ErgoStateContext.empty(settings, LaunchParameters) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index a67bd1c345..b86b4fc6dc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -89,8 +89,9 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { */ def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height, - expectedRootHash: Array[Byte]): Try[Array[Byte]] = { - pullFrom.dumpSnapshot(store, ManifestSerializer.MainnetManifestDepth, expectedRootHash).map { manifestId => + expectedRootHash: Array[Byte], + manifestDepth: Byte = ManifestSerializer.MainnetManifestDepth): Try[Array[Byte]] = { + pullFrom.dumpSnapshot(store, manifestDepth, expectedRootHash).map { manifestId => val si = readSnapshotsInfo.withNewManifest(height, Digest32 @@ manifestId) writeSnapshotsInfo(si) manifestId @@ -117,10 +118,9 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { object SnapshotsDb { - /** - * Open or init snapshots database in given folder - */ - def create(dir: String): SnapshotsDb = { + // internal method to open or init snapshots database in given folder + // private[nodeView] to use it in tests also + private[nodeView] def create(dir: String): SnapshotsDb = { val store = LDBFactory.createKvDb(dir) new SnapshotsDb(store) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala b/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala deleted file mode 100644 index ba606c5efb..0000000000 --- a/src/main/scala/org/ergoplatform/nodeView/state/StateConstants.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.ergoplatform.nodeView.state - -import org.ergoplatform.settings.{ErgoSettings, VotingSettings} -import scorex.crypto.authds.ADDigest - -/** - * Constants that do not change when state version changes - * - * @param settings - node settings - */ -case class StateConstants(settings: ErgoSettings) { - - lazy val keepVersions: Int = settings.nodeSettings.keepVersions - lazy val votingSettings: VotingSettings = settings.chainSettings.voting - - lazy val genesisStateDigest: ADDigest = settings.chainSettings.genesisStateDigest -} diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index a9b9777b85..6f29ac83c9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -6,7 +6,9 @@ import org.ergoplatform.settings.Algos.HF import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging -import org.ergoplatform.settings.Constants.{MakeSnapshotEvery, timeToTakeSnapshot} +import org.ergoplatform.settings.Constants.MakeSnapshotEvery +import org.ergoplatform.settings.ErgoSettings +import scorex.core.serialization.ManifestSerializer import scala.concurrent.Future import scala.util.Try @@ -16,16 +18,18 @@ import scala.util.Try */ trait UtxoSetSnapshotPersistence extends ScorexLogging { - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings protected def persistentProver: PersistentBatchAVLProver[Digest32, HF] - private[nodeView] val snapshotsDb = SnapshotsDb.create(constants.settings) + private[nodeView] val snapshotsDb = SnapshotsDb.create(ergoSettings) // Dump current UTXO set snapshot to persistent snapshots database // private[nodeView] as used in tests also - private[nodeView] def dumpSnapshot(height: Height, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { + private[nodeView] def dumpSnapshot(height: Height, + expectedRootHash: Array[Byte], + manifestDepth: Byte = ManifestSerializer.MainnetManifestDepth): Try[Array[Byte]] = { val storage = persistentProver.storage.asInstanceOf[VersionedLDBAVLStorage] - snapshotsDb.writeSnapshot(storage, height, expectedRootHash) + snapshotsDb.writeSnapshot(storage, height, expectedRootHash, manifestDepth) } /** @@ -40,7 +44,11 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { * @param estimatedTip - estimated height of best blockchain in the network */ protected def saveSnapshotIfNeeded(height: Height, estimatedTip: Option[Height]): Unit = { - if (constants.settings.nodeSettings.areSnapshotsStored && + def timeToTakeSnapshot(height: Int): Boolean = { + height % MakeSnapshotEvery == MakeSnapshotEvery - 1 + } + + if (ergoSettings.nodeSettings.areSnapshotsStored && timeToTakeSnapshot(height) && estimatedTip.nonEmpty && estimatedTip.get - height <= MakeSnapshotEvery) { @@ -56,7 +64,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { log.info("Started work within future") val ft0 = System.currentTimeMillis() dumpSnapshot(height, expectedRootHash) - snapshotsDb.pruneSnapshots(constants.settings.nodeSettings.storingUtxoSnapshots) + snapshotsDb.pruneSnapshots(ergoSettings.nodeSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() log.info("Work within future finished in: " + (ft - ft0) + " ms.") } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 6ec953de20..0e99b323d2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.modifiers.{BlockSection, ErgoFullBlock} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.ValidationRules.{fbDigestIncorrect, fbOperationFailed} -import org.ergoplatform.settings.{Algos, Parameters} +import org.ergoplatform.settings.{Algos, ErgoSettings, Parameters} import org.ergoplatform.utils.LoggingUtil import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedModifier import scorex.core._ @@ -31,12 +31,12 @@ import scala.util.{Failure, Success, Try} * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata * @param version - current state version - * @param constants - constants, that do not change with state version changes + * @param ergoSettings - protocol and client config to to get state-related settings from */ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, override val store: LDBVersionedStore, - override protected val constants: StateConstants) + override protected val ergoSettings: ErgoSettings) extends ErgoState[UtxoState] with TransactionValidation with UtxoStateReader @@ -55,7 +55,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 case Some(hash) => val rootHash: ADDigest = ADDigest @@ hash val rollbackResult = p.rollback(rootHash).map { _ => - new UtxoState(p, version, store, constants) + new UtxoState(p, version, store, ergoSettings) } rollbackResult case None => @@ -114,12 +114,14 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 (generate: LocallyGeneratedModifier => Unit): Try[UtxoState] = mod match { case fb: ErgoFullBlock => + val keepVersions = ergoSettings.nodeSettings.keepVersions + // avoid storing versioned information in the database when block being processed is behind // blockchain tip by `keepVersions` blocks at least // we store `keepVersions` diffs in the database if chain tip is not known yet - if (fb.height >= estimatedTip.getOrElse(0) - constants.keepVersions) { - if (store.getKeepVersions < constants.keepVersions) { - store.setKeepVersions(constants.keepVersions) + if (fb.height >= estimatedTip.getOrElse(0) - keepVersions) { + if (store.getKeepVersions < keepVersions) { + store.setKeepVersions(keepVersions) } } else { if (store.getKeepVersions > 0) { @@ -201,7 +203,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 log.info(s"Valid modifier with header ${fb.header.encodedId} and emission box " + s"${emissionBox.map(e => Algos.encode(e.id))} applied to UtxoState at height ${fb.header.height}") saveSnapshotIfNeeded(fb.height, estimatedTip) - new UtxoState(persistentProver, idToVersion(fb.id), store, constants) + new UtxoState(persistentProver, idToVersion(fb.id), store, ergoSettings) } } stateTry.recoverWith[UtxoState] { case e => @@ -218,7 +220,7 @@ class UtxoState(override val persistentProver: PersistentBatchAVLProver[Digest32 //todo: update state context with headers (when snapshot downloading is done), so //todo: application of the first full block after the snapshot should have correct state context //todo: (in particular, "lastHeaders" field of it) - Success(new UtxoState(persistentProver, idToVersion(h.id), this.store, constants)) + Success(new UtxoState(persistentProver, idToVersion(h.id), this.store, ergoSettings)) case a: Any => log.error(s"Unhandled unknown modifier: $a") @@ -284,8 +286,8 @@ object UtxoState { Array(idStateDigestIdxElem, stateDigestIdIdxElem, bestVersion, eb, cb) } - def create(dir: File, constants: StateConstants): UtxoState = { - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + def create(dir: File, settings: ErgoSettings): UtxoState = { + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) .getOrElse(ErgoState.genesisStateVersion) val persistentProver: PersistentBatchAVLProver[Digest32, HF] = { @@ -293,7 +295,7 @@ object UtxoState { val storage = new VersionedLDBAVLStorage(store) PersistentBatchAVLProver.create(bp, storage).get } - new UtxoState(persistentProver, version, store, constants) + new UtxoState(persistentProver, version, store, settings) } /** @@ -303,16 +305,16 @@ object UtxoState { def fromBoxHolder(bh: BoxHolder, currentEmissionBoxOpt: Option[ErgoBox], dir: File, - constants: StateConstants, + settings: ErgoSettings, parameters: Parameters): UtxoState = { val p = new BatchAVLProver[Digest32, HF](keyLength = 32, valueLengthOpt = None) bh.sortedBoxes.foreach { b => p.performOneOperation(Insert(b.id, ADValue @@ b.bytes)).ensuring(_.isSuccess) } - val store = new LDBVersionedStore(dir, initialKeepVersions = constants.keepVersions) + val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) - val defaultStateContext = ErgoStateContext.empty(constants, parameters) + val defaultStateContext = ErgoStateContext.empty(settings, parameters) val storage = new VersionedLDBAVLStorage(store) val persistentProver = PersistentBatchAVLProver.create( p, @@ -321,7 +323,7 @@ object UtxoState { paranoidChecks = true ).get - new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, constants) + new UtxoState(persistentProver, ErgoState.genesisStateVersion, store, settings) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 75bc36d54a..09a7d67838 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -5,7 +5,7 @@ import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.settings.Algos +import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import org.ergoplatform.wallet.interpreter.ErgoInterpreter @@ -22,8 +22,11 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi protected implicit val hf: HF = Algos.hash - protected def constants: StateConstants + protected def ergoSettings: ErgoSettings + /** + * Versioned database where UTXO set and its authenticating AVL+ tree are stored + */ protected lazy val storage = new VersionedLDBAVLStorage(store) protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] @@ -81,13 +84,13 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi */ protected[state] def extractEmissionBox(fb: ErgoFullBlock): Option[ErgoBox] = { def hasEmissionBox(tx: ErgoTransaction): Boolean = - if(fb.height > constants.settings.chainSettings.reemission.activationHeight) { + if(fb.height > ergoSettings.chainSettings.reemission.activationHeight) { // after EIP-27 we search for emission box NFT for efficiency's sake tx.outputs.size == 2 && !tx.outputs.head.additionalTokens.isEmpty && - java.util.Arrays.equals(tx.outputs.head.additionalTokens(0)._1, constants.settings.chainSettings.reemission.emissionNftIdBytes) + java.util.Arrays.equals(tx.outputs.head.additionalTokens(0)._1, ergoSettings.chainSettings.reemission.emissionNftIdBytes) } else { - tx.outputs.head.ergoTree == constants.settings.chainSettings.monetary.emissionBoxProposition + tx.outputs.head.ergoTree == ergoSettings.chainSettings.monetary.emissionBoxProposition } def fullSearch(fb: ErgoFullBlock): Option[ErgoBox] = { @@ -166,7 +169,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * Useful when checking mempool transactions. */ def withUnconfirmedTransactions(unconfirmedTxs: Seq[UnconfirmedTransaction]): UtxoState = { - new UtxoState(persistentProver, version, store, constants) { + new UtxoState(persistentProver, version, store, ergoSettings) { lazy val createdBoxes: Seq[ErgoBox] = unconfirmedTxs.map(_.transaction).flatMap(_.outputs) override def boxById(id: ADKey): Option[ErgoBox] = { @@ -180,7 +183,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * Useful when checking mempool transactions. */ def withTransactions(transactions: Seq[ErgoTransaction]): UtxoState = { - new UtxoState(persistentProver, version, store, constants) { + new UtxoState(persistentProver, version, store, ergoSettings) { lazy val createdBoxes: Seq[ErgoBox] = transactions.flatMap(_.outputs) override def boxById(id: ADKey): Option[ErgoBox] = { @@ -194,4 +197,5 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi * Useful when checking mempool transactions. */ def withMempool(mp: ErgoMemPoolReader): UtxoState = withUnconfirmedTransactions(mp.getAll) + } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index a4b8e9199e..18b422095a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -21,6 +21,7 @@ import org.ergoplatform.wallet.interpreter.TransactionHintsBag import org.ergoplatform.{ErgoAddressEncoder, ErgoApp, ErgoBox, GlobalConstants, P2PKAddress} import scorex.core.VersionTag import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedMempool, ChangedState} +import org.ergoplatform.wallet.secrets.DerivationPath import scorex.core.utils.ScorexEncoding import scorex.util.{ModifierId, ScorexLogging} import sigmastate.Values.SigmaBoolean @@ -152,6 +153,12 @@ class ErgoWalletActor(settings: ErgoSettings, case ReadPublicKeys(from, until) => sender() ! state.walletVars.publicKeyAddresses.slice(from, until) + case ReadExtendedPublicKeys() => + sender() ! state.storage.readAllKeys() + + case GetPrivateKeyFromPath(path: DerivationPath) => + sender() ! ergoWalletService.getPrivateKeyFromPath(state, path) + case GetMiningPubKey => state.walletVars.trackedPubKeys.headOption match { case Some(pk) => @@ -619,6 +626,16 @@ object ErgoWalletActor extends ScorexLogging { */ 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 */ diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala index 4fdcba607f..7e10887d71 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala @@ -17,9 +17,11 @@ import org.ergoplatform.wallet.boxes.ChainStatus import org.ergoplatform.wallet.boxes.ChainStatus.{OffChain, OnChain} import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.interpreter.TransactionHintsBag +import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedPublicKey} import scorex.core.NodeViewComponent import scorex.util.ModifierId import sigmastate.Values.SigmaBoolean +import sigmastate.basics.DLogProtocol.DLogProverInput import scala.concurrent.Future import scala.util.Try @@ -72,6 +74,12 @@ trait ErgoWalletReader extends NodeViewComponent { def publicKeys(from: Int, to: Int): Future[Seq[P2PKAddress]] = (walletActor ? ReadPublicKeys(from, to)).mapTo[Seq[P2PKAddress]] + def allExtendedPublicKeys(): Future[Seq[ExtendedPublicKey]] = + (walletActor ? ReadExtendedPublicKeys()).mapTo[Seq[ExtendedPublicKey]] + + def getPrivateKeyFromPath(path: DerivationPath): Future[Try[DLogProverInput]] = + (walletActor ? GetPrivateKeyFromPath(path)).mapTo[Try[DLogProverInput]] + def walletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean): Future[Seq[WalletBox]] = (walletActor ? GetWalletBoxes(unspentOnly, considerUnconfirmed)).mapTo[Seq[WalletBox]] diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala index 27a2916667..33885b6fcb 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala @@ -23,6 +23,7 @@ import org.ergoplatform.wallet.utils.FileUtils import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId} import sigmastate.Values.SigmaBoolean +import sigmastate.basics.DLogProtocol.DLogProverInput import java.io.FileNotFoundException import scala.util.{Failure, Success, Try} @@ -160,6 +161,14 @@ trait ErgoWalletService { */ def deriveKeyFromPath(state: ErgoWalletState, encodedPath: String, addrEncoder: ErgoAddressEncoder): Try[(P2PKAddress, ErgoWalletState)] + /** + * Get the secret key for a give derivation path. + * @param state current wallet state + * @param path derivation path from the master key + * @return Try of private key + */ + def getPrivateKeyFromPath(state: ErgoWalletState, path: DerivationPath): Try[DLogProverInput] + /** * Derive next key from master key * @param state current wallet state @@ -544,6 +553,18 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg Failure(new Exception("Unable to derive key from path, wallet is not initialized")) } + override def getPrivateKeyFromPath(state: ErgoWalletState, path: DerivationPath): Try[DLogProverInput] = + state.secretStorageOpt match { + case Some(secretStorage) if !secretStorage.isLocked => + val rootSecret = secretStorage.secret.get // unlocked means Some(secret) + Success(rootSecret.derive(path.toPrivateBranch).privateInput) + case Some(_) => + Failure(new Exception("Unable to derive key from path, wallet is locked")) + case None => + Failure(new Exception("Unable to derive key from path, wallet is not initialized")) + } + + override def deriveNextKey(state: ErgoWalletState, usePreEip3Derivation: Boolean): Try[(DeriveNextKeyResult, ErgoWalletState)] = state.secretStorageOpt match { case Some(secretStorage) if !secretStorage.isLocked => 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 ca5ebbec3b..91b2af73cc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -6,7 +6,6 @@ import org.ergoplatform.settings.Constants import scorex.core.serialization.ErgoSerializer import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} - import scala.collection.mutable import scorex.util.Extensions._ diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index afaa82cef5..4346ab929e 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -70,11 +70,8 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 - val MakeSnapshotEvery = 1024 // test value, switch to 51200 after testing - - def timeToTakeSnapshot(height: Int): Boolean = { - height % MakeSnapshotEvery == MakeSnapshotEvery - 1 - } + // UTXO set snapshot to be taken every this number of blocks + val MakeSnapshotEvery = 51200 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. diff --git a/src/main/scala/scorex/core/NodeViewModifier.scala b/src/main/scala/scorex/core/NodeViewModifier.scala index 55a6faede7..c11288a0f6 100644 --- a/src/main/scala/scorex/core/NodeViewModifier.scala +++ b/src/main/scala/scorex/core/NodeViewModifier.scala @@ -5,7 +5,6 @@ import org.ergoplatform.modifiers.mempool.ErgoTransaction import scorex.core.serialization.BytesSerializable import scorex.core.utils.ScorexEncoding - sealed trait NodeViewModifier extends BytesSerializable with ScorexEncoding {self => val modifierTypeId: NetworkObjectTypeId.Value diff --git a/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala index 4b5e50c6b9..ed0e328ed4 100644 --- a/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/BlocksApiRouteSpec.scala @@ -33,7 +33,7 @@ class BlocksApiRouteSpec extends AnyFlatSpec } it should "post block correctly" in { - val (st, bh) = createUtxoState(parameters) + val (st, bh) = createUtxoState(settings) val block: ErgoFullBlock = validFullBlock(parentOpt = None, st, bh) val blockJson: UniversalEntity = HttpEntity(block.asJson.toString).withContentType(ContentTypes.`application/json`) Post(prefix, blockJson) ~> route ~> check { diff --git a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala index 09974da381..5096dec09d 100644 --- a/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala +++ b/src/test/scala/org/ergoplatform/local/MempoolAuditorSpec.scala @@ -41,10 +41,10 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH val testProbe = new TestProbe(actorSystem) actorSystem.eventStream.subscribe(testProbe.ref, newTx) - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settingsToTest) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, settingsToTest, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod } .get @@ -95,7 +95,7 @@ class MempoolAuditorSpec extends AnyFlatSpec with NodeViewTestOps with ErgoTestH it should "rebroadcast transactions correctly" in { - val (us0, bh0) = createUtxoState(parameters) + val (us0, bh0) = createUtxoState(settingsToTest) val (txs0, bh1) = validTransactionsFromBoxHolder(bh0) val b1 = validFullBlock(None, us0, txs0) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index a8c6db6a3f..74e16b396a 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -51,7 +51,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { } property("collect reward from emission box only") { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 us.emissionBoxOpt should not be None val expectedReward = emission.minersRewardAtHeight(us.stateContext.currentHeight) @@ -240,7 +240,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { } property("collect reward from both emission box and fees") { - val (us, _) = createUtxoState(parameters) + val (us, _) = createUtxoState(settings) us.emissionBoxOpt should not be None val expectedReward = emission.minersRewardAtHeight(us.stateContext.currentHeight) diff --git a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala index 679afce19b..c867070220 100644 --- a/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala +++ b/src/test/scala/org/ergoplatform/nodeView/NodeViewSynchronizerTests.scala @@ -279,7 +279,7 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec val height = 1 usr.applyModifier(mod, Some(height))(_ => ()) - val manifestId = usr.dumpSnapshot(height).get + val manifestId = usr.dumpSnapshot(height, usr.rootDigest.dropRight(1)).get // Then send message to request it node ! Message[ManifestId](GetManifestSpec, Left(manifestId), Option(peer)) @@ -311,8 +311,9 @@ trait NodeViewSynchronizerTests[ST <: ErgoState[ST]] extends AnyPropSpec usr.applyModifier(mod, Some(height))(_ => ()) - val serializer = ManifestSerializer.defaultSerializer - usr.dumpSnapshot(height) + val manifestDepth = 2.toByte + val serializer = new ManifestSerializer(manifestDepth) + usr.dumpSnapshot(height, usr.rootDigest.dropRight(1), manifestDepth) val manifestId = usr.snapshotsDb.readSnapshotsInfo.availableManifests.apply(height) val manifestBytes = usr.snapshotsDb.readManifestBytes(manifestId).get val manifest = serializer.parseBytes(manifestBytes) diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 9072f229cf..3099358f39 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor -import org.ergoplatform.nodeView.state.{StateConstants, UtxoState} +import org.ergoplatform.nodeView.state.UtxoState import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers import scorex.core.VersionTag @@ -29,7 +29,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val snapshotHeight = 1 val serializer = ManifestSerializer.defaultSerializer - us.dumpSnapshot(snapshotHeight) + us.dumpSnapshot(snapshotHeight, us.rootDigest.dropRight(1)) val manifestId = us.snapshotsDb.readSnapshotsInfo.availableManifests.apply(snapshotHeight) val manifestBytes = us.snapshotsDb.readManifestBytes(manifestId).get val manifest = serializer.parseBytes(manifestBytes) @@ -65,7 +65,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { restoredProver.unauthenticatedLookup(box.id).isDefined shouldBe true } restoredProver.checkTree(postProof = false) - val restoredState = new UtxoState(restoredProver, version = VersionTag @@ blockId, store, StateConstants(settings)) + val restoredState = new UtxoState(restoredProver, version = VersionTag @@ blockId, store, settings) bh.sortedBoxes.foreach { box => restoredState.boxById(box.id).isDefined shouldBe true } diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala index 935b5713c5..cb024dd9bb 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyNonADHistorySpecification.scala @@ -156,7 +156,7 @@ class VerifyNonADHistorySpecification extends HistoryTestHelpers { } property("append header to genesis - 2") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val block = validFullBlock(None, us, bh) 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 1cc3e8e437..d97d4f7384 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -13,7 +13,7 @@ import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransact import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UtxoState, UtxoStateReader} +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoState, UtxoStateReader} import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} import scorex.crypto.hash.Digest32 @@ -251,7 +251,7 @@ object ChainGenerator extends ErgoTestHelpers { def generate(length: Int, dir: File)(history: ErgoHistory): Unit = { val stateDir = new File(s"${dir.getAbsolutePath}/state") stateDir.mkdirs() - val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, StateConstants(initSettings)) + 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) diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala index c68d279f50..d08408e7cf 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ErgoMemPoolSpec.scala @@ -19,9 +19,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec with ScalaCheckPropertyChecks { it should "accept valid transaction" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) val pool0 = ErgoMemPool.empty(settings) val poolAfter = txs.foldLeft(pool0) { case (pool, tx) => @@ -42,9 +42,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "respect given sorting order" in { implicit val ms = settings.chainSettings.monetary - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val inputBox = wus.takeBoxes(1).head val feeOut = new ErgoBoxCandidate(inputBox.value, feeProp, creationHeight = 0) val tx = ErgoTransaction( @@ -77,9 +77,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline already contained transaction" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -93,9 +93,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "reject double-spending transaction if it is paying no more than one already sitting in the pool" in { forAll(smallPositiveInt, smallPositiveInt) { case (n1, n2) => whenever(n1 != n2) { - val (us, bh) = createUtxoState(extendedParameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, extendedParameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, extendedParameters).applyModifier(genesis)(_ => ()).get val feeProp = settings.chainSettings.monetary.feeProposition val inputBox = wus.takeBoxes(100).collectFirst{ @@ -141,7 +141,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline transactions invalidated earlier" in { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 var pool = ErgoMemPool.empty(settings) forAll(invalidBlockTransactionsGen) { blockTransactions => val unconfirmedTxs = blockTransactions.txs.map(tx => UnconfirmedTransaction(tx, None)) @@ -152,9 +152,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "decline transactions not meeting min fee" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus) val unconfirmedTxs = txs.map(tx => UnconfirmedTransaction(tx, None)) @@ -176,7 +176,7 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "invalidate or reject invalid transaction" in { - val us = createUtxoState(parameters)._1 + val us = createUtxoState(settings)._1 val pool = ErgoMemPool.empty(settings) forAll(invalidBlockTransactionsGen) { blockTransactions => blockTransactions.txs.forall{tx => @@ -214,9 +214,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "Accept output of pooled transactions" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var pool = ErgoMemPool.empty(settings) txs.foreach { tx => @@ -234,9 +234,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "consider families for replacement policy" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -269,9 +269,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "correctly remove transaction from pool and rebuild families" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var allTxs = txs val family_depth = 10 @@ -302,9 +302,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec it should "return results take / getAll / getAllPrioritized sorted by priority" in { val feeProp = settings.chainSettings.monetary.feeProposition - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) val family_depth = 10 val limitedPoolSettings = settings.copy(nodeSettings = settings.nodeSettings.copy(mempoolCapacity = (family_depth + 1) * txs.size)) @@ -344,9 +344,9 @@ class ErgoMemPoolSpec extends AnyFlatSpec } it should "add removed transaction to mempool statistics" in { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(None, us, bh) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wus = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get var txs = validTransactionsFromUtxoState(wus).map(tx => UnconfirmedTransaction(tx, None)) var allTxs = txs val family_depth = 10 diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index 96c8aad776..36940f420f 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -67,7 +67,7 @@ class ScriptsSpec extends ErgoPropertyTest { private def applyBlockSpendingScript(script: ErgoTree): Try[UtxoState] = { val scriptBox = ergoBoxGen(script, heightGen = 0).sample.get val bh = BoxHolder(Seq(fixedBox, scriptBox)) - val us = UtxoState.fromBoxHolder(bh, None, createTempDir, stateConstants, parameters) + val us = UtxoState.fromBoxHolder(bh, None, createTempDir, settings, parameters) bh.boxes.map(b => us.boxById(b._2.id) shouldBe Some(b._2)) val tx = validTransactionsFromBoxHolder(bh, new RandomWrapper(Some(1)), 201)._1 tx.size shouldBe 1 diff --git a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala index 4d670365d1..7cf71def1a 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/DigestStateSpecification.scala @@ -21,19 +21,19 @@ class DigestStateSpecification extends ErgoPropertyTest { val fb = validFullBlock(parentOpt = None, us, bh) val dir2 = createTempDir - val ds = DigestState.create(Some(us.version), Some(us.rootDigest), dir2, stateConstants) + val ds = DigestState.create(Some(us.version), Some(us.rootDigest), dir2, settings) ds.applyModifier(fb, None)(_ => ()) shouldBe 'success ds.close() - val state = DigestState.create(None, None, dir2, stateConstants) + val state = DigestState.create(None, None, dir2, settings) state.version shouldEqual fb.header.id state.rootDigest shouldEqual fb.header.stateRoot } } property("validate() - valid block") { - var (us, bh) = createUtxoState(parameters) - var ds = createDigestState(us.version, us.rootDigest, parameters) + var (us, bh) = createUtxoState(settings) + var ds = createDigestState(us.version, us.rootDigest) var parentOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -48,7 +48,7 @@ class DigestStateSpecification extends ErgoPropertyTest { property("validate() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createDigestState(emptyVersion, emptyAdDigest, parameters) + val state = createDigestState(emptyVersion, emptyAdDigest) state.validate(b).isFailure shouldBe true } } @@ -61,14 +61,14 @@ class DigestStateSpecification extends ErgoPropertyTest { val block = validFullBlock(parentOpt = None, us, bh) block.blockTransactions.transactions.exists(_.dataInputs.nonEmpty) shouldBe true - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) ds.applyModifier(block, None)(_ => ()) shouldBe 'success } } property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createDigestState(emptyVersion, emptyAdDigest, parameters) + val state = createDigestState(emptyVersion, emptyAdDigest) state.applyModifier(b, None)(_ => ()).isFailure shouldBe true } } @@ -80,7 +80,7 @@ class DigestStateSpecification extends ErgoPropertyTest { val block = validFullBlock(parentOpt = None, us, bh) - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) ds.rollbackVersions.size shouldEqual 1 @@ -104,7 +104,7 @@ class DigestStateSpecification extends ErgoPropertyTest { property("validateTransactions() - dataInputs") { forAll(boxesHolderGen) { bh => val us = createUtxoState(bh, parameters) - val ds = createDigestState(us.version, us.rootDigest, parameters) + val ds = createDigestState(us.version, us.rootDigest) // generate 2 independent transactions spending state boxes only val headTx = validTransactionsFromBoxes(1, bh.boxes.take(10).values.toSeq, new RandomWrapper())._1.head diff --git a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala index 60d49ff68a..059b4e0d1c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/ErgoStateSpecification.scala @@ -20,7 +20,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { property("applyModifier() - double spending") { forAll(boxesHolderGen, Gen.choose(1: Byte, 2: Byte)) { case (bh, version) => val us = createUtxoState(bh, parameters) - val ds = createDigestState(bytesToVersion(Array.fill(32)(100: Byte)), us.rootDigest, parameters) + val ds = createDigestState(bytesToVersion(Array.fill(32)(100: Byte)), us.rootDigest) val validBlock = validFullBlock(None, us, bh) val dsTxs = validBlock.transactions ++ validBlock.transactions @@ -51,8 +51,8 @@ class ErgoStateSpecification extends ErgoPropertyTest { s1.genesisStateDigest shouldBe s2.genesisStateDigest } - var (us, bh) = createUtxoState(parameters) - var ds = createDigestState(us.version, us.rootDigest, parameters) + var (us, bh) = createUtxoState(settings) + var ds = createDigestState(us.version, us.rootDigest) var lastBlocks: Seq[ErgoFullBlock] = Seq() forAll { seed: Int => val blBh = validFullBlockWithBoxHolder(lastBlocks.headOption, us, bh, new RandomWrapper(Some(seed))) @@ -68,13 +68,13 @@ class ErgoStateSpecification extends ErgoPropertyTest { property("generateGenesisUtxoState & generateGenesisDigestState are compliant") { val settings = ErgoSettings.read(Args.empty) val dir = createTempDir - val rootHash = createUtxoState(parameters)._1.rootDigest + val rootHash = createUtxoState(settings)._1.rootDigest val expectedRootHash = ErgoState.generateGenesisDigestState(dir, settings).rootDigest rootHash shouldBe expectedRootHash } property("ErgoState.boxChanges() should generate operations in the same order") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) var parentOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -92,7 +92,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { } property("ErgoState.boxChanges() double spend attempt") { - val (_, bh) = createUtxoState(parameters) + val (_, bh) = createUtxoState(settings) val emissionBox = genesisBoxes.head forAll { seed: Int => @@ -116,7 +116,7 @@ class ErgoStateSpecification extends ErgoPropertyTest { } property("ErgoState.stateChanges()") { - val bh = createUtxoState(parameters)._2 + val bh = createUtxoState(settings)._2 val emissionBox = genesisBoxes.head forAll { seed: Int => diff --git a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala index 205caa634a..a0cd911b10 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/UtxoStateSpecification.scala @@ -35,7 +35,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera private val emptyModifierId: ModifierId = bytesToId(Array.fill(32)(0.toByte)) property("Founders box workflow") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) var foundersBox = genesisBoxes.last var lastBlock = validFullBlock(parentOpt = None, us, bh) us = us.applyModifier(lastBlock, None)(_ => ()).get @@ -60,7 +60,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("Founders should be able to spend genesis founders box") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) val foundersBox = genesisBoxes.last var height: Int = ErgoHistory.GenesisHeight @@ -111,7 +111,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("Correct genesis state") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val boxes = bh.boxes.values.toList boxes.size shouldBe 3 @@ -135,7 +135,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("extractEmissionBox() should extract correct box") { - var (us, bh) = createUtxoState(parameters) + var (us, bh) = createUtxoState(settings) us.emissionBoxOpt should not be None var lastBlockOpt: Option[ErgoFullBlock] = None forAll { seed: Int => @@ -158,7 +158,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera } property("proofsForTransactions") { - var (us: UtxoState, bh) = createUtxoState(parameters) + var (us: UtxoState, bh) = createUtxoState(settings) var height: Int = ErgoHistory.GenesisHeight forAll(defaultHeaderGen) { header => val t = validTransactionsFromBoxHolder(bh, new RandomWrapper(Some(height))) @@ -435,13 +435,13 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("applyModifier() - invalid block") { forAll(invalidErgoFullBlockGen) { b => - val state = createUtxoState(parameters)._1 + val state = createUtxoState(settings)._1 state.applyModifier(b, None)(_ => ()).isFailure shouldBe true } } property("applyModifier() - valid full block after invalid one") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val validBlock = validFullBlock(parentOpt = None, us, bh) //Different state @@ -460,20 +460,20 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera property("2 forks switching") { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wusAfterGenesis = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get val chain1block1 = validFullBlock(Some(genesis), wusAfterGenesis) val wusChain1Block1 = wusAfterGenesis.applyModifier(chain1block1)(_ => ()).get val chain1block2 = validFullBlock(Some(chain1block1), wusChain1Block1) - val (us2, bh2) = createUtxoState(parameters) - val wus2AfterGenesis = WrappedUtxoState(us2, bh2, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val (us2, bh2) = createUtxoState(settings) + val wus2AfterGenesis = WrappedUtxoState(us2, bh2, settings, parameters).applyModifier(genesis)(_ => ()).get val chain2block1 = validFullBlock(Some(genesis), wus2AfterGenesis) val wusChain2Block1 = wus2AfterGenesis.applyModifier(chain2block1)(_ => ()).get val chain2block2 = validFullBlock(Some(chain2block1), wusChain2Block1) - var (state, _) = createUtxoState(parameters) + var (state, _) = createUtxoState(settings) state = state.applyModifier(genesis, None)(_ => ()).get state = state.applyModifier(chain1block1, None)(_ => ()).get @@ -494,7 +494,7 @@ class UtxoStateSpecification extends ErgoPropertyTest with ErgoTransactionGenera val us = createUtxoState(bh, parameters) bh.sortedBoxes.foreach(box => us.boxById(box.id) should not be None) val genesis = validFullBlock(parentOpt = None, us, bh) - val wusAfterGenesis = WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis)(_ => ()).get + val wusAfterGenesis = WrappedUtxoState(us, bh, settings, parameters).applyModifier(genesis)(_ => ()).get wusAfterGenesis.rootDigest shouldEqual genesis.header.stateRoot val (finalState: WrappedUtxoState, chain: Seq[ErgoFullBlock]) = (0 until depth) 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 fbb2cf883b..d747a4b5be 100644 --- a/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala +++ b/src/test/scala/org/ergoplatform/nodeView/state/wrapped/WrappedUtxoState.scala @@ -22,8 +22,8 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], override val version: VersionTag, store: LDBVersionedStore, val versionedBoxHolder: VersionedInMemoryBoxHolder, - constants: StateConstants) - extends UtxoState(prover, version, store, constants) { + settings: ErgoSettings) + extends UtxoState(prover, version, store, settings) { def size: Int = versionedBoxHolder.size @@ -32,7 +32,7 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], override def rollbackTo(version: VersionTag): Try[WrappedUtxoState] = super.rollbackTo(version) match { case Success(us) => val updHolder = versionedBoxHolder.rollback(us.version) - Success(new WrappedUtxoState(us.persistentProver, version, us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, version, us.store, updHolder, settings)) case Failure(e) => Failure(e) } @@ -49,10 +49,10 @@ class WrappedUtxoState(prover: PersistentBatchAVLProver[Digest32, HF], us.version, changes.toRemove.map(_.key).map(ByteArrayWrapper.apply), changes.toAppend.map(_.value).map(ErgoBoxSerializer.parseBytes)) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, settings)) case _ => val updHolder = versionedBoxHolder.applyChanges(us.version, Seq(), Seq()) - Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, constants)) + Success(new WrappedUtxoState(us.persistentProver, idToVersion(mod.id), us.store, updHolder, settings)) } case Failure(e) => Failure(e) } @@ -65,13 +65,12 @@ object WrappedUtxoState { nodeViewHolderRef: Option[ActorRef], parameters: Parameters, settings: ErgoSettings): WrappedUtxoState = { - val constants = StateConstants(settings) - val emissionBox = ErgoState.genesisBoxes(constants.settings.chainSettings).headOption - val us = UtxoState.fromBoxHolder(boxHolder, emissionBox, dir, constants, parameters) - WrappedUtxoState(us, boxHolder, constants, parameters) + val emissionBox = ErgoState.genesisBoxes(settings.chainSettings).headOption + val us = UtxoState.fromBoxHolder(boxHolder, emissionBox, dir, settings, parameters) + WrappedUtxoState(us, boxHolder, settings, parameters) } - def apply(us: UtxoState, boxHolder: BoxHolder, constants: StateConstants, parameters: Parameters): WrappedUtxoState = { + def apply(us: UtxoState, boxHolder: BoxHolder, settings: ErgoSettings, parameters: Parameters): WrappedUtxoState = { val boxes = boxHolder.boxes val version = us.version @@ -81,6 +80,6 @@ object WrappedUtxoState { Map(version -> (Seq() -> boxHolder.sortedBoxes.toSeq)) ) - new WrappedUtxoState(us.persistentProver, ErgoState.genesisStateVersion, us.store, vbh, constants) + new WrappedUtxoState(us.persistentProver, ErgoState.genesisStateVersion, us.store, vbh, settings) } } diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala index f3b89434a5..83e9243ad9 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/ErgoNodeViewHolderSpec.scala @@ -22,7 +22,7 @@ import scorex.util.{ModifierId, bytesToId} class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers with NodeViewTestOps with NoShrink { private val t0 = TestCase("check chain is healthy") { fixture => - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(settings) val block = validFullBlock(None, us, bh) val history = generateHistory(true, StateType.Utxo, false, 2) @@ -51,7 +51,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t3 = TestCase("apply valid block header") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -71,7 +71,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t3a = TestCase("do not apply block headers in invalid order") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val parentBlock = validFullBlock(None, us, bh) val block = validFullBlock(Some(parentBlock), us, bh) @@ -96,7 +96,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t4 = TestCase("apply valid block as genesis") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) subscribeEvents(classOf[SyntacticallySuccessfulModifier]) @@ -116,10 +116,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t5 = TestCase("apply full blocks after genesis") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get applyBlock(genesis) shouldBe 'success @@ -138,7 +138,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t6 = TestCase("add transaction to memory pool") { fixture => import fixture._ if (stateType == Utxo) { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) applyBlock(genesis) shouldBe 'success @@ -155,10 +155,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t7 = TestCase("apply statefully invalid full block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get // TODO looks like another bug is still present here, see https://github.com/ergoplatform/ergo/issues/309 @@ -210,10 +210,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t8 = TestCase("switching for a better chain") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get @@ -247,7 +247,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t9 = TestCase("UTXO state should generate adProofs and put them in history") { fixture => import fixture._ if (stateType == StateType.Utxo) { - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) nodeViewHolderRef ! LocallyGeneratedModifier(genesis.header) @@ -261,10 +261,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t10 = TestCase("NodeViewHolder start from inconsistent state") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get applyBlock(genesis) shouldBe 'success @@ -284,10 +284,10 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t11 = TestCase("apply payload in incorrect order (excluding extension)") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val genesis = validFullBlock(parentOpt = None, us, bh) val wusAfterGenesis = - WrappedUtxoState(us, bh, stateConstants, parameters).applyModifier(genesis) { mod => + WrappedUtxoState(us, bh, fixture.settings, parameters).applyModifier(genesis) { mod => nodeViewHolderRef ! mod }.get @@ -314,7 +314,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t12 = TestCase("Do not apply txs with wrong header id") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None getHistoryHeight shouldBe ErgoHistory.EmptyHistoryHeight @@ -367,7 +367,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t13 = TestCase("Do not apply wrong adProofs") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -400,7 +400,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi "it's not equal to genesisId from config") { fixture => import fixture._ updateConfig(genesisIdConfig(modifierIdGen.sample)) - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) getBestHeaderOpt shouldBe None @@ -419,7 +419,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t15 = TestCase("apply genesis block header if it's equal to genesisId from config") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val block = validFullBlock(None, us, bh) updateConfig(genesisIdConfig(Some(block.header.id))) @@ -439,8 +439,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t16 = TestCase("apply forks that include genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) - val wusGenesis = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wusGenesis = WrappedUtxoState(us, bh, fixture.settings, parameters) val chain1block1 = validFullBlock(parentOpt = None, us, bh) @@ -469,7 +469,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t17 = TestCase("apply invalid genesis header") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val header = validFullBlock(None, us, bh).header.copy(parentId = bytesToId(Array.fill(32)(9: Byte))) getBestHeaderOpt shouldBe None @@ -488,7 +488,7 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t18 = TestCase("apply syntactically invalid genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) + val (us, bh) = createUtxoState(fixture.settings) val validBlock = validFullBlock(parentOpt = None, us, bh) val invalidBlock = validBlock.copy(header = validBlock.header.copy(parentId = bytesToId(Array.fill(32)(9: Byte)))) @@ -501,8 +501,8 @@ class ErgoNodeViewHolderSpec extends ErgoPropertyTest with HistoryTestHelpers wi private val t19 = TestCase("apply semantically invalid genesis block") { fixture => import fixture._ - val (us, bh) = createUtxoState(parameters) - val wusGenesis = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wusGenesis = WrappedUtxoState(us, bh, fixture.settings, parameters) val invalidBlock = generateInvalidFullBlock(None, wusGenesis) diff --git a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala index 7087481567..0dd39c88c8 100644 --- a/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/viewholder/PrunedNodeViewHolderSpec.scala @@ -48,8 +48,8 @@ class PrunedNodeViewHolderSpec extends ErgoPropertyTest with NodeViewTestOps wit private def testCode(fixture: NodeViewFixture, toSkip: Int, totalBlocks: Int = 20) = { import fixture._ - val (us, bh) = createUtxoState(stateConstants, parameters) - val wus = WrappedUtxoState(us, bh, stateConstants, parameters) + val (us, bh) = createUtxoState(fixture.settings) + val wus = WrappedUtxoState(us, bh, fixture.settings, parameters) val fullChain = genFullChain(wus, totalBlocks, nodeViewHolderRef) diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala index ba5a5015cb..1f5cbfabf0 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala @@ -19,7 +19,7 @@ import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, ReplaceCompactCollectBo import org.ergoplatform.wallet.crypto.ErgoSignature import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.ExtendedSecretKey +import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.scalacheck.Gen import org.scalatest.BeforeAndAfterAll import scorex.db.{LDBKVStore, LDBVersionedStore} @@ -320,4 +320,25 @@ class ErgoWalletServiceSpec } } } + + property("it should derive private key correctly") { + withVersionedStore(2) { versionedStore => + withStore { store => + + val pass = SecretString.create(Random.nextString(10)) + val mnemonic = "edge talent poet tortoise trumpet dose" + + val walletService = new ErgoWalletServiceImpl(settings) + val ws1 = initialState(store, versionedStore) + val ws2 = walletService.initWallet(ws1, settings, pass, Some(SecretString.create(mnemonic))).get._2 + ws2.secretStorageOpt.get.unlock(pass) + + val path = DerivationPath.fromEncoded("m/44/1/1/0/0").get + val sk = ws2.secretStorageOpt.get.secret.get + val pk = sk.derive(path).publicKey + + walletService.getPrivateKeyFromPath(ws2, pk.path).get.w shouldBe sk.derive(path).privateInput.w + } + } + } } diff --git a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala index 4b8b3685ac..74955421b2 100644 --- a/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala +++ b/src/test/scala/org/ergoplatform/sanity/ErgoSanityDigest.scala @@ -28,7 +28,7 @@ class ErgoSanityDigest extends ErgoSanity[DIGEST_ST] { override val stateGen: Gen[WrappedDigestState] = { boxesHolderGen.map(WrappedUtxoState(_, createTempDir, None, parameters, settings)).map { wus => - val digestState = DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, stateConstants) + val digestState = DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, settings) new WrappedDigestState(digestState, wus, settings) } } diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 48e9cb2732..ff5c142c72 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -28,7 +28,7 @@ class JsonSerializationSpec extends ErgoPropertyTest with WalletGenerators with property("ErgoFullBlock should be encoded into JSON and decoded back correctly") { - val (st, bh) = createUtxoState(parameters) + val (st, bh) = createUtxoState(settings) val block: ErgoFullBlock = validFullBlock(parentOpt = None, st, bh) val blockJson: Json = block.asJson diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 71bcaeb3cc..5be22f7827 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -78,7 +78,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val history = ErgoHistory.readOrGenerate(fullHistorySettings)(null) HistoryTestHelpers.allowToApplyOldBlocks(history) - val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, StateConstants(fullHistorySettings)) + val (state, _) = ErgoState.generateGenesisUtxoState(stateDir, fullHistorySettings) log.info(s"Going to generate a chain at ${dir.getAbsoluteFile} starting from ${history.bestFullBlockOpt}") val chain = loop(state, None, None, Seq()) diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 7dabb08224..775190285f 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -6,7 +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.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UpcomingStateContext} +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UpcomingStateContext} import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.Parameters.{MaxBlockCostIncrease, MinValuePerByteIncrease} import org.ergoplatform.settings.ValidationRules._ @@ -58,7 +58,6 @@ trait ErgoTestConstants extends ScorexLogging { val emission: EmissionRules = settings.chainSettings.emissionRules val coinsTotal: Long = emission.coinsTotal - val stateConstants: StateConstants = StateConstants(settings) val genesisStateDigest: ADDigest = settings.chainSettings.genesisStateDigest val feeProp: ErgoTree = ErgoScriptPredef.feeProposition(emission.settings.minerRewardDelay) diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index ec887abd75..00540a1e69 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -58,7 +58,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val digestState: DigestState = { boxesHolderGen.map(WrappedUtxoState(_, createTempDir, None, parameters, settings)).map { wus => - DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, stateConstants) + DigestState.create(Some(wus.version), Some(wus.rootDigest), createTempDir, settings) } }.sample.value @@ -257,7 +257,7 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with sender() ! Success(tx) case SignTransaction(tx, secrets, hints, boxesToSpendOpt, dataBoxesOpt) => - val sc = ErgoStateContext.empty(stateConstants, parameters) + val sc = ErgoStateContext.empty(settings, 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/generators/ValidBlocksGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala index 31f60d02cb..b4ef8203c2 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala @@ -9,7 +9,7 @@ import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.state._ import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState -import org.ergoplatform.settings.{Algos, Constants, Parameters} +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, Parameters} import org.ergoplatform.utils.{LoggingUtil, RandomLike, RandomWrapper} import org.ergoplatform.wallet.utils.TestFileUtils import org.scalatest.matchers.should.Matchers @@ -26,19 +26,15 @@ import scala.util.{Failure, Random, Success} trait ValidBlocksGenerators extends TestkitHelpers with TestFileUtils with Matchers with ChainGenerator with ErgoTransactionGenerators { - def createUtxoState(parameters: Parameters): (UtxoState, BoxHolder) = { - createUtxoState(StateConstants(settings), parameters) - } - - def createUtxoState(constants: StateConstants, parameters: Parameters): (UtxoState, BoxHolder) = { - ErgoState.generateGenesisUtxoState(createTempDir, constants) + def createUtxoState(settings: ErgoSettings): (UtxoState, BoxHolder) = { + ErgoState.generateGenesisUtxoState(createTempDir, settings) } def createUtxoState(bh: BoxHolder, parameters: Parameters): UtxoState = - UtxoState.fromBoxHolder(bh, None, createTempDir, stateConstants, parameters) + UtxoState.fromBoxHolder(bh, None, createTempDir, settings, parameters) - def createDigestState(version: VersionTag, digest: ADDigest, parameters: Parameters): DigestState = - DigestState.create(Some(version), Some(digest), createTempDir, stateConstants) + def createDigestState(version: VersionTag, digest: ADDigest): DigestState = + DigestState.create(Some(version), Some(digest), createTempDir, settings) def validTransactionsFromBoxHolder(boxHolder: BoxHolder): (Seq[ErgoTransaction], BoxHolder) = validTransactionsFromBoxHolder(boxHolder, new RandomWrapper) From 7778623f80dfe9d632cdbced175d58af4d622759 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 25 Apr 2023 13:45:47 +0300 Subject: [PATCH 135/204] merging w 5.0.11 --- avldb/build.sbt | 3 ++- .../avltree/batch/ProverNodeSerializer.scala | 2 +- .../batch/VersionedLDBAVLStorage.scala | 8 +++--- .../avltree/batch/benchmark/OOMTest.scala | 2 +- build.sbt | 2 +- .../interface4j/crypto/ErgoSignature.java | 3 ++- .../wallet/interpreter/ErgoInterpreter.scala | 15 ++++++++--- .../wallet/secrets/ExtendedPublicKey.scala | 16 ++++++++---- .../wallet/secrets/ExtendedSecretKey.scala | 13 +++++++--- .../wallet/boxes/DefaultBoxSelectorSpec.scala | 2 +- .../wallet/crypto/ErgoSignatureSpec.scala | 2 +- .../ErgoProvingInterpreterSpec.scala | 4 +-- .../interpreter/ErgoUnsafeProverSpec.scala | 4 +-- .../transactions/TransactionBuilderSpec.scala | 8 +++--- .../wallet/utils/Generators.scala | 2 +- src/main/resources/api/openapi.yaml | 2 +- .../org/ergoplatform/http/api/ApiCodecs.scala | 2 +- .../ergoplatform/http/api/UtxoApiRoute.scala | 3 +-- .../mining/AutolykosPowScheme.scala | 11 +++++--- .../mining/CandidateGenerator.scala | 7 ++--- .../org/ergoplatform/mining/mining.scala | 2 +- .../modifiers/mempool/ErgoTransaction.scala | 4 +-- .../network/ErgoNodeViewSynchronizer.scala | 5 ++-- .../network/PeerFilteringRule.scala | 6 ++++- .../nodeView/ErgoNodeViewHolder.scala | 4 +-- .../nodeView/history/ErgoSyncInfo.scala | 2 +- .../history/extra/ExtraIndexSerializer.scala | 2 +- .../history/extra/IndexedErgoAddress.scala | 26 +++++++++++-------- .../UtxoSetSnapshotProcessor.scala | 2 +- .../nodeView/state/SnapshotsDb.scala | 3 ++- .../nodeView/state/UtxoStateReader.scala | 2 +- .../nodeView/wallet/ErgoWalletSupport.scala | 4 +-- .../utils/DefaultErgoLogger.scala | 7 +++++ src/main/scala/scorex/core/core.scala | 6 ++--- .../core/validation/ModifierValidator.scala | 2 +- .../http/routes/TransactionApiRouteSpec.scala | 2 +- .../mempool/ErgoTransactionSpec.scala | 4 +-- ...txoSetSnapshotProcessorSpecification.scala | 2 +- .../extra/ExtraIndexerSpecification.scala | 2 +- .../wallet/ErgoWalletServiceSpec.scala | 2 +- .../nodeView/wallet/ErgoWalletSpec.scala | 2 +- .../persistence/OffChainRegistrySpec.scala | 8 +++--- .../utils/ErgoTestConstants.scala | 2 +- .../ergoplatform/utils/WalletTestOps.scala | 4 +-- 44 files changed, 128 insertions(+), 88 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala diff --git a/avldb/build.sbt b/avldb/build.sbt index 3378dd64ee..7add06c7d9 100644 --- a/avldb/build.sbt +++ b/avldb/build.sbt @@ -5,7 +5,8 @@ name := "avldb" libraryDependencies ++= Seq( "javax.xml.bind" % "jaxb-api" % "2.4.0-b180830.0359", "ch.qos.logback" % "logback-classic" % "1.2.3", - "org.scorexfoundation" %% "scrypto" % "2.2.1" + "com.google.guava" % "guava" % "23.0", + "org.scorexfoundation" %% "scrypto" % "2.3.0" ) libraryDependencies ++= Seq( 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 7a89b50ffa..a2bffc75ca 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 @@ -54,7 +54,7 @@ class ProverNodeSerializer(store: LDBVersionedStore) extends ErgoSerializer[Prov if (store != null) { new ProxyInternalProverNode(key, leftKey, rightKey, balance)(store) } else { - new ProxyInternalNode[DigestType](key, Digest32 @@ leftKey, Digest32 @@ rightKey, balance)(hashFn) + new ProxyInternalNode[DigestType](key, Digest32 @@@ leftKey, Digest32 @@@ rightKey, balance)(hashFn) } case LeafPrefix => val key = ADKey @@ r.getBytes(StateTreeParameters.keySize) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index c1b50a7621..078eb1988b 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -106,8 +106,8 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) node match { case in: ProxyInternalNode[DigestType] => - subtreeLoop(Digest32 @@ in.leftLabel, builder) - subtreeLoop(Digest32 @@ in.rightLabel, builder) + subtreeLoop(Digest32 @@@ in.leftLabel, builder) + subtreeLoop(Digest32 @@@ in.rightLabel, builder) case _ => } } @@ -125,8 +125,8 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) val node = VersionedLDBAVLStorage.noStoreSerializer.parseBytes(nodeBytes) node match { case in: ProxyInternalNode[DigestType] if level == manifestDepth => - dumpSubtree(Digest32 @@ in.leftLabel) - dumpSubtree(Digest32 @@ in.rightLabel) + dumpSubtree(Digest32 @@@ in.leftLabel) + dumpSubtree(Digest32 @@@ in.rightLabel) case in: ProxyInternalNode[DigestType] => manifestLoop(in.leftLabel, level + 1, manifestBuilder) manifestLoop(in.rightLabel, level + 1, manifestBuilder) diff --git a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala index 03e82ed774..03b34284e2 100644 --- a/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala +++ b/avldb/src/test/scala/scorex/crypto/authds/avltree/batch/benchmark/OOMTest.scala @@ -66,7 +66,7 @@ object OOMTest extends App { Longs.toByteArray(value) ++ propBytes ++ (0.toByte +: Array.emptyByteArray) ++ transactionId ++ Shorts.toByteArray(boxId) val id = Blake2b256.hash(bytes) - ADKey @@ id -> ADValue @@ bytes + ADKey @@@ id -> ADValue @@@ bytes } private def metadata(modId: Array[Byte], stateRoot: ADDigest): Seq[(Array[Byte], Array[Byte])] = { diff --git a/build.sbt b/build.sbt index 18b62c9859..70fd1c86a1 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.5" +val sigmaStateVersion = "5.0.7" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) diff --git a/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoSignature.java b/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoSignature.java index 8e27003ce4..989363a6f2 100644 --- a/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoSignature.java +++ b/ergo-wallet/src/main/java/org/ergoplatform/wallet/interface4j/crypto/ErgoSignature.java @@ -2,6 +2,7 @@ import org.bouncycastle.math.ec.custom.sec.SecP256K1Point; import scala.math.BigInt; +import sigmastate.crypto.Platform; import java.math.BigInteger; @@ -25,7 +26,7 @@ public byte[] sign(byte[] msg, BigInteger sk) { * @return `true` is the signature is valid, `false` otherwise */ public boolean verify(byte[] msg, byte[] signature, SecP256K1Point pk) { - return org.ergoplatform.wallet.crypto.ErgoSignature.verify(msg, signature, pk); + return org.ergoplatform.wallet.crypto.ErgoSignature.verify(msg, signature, new Platform.Ecp(pk)); } } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala index 49460d9a3b..449ff6cb9a 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala @@ -3,10 +3,11 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.wallet.protocol.Constants import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters -import org.ergoplatform.{ErgoLikeContext, ErgoBox, ErgoBoxCandidate, ErgoLikeInterpreter} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeInterpreter} import scorex.crypto.authds.ADDigest +import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree -import sigmastate.interpreter.Interpreter.{VerificationResult, ScriptEnv} +import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} import sigmastate.{AvlTreeData, AvlTreeFlags} import scala.util.Try @@ -18,7 +19,15 @@ import scala.util.Try * @param params - current values of adjustable blockchain settings */ class ErgoInterpreter(params: ErgoLikeParameters) - extends ErgoLikeInterpreter { + extends ErgoLikeInterpreter with ScorexLogging { + + /** Override default logging for all Ergo interpreters. */ + override protected def logMessage(msg: String): Unit = { + log.error(msg) + } + override protected def logMessage(msg: String, t: Throwable): Unit = { + log.error(msg, t) + } override type CTX = ErgoLikeContext diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala index ad3955bf0f..4fe572243c 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala @@ -1,13 +1,13 @@ package org.ergoplatform.wallet.secrets import java.util - import org.bouncycastle.util.BigIntegers import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.crypto.HmacSHA512 import org.ergoplatform.wallet.serialization.ErgoWalletSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.CryptoFacade import sigmastate.interpreter.CryptoConstants import scala.annotation.tailrec @@ -24,7 +24,7 @@ final class ExtendedPublicKey(private[secrets] val keyBytes: Array[Byte], def selfReflection: ExtendedPublicKey = this def key: ProveDlog = ProveDlog( - CryptoConstants.dlogGroup.curve.decodePoint(keyBytes).asInstanceOf[CryptoConstants.EcPointType] + CryptoConstants.dlogGroup.ctx.decodePoint(keyBytes) ) def child(idx: Int): ExtendedPublicKey = ExtendedPublicKey.deriveChildPublicKey(this, idx) @@ -56,11 +56,17 @@ object ExtendedPublicKey { .hash(parentKey.chainCode, parentKey.keyBytes ++ Index.serializeIndex(idx)) .splitAt(Constants.SecretKeyLength) val childKeyProtoDecoded = BigIntegers.fromUnsignedByteArray(childKeyProto) - val childKey = DLogProverInput(childKeyProtoDecoded).publicImage.value.add(parentKey.key.value) - if (childKeyProtoDecoded.compareTo(CryptoConstants.groupOrder) >= 0 || childKey.isInfinity) { + val childKey = CryptoFacade.multiplyPoints( + DLogProverInput(childKeyProtoDecoded).publicImage.value, + parentKey.key.value) + if (childKeyProtoDecoded.compareTo(CryptoConstants.groupOrder) >= 0 || CryptoFacade.isInfinityPoint(childKey)) { deriveChildPublicKey(parentKey, idx + 1) } else { - new ExtendedPublicKey(childKey.getEncoded(true), childChainCode, parentKey.path.extended(idx)) + new ExtendedPublicKey( + keyBytes = CryptoFacade.encodePoint(childKey, compressed = true), + chainCode = childChainCode, + path = parentKey.path.extended(idx) + ) } } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala index 35825a0800..1ac40239f4 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala @@ -2,7 +2,6 @@ package org.ergoplatform.wallet.secrets import java.math.BigInteger import java.util - import org.bouncycastle.util.BigIntegers import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.crypto.HmacSHA512 @@ -10,6 +9,7 @@ import org.ergoplatform.wallet.serialization.ErgoWalletSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.basics.DLogProtocol import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.crypto.CryptoFacade import sigmastate.interpreter.CryptoConstants /** @@ -30,8 +30,12 @@ final class ExtendedSecretKey(private[secrets] val keyBytes: Array[Byte], def child(idx: Int): ExtendedSecretKey = ExtendedSecretKey.deriveChildSecretKey(this, idx) + /** Returns extended public key corresponding to this secret key. */ def publicKey: ExtendedPublicKey = - new ExtendedPublicKey(privateInput.publicImage.value.getEncoded(true), chainCode, path.toPublicBranch) + new ExtendedPublicKey( + keyBytes = CryptoFacade.encodePoint(privateInput.publicImage.value, compressed = true), + chainCode = chainCode, + path = path.toPublicBranch) def isErased: Boolean = keyBytes.forall(_ == 0x00) @@ -60,7 +64,7 @@ object ExtendedSecretKey { def deriveChildSecretKey(parentKey: ExtendedSecretKey, idx: Int): ExtendedSecretKey = { val keyCoded: Array[Byte] = if (Index.isHardened(idx)) (0x00: Byte) +: parentKey.keyBytes - else parentKey.privateInput.publicImage.value.getEncoded(true) + else CryptoFacade.encodePoint(parentKey.privateInput.publicImage.value, compressed = true) val (childKeyProto, childChainCode) = HmacSHA512 .hash(parentKey.chainCode, keyCoded ++ Index.serializeIndex(idx)) .splitAt(Constants.SecretKeyLength) @@ -85,7 +89,8 @@ object ExtendedSecretKey { def deriveChildPublicKey(parentKey: ExtendedSecretKey, idx: Int): ExtendedPublicKey = { val derivedSecret = deriveChildSecretKey(parentKey, idx) - val derivedPk = derivedSecret.privateInput.publicImage.value.getEncoded(true) + val derivedPk = CryptoFacade.encodePoint( + derivedSecret.privateInput.publicImage.value, compressed = true) val derivedPath = derivedSecret.path.copy(publicBranch = true) new ExtendedPublicKey(derivedPk, derivedSecret.chainCode, derivedPath) } diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala index 44f8a6e211..aee68d6250 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala @@ -222,7 +222,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val tokenData = genTokens(3).last tokenData._2 shouldBe 2 - val tokenId = ModifierId @@ bytesToId(tokenData._1) + val tokenId = ModifierId @@@ bytesToId(tokenData._1) val ergValue = 10 * MinBoxValue diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala index f9286e5dd1..42993ec9df 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/crypto/ErgoSignatureSpec.scala @@ -18,7 +18,7 @@ class ErgoSignatureSpec extends AnyPropSpec with Matchers with Generators { val sig = sign(msg, secret.w) - verify(msg, sig, pk.h) shouldBe true + verify(msg, sig, pk.value) shouldBe true } property("always produce signature of fixed length") { diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala index 438770a1f1..123b70695e 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala @@ -42,8 +42,8 @@ class ErgoProvingInterpreterSpec signedTxFull.inputs.map(_.spendingProof.proof).zip(signedTxFull.inputs.map(_.spendingProof.proof)) .foreach { case (fullProof, unsafeProof) => - ErgoSignature.verify(unsignedTx.messageToSign, fullProof, extendedSecretKey.publicKey.key.h) shouldBe - ErgoSignature.verify(unsignedTx.messageToSign, unsafeProof, extendedSecretKey.publicKey.key.h) + ErgoSignature.verify(unsignedTx.messageToSign, fullProof, extendedSecretKey.publicKey.key.value) shouldBe + ErgoSignature.verify(unsignedTx.messageToSign, unsafeProof, extendedSecretKey.publicKey.key.value) } } } diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala index 2be289322c..e19690d8cb 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala @@ -32,8 +32,8 @@ class ErgoUnsafeProverSpec signedTxFull.inputs.map(_.spendingProof.proof).zip(signedTxFull.inputs.map(_.spendingProof.proof)) .foreach { case (fullProof, unsafeProof) => - ErgoSignature.verify(unsignedTx.messageToSign, fullProof, extendedSecretKey.publicKey.key.h) shouldBe - ErgoSignature.verify(unsignedTx.messageToSign, unsafeProof, extendedSecretKey.publicKey.key.h) + ErgoSignature.verify(unsignedTx.messageToSign, fullProof, extendedSecretKey.publicKey.key.value) shouldBe + ErgoSignature.verify(unsignedTx.messageToSign, unsafeProof, extendedSecretKey.publicKey.key.value) } } } diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala index 3f41745308..9dfaad23a0 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala @@ -74,7 +74,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("token minting") { val inputBox = box(minBoxValue * 2) - val tokenId = Digest32 @@ inputBox.id + val tokenId = Digest32 @@@ inputBox.id val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox) @@ -87,7 +87,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("token burning") { val inputBox = box(minBoxValue * 3, Seq(Digest32 @@ idToBytes(tid1) -> 1000L, Digest32 @@ idToBytes(tid2) -> 2000L)) - val tokenId = Digest32 @@ inputBox.id + val tokenId = Digest32 @@@ inputBox.id val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, burnTokens = Map(tid1 -> 400L, tid2 -> 800L)) @@ -105,7 +105,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("no fees") { val inputBox = box(minBoxValue) - val tokenId = Digest32 @@ inputBox.id + val tokenId = Digest32 @@@ inputBox.id val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) @@ -117,7 +117,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("change goes to fee, but no outFee box") { val inputBox = box(minBoxValue + minBoxValue / 2) - val tokenId = Digest32 @@ inputBox.id + val tokenId = Digest32 @@@ inputBox.id val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala index ec4f7ff88f..0a515eb024 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala @@ -77,7 +77,7 @@ trait Generators { val assetGen: Gen[(TokenId, Long)] = for { id <- boxIdGen amt <- Gen.oneOf(1, 500, 20000, 10000000, Long.MaxValue) - } yield Digest32 @@ id -> amt + } yield Digest32 @@@ id -> amt def additionalTokensGen(cnt: Int): Gen[Seq[(TokenId, Long)]] = Gen.listOfN(cnt, assetGen) diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 748ea3c741..12f3b7b9a1 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "5.0.10" + version: "5.0.11" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 526d49d856..e0b2a1292f 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -206,7 +206,7 @@ trait ApiCodecs extends JsonCodecs { sigma => val op = sigma.opCode.toByte.asJson sigma match { - case dlog: ProveDlog => Map("op" -> op, "h" -> dlog.h.asJson).asJson + case dlog: ProveDlog => Map("op" -> op, "h" -> dlog.value.asJson).asJson case dht: ProveDHTuple => Map("op" -> op, "g" -> dht.g.asJson, "h" -> dht.h.asJson, "u" -> dht.u.asJson, "v" -> dht.v.asJson).asJson case tp: TrivialProp => Map("op" -> op, "condition" -> tp.condition.asJson).asJson case and: CAND => diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index d2bf3661b2..531245a73f 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -15,8 +15,7 @@ import scorex.util.encode.Base16 import scala.concurrent.Future -case class UtxoApiRoute(readersHolder: ActorRef, - override val settings: RESTApiSettings) +case class UtxoApiRoute(readersHolder: ActorRef, override val settings: RESTApiSettings) (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute with ApiCodecs { private def getState: Future[ErgoStateReader] = (readersHolder ? GetReaders).mapTo[Readers].map(_.s) diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala index 27ec642ce4..fd9b853a9c 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala @@ -16,6 +16,7 @@ import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ModifierId, ScorexLogging} import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.CryptoFacade import scala.annotation.tailrec import scala.math.BigInt @@ -126,8 +127,8 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { val N = calcN(header) require(s.d < b, s"Incorrect d = ${s.d} for b = $b") - require(s.pk.getCurve == group.curve && !s.pk.isInfinity, "pk is incorrect") - require(s.w.getCurve == group.curve && !s.w.isInfinity, "w is incorrect") + require(CryptoFacade.getCurve(s.pk) == group.ctx.curve && !CryptoFacade.isInfinityPoint(s.pk), "pk is incorrect") + require(CryptoFacade.getCurve(s.w) == group.ctx.curve && !CryptoFacade.isInfinityPoint(s.w), "w is incorrect") val pkBytes = groupElemToBytes(s.pk) val wBytes = groupElemToBytes(s.w) @@ -137,8 +138,10 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { //height is not used in v1 val f = indexes.map(idx => genElement(version, msg, pkBytes, wBytes, Ints.toByteArray(idx), null)).sum.mod(q) - val left = s.w.multiply(f.bigInteger) - val right = group.generator.multiply(s.d.bigInteger).add(s.pk) + val left = CryptoFacade.exponentiatePoint(s.w, f.bigInteger) + val right = CryptoFacade.multiplyPoints( + CryptoFacade.exponentiatePoint(group.generator, s.d.bigInteger), + s.pk) left == right } diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 8c3af63814..d6b3dfb7b3 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -13,7 +13,7 @@ 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 ReceivableMessages.{ChangedHistory, ChangedMempool, ChangedState, NodeViewChange, FullBlockApplied} +import ReceivableMessages.{ChangedHistory, ChangedMempool, ChangedState, FullBlockApplied, NodeViewChange} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} @@ -29,6 +29,7 @@ import scorex.util.encode.Base16 import scorex.util.{ModifierId, ScorexLogging} import sigmastate.SType.ErgoBoxRType import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.CryptoFacade import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.interpreter.ProverResult @@ -183,7 +184,7 @@ class CandidateGenerator( if state.solvedBlock.isEmpty && state.cache.nonEmpty => // Inject node pk if it is not externally set (in Autolykos 2) val solution = - if (preSolution.pk.isInfinity) { + if (CryptoFacade.isInfinityPoint(preSolution.pk)) { AutolykosSolution(minerPk.value, preSolution.w, preSolution.n, preSolution.d) } else { preSolution @@ -506,7 +507,7 @@ object CandidateGenerator extends ScorexLogging { ) val upcomingContext = state.stateContext.upcoming( - minerPk.h, + minerPk.value, timestamp, nBits, votes, diff --git a/src/main/scala/org/ergoplatform/mining/mining.scala b/src/main/scala/org/ergoplatform/mining/mining.scala index e7da407f4d..9b3a7fb3d3 100644 --- a/src/main/scala/org/ergoplatform/mining/mining.scala +++ b/src/main/scala/org/ergoplatform/mining/mining.scala @@ -14,7 +14,7 @@ package object mining { val PublicKeyLength: Byte = 33 - val group: BcDlogGroup[EcPointType] = CryptoConstants.dlogGroup + val group: BcDlogGroup = CryptoConstants.dlogGroup // Group order, used in Autolykos V.1 for non-outsourceability, // and also to obtain target in both Autolykos v1 and v2 diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 99b74cf602..80125bdaff 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -227,10 +227,10 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], lazy val reemissionSettings = stateContext.ergoSettings.chainSettings.reemission lazy val reemissionRules = reemissionSettings.reemissionRules - lazy val reemissionTokenId = ModifierId @@ reemissionSettings.reemissionTokenId + lazy val reemissionTokenId = ModifierId @@@ reemissionSettings.reemissionTokenId lazy val reemissionTokenIdBytes = reemissionSettings.reemissionTokenIdBytes - lazy val emissionNftId = ModifierId @@ reemissionSettings.emissionNftId + lazy val emissionNftId = ModifierId @@@ reemissionSettings.emissionNftId lazy val emissionNftIdBytes = reemissionSettings.emissionNftIdBytes lazy val chainSettings = stateContext.ergoSettings.chainSettings diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e8266293f7..25bf6b41d3 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -47,6 +47,7 @@ import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.utils.DefaultErgoLogger import scorex.core.serialization.ErgoSerializer /** @@ -860,7 +861,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { //todo : check that manifestBytes were requested - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => log.info(s"Got manifest ${Algos.encode(manifest.id)} from $remote") @@ -886,7 +887,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, hr: ErgoHistory, remote: ConnectedPeer): Unit = { //todo: check if subtree was requested, penalize remote is not so - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) serializer.subtreeFromBytes(serializedChunk, 32) match { case Success(subtree) => deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UtxoSnapshotChunkTypeId.value) diff --git a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala index e34e159094..2a1a979b76 100644 --- a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala @@ -86,8 +86,12 @@ object SyncV2Filter extends PeerFilteringRule { } +/** + * Filter used to differentiate peers supporting UTXO state snapshots, so possibly + * storing and serving them, from peers do not supporting UTXO set snapshots related networking protocol + */ object UtxoSetNetworkingFilter extends PeerFilteringRule { - val UtxoSnapsnotActivationVersion = Version(5, 0, 6) // todo: set proper version around release + val UtxoSnapsnotActivationVersion = Version(5, 0, 12) // todo: set proper version around release def condition(version: Version): Boolean = { // If neighbour version is >= `UtxoSnapsnotActivationVersion`, the neighbour supports utxo snapshots exchange diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 30c8e0532f..70e3854b9b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -286,7 +286,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case Success(pp) => log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) - val newState = new UtxoState(pp, version = VersionTag @@ blockId, store, settings) + val newState = new UtxoState(pp, version = VersionTag @@@ blockId, store, settings) // todo: apply 10 headers before utxo set snapshot updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(_) => ??? @@ -759,9 +759,7 @@ object ErgoNodeViewHolder { val ChainProgress(lastMod, headersHeight, blockHeight, lastUpdate) = progress val chainUpdateDelay = System.currentTimeMillis() - lastUpdate val acceptableChainUpdateDelay = settings.nodeSettings.acceptableChainUpdateDelay - def chainUpdateDelayed = chainUpdateDelay > acceptableChainUpdateDelay.toMillis - def chainSynced = history.bestFullBlockOpt.map(_.id) == history.bestHeaderOpt.map(_.id) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala index ab6756c5a6..be424d522d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoSyncInfo.scala @@ -13,7 +13,7 @@ import scorex.util.{ModifierId, ScorexLogging, bytesToId, idToBytes} * */ sealed trait ErgoSyncInfo extends SyncInfo { - /* + /** * Whether sync info message corresponds to non-empty blockchain */ val nonEmpty: Boolean 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 976ed9fdbb..e8d1c37a33 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexSerializer.scala @@ -3,7 +3,7 @@ package org.ergoplatform.nodeView.history.extra import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -object ExtraIndexSerializer extends ErgoSerializer[ExtraIndex]{ +object ExtraIndexSerializer extends ErgoSerializer[ExtraIndex]{ override def serialize(obj: ExtraIndex, w: Writer): Unit = { obj match { 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 037c179d83..914cc89a0f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedErgoAddress.scala @@ -11,6 +11,7 @@ import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.ErgoTree +import spire.ClassTag import scala.collection.mutable.ArrayBuffer import spire.syntax.all.cfor @@ -397,22 +398,25 @@ object IndexedErgoAddress { * @tparam T - type of desired indexes, either [[IndexedErgoTransaction]] or [[IndexedErgoBox]] * @return */ - private def getFromSegments[T](history: ErgoHistoryReader, - treeHash: ModifierId, - offset: Int, - limit: Int, - segmentCount: Int, - array: ArrayBuffer[Long], - idOf: (ModifierId, Int) => ModifierId, - arraySelector: IndexedErgoAddress => ArrayBuffer[Long], - retreive: (ArrayBuffer[Long], ErgoHistoryReader) => Array[T]) - (implicit segmentTreshold: Int): Array[T] = { + private def getFromSegments[T : ClassTag](history: ErgoHistoryReader, + treeHash: ModifierId, + offset: Int, + limit: Int, + segmentCount: Int, + array: ArrayBuffer[Long], + idOf: (ModifierId, Int) => ModifierId, + arraySelector: IndexedErgoAddress => ArrayBuffer[Long], + retreive: (ArrayBuffer[Long], ErgoHistoryReader) => Array[T]) + (implicit segmentTreshold: Int): Array[T] = { + if(offset >= segmentTreshold * segmentCount + array.length) + return Array.empty[T] // return empty array if all elements are skipped if (offset + limit > array.length && segmentCount > 0) { val range: Array[Int] = getSegmentsForRange(offset, limit) val data: ArrayBuffer[Long] = ArrayBuffer.empty[Long] cfor(0)(_ < range.length, _ + 1) { i => + val id: ModifierId = idOf(treeHash, math.max(segmentCount - range(i), 0)) arraySelector( - history.typedExtraIndexById[IndexedErgoAddress](idOf(treeHash, segmentCount - range(i))).get + history.typedExtraIndexById[IndexedErgoAddress](id).get ) ++=: data } retreive(sliceReversed(data ++= (if (offset < array.length) array else Nil), offset % segmentTreshold, limit), history) 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 f05eaac3ad..4d8280c6ed 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 @@ -214,7 +214,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { val manifest = _manifest.get //todo: .get log.info("Starting UTXO set snapshot transfer into state database") val esc = ErgoStateReader.storageStateContext(stateStore, settings) - val metadata = UtxoState.metadata(VersionTag @@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) + val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { ldbStorage => log.info("Finished UTXO set snapshot transfer into state database") diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index b86b4fc6dc..94b70eb119 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -4,6 +4,7 @@ import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.settings.Algos.HF import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} +import org.ergoplatform.utils.DefaultErgoLogger import org.ergoplatform.wallet.Constants import scorex.core.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage @@ -20,7 +21,7 @@ import scala.util.{Failure, Success, Try} */ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { - private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash) + private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 09a7d67838..69ef7ffc75 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -32,7 +32,7 @@ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence wi protected val persistentProver: PersistentBatchAVLProver[Digest32, HF] def generateBatchProofForBoxes(boxes: Seq[ErgoBox.BoxId]): SerializedAdProof = persistentProver.synchronized { - boxes.map { box => persistentProver.performOneOperation(Lookup(ADKey @@ box)) } + boxes.map { box => persistentProver.performOneOperation(Lookup(ADKey @@@ box)) } persistentProver.prover().generateProof() } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index ba19e06b59..cef8c8e37b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -212,7 +212,7 @@ trait ErgoWalletSupport extends ScorexLogging { def minimalErgoAmount: Long = BoxUtils.minimalErgoAmountSimulated( lockWithAddress.script, - Colls.fromItems((Digest32 @@ assetId) -> amount), + Colls.fromItems((Digest32 @@@ assetId) -> amount), nonMandatoryRegisters, parameters ) @@ -221,7 +221,7 @@ trait ErgoWalletSupport extends ScorexLogging { valueOpt.getOrElse(minimalErgoAmount), lockWithAddress.script, fullHeight, - Colls.fromItems((Digest32 @@ assetId) -> amount), + Colls.fromItems((Digest32 @@@ assetId) -> amount), nonMandatoryRegisters ) } diff --git a/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala b/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala new file mode 100644 index 0000000000..e8e493defa --- /dev/null +++ b/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala @@ -0,0 +1,7 @@ +package org.ergoplatform.utils + +import scorex.util.ScorexLogging + +object DefaultErgoLogger extends scorex.utils.Logger with ScorexLogging { + override def error(message: String): Unit = log.error(message) +} diff --git a/src/main/scala/scorex/core/core.scala b/src/main/scala/scorex/core/core.scala index 8609bd0eb1..1bc9628cfd 100644 --- a/src/main/scala/scorex/core/core.scala +++ b/src/main/scala/scorex/core/core.scala @@ -29,12 +29,12 @@ package object core { def idToBytes: util.ModifierId => Array[Byte] = scorex.util.idToBytes - def bytesToVersion(bytes: Array[Byte]): VersionTag = VersionTag @@ Base16.encode(bytes) + def bytesToVersion(bytes: Array[Byte]): VersionTag = VersionTag @@@ Base16.encode(bytes) def versionToBytes(id: VersionTag): Array[Byte] = Base16.decode(id).get - def versionToId(version: VersionTag): util.ModifierId = util.ModifierId @@ version + def versionToId(version: VersionTag): util.ModifierId = util.ModifierId @@@ version - def idToVersion(id: util.ModifierId): VersionTag = VersionTag @@ id + def idToVersion(id: util.ModifierId): VersionTag = VersionTag @@@ id } diff --git a/src/main/scala/scorex/core/validation/ModifierValidator.scala b/src/main/scala/scorex/core/validation/ModifierValidator.scala index 8e23763c69..a8c35aea5b 100644 --- a/src/main/scala/scorex/core/validation/ModifierValidator.scala +++ b/src/main/scala/scorex/core/validation/ModifierValidator.scala @@ -151,7 +151,7 @@ case class ValidationState[T](result: ValidationResult[T], settings: ValidationS case Success(_) => result case Failure(unexpectedEx) => - settings.getError(id, unexpectedEx, ModifierId @@ bytesToId(Array.fill(32)(0.toByte)), modifierTypeId) + settings.getError(id, unexpectedEx, ModifierId @@@ bytesToId(Array.fill(32)(0.toByte)), modifierTypeId) } } } diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index 04d0e60ca1..ab267a4c33 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -44,7 +44,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec val dataInput = DataInput(input.boxId) val absentModifierId = "0000000000000000000000000000000000000000000000000000000000000000" - val tokens = List[(TokenId, Long)](Digest32 @@ inputBox.id -> 10) + val tokens = List[(TokenId, Long)](Digest32 @@@ inputBox.id -> 10) val registers = Map( ErgoBox.R4 -> ByteArrayConstant("name".getBytes("UTF-8")), diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index 16a2389dfa..b3f9ead789 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -41,7 +41,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { (seq :+ ebc) -> true } else { if (ebc.additionalTokens.nonEmpty && ebc.additionalTokens.exists(t => !java.util.Arrays.equals(t._1, from.head.id))) { - (seq :+ modifyAsset(ebc, deltaFn, Digest32 @@ from.head.id)) -> true + (seq :+ modifyAsset(ebc, deltaFn, Digest32 @@@ from.head.id)) -> true } else { (seq :+ ebc) -> false } @@ -96,7 +96,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { val minerPkHex = "0326df75ea615c18acc6bb4b517ac82795872f388d5d180aac90eaa84de750b942" val minerPk = Base16.decode(minerPkHex).map { point => ProveDlog( - CryptoConstants.dlogGroup.curve.decodePoint(point).asInstanceOf[CryptoConstants.EcPointType] + CryptoConstants.dlogGroup.ctx.decodePoint(point) ) }.get val inputs: IndexedSeq[Input] = IndexedSeq( diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index 3099358f39..b8d065fdc3 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -65,7 +65,7 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { restoredProver.unauthenticatedLookup(box.id).isDefined shouldBe true } restoredProver.checkTree(postProof = false) - val restoredState = new UtxoState(restoredProver, version = VersionTag @@ blockId, store, settings) + val restoredState = new UtxoState(restoredProver, version = VersionTag @@@ blockId, store, settings) bh.sortedBoxes.foreach { box => restoredState.boxById(box.id).isDefined shouldBe true } 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 d97d4f7384..c37a8baa8d 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -298,7 +298,7 @@ object ChainGenerator extends ErgoTestHelpers { val tokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] inOpt match { case Some(input) if cond => - tokens += Tuple2(Digest32 @@ input.id, math.abs(Random.nextInt())) + tokens += Tuple2(Digest32 @@@ input.id, math.abs(Random.nextInt())) case Some(tokenBox) if !cond => tokenBox.additionalTokens.toArray.foreach(tokens += _) case _ => diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala index 1f5cbfabf0..6b4b8fc76b 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala @@ -259,7 +259,7 @@ class ErgoWalletServiceSpec val walletService = new ErgoWalletServiceImpl(settings) val signedTx = walletService.generateTransaction(wState, boxSelector, Seq(paymentRequest), inputsRaw = encodedBoxes, dataInputsRaw = Seq.empty, sign = true).get.asInstanceOf[ErgoTransaction] - ErgoSignature.verify(signedTx.messageToSign, signedTx.inputs.head.spendingProof.proof, pks.head.pubkey.h) shouldBe true + ErgoSignature.verify(signedTx.messageToSign, signedTx.inputs.head.spendingProof.proof, pks.head.pubkey.value) shouldBe true signedTx.inputs.size shouldBe 1 signedTx.outputs.size shouldBe 2 diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala index 59ddc3e26d..9c223aa364 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala @@ -242,7 +242,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually property("whitelist set, preserve tokens from auto-burn") { val inputs = { val x = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) - Seq(encodedTokenId(Digest32 @@ x.head.boxId)) + Seq(encodedTokenId(Digest32 @@@ x.head.boxId)) } implicit val ww: WalletFixture = new WalletFixture(settings diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistrySpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistrySpec.scala index def88210a8..208dd74b87 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistrySpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/OffChainRegistrySpec.scala @@ -35,7 +35,7 @@ class OffChainRegistrySpec registry.digest.walletAssetBalances.toMap shouldEqual assetsBalance.toMap //spend all the outputs - registry = registry.updateOnTransaction(Seq.empty, boxes.map(EncodedBoxId @@ _.boxId), Seq.empty) + registry = registry.updateOnTransaction(Seq.empty, boxes.map(EncodedBoxId @@@ _.boxId), Seq.empty) registry.digest.walletBalance shouldEqual 0 registry.digest.walletAssetBalances shouldEqual Seq.empty @@ -54,12 +54,12 @@ class OffChainRegistrySpec registry.digest.walletBalance shouldEqual fbalance registry.digest.walletAssetBalances.toMap shouldEqual fassetsBalance.toMap - registry = registry.updateOnTransaction(Seq.empty, filtered.map(EncodedBoxId @@ _.boxId), Seq(scan)) + registry = registry.updateOnTransaction(Seq.empty, filtered.map(EncodedBoxId @@@ _.boxId), Seq(scan)) registry.digest.walletBalance shouldEqual fbalance registry.digest.walletAssetBalances.toMap shouldEqual fassetsBalance.toMap val scan2 = Scan(scanId, "_", p, ScanWalletInteraction.Off, removeOffchain = true) - registry = registry.updateOnTransaction(Seq.empty, filtered.map(EncodedBoxId @@ _.boxId), Seq(scan2)) + registry = registry.updateOnTransaction(Seq.empty, filtered.map(EncodedBoxId @@@ _.boxId), Seq(scan2)) registry.digest.walletBalance shouldEqual 0 registry.digest.walletAssetBalances shouldEqual Seq.empty } @@ -72,7 +72,7 @@ class OffChainRegistrySpec val height = Random.nextInt(500) + 1 //apply block to empty registry - val registry = OffChainRegistry.empty.updateOnBlock(height, boxes, boxes.map(EncodedBoxId @@ _.boxId)) + val registry = OffChainRegistry.empty.updateOnBlock(height, boxes, boxes.map(EncodedBoxId @@@ _.boxId)) val balance = balanceAmount(boxes.map(_.box)) val assetsBalance = assetAmount(boxes.map(_.box)) registry.height shouldEqual height diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 775190285f..4945e96876 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -74,7 +74,7 @@ trait ErgoTestConstants extends ScorexLogging { val defaultMinerSecret: DLogProverInput = defaultProver.hdKeys.head.privateInput val defaultMinerSecretNumber: BigInt = defaultProver.hdKeys.head.privateInput.w val defaultMinerPk: ProveDlog = defaultMinerSecret.publicImage - val defaultMinerPkPoint: EcPointType = defaultMinerPk.h + val defaultMinerPkPoint: EcPointType = defaultMinerPk.value val defaultTimestamp: Long = 1552217190000L val defaultNBits: Long = settings.chainSettings.initialNBits diff --git a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala index 2ad09180ad..ac6df7067d 100644 --- a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala @@ -78,7 +78,7 @@ trait WalletTestOps extends NodeViewBaseOps { def makeGenesisTxWithAsset(publicKey: ProveDlog, issueAsset: Boolean): ErgoTransaction = { val inputs = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) val assets: Seq[(TokenId, Long)] = if (issueAsset) { - Seq((Digest32 @@ inputs.head.boxId) -> 1L) + Seq((Digest32 @@@ inputs.head.boxId) -> 1L) } else { Seq.empty } @@ -128,7 +128,7 @@ trait WalletTestOps extends NodeViewBaseOps { def isNewAsset(tokenId: TokenId, value: Long): Boolean = java.util.Arrays.equals(tokenId, newAssetIdStub) val (newAsset, spentAssets) = assets.partition((isNewAsset _).tupled) - newAsset.map(Digest32 @@ inputs.head.boxId -> _._2) ++ spentAssets + newAsset.map(Digest32 @@@ inputs.head.boxId -> _._2) ++ spentAssets } def randomNewAsset: Seq[(TokenId, Long)] = Seq(newAssetIdStub -> randomLong()) From 55056364149adc62a02dc85573b07474f8f2a132 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 25 Apr 2023 17:13:16 +0200 Subject: [PATCH 136/204] utxo-set-bootstrapping-preparation: typos --- .../crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala | 2 +- avldb/src/main/scala/scorex/db/LDBKVStore.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 492a83e1f8..8a085fe90e 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -95,7 +95,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) * * @param dumpStorage - non-versioned storage to dump tree to * @param manifestDepth - depth of manifest tree - * @param expectedRootHash - expected UTXO set aunthenticating tree root hash + * @param expectedRootHash - expected UTXO set authenticating tree root hash * @return - hash of root node of tree, or failure if an error (e.g. in database) happened */ def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Int, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index a8ead25e37..01f3748c16 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -17,8 +17,8 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging /** * Update this database atomically with a batch of insertion and removal operations * - * @param toInsertKeys - keys pf key-value pairs to insert into database - * @param toInsertValues - values pf key-value pairs to insert into database + * @param toInsertKeys - keys of key-value pairs to insert into database + * @param toInsertValues - values of key-value pairs to insert into database * @param toRemove - keys of key-value pairs to remove from the database * @return - error if it happens, or success status */ From 2c8a8bbd586fa477f582b3b5f41a6c76ef336001 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 26 Apr 2023 00:03:02 +0300 Subject: [PATCH 137/204] remove manifests for height if contradictions are found --- .../network/ErgoNodeViewSynchronizer.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 25bf6b41d3..3b1109461a 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -39,16 +39,16 @@ import scorex.core.app.Version import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 +import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.utils.DefaultErgoLogger +import scorex.core.serialization.ErgoSerializer import scala.annotation.tailrec import scala.collection.mutable import scala.concurrent.ExecutionContext import scala.concurrent.duration._ import scala.util.{Failure, Random, Success} -import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} -import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.utils.DefaultErgoLogger -import scorex.core.serialization.ErgoSerializer /** * Contains most top-level logic for p2p networking, communicates with lower-level p2p code and other parts of the @@ -826,9 +826,13 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + /** + * Process information about snapshots got from another peer + */ protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => log.debug(s"Got manifest $manifestId for height $height from $remote") + // add manifest to available manifests dictionary if it is not written there yet val existingOffers = availableManifests.getOrElse(height, Seq.empty) if (!existingOffers.exists(_._1 == remote)) { log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") @@ -1197,6 +1201,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val idsSet = records.map(_._2).map(Base16.encode).toSet if (idsSet.size > 1) { log.warn(s"Different manifests found at height $h: $idsSet") + availableManifests.remove(h) false } else { true From 7e12ac7c34a3fc117e28bf3016f384e0a73f907d Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 26 Apr 2023 12:33:09 +0300 Subject: [PATCH 138/204] UtxoSettings --- src/main/resources/application.conf | 19 ++++++++++----- .../network/ErgoNodeViewSynchronizer.scala | 3 ++- .../ToDownloadProcessor.scala | 2 +- .../state/UtxoSetSnapshotPersistence.scala | 2 +- .../settings/NodeConfigurationSettings.scala | 23 ++++++++++++++----- .../extra/ExtraIndexerSpecification.scala | 6 ++--- .../settings/ErgoSettingsSpecification.scala | 9 +++----- .../ergoplatform/tools/ChainGenerator.scala | 4 ++-- .../utils/HistoryTestHelpers.scala | 4 ++-- .../scala/org/ergoplatform/utils/Stubs.scala | 4 ++-- 10 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 6af5a8fa95..b3547ef9ac 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -21,8 +21,18 @@ ergo { # otherwise, it could be hard to find proofs around the peers blocksToKeep = -1 - # Download and apply UTXO set snapshot and full-blocks after that - utxoBootstrap = false + utxo { + # Download and apply UTXO set snapshot and full-blocks after that + utxoBootstrap = false + + # how many utxo set snapshots to store, 0 means that they are not stored at all + storingUtxoSnapshots = 0 + + # how many utxo set snapshots for a height with same id we need to find in p2p network + # in order to start downloading it + p2pUtxoSnapshots = 2 + } + # Download PoPoW proof on node bootstrap PoPoWBootstrap = false @@ -30,9 +40,6 @@ ergo { # Minimal suffix size for PoPoW proof (may be pre-defined constant or settings parameter) minimalSuffix = 10 - # how many utxo set snapshots to store, 0 means that they are not stored at all - storingUtxoSnapshots = 0 - # Is the node is doing mining mining = false @@ -106,7 +113,7 @@ ergo { # } # # Before the height given (including it) validation of scripts is missed. - # This improving perfomance and memory usage during initial bootstrapping. + # This improving performance and memory usage during initial bootstrapping. # The node still applying transactions to UTXO set and so checks UTXO set digests for each block. # Block at checkpoint height is to be checked against expected one. checkpoint = null diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 3b1109461a..23433d7e8d 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1189,10 +1189,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + // check if we have enough UTXO set snapshots for some height protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { val MinSnapshots = 2 //todo: set to 3 after testing, or move to settings? - if (settings.nodeSettings.utxoBootstrap && + if (settings.nodeSettings.utxoSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && historyReader.utxoSetSnapshotDownloadPlan().isEmpty) { 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 fa4efc519e..aa7df2cf6b 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 @@ -83,7 +83,7 @@ trait ToDownloadProcessor // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) - case None if (nodeSettings.utxoBootstrap && !_utxoSnapshotApplied) => + case None if (nodeSettings.utxoSettings.utxoBootstrap && !_utxoSnapshotApplied) => // todo: can be requested multiple times, prevent it if (utxoSetSnapshotDownloadPlan().isEmpty) { Map(SnapshotsInfoTypeId.value -> Seq.empty) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 6f29ac83c9..ba80c504b7 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -64,7 +64,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { log.info("Started work within future") val ft0 = System.currentTimeMillis() dumpSnapshot(height, expectedRootHash) - snapshotsDb.pruneSnapshots(ergoSettings.nodeSettings.storingUtxoSnapshots) + snapshotsDb.pruneSnapshots(ergoSettings.nodeSettings.utxoSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() log.info("Work within future finished in: " + (ft - ft0) + " ms.") } diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index e30f385901..0a87d47f07 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -20,6 +20,18 @@ trait CheckpointingSettingsReader extends ModifierIdReader { } } +case class UtxoSettings(utxoBootstrap: Boolean, storingUtxoSnapshots: Int, p2pUtxoSnapshots: Int) + +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") + ) + } +} + /** * Configuration file for Ergo node regime * @@ -28,7 +40,7 @@ trait CheckpointingSettingsReader extends ModifierIdReader { case class NodeConfigurationSettings(stateType: StateType, verifyTransactions: Boolean, blocksToKeep: Int, - utxoBootstrap: Boolean, + utxoSettings: UtxoSettings, poPoWBootstrap: Boolean, minimalSuffix: Int, mining: Boolean, @@ -40,7 +52,6 @@ case class NodeConfigurationSettings(stateType: StateType, miningPubKeyHex: Option[String], offlineGeneration: Boolean, keepVersions: Int, - storingUtxoSnapshots: Int, acceptableChainUpdateDelay: FiniteDuration, mempoolCapacity: Int, mempoolCleanupDuration: FiniteDuration, @@ -58,10 +69,11 @@ case class NodeConfigurationSettings(stateType: StateType, */ val isFullBlocksPruned: Boolean = blocksToKeep >= 0 - val areSnapshotsStored = storingUtxoSnapshots > 0 + val areSnapshotsStored = utxoSettings.storingUtxoSnapshots > 0 } -trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettingsReader with ModifierIdReader { +trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettingsReader + with UtxoSettingsReader with ModifierIdReader { implicit val nodeConfigurationReader: ValueReader[NodeConfigurationSettings] = { (cfg, path) => val stateTypeKey = s"$path.stateType" @@ -70,7 +82,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin stateType, cfg.as[Boolean](s"$path.verifyTransactions"), cfg.as[Int](s"$path.blocksToKeep"), - cfg.as[Boolean](s"$path.utxoBootstrap"), + cfg.as[UtxoSettings](s"$path.utxoSettings"), cfg.as[Boolean](s"$path.PoPoWBootstrap"), cfg.as[Int](s"$path.minimalSuffix"), cfg.as[Boolean](s"$path.mining"), @@ -82,7 +94,6 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin cfg.as[Option[String]](s"$path.miningPubKeyHex"), cfg.as[Boolean](s"$path.offlineGeneration"), cfg.as[Int](s"$path.keepVersions"), - cfg.as[Int](s"$path.storingUtxoSnapshots"), cfg.as[FiniteDuration](s"$path.acceptableChainUpdateDelay"), cfg.as[Int](s"$path.mempoolCapacity"), cfg.as[FiniteDuration](s"$path.mempoolCleanupDuration"), 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 c37a8baa8d..a1986c0990 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -14,7 +14,7 @@ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoState, UtxoStateReader} -import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} +import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings, UtxoSettings} import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} import scorex.crypto.hash.Digest32 import scorex.util.{ModifierId, bytesToId} @@ -38,9 +38,9 @@ class ExtraIndexerSpecification extends ErgoPropertyTest with ExtraIndexerBase w override protected implicit val addressEncoder: ErgoAddressEncoder = initSettings.chainSettings.addressEncoder val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, utxoBootstrap = false, poPoWBootstrap = false, ChainGenerator.minimalSuffix, mining = false, ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, + -1, UtxoSettings(false, 0, 2), poPoWBootstrap = false, ChainGenerator.minimalSuffix, mining = false, ChainGenerator.txCostLimit, ChainGenerator.txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, - 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, + 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112 * 1024, extraIndex = false) val HEIGHT: Int = 50 diff --git a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala index db14ea932e..c81ac939a4 100644 --- a/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala +++ b/src/test/scala/org/ergoplatform/settings/ErgoSettingsSpecification.scala @@ -24,7 +24,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 1000, - utxoBootstrap = false, + utxoSettings = UtxoSettings(false, 0, 2), poPoWBootstrap = false, 10, mining = true, @@ -36,7 +36,6 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, - storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -75,7 +74,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 12, - utxoBootstrap = false, + utxoSettings = UtxoSettings(false, 0, 2), poPoWBootstrap = false, 10, mining = true, @@ -87,7 +86,6 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, - storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, @@ -119,7 +117,7 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { StateType.Utxo, verifyTransactions = true, 13, - utxoBootstrap = false, + utxoSettings = UtxoSettings(false, 0, 2), poPoWBootstrap = false, 10, mining = true, @@ -131,7 +129,6 @@ class ErgoSettingsSpecification extends ErgoPropertyTest { miningPubKeyHex = None, offlineGeneration = false, keepVersions = 200, - storingUtxoSnapshots = 0, acceptableChainUpdateDelay = 30.minutes, mempoolCapacity = 100000, mempoolCleanupDuration = 10.seconds, diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index 5be22f7827..858c4df57a 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -59,9 +59,9 @@ object ChainGenerator extends App with ErgoTestHelpers { val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(StateType.Utxo, verifyTransactions = true, - -1, utxoBootstrap = false, poPoWBootstrap = false, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + -1, UtxoSettings(false, 0, 2), poPoWBootstrap = false, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, offlineGeneration = false, - 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, + 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 20, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false) val ms = settings.chainSettings.monetary.copy( minerRewardDelay = RewardDelay diff --git a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala index 7d2ea4fcbe..517563d142 100644 --- a/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala +++ b/src/test/scala/org/ergoplatform/utils/HistoryTestHelpers.scala @@ -46,9 +46,9 @@ trait HistoryTestHelpers extends ErgoPropertyTest { val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + UtxoSettings(false, 0, 2), poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second, miningPubKeyHex = None, - offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 00540a1e69..564521830b 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -370,9 +370,9 @@ trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with val txCostLimit = initSettings.nodeSettings.maxTransactionCost val txSizeLimit = initSettings.nodeSettings.maxTransactionSize val nodeSettings: NodeConfigurationSettings = NodeConfigurationSettings(stateType, verifyTransactions, blocksToKeep, - utxoBootstrap = false, poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, + UtxoSettings(false, 0, 2), poPoWBootstrap = PoPoWBootstrap, minimalSuffix, mining = false, txCostLimit, txSizeLimit, useExternalMiner = false, internalMinersCount = 1, internalMinerPollingInterval = 1.second,miningPubKeyHex = None, - offlineGeneration = false, 200, 0, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, + offlineGeneration = false, 200, 5.minutes, 100000, 1.minute, mempoolSorting = SortingOption.FeePerByte, rebroadcastCount = 200, 1000000, 100, adProofsSuffixLength = 112*1024, extraIndex = false ) val scorexSettings: ScorexSettings = null From 6db2c62d1b64f04f211e10b988f8d224626267ab Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 26 Apr 2023 12:40:38 +0300 Subject: [PATCH 139/204] fixing MinSnapshots in checkUtxoSetManifests --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 23433d7e8d..cbbebe47b4 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1191,8 +1191,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // check if we have enough UTXO set snapshots for some height protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { - val MinSnapshots = 2 //todo: set to 3 after testing, or move to settings? - + val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots + if (settings.nodeSettings.utxoSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && From 111b8815ac9956af89d16c1845b1a3ead64df0f0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 26 Apr 2023 17:58:06 +0300 Subject: [PATCH 140/204] BlockSectionTypeId / AuxiliaryTypeId --- .../modifiers/NetworkObjectTypeId.scala | 28 ++++++++++++++----- .../network/ErgoNodeViewSynchronizer.scala | 7 ++--- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala index c37b2544f5..ff08a99ad7 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala @@ -21,6 +21,20 @@ object NetworkObjectTypeId { def fromByte(value: Byte): Value = Value @@ value } +/** + * Block section to be sent over the wire (header, transactions section, extension, UTXO set transformation proofs) + */ +sealed trait BlockSectionTypeId extends NetworkObjectTypeId { + require(value > 50, "Type id for block section must be > 0") +} + +/** + * Non-block network objects: unconfirmed transactions, utxo set snapshot related data, nipopow related data etc + */ +sealed trait AuxiliaryTypeId extends NetworkObjectTypeId { + require(value < 50, "Type id for auxiliary network object must be < 0") +} + /** * Unconfirmed transactions sent outside blocks */ @@ -31,14 +45,14 @@ object TransactionTypeId extends NetworkObjectTypeId { /** * Block header, section of a block PoW is done on top of. This section is committing to other sections */ -object HeaderTypeId extends NetworkObjectTypeId { +object HeaderTypeId extends BlockSectionTypeId { override val value: Value = fromByte(101) } /** * Block transactions sections. Contains all the transactions for a block. */ -object BlockTransactionsTypeId extends NetworkObjectTypeId { +object BlockTransactionsTypeId extends AuxiliaryTypeId { override val value: Value = fromByte(102) } @@ -46,7 +60,7 @@ object BlockTransactionsTypeId extends NetworkObjectTypeId { * Block section which contains proofs of correctness for UTXO set transformations. * The section contains proofs for all the transformations (i.e. for all the block transactions) */ -object ProofsTypeId extends NetworkObjectTypeId { +object ProofsTypeId extends BlockSectionTypeId { override val value: Value = fromByte(104) } @@ -56,7 +70,7 @@ object ProofsTypeId extends NetworkObjectTypeId { * Interlinks vector (for nipopow proofs) written there, as well as current network parameters * (at the beginning of voting epoch), but miners can also put arbitrary data there. */ -object ExtensionTypeId extends NetworkObjectTypeId { +object ExtensionTypeId extends BlockSectionTypeId { override val value: Value = fromByte(108) } @@ -65,20 +79,20 @@ object ExtensionTypeId extends NetworkObjectTypeId { * got over the wire (header, transactions, extension in the "utxo" mode, those three sections plus proofs in * the "digest" mode). */ -object FullBlockTypeId extends NetworkObjectTypeId { +object FullBlockTypeId extends AuxiliaryTypeId { override val value: Value = fromByte(-127) } /** * Not a block section, but a chunk of UTXO set */ -object UtxoSnapshotChunkTypeId extends NetworkObjectTypeId { +object UtxoSnapshotChunkTypeId extends AuxiliaryTypeId { override val value: Value = fromByte(-126) } /** * Not a block section, but registry of UTXO set snapshots available */ -object SnapshotsInfoTypeId extends NetworkObjectTypeId { +object SnapshotsInfoTypeId extends AuxiliaryTypeId { override val value: Value = fromByte(-125) } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index cbbebe47b4..9bceb8a6d7 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -59,8 +59,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, syncInfoSpec: ErgoSyncInfoMessageSpec.type, settings: ErgoSettings, syncTracker: ErgoSyncTracker, - deliveryTracker: DeliveryTracker - )(implicit ex: ExecutionContext) + deliveryTracker: DeliveryTracker)(implicit ex: ExecutionContext) extends Actor with Synchronizer with ScorexLogging with ScorexEncoding { override val supervisorStrategy: OneForOneStrategy = OneForOneStrategy( @@ -864,7 +863,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { - //todo : check that manifestBytes were requested + //todo : check that manifest was requested val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { case Success(manifest) => @@ -1192,7 +1191,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // check if we have enough UTXO set snapshots for some height protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots - + if (settings.nodeSettings.utxoSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && From f91980507b15a3f868a6b33222c326d6e8d1d21c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 27 Apr 2023 01:06:01 +0300 Subject: [PATCH 141/204] BlockSectionThreshold / isBlockSection --- .../modifiers/NetworkObjectTypeId.scala | 19 +++++++++++++++++-- .../scorex/core/network/DeliveryTracker.scala | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala index ff08a99ad7..1c45168429 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala @@ -19,20 +19,35 @@ object NetworkObjectTypeId { @inline def fromByte(value: Byte): Value = Value @@ value + + /** + * Threshold for block section type ids + * Block section could have ids >= this threshold only + * Other p2p network objects have type id below the threshold + */ + val BlockSectionThreshold: Value = Value @@ 50.toByte + + /** + * Whether network object type corresponding to block sections, returns true if so + */ + def isBlockSection(typeId: Value): Boolean = { + typeId >= BlockSectionThreshold + } + } /** * Block section to be sent over the wire (header, transactions section, extension, UTXO set transformation proofs) */ sealed trait BlockSectionTypeId extends NetworkObjectTypeId { - require(value > 50, "Type id for block section must be > 0") + require(value >= BlockSectionThreshold, s"Type id for block section must be >= $BlockSectionThreshold") } /** * Non-block network objects: unconfirmed transactions, utxo set snapshot related data, nipopow related data etc */ sealed trait AuxiliaryTypeId extends NetworkObjectTypeId { - require(value < 50, "Type id for auxiliary network object must be < 0") + require(value < BlockSectionThreshold, s"Type id for auxiliary network object must be < DistinguihsingValue") } /** diff --git a/src/main/scala/scorex/core/network/DeliveryTracker.scala b/src/main/scala/scorex/core/network/DeliveryTracker.scala index 43ecf16889..3e8d37a578 100644 --- a/src/main/scala/scorex/core/network/DeliveryTracker.scala +++ b/src/main/scala/scorex/core/network/DeliveryTracker.scala @@ -84,8 +84,10 @@ class DeliveryTracker(cacheSettings: NetworkCacheSettings, requested.foldLeft(0) { case (sum, (modTypeId, _)) if modTypeId == Header.modifierTypeId => sum - case (sum, (_, mid)) => + case (sum, (modTypeId, mid)) if NetworkObjectTypeId.isBlockSection(modTypeId) => sum + mid.size + case (sum, _) => + sum } Math.max(0, desiredSizeOfExpectingModifierQueue - nonHeaderModifiersCount) } From 3aa87db5703048f3638b99d9c44d8fe9a583f6ad Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 29 Apr 2023 01:06:45 +0300 Subject: [PATCH 142/204] ManifestTypeId, checking if utxo set snapshot chunk was requested --- .../modifiers/NetworkObjectTypeId.scala | 7 +++ .../network/ErgoNodeViewSynchronizer.scala | 44 +++++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala index 1c45168429..2edffa6d6f 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala @@ -111,3 +111,10 @@ object UtxoSnapshotChunkTypeId extends AuxiliaryTypeId { object SnapshotsInfoTypeId extends AuxiliaryTypeId { override val value: Value = fromByte(-125) } + +/** + * Not a block section, but manifest of a UTXO set snapshot + */ +object ManifestTypeId extends AuxiliaryTypeId { + override val value: Value = fromByte(-124) +} diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 9bceb8a6d7..4cbbed759c 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -42,7 +42,7 @@ import scorex.util.encode.Base16 import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.utils.DefaultErgoLogger -import scorex.core.serialization.ErgoSerializer +import scorex.core.serialization.{ErgoSerializer, SubtreeSerializer} import scala.annotation.tailrec import scala.collection.mutable @@ -889,26 +889,32 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], hr: ErgoHistory, remote: ConnectedPeer): Unit = { - //todo: check if subtree was requested, penalize remote is not so - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) - serializer.subtreeFromBytes(serializedChunk, 32) match { + SubtreeSerializer.parseBytesTry(serializedChunk) match { case Success(subtree) => - deliveryTracker.setUnknown(ModifierId @@ Algos.encode(subtree.id), UtxoSnapshotChunkTypeId.value) - log.info(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") //todo: change to debug on release? - hr.registerDownloadedChunk(subtree.id, serializedChunk) - - val downloadPlanOpt = hr.utxoSetSnapshotDownloadPlan() // todo: match for optional result - if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { - if (!hr.isUtxoSnapshotApplied) { - val h = downloadPlanOpt.get.snapshotHeight // todo: .get - val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get - viewHolderRef ! InitStateFromSnapshot(h, blockId) - } else { - log.warn("UTXO set snapshot already applied, double application attemt") - } - } else{ - requestMoreChunksIfNeeded(hr) + val chunkId = ModifierId @@ Algos.encode(subtree.id) + deliveryTracker.getRequestedInfo(UtxoSnapshotChunkTypeId.value, chunkId) match { + case Some(ri) if ri.peer == remote => + log.debug(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") + deliveryTracker.setUnknown(chunkId, UtxoSnapshotChunkTypeId.value) + hr.registerDownloadedChunk(subtree.id, serializedChunk) + + val downloadPlanOpt = hr.utxoSetSnapshotDownloadPlan() // todo: match for optional result + if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { + if (!hr.isUtxoSnapshotApplied) { + val h = downloadPlanOpt.get.snapshotHeight // todo: .get + val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get + viewHolderRef ! InitStateFromSnapshot(h, blockId) + } else { + log.warn("UTXO set snapshot already applied, double application attemt") + } + } else{ + requestMoreChunksIfNeeded(hr) + } + case _ => + log.info(s"Penalizing spamming peer $remote sent non-asked UTXO set snapshot $chunkId") + penalizeSpammingPeer(remote) } + case Failure(e) => log.info(s"Cant' restore snapshot chunk (got from $remote) from bytes ", e) penalizeMisbehavingPeer(remote) From 724958a834e76a446c2f3b74263ddd31e678119f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 1 May 2023 13:31:25 +0300 Subject: [PATCH 143/204] eliminating BatchAVLProverSerializer usages, spam manifest check --- papers/emission.md | 6 --- .../network/ErgoNodeViewSynchronizer.scala | 50 +++++++++++-------- .../nodeView/state/SnapshotsDb.scala | 10 +--- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/papers/emission.md b/papers/emission.md index 0450143f7e..cbff204297 100644 --- a/papers/emission.md +++ b/papers/emission.md @@ -97,12 +97,6 @@ second one is to pay mining fee supposedly (its value can be 0.01 ERG at most) val correctNftId = EQ(firstTokenId, ByteArrayConstant(reemissionNftId)) - // output of the reemission contract - val reemissionOut = ByIndex(Outputs, IntConstant(0)) - - // output to pay miner - val minerOut = ByIndex(Outputs, IntConstant(1)) - // miner's output must have script which is time-locking reward for miner's pubkey // box height must be the same as block height val correctMinerOutput = AND( diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 4cbbed759c..93bc2287b0 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -4,14 +4,14 @@ 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.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} -import org.ergoplatform.modifiers.{BlockSection, NetworkObjectTypeId, SnapshotsInfoTypeId, UtxoSnapshotChunkTypeId} +import org.ergoplatform.modifiers.{BlockSection, ManifestTypeId, NetworkObjectTypeId, SnapshotsInfoTypeId, UtxoSnapshotChunkTypeId} 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, ErgoAlgos, ErgoSettings} +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.ErgoNodeViewHolder._ @@ -23,7 +23,6 @@ import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ import org.ergoplatform.nodeView.state.{ErgoStateReader, SnapshotsInfo, UtxoSetSnapshotPersistence, UtxoStateReader} import scorex.core.network.message._ import org.ergoplatform.nodeView.wallet.ErgoWalletReader -import org.ergoplatform.settings.Algos.HF import scorex.core.network.message.{InvSpec, MessageSpec, ModifiersSpec, RequestModifierSpec} import scorex.core.network._ import scorex.core.network.{ConnectedPeer, ModifiersStatus, SendToPeer, SendToPeers} @@ -36,13 +35,11 @@ import scorex.core.network.DeliveryTracker import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.utils.DefaultErgoLogger -import scorex.core.serialization.{ErgoSerializer, SubtreeSerializer} +import scorex.core.serialization.{ErgoSerializer, ManifestSerializer, SubtreeSerializer} import scala.annotation.tailrec import scala.collection.mutable @@ -560,6 +557,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { + deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(manifestId), peer){ deliveryCheck => + context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) + } val msg = Message(GetManifestSpec, Right(manifestId), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } @@ -863,22 +863,28 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { - //todo : check that manifest was requested - val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) - serializer.manifestFromBytes(manifestBytes, keyLength = 32) match { + val serializer = ManifestSerializer.defaultSerializer + serializer.parseBytesTry(manifestBytes) match { case Success(manifest) => - log.info(s"Got manifest ${Algos.encode(manifest.id)} from $remote") - val manifestRecords = availableManifests.filter(_._2.exists(_._2.sameElements(manifest.id))) - val heightOfManifest = manifestRecords.headOption.map(_._1) - val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq - heightOfManifest match { - case Some(height) => - log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") - hr.registerManifestToDownload(manifest, height, peersToDownload) - availableManifests.clear() - requestMoreChunksIfNeeded(hr) - case None => - log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") + val manifestId = ModifierId @@ Algos.encode(manifest.id) + log.info(s"Got manifest $manifestId from $remote") + deliveryTracker.getRequestedInfo(ManifestTypeId.value, manifestId) match { + case Some(ri) if ri.peer == remote => + val manifestRecords = availableManifests.filter(_._2.exists(_._2.sameElements(manifest.id))) + val heightOfManifest = manifestRecords.headOption.map(_._1) + val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq + heightOfManifest match { + case Some(height) => + log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") + hr.registerManifestToDownload(manifest, height, peersToDownload) + availableManifests.clear() + requestMoreChunksIfNeeded(hr) + case None => + log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") + } + case _ => + log.info(s"Penalizing spamming peer $remote sent non-asked manifest $manifestId") + penalizeSpammingPeer(remote) } case Failure(e) => log.info(s"Cant' restore manifest (got from $remote) from bytes ", e) @@ -894,7 +900,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val chunkId = ModifierId @@ Algos.encode(subtree.id) deliveryTracker.getRequestedInfo(UtxoSnapshotChunkTypeId.value, chunkId) match { case Some(ri) if ri.peer == remote => - log.debug(s"Got utxo snapshot chunk, id: ${Algos.encode(subtree.id)}, size: ${serializedChunk.length}") + log.debug(s"Got utxo snapshot chunk, id: $chunkId, size: ${serializedChunk.length}") deliveryTracker.setUnknown(chunkId, UtxoSnapshotChunkTypeId.value) hr.registerDownloadedChunk(subtree.id, serializedChunk) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 94b70eb119..810c6c0d01 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -2,13 +2,9 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} -import org.ergoplatform.settings.Algos.HF -import org.ergoplatform.settings.{ErgoAlgos, ErgoSettings} -import org.ergoplatform.utils.DefaultErgoLogger -import org.ergoplatform.wallet.Constants +import org.ergoplatform.settings.ErgoSettings import scorex.core.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage -import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverSerializer import scorex.crypto.hash.Digest32 import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging @@ -21,8 +17,6 @@ import scala.util.{Failure, Success, Try} */ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { - private val serializer = new BatchAVLProverSerializer[Digest32, HF]()(ErgoAlgos.hash, DefaultErgoLogger) - private val snapshotInfoKey: Array[Byte] = Array.fill(32)(0: Byte) // helper method to write information about store UTXO set snapshots into the database @@ -59,7 +53,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { log.info(s"Pruning snapshot at height $h") val keysToRemove: Array[Array[Byte]] = store.get(manifestId) match { case Some(manifestBytes) => - serializer.manifestFromBytes(manifestBytes, Constants.ModifierIdLength) match { + ManifestSerializer.defaultSerializer.parseBytesTry(manifestBytes) match { case Success(m) => (m.subtreesIds += manifestId).toArray // todo: more efficient construction case Failure(e) => From a5451642351914b1bdfc8af42068b0cc378de0d3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 1 May 2023 18:34:47 +0300 Subject: [PATCH 144/204] polishing requestMoreChunksIfNeeded --- .../network/ErgoNodeViewSynchronizer.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 93bc2287b0..7e33c58ee6 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -842,16 +842,20 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { + // we request more chunks if currently less than `ChunksInParallelMin` chunks are being downloading + // we request up to `ChunksInParallelMin`, so in extreme case may download almost 2 * `ChunksInParallelMin` + // we download new chunks from random peers, `ChunksPerPeer` chunks from a random peer (but we may ask the same + // peer twice as choice is truly random) + val ChunksInParallelMin = 16 + val ChunksPerPeer = 4 hr.utxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => - if (downloadPlan.downloadingChunks < 16) { - (1 to 4).foreach { _ => - val toRequest = hr.getChunkIdsToDownload(4) + + if (downloadPlan.downloadingChunks < ChunksInParallelMin) { + (1 to ChunksPerPeer).foreach { _ => + val toRequest = hr.getChunkIdsToDownload(howMany = ChunksInParallelMin / ChunksPerPeer) hr.randomPeerToDownloadChunks() match { - case Some(remote) => toRequest.foreach { - subtreeId => - requestUtxoSetChunk(subtreeId, remote) - } + case Some(remote) => toRequest.foreach(subtreeId => requestUtxoSetChunk(subtreeId, remote)) case None => log.warn(s"No peers to download chunks from") } From 6b14ebfc558ef0594cc382efccd73fd749598a5c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 1 May 2023 19:01:33 +0300 Subject: [PATCH 145/204] 5.0.12 version set --- src/main/resources/api/openapi.yaml | 2 +- src/main/resources/application.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index a19ecca611..24babc3b23 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: - version: "5.0.10" + version: "5.0.12" title: Ergo Node API description: API docs for Ergo Node. Models are shared between all Ergo products contact: diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 90f89c26e9..e48d13fdf1 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -405,7 +405,7 @@ scorex { nodeName = "ergo-node" # Network protocol version to be sent in handshakes - appVersion = 5.0.10 + appVersion = 5.0.12 # Network agent name. May contain information about client code # stack, starting from core code-base up to the end graphical interface. From 957429b1407aef3f6e719205a3eff1c8ff466289 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 1 May 2023 23:56:32 +0300 Subject: [PATCH 146/204] matching download plan in processUtxoSnapshotChunk --- .../network/ErgoNodeViewSynchronizer.scala | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 7e33c58ee6..def0e31e87 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -908,18 +908,23 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.setUnknown(chunkId, UtxoSnapshotChunkTypeId.value) hr.registerDownloadedChunk(subtree.id, serializedChunk) - val downloadPlanOpt = hr.utxoSetSnapshotDownloadPlan() // todo: match for optional result - if (downloadPlanOpt.map(_.fullyDownloaded).getOrElse(false)) { - if (!hr.isUtxoSnapshotApplied) { - val h = downloadPlanOpt.get.snapshotHeight // todo: .get - val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get - viewHolderRef ! InitStateFromSnapshot(h, blockId) - } else { - log.warn("UTXO set snapshot already applied, double application attemt") - } - } else{ - requestMoreChunksIfNeeded(hr) + hr.utxoSetSnapshotDownloadPlan() match { + case Some(downloadPlan) => + if (downloadPlan.fullyDownloaded) { + if (!hr.isUtxoSnapshotApplied) { + val h = downloadPlan.snapshotHeight + val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get + viewHolderRef ! InitStateFromSnapshot(h, blockId) + } else { + log.warn("UTXO set snapshot already applied, double application attemt") + } + } else { + requestMoreChunksIfNeeded(hr) + } + case None => + log.warn(s"No download plan found when processing UTXO set snapshot chunk $chunkId") } + case _ => log.info(s"Penalizing spamming peer $remote sent non-asked UTXO set snapshot $chunkId") penalizeSpammingPeer(remote) From b59e04f4301b4392c69fc4beb3c22cdf062bdefc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 2 May 2023 01:31:24 +0300 Subject: [PATCH 147/204] bestHeaderIdAtHeight.get fixed --- .../network/ErgoNodeViewSynchronizer.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index def0e31e87..49a94e21dd 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -913,8 +913,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (downloadPlan.fullyDownloaded) { if (!hr.isUtxoSnapshotApplied) { val h = downloadPlan.snapshotHeight - val blockId = hr.bestHeaderIdAtHeight(h).get // todo: .get - viewHolderRef ! InitStateFromSnapshot(h, blockId) + hr.bestHeaderIdAtHeight(h) match{ + case Some(blockId) => + viewHolderRef ! InitStateFromSnapshot(h, blockId) + case None => + // shouldn't happen in principle + log.error("No best header found when all chunks are downloaded. Please contact developers.") + } + } else { log.warn("UTXO set snapshot already applied, double application attemt") } From 95d216823d35497070e3e2629c141b2d057b6dd7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 2 May 2023 14:22:11 +0300 Subject: [PATCH 148/204] _utxoSnapshotApplied made private --- .../storage/modifierprocessors/ToDownloadProcessor.scala | 2 +- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) 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 aa7df2cf6b..0506a567c7 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 @@ -83,7 +83,7 @@ trait ToDownloadProcessor // download children blocks of last 100 full blocks applied to the best chain, to get block sections from forks val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) - case None if (nodeSettings.utxoSettings.utxoBootstrap && !_utxoSnapshotApplied) => + case None if (nodeSettings.utxoSettings.utxoBootstrap && !isUtxoSnapshotApplied) => // todo: can be requested multiple times, prevent it if (utxoSetSnapshotDownloadPlan().isEmpty) { Map(SnapshotsInfoTypeId.value -> Seq.empty) 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 4d8280c6ed..071b76dd69 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 @@ -19,6 +19,8 @@ import scala.util.{Random, Try} import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} /** + * Parts of history processing and storage corresponding to UTXO set snapshot processing and storage + * * Stores: * - writes chunks * - writes data for incomplete snapshots @@ -32,9 +34,12 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private[history] var minimalFullBlockHeightVar: Int - var _utxoSnapshotApplied = false //todo: persistence? + private var _utxoSnapshotApplied = false //todo: persistence? + + def isUtxoSnapshotApplied = { + _utxoSnapshotApplied + } - def isUtxoSnapshotApplied = _utxoSnapshotApplied def utxoSnapshotApplied(height: Height): Unit = { _utxoSnapshotApplied = true minimalFullBlockHeightVar = height + 1 From 57f1010fabe8d2b2796e72f18389ad5b80bce35c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 3 May 2023 01:32:15 +0300 Subject: [PATCH 149/204] removing unused code #1 --- .../UtxoSetSnapshotProcessor.scala | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 071b76dd69..f21c536a9f 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 @@ -45,10 +45,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { minimalFullBlockHeightVar = height + 1 } - // private val expectedChunksPrefix = Blake2b256.hash("expected chunk").drop(4) private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) - private val downloadPlanKey = Blake2b256.hash("download plan") + // private val downloadPlanKey = Blake2b256.hash("download plan") private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None @@ -65,6 +64,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { _manifest = Some(manifest) _peersToDownload = peersToDownload updateUtxoSetSnashotDownloadPlan(plan) + plan } def utxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { @@ -146,15 +146,15 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } - private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan) = { + private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan): Unit = { _cachedDownloadPlan = Some(plan) - historyStorage.insert(downloadPlanKey, plan.id) - writeDownloadPlanToTheDb(plan) // todo: not always write to db - plan + // historyStorage.insert(downloadPlanKey, plan.id) + // writeDownloadPlanToTheDb(plan) // todo: not always write to db } +/* private def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { - /* val w = new VLQByteBufferWriter(new ByteArrayBuilder()) + val w = new VLQByteBufferWriter(new ByteArrayBuilder()) w.putULong(plan.startingTime) w.putULong(plan.latestUpdateTime) w.putUInt(plan.snapshotHeight) @@ -172,10 +172,8 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { historyStorage.insert(expectedChunksPrefix ++ idxBytes, chunkId) idx = idx + 1 } -*/ } - /* private def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { historyStorage.get(id).map { bytes => val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) From ed8b416490e312eb68bcdd8c147ddfbb475b31aa Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 3 May 2023 01:57:09 +0300 Subject: [PATCH 150/204] removing unused code #2 --- .../UtxoSetSnapshotProcessor.scala | 73 ------------------- 1 file changed, 73 deletions(-) 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 f21c536a9f..82af612600 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 @@ -47,16 +47,12 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) - // private val downloadPlanKey = Blake2b256.hash("download plan") - private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None private var _peersToDownload: Seq[ConnectedPeer] = Seq.empty //todo: move to ErgoNodeViewSynchronizer? - def pruneSnapshot(downloadPlan: UtxoSetSnapshotDownloadPlan) = ??? //todo: implement - def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height, peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { @@ -71,13 +67,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { _cachedDownloadPlan match { case s@Some(_) => s case None => None - /* - historyStorage.get(downloadPlanKey).flatMap { planId => - val planOpt = readDownloadPlanFromDb(Digest32 @@ planId) - if (planOpt.isEmpty) log.warn(s"No download plan with id ${Algos.encode(planId)} found") - if (planOpt.nonEmpty) _cachedDownloadPlan = planOpt - planOpt - }*/ } } @@ -148,70 +137,8 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan): Unit = { _cachedDownloadPlan = Some(plan) - // historyStorage.insert(downloadPlanKey, plan.id) - // writeDownloadPlanToTheDb(plan) // todo: not always write to db - } - -/* - private def writeDownloadPlanToTheDb(plan: UtxoSetSnapshotDownloadPlan) = { - val w = new VLQByteBufferWriter(new ByteArrayBuilder()) - w.putULong(plan.startingTime) - w.putULong(plan.latestUpdateTime) - w.putUInt(plan.snapshotHeight) - w.putBytes(plan.utxoSetRootHash) - w.put(plan.utxoSetTreeHeight) - w.putUInt(plan.expectedChunkIds.size) - w.putUInt(plan.downloadedChunkIds.size) - val metaDataBytes = w.result().toBytes - - historyStorage.insert(plan.id, metaDataBytes) - - var idx = 0 - plan.expectedChunkIds.foreach { chunkId => - val idxBytes = Ints.toByteArray(idx) - historyStorage.insert(expectedChunksPrefix ++ idxBytes, chunkId) - idx = idx + 1 - } } - private def readDownloadPlanFromDb(id: Digest32): Option[UtxoSetSnapshotDownloadPlan] = { - historyStorage.get(id).map { bytes => - val r = new VLQByteBufferReader(ByteBuffer.wrap(bytes)) - val startingTime = r.getULong() - val latestChunkFetchTime = r.getULong() - val snapshotHeight = r.getUInt().toInt - val utxoSetRootHash = r.getBytes(Constants.HashLength) - val utxoSetTreeHeight = r.getByte() - val expectedChunksSize = r.getUInt().toInt - val downloadedChunksSize = r.getUInt().toInt - - val expectedChunks = new mutable.ArrayBuffer[SubtreeId](initialSize = expectedChunksSize) - (0 until expectedChunksSize).foreach { idx => - val idxBytes = Ints.toByteArray(idx) - historyStorage.get(expectedChunksPrefix ++ idxBytes) match { - case Some(chunkBytes) => expectedChunks += (Digest32 @@ chunkBytes) - case None => log.warn(s"Expected chunk #${id} not found in the database") - } - } - val downloadedChunks = new mutable.ArrayBuffer[Boolean](initialSize = downloadedChunksSize) - (0 until downloadedChunksSize).foreach { idx => - val idxBytes = Ints.toByteArray(idx) - downloadedChunks += historyStorage.contains(downloadedChunksPrefix ++ idxBytes) - } - - UtxoSetSnapshotDownloadPlan( - startingTime, - latestChunkFetchTime, - snapshotHeight, - Digest32 @@ utxoSetRootHash, - utxoSetTreeHeight, - expectedChunks, - downloadedChunks, - downloadingChunks = 0 - ) - } - }*/ - def createPersistentProver(stateStore: LDBVersionedStore, blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { val manifest = _manifest.get //todo: .get From b73ef2769d9b02e7cdaf16484e4d01a8816506b3 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 3 May 2023 15:53:51 +0300 Subject: [PATCH 151/204] fixes in NetworkObjectTypeId hierarchy, fix in nodeConfigurationReader --- .../modifiers/NetworkObjectTypeId.scala | 25 ++++++++----------- .../settings/NodeConfigurationSettings.scala | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala index 2edffa6d6f..3b6ce1b50d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala +++ b/src/main/scala/org/ergoplatform/modifiers/NetworkObjectTypeId.scala @@ -10,7 +10,7 @@ sealed trait NetworkObjectTypeId { /** * 1-byte ID of network object type */ - val value: NetworkObjectTypeId.Value + def value: NetworkObjectTypeId.Value } object NetworkObjectTypeId { @@ -39,23 +39,13 @@ object NetworkObjectTypeId { /** * Block section to be sent over the wire (header, transactions section, extension, UTXO set transformation proofs) */ -sealed trait BlockSectionTypeId extends NetworkObjectTypeId { - require(value >= BlockSectionThreshold, s"Type id for block section must be >= $BlockSectionThreshold") -} +sealed trait BlockSectionTypeId extends NetworkObjectTypeId /** * Non-block network objects: unconfirmed transactions, utxo set snapshot related data, nipopow related data etc */ -sealed trait AuxiliaryTypeId extends NetworkObjectTypeId { - require(value < BlockSectionThreshold, s"Type id for auxiliary network object must be < DistinguihsingValue") -} +sealed trait AuxiliaryTypeId extends NetworkObjectTypeId -/** - * Unconfirmed transactions sent outside blocks - */ -object TransactionTypeId extends NetworkObjectTypeId { - override val value: Value = fromByte(2) -} /** * Block header, section of a block PoW is done on top of. This section is committing to other sections @@ -67,7 +57,7 @@ object HeaderTypeId extends BlockSectionTypeId { /** * Block transactions sections. Contains all the transactions for a block. */ -object BlockTransactionsTypeId extends AuxiliaryTypeId { +object BlockTransactionsTypeId extends BlockSectionTypeId { override val value: Value = fromByte(102) } @@ -89,6 +79,13 @@ object ExtensionTypeId extends BlockSectionTypeId { override val value: Value = fromByte(108) } +/** + * Unconfirmed transactions sent outside blocks + */ +object TransactionTypeId extends AuxiliaryTypeId { + override val value: Value = fromByte(2) +} + /** * Virtual object which is not being sent over the wire rather, constructed locally from different sections * got over the wire (header, transactions, extension in the "utxo" mode, those three sections plus proofs in diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index 0a87d47f07..705da99f6e 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -82,7 +82,7 @@ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettin stateType, cfg.as[Boolean](s"$path.verifyTransactions"), cfg.as[Int](s"$path.blocksToKeep"), - cfg.as[UtxoSettings](s"$path.utxoSettings"), + cfg.as[UtxoSettings](s"$path.utxo"), cfg.as[Boolean](s"$path.PoPoWBootstrap"), cfg.as[Int](s"$path.minimalSuffix"), cfg.as[Boolean](s"$path.mining"), From 9be0153dca8255492985158ab31fd3a3d9bea4b1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 3 May 2023 20:38:49 +0300 Subject: [PATCH 152/204] close #1991: LDBKVStore.insert for pairs eliminated --- .../batch/VersionedLDBAVLStorage.scala | 2 +- .../src/main/scala/scorex/db/LDBKVStore.scala | 3 --- .../org/ergoplatform/db/LDBStoreBench.scala | 4 ++-- .../history/storage/HistoryStorage.scala | 18 +++++++++++---- .../nodeView/state/SnapshotsDb.scala | 2 +- .../nodeView/wallet/ErgoWalletSupport.scala | 10 ++++---- .../wallet/persistence/WalletStorage.scala | 23 ++++++++----------- .../core/network/peer/PeerDatabase.scala | 2 +- .../ergoplatform/db/KvStoreReaderSpec.scala | 4 ++-- .../org/ergoplatform/db/LDBKVStoreSpec.scala | 7 +++--- .../persistence/WalletRegistryBenchmark.scala | 2 +- .../persistence/WalletStorageSpec.scala | 4 ++-- 12 files changed, 41 insertions(+), 40 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 078eb1988b..da58e31562 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -94,7 +94,7 @@ class VersionedLDBAVLStorage(store: LDBVersionedStore) * * @param dumpStorage - non-versioned storage to dump tree to * @param manifestDepth - depth of manifest tree - * @param expectedRootHash - expected UTXO set aunthenticating tree root hash + * @param expectedRootHash - expected UTXO set authenticating tree root hash * @return - hash of root node of tree, or failure if an error (e.g. in database) happened */ def dumpSnapshot(dumpStorage: LDBKVStore, manifestDepth: Byte, expectedRootHash: Array[Byte]): Try[Array[Byte]] = { diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index a8ead25e37..8f0e3e8d09 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -57,9 +57,6 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging */ def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) - def insert(values: Array[(K, V)]): Try[Unit] = update(values.map(_._1), values.map(_._2), Array.empty) - - /** * `update` variant where we only remove values from this database */ diff --git a/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala b/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala index 652bb40982..cb21d133ab 100644 --- a/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala +++ b/benchmarks/src/test/scala/org/ergoplatform/db/LDBStoreBench.scala @@ -39,7 +39,7 @@ object LDBStoreBench 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(db.insert(_).get) + toInsert.grouped(5).foreach(kv => db.insert(kv.map(_._1), kv.map(_._2)).get) bts -> storeLDB } @@ -55,7 +55,7 @@ object LDBStoreBench 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(db.insert(_).get) + toInsert.grouped(5).foreach(kv => db.insert(kv.map(_._1), kv.map(_._2)).get) } private def benchReadLDB(bts: Seq[BlockTransactions], db: LDBKVStore): Unit = { 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 955df2d600..1cf6f6e151 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -122,13 +122,18 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, e def insert(indexesToInsert: Array[(ByteArrayWrapper, Array[Byte])], objectsToInsert: Array[BlockSection]): Try[Unit] = { objectsStore.insert( - objectsToInsert.map(mod => (mod.serializedId, HistoryModifierSerializer.toBytes(mod))) + objectsToInsert.map(mod => mod.serializedId), + objectsToInsert.map(mod => HistoryModifierSerializer.toBytes(mod)) ).flatMap { _ => cfor(0)(_ < objectsToInsert.length, _ + 1) { i => cacheModifier(objectsToInsert(i))} if (indexesToInsert.nonEmpty) { - indexStore.insert(indexesToInsert.map { case (k, v) => k.data -> v }).map { _ => - cfor(0)(_ < indexesToInsert.length, _ + 1) { i => indexCache.put(indexesToInsert(i)._1, indexesToInsert(i)._2)} - () + indexStore.insert( + indexesToInsert.map(_._1.data), + indexesToInsert.map(_._2) + ).map { _ => + cfor(0)(_ < indexesToInsert.length, _ + 1) { i => + indexCache.put(indexesToInsert(i)._1, indexesToInsert(i)._2) + } } } else Success(()) } @@ -136,7 +141,10 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, e def insertExtra(indexesToInsert: Array[(Array[Byte], Array[Byte])], objectsToInsert: Array[ExtraIndex]): Unit = { - extraStore.insert(objectsToInsert.map(mod => (mod.serializedId, ExtraIndexSerializer.toBytes(mod)))) + extraStore.insert( + 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/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 810c6c0d01..104c073bd8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -22,7 +22,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { // helper method to write information about store UTXO set snapshots into the database /// private[nodeView] as used in some tests private[nodeView] def writeSnapshotsInfo(snapshotsInfo: SnapshotsInfo): Try[Unit] = { - store.insert(Array(snapshotInfoKey -> SnapshotsInfoSerializer.toBytes(snapshotsInfo))) + store.insert(snapshotInfoKey, SnapshotsInfoSerializer.toBytes(snapshotsInfo)) } // helper method to read information about store UTXO set snapshots from the database diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index cef8c8e37b..e8e9a3b224 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -44,7 +44,7 @@ trait ErgoWalletSupport extends ScorexLogging { protected def addSecretToStorage(state: ErgoWalletState, secret: ExtendedSecretKey): Try[ErgoWalletState] = state.walletVars.withExtendedKey(secret).flatMap { newWalletVars => - state.storage.addPublicKeys(secret.publicKey).flatMap { _ => + state.storage.addPublicKey(secret.publicKey).flatMap { _ => newWalletVars.stateCacheOpt.get.withNewPubkey(secret.publicKey).map { updCache => state.copy(walletVars = newWalletVars.copy(stateCacheProvided = Some(updCache))(newWalletVars.settings)) } @@ -94,7 +94,7 @@ trait ErgoWalletSupport extends ScorexLogging { path => masterKey.derive(path) } val oldPubKeys = oldDerivedSecrets.map(_.publicKey) - oldPubKeys.foreach(storage.addPublicKeys(_).get) + oldPubKeys.foreach(storage.addPublicKey(_).get) storage.removePaths().get } } @@ -135,7 +135,7 @@ trait ErgoWalletSupport extends ScorexLogging { if (usePreEip3Derivation) { // If usePreEip3Derivation flag is set in the wallet settings, the first key is the master key val masterPubKey = masterKey.publicKey - state.storage.addPublicKeys(masterPubKey).map { _ => + state.storage.addPublicKey(masterPubKey).map { _ => log.info("Wallet unlock finished using usePreEip3Derivation") updatePublicKeys(state, masterKey, Vector(masterPubKey)) } @@ -150,7 +150,7 @@ trait ErgoWalletSupport extends ScorexLogging { deriveNextKeyForMasterKey(sp, masterKey, usePreEip3Derivation).flatMap { case (derivationResult, newState) => derivationResult.result.flatMap { case (_, _, firstSk) => val firstPk = firstSk.publicKey - newState.storage.addPublicKeys(firstPk).flatMap { _ => + newState.storage.addPublicKey(firstPk).flatMap { _ => newState.storage.updateChangeAddress(P2PKAddress(firstPk.key)(addressEncoder)).map { _ => log.info("Wallet unlock finished") updatePublicKeys(newState, masterKey, Vector(firstPk)) @@ -169,7 +169,7 @@ trait ErgoWalletSupport extends ScorexLogging { } // Add master key's public key to the storage to track payments to it when the wallet is locked if (!state.storage.containsPublicKey(masterKey.path.toPublicBranch)) { - state.storage.addPublicKeys(masterKey.publicKey) + state.storage.addPublicKey(masterKey.publicKey) } log.info("Wallet unlock finished using existing keys in storage") Try(updatePublicKeys(state, masterKey, pubKeys)) 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 fe1cbb237b..87fb3d4ce2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala @@ -53,14 +53,10 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco /** * Store wallet-related public key in the database * - * @param publicKeys - public key to store + * @param publicKey - public key to store */ - def addPublicKeys(publicKeys: ExtendedPublicKey*): Try[Unit] = { - store.insert { - publicKeys.map { publicKey => - pubKeyPrefixKey(publicKey) -> ExtendedPublicKeySerializer.toBytes(publicKey) - }.toArray - } + def addPublicKey(publicKey: ExtendedPublicKey): Try[Unit] = { + store.insert(pubKeyPrefixKey(publicKey), ExtendedPublicKeySerializer.toBytes(publicKey)) } /** @@ -98,8 +94,7 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco * Write state context into the database * @param ctx - state context */ - def updateStateContext(ctx: ErgoStateContext): Try[Unit] = store - .insert(Array(StateContextKey -> ctx.bytes)) + def updateStateContext(ctx: ErgoStateContext): Try[Unit] = store.insert(StateContextKey, ctx.bytes) /** * Read state context from the database @@ -116,7 +111,7 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco */ def updateChangeAddress(address: P2PKAddress): Try[Unit] = { val bytes = settings.chainSettings.addressEncoder.toString(address).getBytes(Constants.StringEncoding) - store.insert(Array(ChangeAddressKey -> bytes)) + store.insert(ChangeAddressKey, bytes) } /** @@ -139,10 +134,10 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco def addScan(scanReq: ScanRequest): Try[Scan] = { val id = ScanId @@ (lastUsedScanId + 1).toShort scanReq.toScan(id).flatMap { app => - store.insert(Array( - scanPrefixKey(id) -> ScanSerializer.toBytes(app), - lastUsedScanIdKey -> Shorts.toByteArray(id) - )).map(_ => app) + store.insert( + Array(scanPrefixKey(id), lastUsedScanIdKey), + Array(ScanSerializer.toBytes(app), Shorts.toByteArray(id)) + ).map(_ => app) } } diff --git a/src/main/scala/scorex/core/network/peer/PeerDatabase.scala b/src/main/scala/scorex/core/network/peer/PeerDatabase.scala index 7abd3b848d..bf9f518bb5 100644 --- a/src/main/scala/scorex/core/network/peer/PeerDatabase.scala +++ b/src/main/scala/scorex/core/network/peer/PeerDatabase.scala @@ -76,7 +76,7 @@ final class PeerDatabase(settings: ErgoSettings) extends ScorexLogging { peerInfo.peerSpec.address.foreach { address => log.debug(s"Updating peer info for $address") peers += address -> peerInfo - persistentStore.insert(Array((serialize(address), PeerInfoSerializer.toBytes(peerInfo)))) + persistentStore.insert(serialize(address), PeerInfoSerializer.toBytes(peerInfo)) } } } diff --git a/src/test/scala/org/ergoplatform/db/KvStoreReaderSpec.scala b/src/test/scala/org/ergoplatform/db/KvStoreReaderSpec.scala index c201ebd15d..9159876876 100644 --- a/src/test/scala/org/ergoplatform/db/KvStoreReaderSpec.scala +++ b/src/test/scala/org/ergoplatform/db/KvStoreReaderSpec.scala @@ -11,10 +11,10 @@ class KvStoreReaderSpec extends AnyPropSpec with Matchers with DBSpec { val keyEnd = byteString("Z") store.getRange(keyStart, keyEnd).length shouldBe 0 - store.insert(Array(keyStart -> keyStart)).get + store.insert(keyStart, keyStart).get store.getRange(keyStart, keyEnd).length shouldBe 1 - store.insert(Array(keyEnd -> keyEnd)).get + store.insert(keyEnd, keyEnd).get store.getRange(keyStart, keyEnd).length shouldBe 2 // keys before the range diff --git a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala index 8182ac3724..cd32b21c29 100644 --- a/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala +++ b/src/test/scala/org/ergoplatform/db/LDBKVStoreSpec.scala @@ -28,11 +28,11 @@ class LDBKVStoreSpec extends AnyPropSpec with Matchers with DBSpec { val valA = byteString("1") val valB = byteString("2") - store.insert(Array(key -> valA)).get + store.insert(key, valA).get store.get(key).toBs shouldBe Some(valA).toBs - store.insert(Array(key -> valB)).get + store.insert(key, valB).get store.get(key).toBs shouldBe Some(valB).toBs @@ -49,7 +49,8 @@ class LDBKVStoreSpec extends AnyPropSpec with Matchers with DBSpec { val valueE = (byteString("E"), byteString("3")) val valueF = (byteString("F"), byteString("4")) - store.insert(Array(valueA, valueB, valueC, valueD, valueE, valueF)).get + val values = Array(valueA, valueB, valueC, valueD, valueE, valueF) + store.insert(values.map(_._1), values.map(_._2)).get store.lastKeyInRange(valueA._1, valueC._1).get.toSeq shouldBe valueC._1.toSeq store.lastKeyInRange(valueD._1, valueF._1).get.toSeq shouldBe valueF._1.toSeq diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala index db0f19b76e..2eec9b4fc6 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala @@ -40,7 +40,7 @@ object WalletRegistryBenchmark extends App with ErgoTestConstants { val derivedSecrets = (1 to 15000).map { i => val k = rootSecret.derive(DerivationPath.fromEncoded(s"m/44'/429'/0'/0/$i").get) - storage.addPublicKeys(k.publicKey).get + storage.addPublicKey(k.publicKey).get k } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala index a78cb1dd4d..1502b35a7c 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala @@ -27,7 +27,7 @@ class WalletStorageSpec val bytes = DerivationPathSerializer.toBytes(path) acc ++ Ints.toByteArray(bytes.length) ++ bytes } - store.insert(Array(SecretPathsKey -> toInsert)).get + store.insert(SecretPathsKey, toInsert).get } forAll(Gen.nonEmptyListOf(derivationPathGen)) { paths => @@ -43,7 +43,7 @@ class WalletStorageSpec forAll(extendedPubKeyListGen) { pubKeys => withStore { store => val storage = new WalletStorage(store, settings) - pubKeys.foreach(storage.addPublicKeys(_).get) + pubKeys.foreach(storage.addPublicKey(_).get) val keysRead = storage.readAllKeys() keysRead.length shouldBe pubKeys.length keysRead should contain theSameElementsAs pubKeys.toSet From fd35b3e23b21734e3037627f192870af804e2f10 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 4 May 2023 12:46:30 +0300 Subject: [PATCH 153/204] 5.0.12 todo removed --- src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala index 2a1a979b76..419fd966a0 100644 --- a/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala +++ b/src/main/scala/org/ergoplatform/network/PeerFilteringRule.scala @@ -91,7 +91,7 @@ object SyncV2Filter extends PeerFilteringRule { * storing and serving them, from peers do not supporting UTXO set snapshots related networking protocol */ object UtxoSetNetworkingFilter extends PeerFilteringRule { - val UtxoSnapsnotActivationVersion = Version(5, 0, 12) // todo: set proper version around release + val UtxoSnapsnotActivationVersion = Version(5, 0, 12) def condition(version: Version): Boolean = { // If neighbour version is >= `UtxoSnapsnotActivationVersion`, the neighbour supports utxo snapshots exchange From 74ef9d6859aa86d2df82106cbaca06a107dc847a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 4 May 2023 15:59:52 +0300 Subject: [PATCH 154/204] handling empty peers in requestDownload, scaladoc fixes --- src/main/resources/api/openapi.yaml | 2 +- .../network/ElementPartitioner.scala | 8 +-- .../network/ErgoNodeViewSynchronizer.scala | 60 ++++++++++--------- .../network/ErgoSyncTracker.scala | 3 + 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/main/resources/api/openapi.yaml b/src/main/resources/api/openapi.yaml index 1d25c831c2..aa8955879c 100644 --- a/src/main/resources/api/openapi.yaml +++ b/src/main/resources/api/openapi.yaml @@ -5090,7 +5090,7 @@ paths: /utxo/getSnapshotsInfo: get: - summary: Get information about saved UTXO snapshots + summary: Get information about locally stored UTXO snapshots operationId: getSnapshotsInfo tags: - utxo diff --git a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala index 4b890189bd..67d1f1e1cc 100644 --- a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala +++ b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala @@ -10,12 +10,12 @@ object ElementPartitioner { * * @param buckets to distribute elements into * @param minElementsPerBucket minimum elements to distribute per bucket + * @param fetchedElems elements to distribute over the buckets * @return elements evenly grouped under unique bucket-type keys */ - def distribute[B, T, I]( - buckets: Iterable[B], - minElementsPerBucket: Int, - fetchedElems: Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { + def distribute[B, T, I](buckets: Iterable[B], + minElementsPerBucket: Int, + fetchedElems: Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { val bucketsCount = buckets.size diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 49a94e21dd..d5c4bcdb71 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -525,18 +525,18 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * A helper method to ask for block section from given peer * * @param modifierTypeId - block section type id - * @param modifierIds - ids of block section to download - * @param peer - peer to download from - * @param checksDone - how many times the block section was requested before - * (non-zero if we're re-requesting the block section, in this case, there should be only - * one id to request in `modifierIds` + * @param modifierIds - ids of block section to download + * @param peer - peer to download from + * @param checksDone - how many times the block section was requested before + * (non-zero if we're re-requesting the block section, in this case, there should be only + * one id to request in `modifierIds` */ def requestBlockSection(modifierTypeId: NetworkObjectTypeId.Value, modifierIds: Seq[ModifierId], peer: ConnectedPeer, checksDone: Int = 0): Unit = { log.debug(s"Requesting block sections of type $modifierTypeId : $modifierIds") - if(checksDone > 0 && modifierIds.length > 1) { + if (checksDone > 0 && modifierIds.length > 1) { log.warn(s"Incorrect state, checksDone > 0 && modifierIds.length > 1 , for $modifierIds of type $modifierTypeId") } val msg = Message(RequestModifierSpec, Right(InvData(modifierTypeId, modifierIds)), None) @@ -557,7 +557,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { - deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(manifestId), peer){ deliveryCheck => + deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(manifestId), peer) { deliveryCheck => context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) } val msg = Message(GetManifestSpec, Right(manifestId), None) @@ -565,7 +565,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { - deliveryTracker.setRequested(UtxoSnapshotChunkTypeId.value, ModifierId @@ Algos.encode(subtreeId), peer){ deliveryCheck => + deliveryTracker.setRequested(UtxoSnapshotChunkTypeId.value, ModifierId @@ Algos.encode(subtreeId), peer) { deliveryCheck => context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) } val msg = Message(GetUtxoSnapshotChunkSpec, Right(subtreeId), None) @@ -575,7 +575,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, def onDownloadRequest(historyReader: ErgoHistory): Receive = { case DownloadRequest(modifiersToFetch: Map[NetworkObjectTypeId.Value, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") - if(modifiersToFetch.nonEmpty) { + if (modifiersToFetch.nonEmpty) { requestDownload( maxModifiers = deliveryTracker.modifiersToDownload, minModifiersPerBucket, @@ -600,7 +600,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * Modifier download method that is given min/max constraints for modifiers to download from peers. * It sends requests for modifiers to given peers in optimally sized batches. * - * @param maxModifiers maximum modifiers to download + * @param maxModifiers maximum modifiers to download * @param minModifiersPerBucket minimum modifiers to download per bucket * @param maxModifiersPerBucket maximum modifiers to download per bucket * @param getPeersOpt optionally get peers to download from, all peers have the same PeerSyncState @@ -608,9 +608,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ protected def requestDownload(maxModifiers: Int, minModifiersPerBucket: Int, maxModifiersPerBucket: Int) (getPeersOpt: => Option[Iterable[ConnectedPeer]]) - (fetchMax: Int => Map[NetworkObjectTypeId.Value, Seq[ModifierId]]): Unit = - getPeersOpt - .foreach { peers => + (fetchMax: Int => Map[NetworkObjectTypeId.Value, Seq[ModifierId]]): Unit = { + getPeersOpt match { + case Some(peers) if peers.nonEmpty => val peersCount = peers.size val maxElementsToFetch = Math.min(maxModifiers, peersCount * maxModifiersPerBucket) val fetched = if (maxElementsToFetch <= 0) { @@ -620,22 +620,26 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } if (fetched.size == 1 && fetched.head._1 == SnapshotsInfoTypeId.value) { requestSnapshotsInfo() + } else { + val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) + // collect and log useful downloading progress information, don't worry it does not run frequently + modifiersByBucket.headOption.foreach { _ => + modifiersByBucket + .groupBy(_._1._2) + .mapValues(_.map(_._2.size)) + .map { case (modType, batchSizes) => + s"Downloading from peers : type[$modType] of ${batchSizes.size} batches each of ~ size: ${batchSizes.take(2).max}" + }.foreach(log.info(_)) + } + // bucket represents a peer and a modifierType as we cannot send mixed types to a peer + modifiersByBucket.foreach { case ((peer, modifierTypeId), modifierIds) => + requestBlockSection(modifierTypeId, modifierIds, peer) + } } - val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) - // collect and log useful downloading progress information, don't worry it does not run frequently - modifiersByBucket.headOption.foreach { _ => - modifiersByBucket - .groupBy(_._1._2) - .mapValues(_.map(_._2.size)) - .map { case (modType, batchSizes) => - s"Downloading from peers : type[$modType] of ${batchSizes.size} batches each of ~ size: ${batchSizes.take(2).max}" - }.foreach(log.info(_)) - } - // bucket represents a peer and a modifierType as we cannot send mixed types to a peer - modifiersByBucket.foreach { case ((peer, modifierTypeId), modifierIds) => - requestBlockSection(modifierTypeId, modifierIds, peer) - } - } + case _ => + log.warn("No peers available in requestDownload") + } + } private def transactionsFromRemote(requestedModifiers: Map[ModifierId, Array[Byte]], mp: ErgoMemPool, diff --git a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala index 95069d6906..3c406f9034 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoSyncTracker.scala @@ -166,6 +166,9 @@ final case class ErgoSyncTracker(networkSettings: NetworkSettings) extends Score } } + /** + * @return all the peers ever sent sync message to the node and still connected + */ def knownPeers(): Iterable[ConnectedPeer] = statuses.keys /** From 0482fc7e7c25aaafedc6b75233866ab0875ce603 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 4 May 2023 23:35:10 +0300 Subject: [PATCH 155/204] handling empty peers case in distribute, more scaladoc --- .../network/ElementPartitioner.scala | 49 ++++++++--------- .../network/ErgoNodeViewSynchronizer.scala | 53 +++++++++++-------- .../nodeView/ErgoNodeViewHolder.scala | 7 +-- .../UtxoSetSnapshotProcessor.scala | 3 ++ .../network/message/BasicMessagesRepo.scala | 15 ++++-- 5 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala index 67d1f1e1cc..939c45f71d 100644 --- a/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala +++ b/src/main/scala/org/ergoplatform/network/ElementPartitioner.scala @@ -16,31 +16,32 @@ object ElementPartitioner { def distribute[B, T, I](buckets: Iterable[B], minElementsPerBucket: Int, fetchedElems: Map[T, Seq[I]]): Map[(B, T), Seq[I]] = { - val bucketsCount = buckets.size - - fetchedElems.foldLeft(Map.empty[(B, T), Seq[I]]) { - case (acc, (elemType, elements)) => - val elementsSize = elements.size - if (elementsSize < 1) { - acc - } else { - val lessBuckets = - if (elementsSize / bucketsCount < minElementsPerBucket) { - buckets.take(Math.max(elementsSize / minElementsPerBucket, 1)) // there must always be at least one bucket - } else { - buckets - } - // now let's distribute elements evenly into buckets - val (quot, rem) = - (elementsSize / lessBuckets.size, elementsSize % lessBuckets.size) - val (smaller, bigger) = elements.splitAt(elementsSize - rem * (quot + 1)) - acc ++ lessBuckets - .zip((smaller.grouped(quot) ++ bigger.grouped(quot + 1)).toSeq) - .map { case (p, elems) => (p, elemType) -> elems } - } - } + if (bucketsCount == 0) { + Map.empty + } else { + fetchedElems.foldLeft(Map.empty[(B, T), Seq[I]]) { + case (acc, (elemType, elements)) => + val elementsSize = elements.size + if (elementsSize < 1) { + acc + } else { + val lessBuckets = + if (elementsSize / bucketsCount < minElementsPerBucket) { + buckets.take(Math.max(elementsSize / minElementsPerBucket, 1)) // there must always be at least one bucket + } else { + buckets + } + // now let's distribute elements evenly into buckets + val (quot, rem) = + (elementsSize / lessBuckets.size, elementsSize % lessBuckets.size) + val (smaller, bigger) = elements.splitAt(elementsSize - rem * (quot + 1)) + acc ++ lessBuckets + .zip((smaller.grouped(quot) ++ bigger.grouped(quot + 1)).toSeq) + .map { case (p, elems) => (p, elemType) -> elems } + } } - + } + } } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index d5c4bcdb71..91aee1350a 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -164,6 +164,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ private var modifiersCacheSize: Int = 0 + /** + * UTXO set snapshot manifests found in the p2p are stored in this table. The table is cleared when a manifest + * if found which available for downloading from at least min number of peers required + */ + private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + /** * To be called when the node is synced and new block arrives, to reset transactions cost counter */ @@ -550,13 +556,18 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - def requestSnapshotsInfo(): Unit = { + /* + * Private helper methods to request UTXO set snapshots metadata and related data (manifests, chunks) from peers + */ + + private def requestSnapshotsInfo(): Unit = { + // ask all the peers supporting UTXO set snapshots for snapshots they have val msg = Message(GetSnapshotsInfoSpec, Right(()), None) val peers = UtxoSetNetworkingFilter.filter(syncTracker.knownPeers()).toSeq networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) } - def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { + private def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(manifestId), peer) { deliveryCheck => context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) } @@ -564,7 +575,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { + private def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { deliveryTracker.setRequested(UtxoSnapshotChunkTypeId.value, ModifierId @@ Algos.encode(subtreeId), peer) { deliveryCheck => context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) } @@ -572,7 +583,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - def onDownloadRequest(historyReader: ErgoHistory): Receive = { + private def onDownloadRequest(historyReader: ErgoHistory): Receive = { case DownloadRequest(modifiersToFetch: Map[NetworkObjectTypeId.Value, Seq[ModifierId]]) => log.debug(s"Downloading via DownloadRequest: $modifiersToFetch") if (modifiersToFetch.nonEmpty) { @@ -619,6 +630,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, fetchMax(maxElementsToFetch) } if (fetched.size == 1 && fetched.head._1 == SnapshotsInfoTypeId.value) { + // special case when underlying logic in `fetchMax` is providing a request + // to start downloading UTXO set snapshots requestSnapshotsInfo() } else { val modifiersByBucket = ElementPartitioner.distribute(peers, minModifiersPerBucket, fetched) @@ -806,33 +819,31 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, modifiersByStatus.getOrElse(Requested, Map.empty) } - protected def sendSnapshotsInfo(usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { + /* + * Private helper methods to send UTXO set snapshots related network messages + */ + + private def sendSnapshotsInfo(usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { val snapshotsInfo = usr.getSnapshotInfo() log.debug(s"Sending snapshots info with ${snapshotsInfo.availableManifests.size} snapshots to $peer") val msg = Message(SnapshotsInfoSpec, Right(snapshotsInfo), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } - protected def sendManifest(id: ManifestId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { + private def sendManifest(id: ManifestId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { usr.getManifestBytes(id) match { - case Some(manifest) => { - val msg = Message(ManifestSpec, Right(manifest), None) + case Some(manifestBytes) => { + val msg = Message(ManifestSpec, Right(manifestBytes), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) } case _ => log.warn(s"No manifest ${Algos.encode(id)} available") } } - /** - * UTXO set snapshot manifests found in the p2p are stored in this table. The table is cleared when manifest - * available for download from at least min number of peers required - */ - private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() - /** * Process information about snapshots got from another peer */ - protected def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { + private def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => log.debug(s"Got manifest $manifestId for height $height from $remote") // add manifest to available manifests dictionary if it is not written there yet @@ -924,7 +935,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // shouldn't happen in principle log.error("No best header found when all chunks are downloaded. Please contact developers.") } - } else { log.warn("UTXO set snapshot already applied, double application attemt") } @@ -1116,11 +1126,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.clearStatusForModifier(modifierId, modifierTypeId, ModifiersStatus.Requested) } else { // A block section is not delivered on time. - log.info(s"Peer ${peer.toString} has not delivered modifier " + + log.info(s"Peer ${peer.toString} has not delivered network object " + s"$modifierTypeId : ${encoder.encodeId(modifierId)} on time") - // Number of block section delivery checks increased or initialized, - // except the case where we can have issues with connectivity, + // Number of delivery checks for a block section, utxo set snapshot chunk or manifest + // increased or initialized, except the case where we can have issues with connectivity, // which is currently defined by comparing request time with time the // node got last modifier (in future we may consider more precise method) val checksDone = deliveryTracker.getRequestedInfo(modifierTypeId, modifierId) match { @@ -1220,7 +1230,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } // check if we have enough UTXO set snapshots for some height - protected def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { + // if so, request manifest from a random peer announced it + private def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots if (settings.nodeSettings.utxoSettings.utxoBootstrap && @@ -1376,7 +1387,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.reset() } - /** get handlers of messages coming from peers */ + /** handlers of messages coming from peers */ private def msgHandlers(hr: ErgoHistory, mp: ErgoMemPool, usrOpt: Option[UtxoStateReader], diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 70e3854b9b..c171c3083f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -282,14 +282,15 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti if (!history().isUtxoSnapshotApplied) { val store = minimalState().store history().createPersistentProver(store, blockId) match { - //todo: pass metadata + //todo: pass metadata? case Success(pp) => log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) val newState = new UtxoState(pp, version = VersionTag @@@ blockId, store, settings) - // todo: apply 10 headers before utxo set snapshot + // todo: apply 10 headers before utxo set snapshot? updateNodeView(updatedState = Some(newState.asInstanceOf[State])) - case Failure(_) => ??? + case Failure(t) => + log.error("UTXO set snapshot application failed: ", t) } } 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 82af612600..93b7d2f28f 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 @@ -101,6 +101,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + /** + * Write serialized UTXO set snapshot chunk to the database + */ def registerDownloadedChunk(chunkId: Array[Byte], chunkSerialized: Array[Byte]): Unit = { utxoSetSnapshotDownloadPlan() match { case Some(plan) => diff --git a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala index 6e760938bb..4ebb13c619 100644 --- a/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala +++ b/src/main/scala/scorex/core/network/message/BasicMessagesRepo.scala @@ -274,6 +274,7 @@ object GetSnapshotsInfoSpec extends MessageSpecV1[Unit] { /** * The `SnapshotsInfo` message is a reply to a `GetSnapshotsInfo` message. + * It contains information about UTXO set snapshots stored locally. */ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { private val SizeLimit = 20000 @@ -301,6 +302,7 @@ object SnapshotsInfoSpec extends MessageSpecV1[SnapshotsInfo] { }.toMap new SnapshotsInfo(manifests) } + } /** @@ -320,13 +322,15 @@ object GetManifestSpec extends MessageSpecV1[ManifestId] { require(r.remaining < SizeLimit, "Too big GetManifest message") Digest32 @@ r.getBytes(Constants.ModifierIdLength) } + } /** * The `Manifest` message is a reply to a `GetManifest` message. + * It contains serialized manifest, top subtree of a tree authenticating UTXO set snapshot */ object ManifestSpec extends MessageSpecV1[Array[Byte]] { - private val SizeLimit = 10000000 + private val SizeLimit = 4000000 override val messageCode: MessageCode = 79: Byte @@ -343,10 +347,11 @@ object ManifestSpec extends MessageSpecV1[Array[Byte]] { val length = r.getUInt().toIntExact r.getBytes(length) } + } /** - * The `GetManifest` sends send utxo subtree (BatchAVLProverSubtree) identifier + * The `GetUtxoSnapshotChunk` sends send utxo subtree (BatchAVLProverSubtree) identifier */ object GetUtxoSnapshotChunkSpec extends MessageSpecV1[SubtreeId] { private val SizeLimit = 100 @@ -363,13 +368,14 @@ object GetUtxoSnapshotChunkSpec extends MessageSpecV1[SubtreeId] { require(r.remaining < SizeLimit, "Too big GetUtxoSnapshotChunk message") Digest32 @@ r.getBytes(Constants.ModifierIdLength) } + } /** - * The `Manifest` message is a reply to a `GetManifest` message. + * The `UtxoSnapshotChunk` message is a reply to a `GetUtxoSnapshotChunk` message. */ object UtxoSnapshotChunkSpec extends MessageSpecV1[Array[Byte]] { - private val SizeLimit = 10000000 + private val SizeLimit = 4000000 override val messageCode: MessageCode = 81: Byte @@ -386,6 +392,7 @@ object UtxoSnapshotChunkSpec extends MessageSpecV1[Array[Byte]] { val length = r.getUInt().toIntExact r.getBytes(length) } + } From fad8784f4d6edeb1483ba7edc86f456917202354 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 5 May 2023 00:36:49 +0300 Subject: [PATCH 156/204] peersToDownload added to download plan --- .../network/ErgoNodeViewSynchronizer.scala | 7 +++++ .../UtxoSetSnapshotProcessor.scala | 26 ++++++++++--------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 91aee1350a..3e039bc861 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1622,6 +1622,13 @@ object ErgoNodeViewSynchronizer { */ 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) } 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 93b7d2f28f..c1ceffe079 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 @@ -34,7 +34,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private[history] var minimalFullBlockHeightVar: Int - private var _utxoSnapshotApplied = false //todo: persistence? + private var _utxoSnapshotApplied = false def isUtxoSnapshotApplied = { _utxoSnapshotApplied @@ -51,14 +51,11 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None - private var _peersToDownload: Seq[ConnectedPeer] = Seq.empty //todo: move to ErgoNodeViewSynchronizer? - def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height, peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { - val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight) + val plan = UtxoSetSnapshotDownloadPlan.fromManifest(manifest, blockHeight, peersToDownload) _manifest = Some(manifest) - _peersToDownload = peersToDownload updateUtxoSetSnashotDownloadPlan(plan) plan } @@ -71,8 +68,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } def randomPeerToDownloadChunks(): Option[ConnectedPeer] = { - if (_peersToDownload.nonEmpty) { - Some(_peersToDownload(Random.nextInt(_peersToDownload.size))) + val peers = _cachedDownloadPlan.map(_.peersToDownload).getOrElse(Seq.empty) + if (peers.nonEmpty) { + Some(peers(Random.nextInt(peers.size))) } else { None } @@ -161,7 +159,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } -//todo: add peers to download from case class UtxoSetSnapshotDownloadPlan(startingTime: Long, latestUpdateTime: Long, snapshotHeight: Height, @@ -169,7 +166,8 @@ case class UtxoSetSnapshotDownloadPlan(startingTime: Long, utxoSetTreeHeight: Byte, expectedChunkIds: IndexedSeq[SubtreeId], downloadedChunkIds: IndexedSeq[Boolean], - downloadingChunks: Int) { + downloadingChunks: Int, + peersToDownload: Seq[ConnectedPeer]) { def id: Digest32 = utxoSetRootHash @@ -185,11 +183,15 @@ case class UtxoSetSnapshotDownloadPlan(startingTime: Long, object UtxoSetSnapshotDownloadPlan { - def fromManifest(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height): UtxoSetSnapshotDownloadPlan = { + def fromManifest(manifest: BatchAVLProverManifest[Digest32], + blockHeight: Height, + peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { val subtrees = manifest.subtreesIds val now = System.currentTimeMillis() - //todo: fix .toByte below by making height byte - UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, IndexedSeq.empty, 0) + + // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less + UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, + IndexedSeq.empty, 0, peersToDownload) } } From 178bd2de81b196ab1d2e204bbfeda4fd1d2996b0 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 5 May 2023 19:06:26 +0300 Subject: [PATCH 157/204] code cleaning 1 --- .../network/ErgoNodeViewSynchronizer.scala | 45 ++++++++++--------- .../ToDownloadProcessor.scala | 7 +-- .../UtxoSetSnapshotProcessor.scala | 4 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 3e039bc861..88ed9a536a 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -840,6 +840,17 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + private def sendUtxoSnapshotChunk(subtreeId: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { + usr.getUtxoSnapshotChunkBytes(subtreeId) match { + case Some(snapChunk) => { + log.debug(s"Sending utxo snapshot chunk (${Algos.encode(subtreeId)}) to $peer") + val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) + networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) + } + case _ => log.warn(s"No chunk ${Algos.encode(subtreeId)} available") + } + } + /** * Process information about snapshots got from another peer */ @@ -851,9 +862,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (!existingOffers.exists(_._1 == remote)) { log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") availableManifests.put(height, existingOffers :+ (remote -> manifestId)) + } else { + log.warn(s"Got manifest $manifestId twice from $remote") } } - checkUtxoSetManifests(hr) + checkUtxoSetManifests(hr) // check if we got enough manifests for the height to download manifests and chunks } private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { @@ -881,14 +894,15 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { - val serializer = ManifestSerializer.defaultSerializer - serializer.parseBytesTry(manifestBytes) match { + // process serialized manifest got from another peer + private def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { + ManifestSerializer.defaultSerializer.parseBytesTry(manifestBytes) match { case Success(manifest) => val manifestId = ModifierId @@ Algos.encode(manifest.id) log.info(s"Got manifest $manifestId from $remote") deliveryTracker.getRequestedInfo(ManifestTypeId.value, manifestId) match { case Some(ri) if ri.peer == remote => + deliveryTracker.setUnknown(manifestId, ManifestTypeId.value) val manifestRecords = availableManifests.filter(_._2.exists(_._2.sameElements(manifest.id))) val heightOfManifest = manifestRecords.headOption.map(_._1) val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq @@ -911,9 +925,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def processUtxoSnapshotChunk(serializedChunk: Array[Byte], - hr: ErgoHistory, - remote: ConnectedPeer): Unit = { + // process utxo set snapshot chunk got from another peer + private def processUtxoSnapshotChunk(serializedChunk: Array[Byte], hr: ErgoHistory, remote: ConnectedPeer): Unit = { SubtreeSerializer.parseBytesTry(serializedChunk) match { case Success(subtree) => val chunkId = ModifierId @@ Algos.encode(subtree.id) @@ -926,9 +939,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, hr.utxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => if (downloadPlan.fullyDownloaded) { + // if all the chunks of snapshot are downloaded, initialize UTXO set state with it if (!hr.isUtxoSnapshotApplied) { val h = downloadPlan.snapshotHeight - hr.bestHeaderIdAtHeight(h) match{ + hr.bestHeaderIdAtHeight(h) match { case Some(blockId) => viewHolderRef ! InitStateFromSnapshot(h, blockId) case None => @@ -939,6 +953,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.warn("UTXO set snapshot already applied, double application attemt") } } else { + // if not all the chunks are downloaded, request more if needed requestMoreChunksIfNeeded(hr) } case None => @@ -956,17 +971,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - protected def sendUtxoSnapshotChunk(subtreeId: SubtreeId, usr: UtxoSetSnapshotPersistence, peer: ConnectedPeer): Unit = { - usr.getUtxoSnapshotChunkBytes(subtreeId) match { - case Some(snapChunk) => { - log.debug(s"Sending utxo snapshot chunk (${Algos.encode(subtreeId)}) to $peer") - val msg = Message(UtxoSnapshotChunkSpec, Right(snapChunk), None) - networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) - } - case _ => log.warn(s"No chunk ${Algos.encode(subtreeId)} available") - } - } - /** * Object ids coming from other node. * Filter out modifier ids that are already in process (requested, received or applied), @@ -1145,6 +1149,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val maxDeliveryChecks = networkSettings.maxDeliveryChecks if (checksDone < maxDeliveryChecks) { if (modifierTypeId == UtxoSnapshotChunkTypeId.value) { + // randomly choosing a peer to download UTXO set snapshot chunk val newPeerOpt = hr.randomPeerToDownloadChunks() log.info(s"Rescheduling request for UTXO set chunk $modifierId , new peer $newPeerOpt") deliveryTracker.setUnknown(modifierId, modifierTypeId) @@ -1153,7 +1158,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case None => log.warn(s"No peer found to download UTXO set chunk $modifierId") } } else { - // randomly choose a peer for another download attempt + // randomly choose a peer for another block sections download attempt val newPeerCandidates: Seq[ConnectedPeer] = if (modifierTypeId == Header.modifierTypeId) { getPeersForDownloadingHeaders(peer).toSeq } else { 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 0506a567c7..8753ef7dd5 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 @@ -34,7 +34,7 @@ trait ToDownloadProcessor def estimatedTip(): Option[Height] /** - * Get modifier ids to download to synchronize full blocks + * Get network object ids to download to synchronize full blocks or start UTXO set snapshot downlood * @param howManyPerType how many ModifierIds per ModifierTypeId to fetch * @param condition only ModifierIds which pass filter are included into results * @return next max howManyPerType ModifierIds by ModifierTypeId to download filtered by condition @@ -84,7 +84,6 @@ trait ToDownloadProcessor val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) case None if (nodeSettings.utxoSettings.utxoBootstrap && !isUtxoSnapshotApplied) => - // todo: can be requested multiple times, prevent it if (utxoSetSnapshotDownloadPlan().isEmpty) { Map(SnapshotsInfoTypeId.value -> Seq.empty) } else { @@ -92,9 +91,7 @@ trait ToDownloadProcessor } case None => // if headers-chain is synced and no full blocks applied yet, find full block height to go from - val res = continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) - log.info("To download: " + res.size) - res + continuation(minimalFullBlockHeight, Map.empty, maxHeight = Int.MaxValue) } } 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 c1ceffe079..2b954e59ef 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 @@ -21,9 +21,7 @@ import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLD /** * Parts of history processing and storage corresponding to UTXO set snapshot processing and storage * - * Stores: - * - writes chunks - * - writes data for incomplete snapshots + * Stores UTXO set snapshots manifests and chunks for incomplete snapshots. */ trait UtxoSetSnapshotProcessor extends ScorexLogging { From 54f601c439094756ab145599ae1cddedd2f03be1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 5 May 2023 20:59:40 +0300 Subject: [PATCH 158/204] _utxoSnapshotApplied removed, startingTime removed from UtxoSetSnapshotDownloadPlan, scaladocs --- .../UtxoSetSnapshotProcessor.scala | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) 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 2b954e59ef..522b8b5a5a 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,6 +2,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId @@ -27,19 +28,33 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { import org.ergoplatform.settings.ErgoAlgos.HF + // node config to read history-related settings here and in descendants protected val settings: ErgoSettings + + // database to read history-related objects here and in descendants protected val historyStorage: HistoryStorage + // minimal height to applu full blocks from + // its value depends on node settings, + // if download with UTXO set snapshot is used, the value is being set to a first block after the snapshot, + // if blockToKeep > 0, the value is being set to a first block of blockchain suffix after headers downloaded private[history] var minimalFullBlockHeightVar: Int - private var _utxoSnapshotApplied = false - def isUtxoSnapshotApplied = { - _utxoSnapshotApplied + /** + * @return if UTXO set snapshot was applied during this session (stored in memory only). + * This flag is needed to prevent double application of UTXO set snapshot. + * After first full-block block application not needed anymore. + */ + def isUtxoSnapshotApplied: Boolean = { + minimalFullBlockHeightVar > ErgoHistory.GenesisHeight } + /** + * Writes that UTXO set snapshot applied at height `height`. Starts full blocks applications since the next block + * after. + */ def utxoSnapshotApplied(height: Height): Unit = { - _utxoSnapshotApplied = true minimalFullBlockHeightVar = height + 1 } @@ -157,8 +172,19 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } -case class UtxoSetSnapshotDownloadPlan(startingTime: Long, - latestUpdateTime: Long, + +/** + * Entity which stores information about state of UTXO set snapshots downloading + * @param latestUpdateTime + * @param snapshotHeight + * @param utxoSetRootHash + * @param utxoSetTreeHeight + * @param expectedChunkIds + * @param downloadedChunkIds + * @param downloadingChunks + * @param peersToDownload + */ +case class UtxoSetSnapshotDownloadPlan(latestUpdateTime: Long, snapshotHeight: Height, utxoSetRootHash: Digest32, utxoSetTreeHeight: Byte, @@ -169,8 +195,14 @@ case class UtxoSetSnapshotDownloadPlan(startingTime: Long, def id: Digest32 = utxoSetRootHash + /** + * @return how many chunks to download + */ def totalChunks: Int = expectedChunkIds.size + /** + * @return whether UTXO set snapshot fully downloaded + */ def fullyDownloaded: Boolean = { (expectedChunkIds.size == downloadedChunkIds.size) && downloadingChunks == 0 && @@ -181,6 +213,10 @@ case class UtxoSetSnapshotDownloadPlan(startingTime: Long, object UtxoSetSnapshotDownloadPlan { + /** + * Create UTXO set snapshot download plan from manifest, height of a block corresponding to UTXO set + * manifest represents, and peers to download UTXO set snapshot from + */ def fromManifest(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height, peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { @@ -188,7 +224,7 @@ object UtxoSetSnapshotDownloadPlan { val now = System.currentTimeMillis() // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less - UtxoSetSnapshotDownloadPlan(now, now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, + UtxoSetSnapshotDownloadPlan(now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, IndexedSeq.empty, 0, peersToDownload) } From 4cc7d5586244d3edd0cba46eef5b133d122980dd Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 May 2023 23:41:13 +0300 Subject: [PATCH 159/204] UtxoSetSnapshotDownloadPlan scaladoc --- .../UtxoSetSnapshotProcessor.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 522b8b5a5a..193898549a 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 @@ -175,14 +175,15 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { /** * Entity which stores information about state of UTXO set snapshots downloading - * @param latestUpdateTime - * @param snapshotHeight - * @param utxoSetRootHash - * @param utxoSetTreeHeight - * @param expectedChunkIds - * @param downloadedChunkIds - * @param downloadingChunks - * @param peersToDownload + * @param latestUpdateTime - latest time when anything was updated for the state of UTXO set snapshot + * @param snapshotHeight - height of a block UTXO set snapshot is corresponding to (UTXO set if after the block applied) + * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snasphot + * @param utxoSetTreeHeight - tree height of AVL+ tree which is authenticating UTXO set snasphot + * @param expectedChunkIds - ids of UTXO set snasphot chunks to be downloaded + * @param downloadedChunkIds - shapshot chunks already downloaded, in form of boolean map over + * `expectedChunkIds` (true = downloaded) + * @param downloadingChunks - number of UTXO set shapshot chunks the node is currently downloading + * @param peersToDownload - peers UTXO set snapshot chunks can be downloaded from */ case class UtxoSetSnapshotDownloadPlan(latestUpdateTime: Long, snapshotHeight: Height, From 133f82d6b387298dad65abd5a9c6b0918160a9f7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 8 May 2023 23:53:27 +0300 Subject: [PATCH 160/204] externalizing UtxoSetSnapshotDownloadPlan --- .../UtxoSetSnapshotDownloadPlan.scala | 67 ++++++++++++++ .../UtxoSetSnapshotProcessor.scala | 91 +++++-------------- 2 files changed, 89 insertions(+), 69 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala new file mode 100644 index 0000000000..098aaa0651 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala @@ -0,0 +1,67 @@ +package org.ergoplatform.nodeView.history.storage.modifierprocessors + +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.nodeView.state.UtxoState.SubtreeId +import scorex.core.network.ConnectedPeer +import scorex.crypto.authds.avltree.batch.serialization.BatchAVLProverManifest +import scorex.crypto.hash.Digest32 + + +/** + * Entity which stores information about state of UTXO set snapshots downloading + * + * @param latestUpdateTime - latest time when anything was updated for the state of UTXO set snapshot + * @param snapshotHeight - height of a block UTXO set snapshot is corresponding to (UTXO set if after the block applied) + * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snasphot + * @param utxoSetTreeHeight - tree height of AVL+ tree which is authenticating UTXO set snasphot + * @param expectedChunkIds - ids of UTXO set snasphot chunks to be downloaded + * @param downloadedChunkIds - shapshot chunks already downloaded, in form of boolean map over + * `expectedChunkIds` (true = downloaded) + * @param downloadingChunks - number of UTXO set shapshot chunks the node is currently downloading + * @param peersToDownload - peers UTXO set snapshot chunks can be downloaded from + */ +case class UtxoSetSnapshotDownloadPlan(latestUpdateTime: Long, + snapshotHeight: Height, + utxoSetRootHash: Digest32, + utxoSetTreeHeight: Byte, + expectedChunkIds: IndexedSeq[SubtreeId], + downloadedChunkIds: IndexedSeq[Boolean], + downloadingChunks: Int, + peersToDownload: Seq[ConnectedPeer]) { + + def id: Digest32 = utxoSetRootHash + + /** + * @return how many chunks to download + */ + def totalChunks: Int = expectedChunkIds.size + + /** + * @return whether UTXO set snapshot fully downloaded + */ + def fullyDownloaded: Boolean = { + (expectedChunkIds.size == downloadedChunkIds.size) && + downloadingChunks == 0 && + downloadedChunkIds.forall(_ == true) + } + +} + +object UtxoSetSnapshotDownloadPlan { + + /** + * Create UTXO set snapshot download plan from manifest, height of a block corresponding to UTXO set + * manifest represents, and peers to download UTXO set snapshot from + */ + def fromManifest(manifest: BatchAVLProverManifest[Digest32], + blockHeight: Height, + peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { + val subtrees = manifest.subtreesIds + val now = System.currentTimeMillis() + + // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less + UtxoSetSnapshotDownloadPlan(now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, + IndexedSeq.empty, 0, peersToDownload) + } + +} 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 193898549a..5dab5ef9f3 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 @@ -6,7 +6,7 @@ import org.ergoplatform.nodeView.history.ErgoHistory 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, ErgoSettings} +import org.ergoplatform.settings.{Algos, ErgoAlgos, ErgoSettings} import scorex.core.VersionTag import scorex.core.network.ConnectedPeer import scorex.core.serialization.SubtreeSerializer @@ -17,7 +17,7 @@ import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor import scala.util.{Random, Try} -import scorex.crypto.authds.avltree.batch.{PersistentBatchAVLProver, VersionedLDBAVLStorage} +import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLProver, VersionedLDBAVLStorage} /** * Parts of history processing and storage corresponding to UTXO set snapshot processing and storage @@ -40,6 +40,11 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { // if blockToKeep > 0, the value is being set to a first block of blockchain suffix after headers downloaded private[history] var minimalFullBlockHeightVar: Int + private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) + + private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None + + private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None /** * @return if UTXO set snapshot was applied during this session (stored in memory only). @@ -58,12 +63,13 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { minimalFullBlockHeightVar = height + 1 } - private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) - - private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None - - private var _cachedDownloadPlan: Option[UtxoSetSnapshotDownloadPlan] = None - + /** + * + * @param manifest + * @param blockHeight + * @param peersToDownload + * @return + */ def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height, peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { @@ -73,6 +79,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { plan } + /** + * @return + */ def utxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { _cachedDownloadPlan match { case s@Some(_) => s @@ -80,6 +89,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + /** + * @return + */ def randomPeerToDownloadChunks(): Option[ConnectedPeer] = { val peers = _cachedDownloadPlan.map(_.peersToDownload).getOrElse(Seq.empty) if (peers.nonEmpty) { @@ -164,69 +176,10 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { log.info("Finished UTXO set snapshot transfer into state database") ldbStorage.restorePrunedProver().map { prunedAvlProver => new PersistentBatchAVLProver[Digest32, HF] { - override var avlProver = prunedAvlProver - override val storage = ldbStorage + override var avlProver: BatchAVLProver[Digest32, ErgoAlgos.HF] = prunedAvlProver + override val storage: VersionedLDBAVLStorage = ldbStorage } } } } } - - -/** - * Entity which stores information about state of UTXO set snapshots downloading - * @param latestUpdateTime - latest time when anything was updated for the state of UTXO set snapshot - * @param snapshotHeight - height of a block UTXO set snapshot is corresponding to (UTXO set if after the block applied) - * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snasphot - * @param utxoSetTreeHeight - tree height of AVL+ tree which is authenticating UTXO set snasphot - * @param expectedChunkIds - ids of UTXO set snasphot chunks to be downloaded - * @param downloadedChunkIds - shapshot chunks already downloaded, in form of boolean map over - * `expectedChunkIds` (true = downloaded) - * @param downloadingChunks - number of UTXO set shapshot chunks the node is currently downloading - * @param peersToDownload - peers UTXO set snapshot chunks can be downloaded from - */ -case class UtxoSetSnapshotDownloadPlan(latestUpdateTime: Long, - snapshotHeight: Height, - utxoSetRootHash: Digest32, - utxoSetTreeHeight: Byte, - expectedChunkIds: IndexedSeq[SubtreeId], - downloadedChunkIds: IndexedSeq[Boolean], - downloadingChunks: Int, - peersToDownload: Seq[ConnectedPeer]) { - - def id: Digest32 = utxoSetRootHash - - /** - * @return how many chunks to download - */ - def totalChunks: Int = expectedChunkIds.size - - /** - * @return whether UTXO set snapshot fully downloaded - */ - def fullyDownloaded: Boolean = { - (expectedChunkIds.size == downloadedChunkIds.size) && - downloadingChunks == 0 && - downloadedChunkIds.forall(_ == true) - } - -} - -object UtxoSetSnapshotDownloadPlan { - - /** - * Create UTXO set snapshot download plan from manifest, height of a block corresponding to UTXO set - * manifest represents, and peers to download UTXO set snapshot from - */ - def fromManifest(manifest: BatchAVLProverManifest[Digest32], - blockHeight: Height, - peersToDownload: Seq[ConnectedPeer]): UtxoSetSnapshotDownloadPlan = { - val subtrees = manifest.subtreesIds - val now = System.currentTimeMillis() - - // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less - UtxoSetSnapshotDownloadPlan(now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, - IndexedSeq.empty, 0, peersToDownload) - } - -} From 4fcb85b1e6097c5648a23f064d9577534f26e270 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 9 May 2023 13:08:08 +0300 Subject: [PATCH 161/204] UtxoSetSnapshotProcessor cleanup and scaladocs --- .../UtxoSetSnapshotProcessor.scala | 75 ++++++++++++------- 1 file changed, 49 insertions(+), 26 deletions(-) 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 5dab5ef9f3..92d0830b7b 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 @@ -16,7 +16,7 @@ import scorex.db.LDBVersionedStore import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor -import scala.util.{Random, Try} +import scala.util.{Failure, Random, Try} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLProver, VersionedLDBAVLStorage} /** @@ -63,12 +63,16 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { minimalFullBlockHeightVar = height + 1 } + private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan): Unit = { + _cachedDownloadPlan = Some(plan) + } + /** - * - * @param manifest - * @param blockHeight - * @param peersToDownload - * @return + * Register manifest as one to be downloaded and create download plan from it + * @param manifest - manifest corresponding to UTXO set snapshot to be downloaded + * @param blockHeight - height of a block corresponding to the manifest + * @param peersToDownload - peers to download chunks related to manifest from + * @return download plan */ def registerManifestToDownload(manifest: BatchAVLProverManifest[Digest32], blockHeight: Height, @@ -80,7 +84,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } /** - * @return + * @return UTXO set snapshot download plan, if available */ def utxoSetSnapshotDownloadPlan(): Option[UtxoSetSnapshotDownloadPlan] = { _cachedDownloadPlan match { @@ -90,7 +94,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } /** - * @return + * @return random peer from which UTXO snapshot chunks can be requested */ def randomPeerToDownloadChunks(): Option[ConnectedPeer] = { val peers = _cachedDownloadPlan.map(_.peersToDownload).getOrElse(Seq.empty) @@ -101,6 +105,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + /** + * @return up to `howMany` ids of UTXO set snapshot chunks to download + */ def getChunkIdsToDownload(howMany: Int): Seq[SubtreeId] = { utxoSetSnapshotDownloadPlan() match { case Some(plan) => @@ -114,7 +121,11 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { log.info(s"Downloaded or waiting ${plan.downloadedChunkIds.size} chunks out of ${expected.size}, downloading ${toDownload.size} more") val newDownloaded = plan.downloadedChunkIds ++ toDownload.map(_ => false) val newDownloading = plan.downloadingChunks + toDownload.size - val updPlan = plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = newDownloaded, downloadingChunks = newDownloading) + val updPlan = plan.copy( + latestUpdateTime = System.currentTimeMillis(), + downloadedChunkIds = newDownloaded, + downloadingChunks = newDownloading + ) _cachedDownloadPlan = Some(updPlan) toDownload @@ -146,6 +157,9 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + /** + * @return iterator for chunks downloded. Reads them from database one-by-one when requested. + */ def downloadedChunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { utxoSetSnapshotDownloadPlan() match { case Some(plan) => @@ -156,30 +170,39 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { .flatMap(bs => SubtreeSerializer.parseBytesTry(bs).toOption) } case None => - log.error("todo: msg") // todo: + log.error("No download plan found in downloadedChunksIterator") Iterator.empty } } - private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan): Unit = { - _cachedDownloadPlan = Some(plan) - } - + /** + * Create disk-persistent authenticated AVL+ tree prover + * @param stateStore - disk database where AVL+ tree will be after restoration + * @param blockId - id of a block corresponding to the tree (tree is on top of a state after the block) + * @return prover with initialized tree database + */ def createPersistentProver(stateStore: LDBVersionedStore, blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { - val manifest = _manifest.get //todo: .get - log.info("Starting UTXO set snapshot transfer into state database") - val esc = ErgoStateReader.storageStateContext(stateStore, settings) - val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { - ldbStorage => - log.info("Finished UTXO set snapshot transfer into state database") - ldbStorage.restorePrunedProver().map { prunedAvlProver => - new PersistentBatchAVLProver[Digest32, HF] { - override var avlProver: BatchAVLProver[Digest32, ErgoAlgos.HF] = prunedAvlProver - override val storage: VersionedLDBAVLStorage = ldbStorage - } + _manifest match { + case Some(manifest) => + log.info("Starting UTXO set snapshot transfer into state database") + val esc = ErgoStateReader.storageStateContext(stateStore, settings) + val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) + VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { + ldbStorage => + log.info("Finished UTXO set snapshot transfer into state database") + ldbStorage.restorePrunedProver().map { + prunedAvlProver => + new PersistentBatchAVLProver[Digest32, HF] { + override var avlProver: BatchAVLProver[Digest32, ErgoAlgos.HF] = prunedAvlProver + override val storage: VersionedLDBAVLStorage = ldbStorage + } + } } + case None => + val msg = "No manifest available in createPersistentProver" + log.error(msg) + Failure(new Exception(msg)) } } } From f635b3768f589d567d50cab51d7574c53a5cae85 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 9 May 2023 13:46:43 +0300 Subject: [PATCH 162/204] more code polishing --- src/main/scala/org/ergoplatform/ErgoApp.scala | 18 ++++++++++-------- .../history/storage/HistoryStorage.scala | 6 ++++++ .../ToDownloadProcessor.scala | 5 ++++- .../nodeView/state/ErgoState.scala | 4 +--- .../nodeView/state/UtxoState.scala | 14 +++++++++----- .../ergoplatform/utils/DefaultErgoLogger.scala | 7 ------- .../network/peer/RestApiUrlPeerFeature.scala | 3 +-- 7 files changed, 31 insertions(+), 26 deletions(-) delete mode 100644 src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala diff --git a/src/main/scala/org/ergoplatform/ErgoApp.scala b/src/main/scala/org/ergoplatform/ErgoApp.scala index 3d5b582968..f83eba0a56 100644 --- a/src/main/scala/org/ergoplatform/ErgoApp.scala +++ b/src/main/scala/org/ergoplatform/ErgoApp.scala @@ -59,15 +59,17 @@ class ErgoApp(args: Args) extends ScorexLogging { else None upnpGateway.foreach(_.addPort(scorexSettings.network.bindAddress.getPort)) - //an address to send to peers - private val externalSocketAddress: Option[InetSocketAddress] = - scorexSettings.network.declaredAddress orElse { - upnpGateway.map(u => - new InetSocketAddress(u.externalAddress, scorexSettings.network.bindAddress.getPort) - ) + // own address to send to peers + private val externalSocketAddress: Option[InetSocketAddress] = { + scorexSettings.network.declaredAddress orElse { + upnpGateway.map(u => + new InetSocketAddress(u.externalAddress, scorexSettings.network.bindAddress.getPort) + ) + } } - private val basicSpecs = { + // descriptors of p2p networking protocol messages + private val p2pMessageSpecifications = { Seq( GetPeersSpec, new PeersSpec(scorexSettings.network.maxPeerSpecObjects), @@ -85,7 +87,7 @@ class ErgoApp(args: Args) extends ScorexLogging { } private val scorexContext = ScorexContext( - messageSpecs = basicSpecs, + messageSpecs = p2pMessageSpecifications, upnpGateway = upnpGateway, externalNodeAddress = externalSocketAddress ) 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 955df2d600..53775b53f1 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -113,9 +113,15 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, e } } + /** + * @return object with `id` if it is in the objects database + */ def get(id: ModifierId): Option[Array[Byte]] = objectsStore.get(idToBytes(id)).orElse(extraStore.get(idToBytes(id))) def get(id: Array[Byte]): Option[Array[Byte]] = objectsStore.get(id).orElse(extraStore.get(id)) + /** + * @return if object with `id` is in the objects database + */ def contains(id: Array[Byte]): Boolean = get(id).isDefined def contains(id: ModifierId): Boolean = get(id).isDefined 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 8753ef7dd5..b666f73130 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 @@ -31,6 +31,9 @@ trait ToDownloadProcessor def isInBestChain(id: ModifierId): Boolean + /** + * @return estimated height of a best chain found in the network + */ def estimatedTip(): Option[Height] /** @@ -44,7 +47,7 @@ trait ToDownloadProcessor val FullBlocksToDownloadAhead = 192 // how many full blocks to download forwards during active sync - def farAwayFromBeingSynced(fb: ErgoFullBlock) = fb.height < (estimatedTip().getOrElse(0) - 128) + def farAwayFromBeingSynced(fb: ErgoFullBlock): Boolean = fb.height < (estimatedTip().getOrElse(0) - 128) @tailrec def continuation(height: Int, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index 8f88940d6f..4ef21d3f2f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -78,9 +78,7 @@ object ErgoState extends ScorexLogging { type ModifierProcessing[T <: ErgoState[T]] = PartialFunction[BlockSection, Try[T]] - def stateDir(settings: ErgoSettings): File = { - new File(s"${settings.directory}/state") - } + def stateDir(settings: ErgoSettings): File = new File(s"${settings.directory}/state") /** * Resolves state changing operations from transactions. There could be invalid sequence diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 0e99b323d2..96a2ce3817 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -26,7 +26,7 @@ import scorex.util.ModifierId import scala.util.{Failure, Success, Try} /** - * Utxo set implementation + * Utxo set based state implementation * * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata @@ -262,15 +262,16 @@ object UtxoState { private lazy val bestVersionKey = Algos.hash("best state version") val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") - // block-specific metadata to write into database (in addition to AVL+ tree) /** + * Block-specific metadata to write into database (in addition to AVL+ tree) * * @param modId - ID of a block (header) corresponding to UTXO set * @param stateRoot - UTXO set digest (hash and tree height) AFTER applying block `modId` - * @param currentEmissionBoxOpt - * @param context - * @return + * @param currentEmissionBoxOpt - current unspent emission script box + * @param context - current state context used in input scripts validation (for the next block) + * + * @return binary-serialized metadata */ def metadata(modId: VersionTag, stateRoot: ADDigest, @@ -286,6 +287,9 @@ object UtxoState { Array(idStateDigestIdxElem, stateDigestIdIdxElem, bestVersion, eb, cb) } + /** + * @return UTXO set based state on top of existing database, or genesis state if the database is empty + */ def create(dir: File, settings: ErgoSettings): UtxoState = { val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) diff --git a/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala b/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala deleted file mode 100644 index e8e493defa..0000000000 --- a/src/main/scala/org/ergoplatform/utils/DefaultErgoLogger.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.ergoplatform.utils - -import scorex.util.ScorexLogging - -object DefaultErgoLogger extends scorex.utils.Logger with ScorexLogging { - override def error(message: String): Unit = log.error(message) -} diff --git a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala index 5216fb6cc6..7f74b765e9 100644 --- a/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala +++ b/src/main/scala/scorex/core/network/peer/RestApiUrlPeerFeature.scala @@ -4,10 +4,9 @@ import scorex.core.network.PeerFeature import scorex.core.network.PeerFeature.Id import scorex.core.serialization.ErgoSerializer import scorex.util.serialization._ - import java.net.URL + import org.ergoplatform.settings.PeerFeatureDescriptors -import scorex.core.serialization.ErgoSerializer /** * Peer may have rest-api URL enabled in which case it needs to be passed to/from other peers From 7a32a78c4842184db3da474b8a964b76a122c6b8 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 9 May 2023 19:06:38 +0300 Subject: [PATCH 163/204] more Scaladoc --- .../ergoplatform/nodeView/state/UtxoState.scala | 14 +++++++++----- .../nodeView/state/UtxoStateReader.scala | 5 +++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala index 0e99b323d2..96a2ce3817 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoState.scala @@ -26,7 +26,7 @@ import scorex.util.ModifierId import scala.util.{Failure, Success, Try} /** - * Utxo set implementation + * Utxo set based state implementation * * @param persistentProver - persistent prover that builds authenticated AVL+ tree on top of utxo set * @param store - storage of persistentProver that also keeps metadata @@ -262,15 +262,16 @@ object UtxoState { private lazy val bestVersionKey = Algos.hash("best state version") val EmissionBoxIdKey: Digest32 = Algos.hash("emission box id key") - // block-specific metadata to write into database (in addition to AVL+ tree) /** + * Block-specific metadata to write into database (in addition to AVL+ tree) * * @param modId - ID of a block (header) corresponding to UTXO set * @param stateRoot - UTXO set digest (hash and tree height) AFTER applying block `modId` - * @param currentEmissionBoxOpt - * @param context - * @return + * @param currentEmissionBoxOpt - current unspent emission script box + * @param context - current state context used in input scripts validation (for the next block) + * + * @return binary-serialized metadata */ def metadata(modId: VersionTag, stateRoot: ADDigest, @@ -286,6 +287,9 @@ object UtxoState { Array(idStateDigestIdxElem, stateDigestIdIdxElem, bestVersion, eb, cb) } + /** + * @return UTXO set based state on top of existing database, or genesis state if the database is empty + */ def create(dir: File, settings: ErgoSettings): UtxoState = { val store = new LDBVersionedStore(dir, initialKeepVersions = settings.nodeSettings.keepVersions) val version = store.get(bestVersionKey).map(w => bytesToVersion(w)) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 69ef7ffc75..56b15d2fe4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -18,6 +18,11 @@ import scorex.crypto.hash.Digest32 import scala.util.{Failure, Success, Try} +/** + * State reader (i.e. state functions not modifyting underlying data) with specialization towards UTXO set as a + * state representation (so functions to generate UTXO set modifiction proofs, do stateful transaction validation, + * get UTXOs are there + */ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { protected implicit val hf: HF = Algos.hash From 1feac924547f5c8d6497a2a7e4db8dc016b2282e Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 11 May 2023 15:17:49 +0300 Subject: [PATCH 164/204] emptyArrayOfByteArray --- avldb/src/main/scala/scorex/db/LDBKVStore.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index 8f0e3e8d09..bd9b70d619 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -13,6 +13,7 @@ import spire.syntax.all.cfor * Both keys and values are var-sized byte arrays. */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { + private val emptyArrayOfByteArray = Array.empty[Array[Byte]] /** * Update this database atomically with a batch of insertion and removal operations @@ -55,12 +56,16 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging /** * `update` variant where we only insert values into this database */ - def insert(keys: Array[K], values: Array[V]): Try[Unit] = update(keys, values, Array.empty) + def insert(keys: Array[K], values: Array[V]): Try[Unit] = { + update(keys, values, emptyArrayOfByteArray) + } /** * `update` variant where we only remove values from this database */ - def remove(keys: Array[K]): Try[Unit] = update(Array.empty, Array.empty, keys) + def remove(keys: Array[K]): Try[Unit] = { + update(emptyArrayOfByteArray, emptyArrayOfByteArray, keys) + } /** * Get last key within some range (inclusive) by used comparator. From f4b26541bbb186d710789106f2c11371efbce840 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 12 May 2023 15:28:54 +0300 Subject: [PATCH 165/204] manifest verification, comments addressed --- .../batch/VersionedLDBAVLStorage.scala | 9 +- .../network/ErgoNodeViewSynchronizer.scala | 107 ++++++++++-------- .../nodeView/ErgoNodeViewHolder.scala | 3 + .../nodeView/state/UtxoStateReader.scala | 5 + .../org/ergoplatform/settings/Constants.scala | 3 + .../settings/NodeConfigurationSettings.scala | 9 ++ 6 files changed, 86 insertions(+), 50 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index da58e31562..2895772ba7 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -231,7 +231,7 @@ object VersionedLDBAVLStorage { * Calculate tree digest, given root node label(hash) and root node height, by appending height to the hash */ def digest[D <: hash.Digest](rootNodeLabel: D, rootNodeHeight: Int): ADDigest = { - assert(rootNodeHeight >= 0 && rootNodeHeight < 256) + assert(rootNodeHeight >= 0 && rootNodeHeight <= 127) // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the // verifier, by adding 256 if it's negative). @@ -240,4 +240,11 @@ object VersionedLDBAVLStorage { ADDigest @@ (rootNodeLabel :+ rootNodeHeight.toByte) } + /** + * splits 33-byte digest into 32-bytes hash and 1-byte tree height + */ + def splitDigest(digest: ADDigest): (Array[Byte], Byte) = { + digest.dropRight(1) -> digest.takeRight(1).head + } + } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 88ed9a536a..47baa3ebd4 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -36,11 +36,10 @@ import scorex.core.network.peer.PenaltyType import scorex.core.transaction.state.TransactionValidation.TooHighCostError import scorex.core.app.Version import scorex.crypto.hash.Digest32 -import scorex.util.encode.Base16 import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} import org.ergoplatform.ErgoLikeContext.Height import scorex.core.serialization.{ErgoSerializer, ManifestSerializer, SubtreeSerializer} - +import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage.splitDigest import scala.annotation.tailrec import scala.collection.mutable import scala.concurrent.ExecutionContext @@ -59,6 +58,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker: DeliveryTracker)(implicit ex: ExecutionContext) extends Actor with Synchronizer with ScorexLogging with ScorexEncoding { + type EncodedManifestId = ModifierId + override val supervisorStrategy: OneForOneStrategy = OneForOneStrategy( maxNrOfRetries = 10, withinTimeRange = 1.minute) { @@ -166,9 +167,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, /** * UTXO set snapshot manifests found in the p2p are stored in this table. The table is cleared when a manifest - * if found which available for downloading from at least min number of peers required + * if found which available for downloading from at least min number of peers required (the min is provided in + * ergo.node.utxo.p2pUtxoSnapshots setting) */ - private val availableManifests = mutable.Map[Height, Seq[(ConnectedPeer, ManifestId)]]() + private val availableManifests = mutable.Map[ModifierId, (Height, Seq[ConnectedPeer])]() /** * To be called when the node is synced and new block arrives, to reset transactions cost counter @@ -851,24 +853,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } - /** - * Process information about snapshots got from another peer - */ - private def processSnapshotsInfo(hr: ErgoHistory, snapshotsInfo: SnapshotsInfo, remote: ConnectedPeer): Unit = { - snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => - log.debug(s"Got manifest $manifestId for height $height from $remote") - // add manifest to available manifests dictionary if it is not written there yet - val existingOffers = availableManifests.getOrElse(height, Seq.empty) - if (!existingOffers.exists(_._1 == remote)) { - log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") - availableManifests.put(height, existingOffers :+ (remote -> manifestId)) - } else { - log.warn(s"Got manifest $manifestId twice from $remote") - } - } - checkUtxoSetManifests(hr) // check if we got enough manifests for the height to download manifests and chunks - } - private def requestMoreChunksIfNeeded(hr: ErgoHistory): Unit = { // we request more chunks if currently less than `ChunksInParallelMin` chunks are being downloading // we request up to `ChunksInParallelMin`, so in extreme case may download almost 2 * `ChunksInParallelMin` @@ -894,6 +878,32 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } } + /** + * Process information about snapshots got from another peer + */ + private def processSnapshotsInfo(hr: ErgoHistory, + snapshotsInfo: SnapshotsInfo, + remote: ConnectedPeer): Unit = { + snapshotsInfo.availableManifests.foreach { case (height, manifestId: ManifestId) => + val encodedManifestId = ModifierId @@ Algos.encode(manifestId) + val ownId = hr.bestHeaderAtHeight(height).map(_.stateRoot).map(stateDigest => splitDigest(stateDigest)._1) + if (ownId.getOrElse(Array.emptyByteArray).sameElements(manifestId)) { + log.debug(s"Got manifest $encodedManifestId for height $height from $remote") + // add manifest to available manifests dictionary if it is not written there yet + val existingOffers = availableManifests.getOrElse(encodedManifestId, (height -> Seq.empty)) + if (!existingOffers._2.contains(remote)) { + log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") + availableManifests.put(encodedManifestId, height -> (existingOffers._2 :+ remote)) + } else { + log.warn(s"Got manifest $manifestId twice from $remote") + } + } else { + log.error(s"Got wrong manifest id $encodedManifestId from $remote") + } + } + checkUtxoSetManifests(hr) // check if we got enough manifests for the height to download manifests and chunks + } + // process serialized manifest got from another peer private def processManifest(hr: ErgoHistory, manifestBytes: Array[Byte], remote: ConnectedPeer): Unit = { ManifestSerializer.defaultSerializer.parseBytesTry(manifestBytes) match { @@ -903,15 +913,28 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, deliveryTracker.getRequestedInfo(ManifestTypeId.value, manifestId) match { case Some(ri) if ri.peer == remote => deliveryTracker.setUnknown(manifestId, ManifestTypeId.value) - val manifestRecords = availableManifests.filter(_._2.exists(_._2.sameElements(manifest.id))) - val heightOfManifest = manifestRecords.headOption.map(_._1) - val peersToDownload: Seq[ConnectedPeer] = manifestRecords.flatMap(_._2.map(_._1)).toSeq - heightOfManifest match { - case Some(height) => - log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") - hr.registerManifestToDownload(manifest, height, peersToDownload) - availableManifests.clear() - requestMoreChunksIfNeeded(hr) + val manifestRecordOpt = availableManifests.get(manifestId) + manifestRecordOpt match { + case Some(manifestRecord) => + val height = manifestRecord._1 + val peersToDownload = manifestRecord._2 + + // check if manifest is valid against root hash and height of AVL+ tree found in a header + val manifestVerified = hr + .bestHeaderAtHeight(height) + .map(_.stateRoot) + .map(splitDigest) + .exists { case (expRoot, expHeight) => manifest.verify(Digest32 @@ expRoot, expHeight) } + + if(manifestVerified) { + log.info(s"Going to download chunks for manifest ${Algos.encode(manifest.id)} at height $height from $peersToDownload") + hr.registerManifestToDownload(manifest, height, peersToDownload) + availableManifests.clear() + requestMoreChunksIfNeeded(hr) + } else { + log.error(s"Got invalid manifest from $remote") + penalizeMaliciousPeer(remote) + } case None => log.error(s"No height found for manifest ${Algos.encode(manifest.id)}") } @@ -1243,25 +1266,11 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, historyReader.fullBlockHeight == 0 && availableManifests.nonEmpty && historyReader.utxoSetSnapshotDownloadPlan().isEmpty) { - val res = availableManifests.filter { case (h, records) => - if (records.length >= MinSnapshots) { - val idsSet = records.map(_._2).map(Base16.encode).toSet - if (idsSet.size > 1) { - log.warn(s"Different manifests found at height $h: $idsSet") - availableManifests.remove(h) - false - } else { - true - } - } else { - false - } - } + val res = availableManifests.filter { case (_, (_, peers)) => peers.length >= MinSnapshots } if (res.nonEmpty) { - val(height, records) = res.maxBy(_._1) - log.info(s"Downloading manifest for height $height from ${records.size} peers") - val manifestId = records.head._2 - val peers = records.map(_._1) + val(encModifierId, (height, peers)) = res.maxBy(_._2._1) + log.info(s"Downloading manifest for height $height from ${peers.size} peers") + val manifestId = Digest32 @@ Algos.decode(encModifierId).get val randomPeer = peers(Random.nextInt(peers.length)) requestManifest(manifestId, randomPeer) } else { diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index c171c3083f..e9481d770f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -277,6 +277,9 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti processingOutcome } + /** + * signal to pull Utxo set snapshot from database and recreate UTXO set from it + */ def processStateSnapshot: Receive = { case InitStateFromSnapshot(height, blockId) => if (!history().isUtxoSnapshotApplied) { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 69ef7ffc75..56b15d2fe4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -18,6 +18,11 @@ import scorex.crypto.hash.Digest32 import scala.util.{Failure, Success, Try} +/** + * State reader (i.e. state functions not modifyting underlying data) with specialization towards UTXO set as a + * state representation (so functions to generate UTXO set modifiction proofs, do stateful transaction validation, + * get UTXOs are there + */ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { protected implicit val hf: HF = Algos.hash diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 4346ab929e..3fc731d858 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -15,6 +15,9 @@ import sigmastate.Values.ErgoTree object Constants { + /** + * Length of hash function output used around the Ergo code + */ val HashLength: Int = scorex.crypto.authds.avltree.batch.Constants.HashLength val CoinsInOneErgo: Long = 1000000000 diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index 705da99f6e..8db13d2657 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -20,8 +20,14 @@ trait CheckpointingSettingsReader extends ModifierIdReader { } } +/** + * Settings related to 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( @@ -72,6 +78,9 @@ case class NodeConfigurationSettings(stateType: StateType, val areSnapshotsStored = utxoSettings.storingUtxoSnapshots > 0 } +/** + * Custom config reader for ergo.node settings section + */ trait NodeConfigurationReaders extends StateTypeReaders with CheckpointingSettingsReader with UtxoSettingsReader with ModifierIdReader { From a272ffc55b8bc1699358e73bca603567f635ed33 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 12 May 2023 15:29:28 +0200 Subject: [PATCH 166/204] utxo-set-bootstrapping: typos --- avldb/src/main/scala/scorex/db/LDBKVStore.scala | 4 ++-- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index bd9b70d619..9c95193e6d 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -18,8 +18,8 @@ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging /** * Update this database atomically with a batch of insertion and removal operations * - * @param toInsertKeys - keys pf key-value pairs to insert into database - * @param toInsertValues - values pf key-value pairs to insert into database + * @param toInsertKeys - keys of key-value pairs to insert into database + * @param toInsertValues - values of key-value pairs to insert into database * @param toRemove - keys of key-value pairs to remove from the database * @return - error if it happens, or success status */ diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 47baa3ebd4..55db9c3fa5 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -167,7 +167,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, /** * UTXO set snapshot manifests found in the p2p are stored in this table. The table is cleared when a manifest - * if found which available for downloading from at least min number of peers required (the min is provided in + * is found which available for downloading from at least min number of peers required (the min is provided in * ergo.node.utxo.p2pUtxoSnapshots setting) */ private val availableManifests = mutable.Map[ModifierId, (Height, Seq[ConnectedPeer])]() From 4c22658743fb6882562d1a114fc9f6f41d9e69e8 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 12 May 2023 19:28:48 +0200 Subject: [PATCH 167/204] utxo-set-bootstrapping-preparation: fixed typos --- .../org/ergoplatform/nodeView/state/UtxoStateReader.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 56b15d2fe4..4e5078290c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -19,9 +19,9 @@ import scorex.crypto.hash.Digest32 import scala.util.{Failure, Success, Try} /** - * State reader (i.e. state functions not modifyting underlying data) with specialization towards UTXO set as a - * state representation (so functions to generate UTXO set modifiction proofs, do stateful transaction validation, - * get UTXOs are there + * State reader (i.e. state functions not modifying underlying data) with specialization towards UTXO set as a + * state representation (so functions to generate UTXO set modification proofs, do stateful transaction validation, + * get UTXOs) are there */ trait UtxoStateReader extends ErgoStateReader with UtxoSetSnapshotPersistence with TransactionValidation { From 76354a69f485e997489f8a3e16d7ac9d8c444a56 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 12:09:03 +0300 Subject: [PATCH 168/204] testing MakeSnapshotEvery value --- src/main/scala/org/ergoplatform/settings/Constants.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 3fc731d858..e2f85f7eba 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -73,8 +73,11 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 - // UTXO set snapshot to be taken every this number of blocks - val MakeSnapshotEvery = 51200 + // todo: change before deployment + /** + * UTXO set snapshot to be taken every this number of blocks + */ + val MakeSnapshotEvery = 53 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From 4bd5d98900ae16210c9348ceb8d4beb1825582b2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 12:18:28 +0300 Subject: [PATCH 169/204] 37 --- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index e2f85f7eba..976e01100a 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 53 + val MakeSnapshotEvery = 37 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From db38364689bf17760c028b735ae5433dc9c52858 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 12:48:55 +0300 Subject: [PATCH 170/204] 60 --- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 976e01100a..418cbdab33 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 37 + val MakeSnapshotEvery = 60 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From a5c9f80e9125d491d78d78b45e6e35060b7491ff Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 13:07:32 +0300 Subject: [PATCH 171/204] mainnet value for storingUtxoSnapshots --- src/main/resources/mainnet.conf | 12 ++++++++++++ .../scala/org/ergoplatform/settings/Constants.scala | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index e84cb490f2..0e47f542a7 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -47,6 +47,18 @@ ergo { injectionBoxBytesEncoded = "80a8d6b9071003040005808098f4e9b5ca6a0402d1ed91c1b2a4730000730193c5a7c5b2a47302008f9e2d0220fa2bf23962cdf51b07722d6237c0c7b8a44f78856c0f7ec308dc1ef1a92a5101d9a2cc8a09abfaed87afacfbb7daee79a6b26f10c6613fc13d3f3953e5521d1a808088fccdbcc32300fca71b8b95f6ad14ce600a126c8842334d40d35f8754176c4cda2c95219f19f700" } + + utxo { + # Download and apply UTXO set snapshot and full-blocks after that + utxoBootstrap = false + + # how many utxo set snapshots to store, 0 means that they are not stored at all + storingUtxoSnapshots = 2 + + # how many utxo set snapshots for a height with same id we need to find in p2p network + # in order to start downloading it + p2pUtxoSnapshots = 2 + } } node { diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 418cbdab33..70520536de 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 60 + val MakeSnapshotEvery = 79 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From ef55f8362dbcd7d13abc6691318cccba7123a137 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 13:34:28 +0300 Subject: [PATCH 172/204] logging in alt branch of InitStateFromSnapshot --- .../scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala | 2 ++ .../nodeView/state/UtxoSetSnapshotPersistence.scala | 5 ++--- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index e9481d770f..30883bcb23 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -295,6 +295,8 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case Failure(t) => log.error("UTXO set snapshot application failed: ", t) } + } else { + log.warn("InitStateFromSnapshot arrived when state already initialized") } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index ba80c504b7..817e1d065e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -51,9 +51,8 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { if (ergoSettings.nodeSettings.areSnapshotsStored && timeToTakeSnapshot(height) && estimatedTip.nonEmpty && - estimatedTip.get - height <= MakeSnapshotEvery) { + estimatedTip.get - height <= MakeSnapshotEvery * ergoSettings.nodeSettings.utxoSettings.storingUtxoSnapshots) { - import scala.concurrent.ExecutionContext.Implicits.global val ms0 = System.currentTimeMillis() // drop tree height byte from digest to get current root hash of AVL+ tree @@ -67,7 +66,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { snapshotsDb.pruneSnapshots(ergoSettings.nodeSettings.utxoSettings.storingUtxoSnapshots) val ft = System.currentTimeMillis() log.info("Work within future finished in: " + (ft - ft0) + " ms.") - } + }(scala.concurrent.ExecutionContext.Implicits.global) val ms = System.currentTimeMillis() log.info("Main thread time to dump utxo set snapshot: " + (ms - ms0)) } diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 70520536de..fc8b259f18 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 79 + val MakeSnapshotEvery = 42 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From 340da7a0cecb31c46c3bf11eaecc1da9232d07e8 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 13:36:04 +0300 Subject: [PATCH 173/204] 43 --- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index fc8b259f18..306b6a2763 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 42 + val MakeSnapshotEvery = 43 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From 899646a579ddde36449be5ea4ac82722c8fcf4ce Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 17:23:03 +0300 Subject: [PATCH 174/204] fixing mainnet utxo bootstrpapping settings --- src/main/resources/mainnet.conf | 24 +++++++++---------- .../state/UtxoSetSnapshotPersistence.scala | 4 ++-- .../org/ergoplatform/settings/Constants.scala | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index 0e47f542a7..cb887169c8 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -47,18 +47,6 @@ ergo { injectionBoxBytesEncoded = "80a8d6b9071003040005808098f4e9b5ca6a0402d1ed91c1b2a4730000730193c5a7c5b2a47302008f9e2d0220fa2bf23962cdf51b07722d6237c0c7b8a44f78856c0f7ec308dc1ef1a92a5101d9a2cc8a09abfaed87afacfbb7daee79a6b26f10c6613fc13d3f3953e5521d1a808088fccdbcc32300fca71b8b95f6ad14ce600a126c8842334d40d35f8754176c4cda2c95219f19f700" } - - utxo { - # Download and apply UTXO set snapshot and full-blocks after that - utxoBootstrap = false - - # how many utxo set snapshots to store, 0 means that they are not stored at all - storingUtxoSnapshots = 2 - - # how many utxo set snapshots for a height with same id we need to find in p2p network - # in order to start downloading it - p2pUtxoSnapshots = 2 - } } node { @@ -84,6 +72,18 @@ ergo { # maximum cost of transaction for it to be propagated maxTransactionCost = 4900000 + + utxo { + # Download and apply UTXO set snapshot and full-blocks after that + utxoBootstrap = false + + # how many utxo set snapshots to store, 0 means that they are not stored at all + storingUtxoSnapshots = 2 + + # how many utxo set snapshots for a height with same id we need to find in p2p network + # in order to start downloading it + p2pUtxoSnapshots = 2 + } } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 817e1d065e..1fea06b39e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -47,11 +47,11 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { def timeToTakeSnapshot(height: Int): Boolean = { height % MakeSnapshotEvery == MakeSnapshotEvery - 1 } - + log.info(s"checking snapshot for $height, simple check: " + timeToTakeSnapshot(height)) if (ergoSettings.nodeSettings.areSnapshotsStored && timeToTakeSnapshot(height) && estimatedTip.nonEmpty && - estimatedTip.get - height <= MakeSnapshotEvery * ergoSettings.nodeSettings.utxoSettings.storingUtxoSnapshots) { + estimatedTip.get - height <= MakeSnapshotEvery) { val ms0 = System.currentTimeMillis() diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 306b6a2763..8576fce566 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 43 + val MakeSnapshotEvery = 100 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From f656c6b3bafab3201dc9df30ae4d4974ba560d0a Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 15 May 2023 17:30:29 +0300 Subject: [PATCH 175/204] 30 --- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 8576fce566..e8d9bb55c9 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -77,7 +77,7 @@ object Constants { /** * UTXO set snapshot to be taken every this number of blocks */ - val MakeSnapshotEvery = 100 + val MakeSnapshotEvery = 30 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From 2698f610956466b9e06e574cdda6ba066ff91292 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 17 May 2023 14:37:26 +0300 Subject: [PATCH 176/204] fixing restoring state context --- .../nodeView/ErgoNodeViewHolder.scala | 4 +- .../UtxoSetSnapshotProcessor.scala | 34 ++++++++++------ .../nodeView/state/ErgoStateReader.scala | 39 ++++++++++++++++++- .../org/ergoplatform/settings/Constants.scala | 7 +++- ...txoSetSnapshotProcessorSpecification.scala | 24 ++++++++++-- 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 30883bcb23..3bc7924c1a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -284,13 +284,11 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti case InitStateFromSnapshot(height, blockId) => if (!history().isUtxoSnapshotApplied) { val store = minimalState().store - history().createPersistentProver(store, blockId) match { - //todo: pass metadata? + history().createPersistentProver(store, history(), height, blockId) match { case Success(pp) => log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") history().utxoSnapshotApplied(height) val newState = new UtxoState(pp, version = VersionTag @@@ blockId, store, settings) - // todo: apply 10 headers before utxo set snapshot? updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(t) => log.error("UTXO set snapshot application failed: ", t) 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 92d0830b7b..22bfe2ed67 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,7 +2,7 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import com.google.common.primitives.Ints import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.nodeView.history.ErgoHistory +import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoState} import org.ergoplatform.nodeView.state.UtxoState.SubtreeId @@ -16,7 +16,7 @@ import scorex.db.LDBVersionedStore import scorex.util.{ModifierId, ScorexLogging} import spire.syntax.all.cfor -import scala.util.{Failure, Random, Try} +import scala.util.{Failure, Random, Success, Try} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLProver, VersionedLDBAVLStorage} /** @@ -178,26 +178,36 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { /** * Create disk-persistent authenticated AVL+ tree prover * @param stateStore - disk database where AVL+ tree will be after restoration + * @param historyReader - history readed to get headers to restore state context + * @param height - height for which prover will be created (prover state will correspond to a + * moment after application of a block at this height) * @param blockId - id of a block corresponding to the tree (tree is on top of a state after the block) * @return prover with initialized tree database */ def createPersistentProver(stateStore: LDBVersionedStore, + historyReader: ErgoHistoryReader, + height: Height, blockId: ModifierId): Try[PersistentBatchAVLProver[Digest32, HF]] = { _manifest match { case Some(manifest) => log.info("Starting UTXO set snapshot transfer into state database") - val esc = ErgoStateReader.storageStateContext(stateStore, settings) - val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) - VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { - ldbStorage => - log.info("Finished UTXO set snapshot transfer into state database") - ldbStorage.restorePrunedProver().map { - prunedAvlProver => - new PersistentBatchAVLProver[Digest32, HF] { - override var avlProver: BatchAVLProver[Digest32, ErgoAlgos.HF] = prunedAvlProver - override val storage: VersionedLDBAVLStorage = ldbStorage + ErgoStateReader.reconstructStateContextBeforeEpoch(stateStore, historyReader, height, settings) match { + case Success(esc) => + val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) + VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { + ldbStorage => + log.info("Finished UTXO set snapshot transfer into state database") + ldbStorage.restorePrunedProver().map { + prunedAvlProver => + new PersistentBatchAVLProver[Digest32, HF] { + override var avlProver: BatchAVLProver[Digest32, ErgoAlgos.HF] = prunedAvlProver + override val storage: VersionedLDBAVLStorage = ldbStorage + } } } + case Failure(e) => + log.warn("Can't reconstruct state context in createPersistentProver ", e) + Failure(e) } case None => val msg = "No manifest available in createPersistentProver" diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala index 74e303d970..18947880c5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateReader.scala @@ -1,13 +1,17 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoBox -import org.ergoplatform.settings.{Algos, ErgoSettings, LaunchParameters, Parameters} +import org.ergoplatform.nodeView.history.ErgoHistory.Height +import org.ergoplatform.nodeView.history.ErgoHistoryReader +import org.ergoplatform.settings.{Algos, Constants, ErgoSettings, LaunchParameters, Parameters} import scorex.core.{NodeViewComponent, VersionTag} import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.LDBVersionedStore import scorex.util.ScorexLogging +import scala.util.{Failure, Success, Try} + /** * State-related data and functions related to any state implementation ("utxo" or "digest") which are * not modifying the state (so only reading it) @@ -69,4 +73,37 @@ object ErgoStateReader extends ScorexLogging { } } + /** + * Method to reconstruct state context (used in scripts execution) corresponding to last block of a voting epoch, + * except of voting-defined blockchain parameters. Basically, this method is setting proper last headers. + * Then the first block of a new epoch will set the parameters. + * @param historyReader - history reader to get heights from + * @param height - height for which state context will be reconstructed + * @param settings - chain and node settings + */ + def reconstructStateContextBeforeEpoch(historyReader: ErgoHistoryReader, + height: Height, + settings: ErgoSettings): Try[ErgoStateContext] = { + val epochLength = settings.chainSettings.voting.votingLength + if (height % epochLength != epochLength - 1) { + Failure(new Exception(s"Wrong height provided in reconstructStateContextBeforeEpoch, height $height, epoch length $epochLength")) + } else { + val lastHeaders = height.until(height - Constants.LastHeadersInContext, -1).flatMap { h => + historyReader.bestHeaderAtHeight(h) + } + if (lastHeaders.size != Constants.LastHeadersInContext) { + Failure(new Exception(s"Only ${lastHeaders.size} headers found in reconstructStateContextBeforeEpoch")) + } else { + val empty = ErgoStateContext.empty(settings, LaunchParameters) + val esc = new ErgoStateContext( lastHeaders, + None, + empty.genesisStateDigest, + empty.currentParameters, + empty.validationSettings, + empty.votingData)(settings) + Success(esc) + } + } + } + } diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index e8d9bb55c9..0c65c56f8e 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -73,10 +73,13 @@ object Constants { // Maximum extension size during bytes parsing val MaxExtensionSizeMax: Int = 1024 * 1024 - // todo: change before deployment /** - * UTXO set snapshot to be taken every this number of blocks + * UTXO set snapshot to be taken every this number of blocks. + * The value MUST be divisible by voting epoch length (chainSettings.voting.votingLength), + * so after snapshot the state is corresponding to a moment before applying first block of a voting epoch, + * and then the first block sets current validation parameters. */ + // todo: change before deployment val MakeSnapshotEvery = 30 /** diff --git a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index b8d065fdc3..dc128d7084 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.history import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.nodeView.history.storage.modifierprocessors.UtxoSetSnapshotProcessor -import org.ergoplatform.nodeView.state.UtxoState +import org.ergoplatform.nodeView.state.{StateType, UtxoState} import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.HistoryTestHelpers import scorex.core.VersionTag @@ -16,17 +16,32 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { private val s = settings + val epochLength = 20 + val utxoSetSnapshotProcessor = new UtxoSetSnapshotProcessor { - override protected val settings: ErgoSettings = s + 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) override private[history] var minimalFullBlockHeightVar = ErgoHistory.GenesisHeight } + var history = generateHistory( + verifyTransactions = true, + StateType.Utxo, + PoPoWBootstrap = false, + blocksToKeep = -1, + epochLength = epochLength, + useLastEpochs = 2, + initialDiffOpt = None) + + val chain = genHeaderChain(epochLength + 1, history, diffBitsOpt = None, useRealTs = false) + history = applyHeaderChain(history, chain) + property("registerManifestToDownload + getUtxoSetSnapshotDownloadPlan + getChunkIdsToDownload") { val bh = boxesHolderGenOfSize(32 * 1024).sample.get val us = createUtxoState(bh, parameters) - val snapshotHeight = 1 + val snapshotHeight = epochLength - 1 val serializer = ManifestSerializer.defaultSerializer us.dumpSnapshot(snapshotHeight, us.rootDigest.dropRight(1)) @@ -60,12 +75,13 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val dir = createTempDir val store = new LDBVersionedStore(dir, initialKeepVersions = 100) - val restoredProver = utxoSetSnapshotProcessor.createPersistentProver(store, blockId).get + val restoredProver = utxoSetSnapshotProcessor.createPersistentProver(store, history, snapshotHeight, blockId).get bh.sortedBoxes.foreach { box => restoredProver.unauthenticatedLookup(box.id).isDefined shouldBe true } restoredProver.checkTree(postProof = false) val restoredState = new UtxoState(restoredProver, version = VersionTag @@@ blockId, store, settings) + restoredState.stateContext.currentHeight shouldBe (epochLength - 1) bh.sortedBoxes.foreach { box => restoredState.boxById(box.id).isDefined shouldBe true } From 4f11ad09613b0ab42acb8c7e5b27dd68270c5258 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 18 May 2023 01:50:03 +0300 Subject: [PATCH 177/204] checking we have enough peers in requestSnapshotsInfo --- .../network/ErgoNodeViewSynchronizer.scala | 20 +++++++++++++++++-- .../UtxoSetSnapshotProcessor.scala | 2 +- .../ergoplatform/settings/ErgoSettings.scala | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 55db9c3fa5..9a6499fb48 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -172,6 +172,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ private val availableManifests = mutable.Map[ModifierId, (Height, Seq[ConnectedPeer])]() + // + private lazy val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots + /** * To be called when the node is synced and new block arrives, to reset transactions cost counter */ @@ -562,11 +565,25 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * Private helper methods to request UTXO set snapshots metadata and related data (manifests, chunks) from peers */ + /* + // todo: register? + + val snapshotsInfoId = ModifierId @@ Algos.encode(Algos.hash("snapshots info")) + deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(snapshotsInfoId), peer) { deliveryCheck => + context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) + } + */ + private def requestSnapshotsInfo(): Unit = { // ask all the peers supporting UTXO set snapshots for snapshots they have val msg = Message(GetSnapshotsInfoSpec, Right(()), None) val peers = UtxoSetNetworkingFilter.filter(syncTracker.knownPeers()).toSeq - networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) + val peersCount = peers.size + if (peersCount >= MinSnapshots) { + networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) + } else { + log.info(s"Less UTXO-snapshot supporting peers found than required mininum ($peersCount < $MinSnapshots)") + } } private def requestManifest(manifestId: ManifestId, peer: ConnectedPeer): Unit = { @@ -1260,7 +1277,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, // check if we have enough UTXO set snapshots for some height // if so, request manifest from a random peer announced it private def checkUtxoSetManifests(historyReader: ErgoHistory): Unit = { - val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots if (settings.nodeSettings.utxoSettings.utxoBootstrap && historyReader.fullBlockHeight == 0 && 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 22bfe2ed67..554a3c207d 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 @@ -191,7 +191,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { _manifest match { case Some(manifest) => log.info("Starting UTXO set snapshot transfer into state database") - ErgoStateReader.reconstructStateContextBeforeEpoch(stateStore, historyReader, height, settings) match { + ErgoStateReader.reconstructStateContextBeforeEpoch(historyReader, height, settings) match { case Success(esc) => val metadata = UtxoState.metadata(VersionTag @@@ blockId, VersionedLDBAVLStorage.digest(manifest.id, manifest.rootHeight), None, esc) VersionedLDBAVLStorage.recreate(manifest, downloadedChunksIterator(), additionalData = metadata.toIterator, stateStore).flatMap { diff --git a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala index 8a1730375c..62e40091ec 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala @@ -211,6 +211,8 @@ object ErgoSettings extends ScorexLogging } 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 { settings } From e0682b7e46611a1ca353bd1909e98e7974dab638 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 18 May 2023 15:42:56 +0300 Subject: [PATCH 178/204] pruning check improved, extra index & pruned blockchain check --- src/main/scala/org/ergoplatform/settings/ErgoSettings.scala | 2 ++ .../org/ergoplatform/settings/NodeConfigurationSettings.scala | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala index 62e40091ec..a90cc88b3d 100644 --- a/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ErgoSettings.scala @@ -213,6 +213,8 @@ object ErgoSettings extends ScorexLogging 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 { settings } diff --git a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala index 8db13d2657..72e47a5a05 100644 --- a/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/NodeConfigurationSettings.scala @@ -73,7 +73,7 @@ case class NodeConfigurationSettings(stateType: StateType, * Whether the node keeping all the full blocks of the blockchain or not. * @return true if the blockchain is pruned, false if not */ - val isFullBlocksPruned: Boolean = blocksToKeep >= 0 + val isFullBlocksPruned: Boolean = blocksToKeep >= 0 || utxoSettings.utxoBootstrap val areSnapshotsStored = utxoSettings.storingUtxoSnapshots > 0 } From f8e56e7622769fe37c954b751479b0e4e690ddd1 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 18 May 2023 20:06:03 +0300 Subject: [PATCH 179/204] MakeSnapshotEvery improved comment --- src/main/scala/org/ergoplatform/settings/Constants.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 0c65c56f8e..a8eb0d596e 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -78,8 +78,11 @@ object Constants { * The value MUST be divisible by voting epoch length (chainSettings.voting.votingLength), * so after snapshot the state is corresponding to a moment before applying first block of a voting epoch, * and then the first block sets current validation parameters. + * + * So for the Ergo mainnet the value should be divisible by 1024 (for testnet, 128, any number divisible by + * 1024 is divisible by 128 also. */ - // todo: change before deployment + // todo: change before deployment to 51200 val MakeSnapshotEvery = 30 /** From 1edaf4d299f7f0a53cece28602810be302fc06af Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 19 May 2023 01:48:39 +0300 Subject: [PATCH 180/204] pruning downloaded chunks --- .../network/ErgoNodeViewSynchronizer.scala | 2 +- .../UtxoSetSnapshotProcessor.scala | 28 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 9a6499fb48..dcf19da0cd 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -610,7 +610,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, maxModifiers = deliveryTracker.modifiersToDownload, minModifiersPerBucket, maxModifiersPerBucket - )(getPeersForDownloadingBlocks) { howManyPerType => + )(getPeersForDownloadingBlocks) { _ => // leave block section ids only not touched before modifiersToFetch.flatMap { case (tid, mids) => val updMids = mids.filter { mid => 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 554a3c207d..9bf88a187f 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 @@ -60,6 +60,20 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { * after. */ def utxoSnapshotApplied(height: Height): Unit = { + // remove downloaded utxo set snapshots chunks + val ts0 = System.currentTimeMillis() + _cachedDownloadPlan.foreach { plan => + val chunkIdsToRemove = downloadedChunkIdsIterator(plan.totalChunks) + .map(chunkId => ModifierId @@ Algos.encode(chunkId)) + .toArray + historyStorage.remove(Array.empty, chunkIdsToRemove) + } + _manifest = None + _cachedDownloadPlan = None + val ts = System.currentTimeMillis() + log.info(s"Imported UTXO set snapshots chunks removed in ${ts - ts0} ms") + + // set height of first full block to be downloaded minimalFullBlockHeightVar = height + 1 } @@ -157,16 +171,22 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + private def downloadedChunkIdsIterator(totalChunks: Int): Iterator[Array[Byte]] = { + Iterator.range(0, totalChunks).map { idx => + val idxBytes = Ints.toByteArray(idx) + downloadedChunksPrefix ++ idxBytes + } + } + /** - * @return iterator for chunks downloded. Reads them from database one-by-one when requested. + * @return iterator for chunks downloaded. Reads them from database one-by-one when requested. */ def downloadedChunksIterator(): Iterator[BatchAVLProverSubtree[Digest32]] = { utxoSetSnapshotDownloadPlan() match { case Some(plan) => - Iterator.range(0, plan.totalChunks).flatMap{idx => - val idxBytes = Ints.toByteArray(idx) + downloadedChunkIdsIterator(plan.totalChunks).flatMap { chunkId => historyStorage - .get(downloadedChunksPrefix ++ idxBytes) + .get(chunkId) .flatMap(bs => SubtreeSerializer.parseBytesTry(bs).toOption) } case None => From 0a033f16cc0d1b4abe353a7e415903371232beeb Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Fri, 19 May 2023 13:55:49 +0300 Subject: [PATCH 181/204] chunkIdFromIndex --- .../UtxoSetSnapshotProcessor.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 9bf88a187f..68ac944c2b 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 @@ -157,8 +157,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { case Some(plan) => cfor(0)(_ < plan.downloadedChunkIds.size, _ + 1) { idx => if (!plan.downloadedChunkIds(idx) && plan.expectedChunkIds(idx).sameElements(chunkId)) { - val idxBytes = Ints.toByteArray(idx) - historyStorage.insert(downloadedChunksPrefix ++ idxBytes, chunkSerialized) + historyStorage.insert(chunkIdFromIndex(idx), chunkSerialized) val updDownloaded = plan.downloadedChunkIds.updated(idx, true) val updDownloading = plan.downloadingChunks - 1 val updPlan = plan.copy(latestUpdateTime = System.currentTimeMillis(), downloadedChunkIds = updDownloaded, downloadingChunks = updDownloading) @@ -171,11 +170,13 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { } } + private def chunkIdFromIndex(index: Int): Array[Byte] = { + val idxBytes = Ints.toByteArray(index) + downloadedChunksPrefix ++ idxBytes + } + private def downloadedChunkIdsIterator(totalChunks: Int): Iterator[Array[Byte]] = { - Iterator.range(0, totalChunks).map { idx => - val idxBytes = Ints.toByteArray(idx) - downloadedChunksPrefix ++ idxBytes - } + Iterator.range(0, totalChunks).map(chunkIdFromIndex) } /** @@ -235,4 +236,5 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { Failure(new Exception(msg)) } } + } From 1b4307cfc7600171a9c2737ea82118c39815beae Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 20 May 2023 01:01:41 +0300 Subject: [PATCH 182/204] dumping snapshot improved --- .../VersionedLDBAVLStorageSpecification.scala | 27 ++++++++++++------- .../network/ErgoNodeViewSynchronizer.scala | 4 +-- 2 files changed, 20 insertions(+), 11 deletions(-) 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 afa039c73d..931ad46baa 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.crypto.authds.avltree.batch.VersionedLDBAVLStorage.topNodeHashKey +import scorex.core.serialization.{ManifestSerializer, SubtreeSerializer} import scorex.crypto.authds.avltree.batch.helpers.TestHelper import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 @@ -19,10 +19,11 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} import scala.util.{Success, Try} -class VersionedLDBAVLStorageSpecification extends AnyPropSpec - with ScalaCheckPropertyChecks - with Matchers - with TestHelper { +class VersionedLDBAVLStorageSpecification + extends AnyPropSpec + with ScalaCheckPropertyChecks + with Matchers + with TestHelper { override protected val KL = 32 override protected val VL = 8 @@ -341,14 +342,22 @@ class VersionedLDBAVLStorageSpecification extends AnyPropSpec } property("dumping snapshot") { + val manifestDepth: Byte = 6 val prover = createPersistentProver() blockchainWorkflowTest(prover) val storage = prover.storage.asInstanceOf[VersionedLDBAVLStorage] - val store = LDBFactory.createKvDb("/tmp/aa") - - storage.dumpSnapshot(store, 4, prover.digest.dropRight(1)) - store.get(topNodeHashKey).sameElements(prover.digest.dropRight(1)) shouldBe true + val store = LDBFactory.createKvDb(getRandomTempDir.getAbsolutePath) + + val rootNodeLabel = storage.dumpSnapshot(store, manifestDepth, prover.digest.dropRight(1)).get + rootNodeLabel.sameElements(prover.digest.dropRight(1)) shouldBe true + val manifestBytes = store.get(rootNodeLabel).get + val manifest = new ManifestSerializer(manifestDepth).parseBytesTry(manifestBytes).get + val subtreeIds = manifest.subtreesIds + subtreeIds.foreach { sid => + val chunkBytes = store.get(sid).get + SubtreeSerializer.parseBytesTry(chunkBytes).get.id.sameElements(sid) shouldBe true + } } } diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index dcf19da0cd..e207eb71d9 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -905,14 +905,14 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, val encodedManifestId = ModifierId @@ Algos.encode(manifestId) val ownId = hr.bestHeaderAtHeight(height).map(_.stateRoot).map(stateDigest => splitDigest(stateDigest)._1) if (ownId.getOrElse(Array.emptyByteArray).sameElements(manifestId)) { - log.debug(s"Got manifest $encodedManifestId for height $height from $remote") + log.debug(s"Discovered manifest $encodedManifestId for height $height from $remote") // add manifest to available manifests dictionary if it is not written there yet val existingOffers = availableManifests.getOrElse(encodedManifestId, (height -> Seq.empty)) if (!existingOffers._2.contains(remote)) { log.info(s"Found new manifest ${Algos.encode(manifestId)} for height $height at $remote") availableManifests.put(encodedManifestId, height -> (existingOffers._2 :+ remote)) } else { - log.warn(s"Got manifest $manifestId twice from $remote") + log.warn(s"Double manifest declaration for $manifestId from $remote") } } else { log.error(s"Got wrong manifest id $encodedManifestId from $remote") From d639335ff05dd532fd12e2674a634b62373b41dc Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 20 May 2023 02:00:12 +0300 Subject: [PATCH 183/204] cross-checking ManifestSerializer in dumping snapshot test --- .../core/serialization/ManifestSerializer.scala | 9 ++++++--- .../batch/VersionedLDBAVLStorageSpecification.scala | 12 +++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index eb451e16b2..9dc05e0cea 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -11,8 +11,7 @@ import scorex.util.serialization.{Reader, Writer} class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLProverManifest[DigestType]] { private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer - override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { - val rootNodeHeight = manifest.rootHeight.toByte + def serialize(rootNode: ProverNodes[DigestType], rootNodeHeight: Byte, w: Writer): Unit = { w.put(rootNodeHeight) w.put(manifestDepth) @@ -24,10 +23,14 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro case i: InternalProverNode[DigestType] if level < manifestDepth => loop(i.left, level + 1) loop(i.right, level + 1) + case _: InternalProverNode[DigestType] => } } - loop(manifest.root, level = 1) + loop(rootNode, level = 1) + } + override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { + serialize(manifest.root, manifest.rootHeight.toByte, w) } override def parse(r: Reader): BatchAVLProverManifest[DigestType] = { 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 931ad46baa..1c72d26b84 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 @@ -12,6 +12,8 @@ import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} import scorex.util.encode.Base16 import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.db.{LDBFactory, LDBVersionedStore} +import scorex.util.ByteArrayBuilder +import scorex.util.serialization.VLQByteBufferWriter import scorex.utils.{Random => RandomBytes} import scala.concurrent.ExecutionContext.Implicits.global @@ -343,6 +345,7 @@ class VersionedLDBAVLStorageSpecification property("dumping snapshot") { val manifestDepth: Byte = 6 + val manifestSerializer = new ManifestSerializer(manifestDepth) val prover = createPersistentProver() blockchainWorkflowTest(prover) @@ -352,7 +355,14 @@ class VersionedLDBAVLStorageSpecification val rootNodeLabel = storage.dumpSnapshot(store, manifestDepth, prover.digest.dropRight(1)).get rootNodeLabel.sameElements(prover.digest.dropRight(1)) shouldBe true val manifestBytes = store.get(rootNodeLabel).get - val manifest = new ManifestSerializer(manifestDepth).parseBytesTry(manifestBytes).get + val manifest = manifestSerializer.parseBytesTry(manifestBytes).get + + val writer = new VLQByteBufferWriter(new ByteArrayBuilder()) + manifestSerializer.serialize(prover.prover().topNode, prover.prover().rootNodeHeight.toByte, writer) + val altManifestBytes = writer.result().toBytes + + manifestBytes.sameElements(altManifestBytes) shouldBe true + val subtreeIds = manifest.subtreesIds subtreeIds.foreach { sid => val chunkBytes = store.get(sid).get From 2e6f6a7d0561f2455fb829485590b91c53a10c05 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 22 May 2023 19:26:01 +0300 Subject: [PATCH 184/204] fixing issue with removal of non-modified subtrees in snapshots db --- .../scala/scorex/db/LDBVersionedStore.scala | 3 + .../UtxoSetSnapshotDownloadPlan.scala | 11 ++- .../nodeView/state/SnapshotsDb.scala | 91 ++++++++++++------- .../state/UtxoSetSnapshotPersistence.scala | 2 +- 4 files changed, 71 insertions(+), 36 deletions(-) diff --git a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala index 7cd107a9ad..51340723f9 100644 --- a/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBVersionedStore.scala @@ -423,7 +423,10 @@ class LDBVersionedStore(protected val dir: File, val initialKeepVersions: Int) def processSnapshot[T](logic: SnapshotReadInterface => T): Try[T] = { val ro = new ReadOptions() try { + lock.writeLock().lock() ro.snapshot(db.getSnapshot) + lock.writeLock().unlock() + object readInterface extends SnapshotReadInterface { def get(key: Array[Byte]): Array[Byte] = db.get(key, ro) } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala index 098aaa0651..9a4e26b865 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala @@ -60,8 +60,15 @@ object UtxoSetSnapshotDownloadPlan { val now = System.currentTimeMillis() // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less - UtxoSetSnapshotDownloadPlan(now, blockHeight, manifest.id, manifest.rootHeight.toByte, subtrees.toIndexedSeq, - IndexedSeq.empty, 0, peersToDownload) + UtxoSetSnapshotDownloadPlan( + latestUpdateTime = now, + snapshotHeight = blockHeight, + utxoSetRootHash = manifest.id, + utxoSetTreeHeight = manifest.rootHeight.toByte, + expectedChunkIds = subtrees.toIndexedSeq, + downloadedChunkIds = IndexedSeq.empty, + downloadingChunks = 0, + peersToDownload = peersToDownload) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index 104c073bd8..ba157672d9 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -2,7 +2,7 @@ package org.ergoplatform.nodeView.state import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.nodeView.state.UtxoState.{ManifestId, SubtreeId} -import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.settings.{Algos, ErgoSettings} import scorex.core.serialization.ManifestSerializer import scorex.crypto.authds.avltree.batch.VersionedLDBAVLStorage import scorex.crypto.hash.Digest32 @@ -10,6 +10,8 @@ import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging import scorex.util.encode.Base16 +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer import scala.util.{Failure, Success, Try} /** @@ -39,48 +41,69 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { // sort manifests by height to prune oldest ones after val manifests = readSnapshotsInfo.availableManifests.toSeq.sortBy(_._1) - - val (toPrune, toLeave) = if (manifests.size > toStore) { - val tp = manifests.dropRight(toStore) - val tl = manifests.takeRight(toStore) - tp -> tl - } else { - log.info("No snapshots to prune") - return - } - - toPrune.foreach { case (h, manifestId) => - log.info(s"Pruning snapshot at height $h") - val keysToRemove: Array[Array[Byte]] = store.get(manifestId) match { - case Some(manifestBytes) => - ManifestSerializer.defaultSerializer.parseBytesTry(manifestBytes) match { - case Success(m) => - (m.subtreesIds += manifestId).toArray // todo: more efficient construction - case Failure(e) => - log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) - Array.empty - } - case None => - log.error(s"Manifest ${Base16.encode(manifestId)} not found:") - Array.empty + val manifestSerializer = ManifestSerializer.defaultSerializer + + if (manifests.size > toStore) { + + val lastManifestBytes = manifests.last._2 + val lastManifestSubtrees = + manifestSerializer + .parseBytesTry(lastManifestBytes) + .map(_.subtreesIds) + .getOrElse(ArrayBuffer.empty) + .map(Algos.encode) + .toSet + + val toPrune = manifests.dropRight(toStore) + val toLeave = manifests.takeRight(toStore) + + toPrune.foreach { case (h, manifestId) => + val pt0 = System.currentTimeMillis() + val keysToRemove: Array[Array[Byte]] = store.get(manifestId) match { + case Some(manifestBytes) => + manifestSerializer.parseBytesTry(manifestBytes) match { + case Success(m) => + val keys = mutable.ArrayBuilder.make[Array[Byte]]() + val subtrees = m.subtreesIds + keys.sizeHint(subtrees.size + 1) + keys += manifestId + // filter out subtrees which are the same in the latest version of tree snapshot + subtrees.foreach { subtreeId => + if (!lastManifestSubtrees.contains(Algos.encode(subtreeId))) { + keys += subtreeId + } + } + keys.result() + case Failure(e) => + log.error(s"Can't parse manifest ${Base16.encode(manifestId)} :", e) + Array.empty + } + case None => + log.error(s"Manifest ${Base16.encode(manifestId)} not found when should be pruned") + Array.empty + } + store.remove(keysToRemove) + val pt = System.currentTimeMillis() + log.info(s"Pruning snapshot at height $h done in ${pt - pt0} ms.") } - store.remove(keysToRemove) - } - val updInfo = new SnapshotsInfo(toLeave.toMap) - writeSnapshotsInfo(updInfo) + val updInfo = new SnapshotsInfo(toLeave.toMap) + writeSnapshotsInfo(updInfo) - log.info("Snapshots pruning finished") + log.info("Snapshots pruning finished") + } else { + log.debug("No snapshots to prune") + } } /** * Lazily read current UTXO set snapshot from versioned AVL+ tree database and store it in this snapshots database * - * @param pullFrom - versioned AVL+ tree database to pull snapshot from - * @param height - height of a block snapshot is corresponding to + * @param pullFrom - versioned AVL+ tree database to pull snapshot from + * @param height - height of a block snapshot is corresponding to * @param expectedRootHash - expected tree root hash in `pullFrom` database * @return - id of the snapshot (root hash of its authenticating AVL+ tree), - * or error happened during read-write process + * or error happened during read-write process */ def writeSnapshot(pullFrom: VersionedLDBAVLStorage, height: Height, @@ -95,6 +118,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { /** * Read manifest bytes without deserializing it. Useful when manifest is to be sent over the wir + * * @param id - manifest id */ def readManifestBytes(id: ManifestId): Option[Array[Byte]] = { @@ -103,6 +127,7 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { /** * Read subtree bytes without deserializing it. Useful when subtree is to be sent over the wir + * * @param id - subtree id */ def readSubtreeBytes(id: SubtreeId): Option[Array[Byte]] = { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala index 1fea06b39e..7194904cc8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoSetSnapshotPersistence.scala @@ -68,7 +68,7 @@ trait UtxoSetSnapshotPersistence extends ScorexLogging { log.info("Work within future finished in: " + (ft - ft0) + " ms.") }(scala.concurrent.ExecutionContext.Implicits.global) val ms = System.currentTimeMillis() - log.info("Main thread time to dump utxo set snapshot: " + (ms - ms0)) + log.info("Main thread time to dump utxo set snapshot: " + (ms - ms0) + " ms.") } } From 322dcf709c823ac114b7fa2c5d2cf93057a64fe2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 23 May 2023 15:24:06 +0300 Subject: [PATCH 185/204] pruneSnapshots fix --- .../scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala index ba157672d9..5170247a7c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsDb.scala @@ -45,10 +45,10 @@ class SnapshotsDb(store: LDBKVStore) extends ScorexLogging { if (manifests.size > toStore) { - val lastManifestBytes = manifests.last._2 + val lastManifestBytesOpt = store.get(manifests.last._2) val lastManifestSubtrees = - manifestSerializer - .parseBytesTry(lastManifestBytes) + lastManifestBytesOpt + .flatMap(bs => manifestSerializer.parseBytesTry(bs).toOption) .map(_.subtreesIds) .getOrElse(ArrayBuffer.empty) .map(Algos.encode) From a4c3b4b55ed6295d5617541d1bd2eb36f70de833 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Tue, 23 May 2023 17:45:59 +0300 Subject: [PATCH 186/204] 500 --- .../org/ergoplatform/network/ErgoNodeViewSynchronizer.scala | 6 +++--- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index e207eb71d9..6ddb56123e 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -971,7 +971,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, case Success(subtree) => val chunkId = ModifierId @@ Algos.encode(subtree.id) deliveryTracker.getRequestedInfo(UtxoSnapshotChunkTypeId.value, chunkId) match { - case Some(ri) if ri.peer == remote => + case Some(_) => log.debug(s"Got utxo snapshot chunk, id: $chunkId, size: ${serializedChunk.length}") deliveryTracker.setUnknown(chunkId, UtxoSnapshotChunkTypeId.value) hr.registerDownloadedChunk(subtree.id, serializedChunk) @@ -1000,8 +1000,8 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, log.warn(s"No download plan found when processing UTXO set snapshot chunk $chunkId") } - case _ => - log.info(s"Penalizing spamming peer $remote sent non-asked UTXO set snapshot $chunkId") + case None => + log.info(s"Penalizing spamming peer $remote sent non-asked UTXO set snapshot chunk $chunkId") penalizeSpammingPeer(remote) } diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index a8eb0d596e..e2d5540b40 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -83,7 +83,7 @@ object Constants { * 1024 is divisible by 128 also. */ // todo: change before deployment to 51200 - val MakeSnapshotEvery = 30 + val MakeSnapshotEvery = 500 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From f3993c0dc9aeae0d22f108bcf5a53e2cad3ba9f2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 24 May 2023 00:33:09 +0300 Subject: [PATCH 187/204] 1024 --- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index e2d5540b40..4fd2c06fd2 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -83,7 +83,7 @@ object Constants { * 1024 is divisible by 128 also. */ // todo: change before deployment to 51200 - val MakeSnapshotEvery = 500 + val MakeSnapshotEvery = 1024 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set. From 2e6d3935d01b7bd4ed9a7279525c72b0f7ba7d56 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 24 May 2023 01:37:11 +0200 Subject: [PATCH 188/204] with-sigma-v5.0.8: upgrade coded --- build.sbt | 2 +- .../contracts/ReemissionContracts.scala | 2 +- .../org/ergoplatform/utils/ArithUtils.scala | 26 --- .../org/ergoplatform/wallet/AssetUtils.scala | 55 ------- .../org/ergoplatform/wallet/Constants.scala | 2 +- .../wallet/boxes/BoxSelector.scala | 4 +- .../wallet/boxes/DefaultBoxSelector.scala | 5 +- .../wallet/boxes/ErgoBoxAssetExtractor.scala | 4 +- .../ReplaceCompactCollectBoxSelector.scala | 2 +- .../wallet/boxes/TrackedBox.scala | 7 +- .../org/ergoplatform/wallet/crypto/AES.scala | 6 +- .../wallet/crypto/ErgoSignature.scala | 2 +- .../wallet/interpreter/ErgoInterpreter.scala | 6 +- .../interpreter/ErgoProvingInterpreter.scala | 19 ++- .../org/ergoplatform/wallet/package.scala | 2 - .../protocol/context/ErgoLikeParameters.scala | 63 -------- .../context/ErgoLikeStateContext.scala | 26 --- .../protocol/context/TransactionContext.scala | 17 -- .../wallet/secrets/DerivationPath.scala | 150 ------------------ .../wallet/secrets/EncryptedSecret.scala | 6 +- .../wallet/secrets/ExtendedKey.scala | 43 ----- .../wallet/secrets/ExtendedPublicKey.scala | 98 ------------ .../wallet/secrets/ExtendedSecretKey.scala | 131 --------------- .../ergoplatform/wallet/secrets/Index.scala | 17 -- .../wallet/secrets/JsonSecretStorage.scala | 8 +- .../wallet/secrets/SecretKey.scala | 39 ----- .../wallet/secrets/SecretStorage.scala | 2 + .../serialization/ErgoWalletSerializer.scala | 17 +- .../wallet/settings/EncryptionSettings.scala | 40 ----- .../settings/SecretStorageSettings.scala | 2 + .../transactions/TransactionBuilder.scala | 23 ++- .../wallet/AddressGenerationDemo.java | 6 +- .../wallet/CreateTransactionDemo.java | 2 +- .../ergoplatform/wallet/AssetUtilsSpec.scala | 3 +- .../wallet/boxes/DefaultBoxSelectorSpec.scala | 30 ++-- .../ErgoProvingInterpreterSpec.scala | 6 +- .../interpreter/ErgoUnsafeProverSpec.scala | 2 +- .../interpreter/InterpreterSpecCommon.scala | 11 +- .../wallet/secrets/DerivationPathSpec.scala | 1 + .../secrets/ExtendedPublicKeySpec.scala | 1 + .../secrets/ExtendedSecretKeySpec.scala | 1 + .../secrets/JsonSecretStorageSpec.scala | 5 +- .../serialization/SerializationSpec.scala | 8 +- .../transactions/TransactionBuilderSpec.scala | 16 +- .../wallet/utils/Generators.scala | 25 ++- .../org/ergoplatform/http/api/ApiCodecs.scala | 20 +-- .../http/api/EmissionApiRoute.scala | 4 +- .../http/api/MiningApiRoute.scala | 8 +- .../http/api/TransactionsApiRoute.scala | 21 +-- .../mining/AutolykosSolution.scala | 6 +- .../mining/CandidateGenerator.scala | 22 +-- .../mining/DefaultFakePowScheme.scala | 4 +- .../org/ergoplatform/mining/mining.scala | 5 +- .../modifiers/history/PreHeader.scala | 2 +- .../modifiers/history/header/Header.scala | 10 +- .../modifiers/mempool/ErgoTransaction.scala | 19 +-- .../ergoplatform/nodeView/ErgoContext.scala | 6 +- .../nodeView/history/extra/BalanceInfo.scala | 4 +- .../nodeView/history/extra/ExtraIndexer.scala | 2 +- .../nodeView/history/extra/IndexedToken.scala | 2 +- .../nodeView/state/ErgoState.scala | 2 +- .../nodeView/state/ErgoStateContext.scala | 19 +-- .../nodeView/state/UtxoStateReader.scala | 4 +- .../nodeView/wallet/BalancesSnapshot.scala | 2 +- .../nodeView/wallet/ErgoWalletActor.scala | 18 +-- .../nodeView/wallet/ErgoWalletReader.scala | 8 +- .../nodeView/wallet/ErgoWalletService.scala | 23 +-- .../nodeView/wallet/ErgoWalletSupport.scala | 18 +-- .../nodeView/wallet/IdUtils.scala | 7 +- .../nodeView/wallet/WalletCache.scala | 8 +- .../nodeView/wallet/WalletVars.scala | 4 +- .../wallet/persistence/WalletDigest.scala | 8 +- .../wallet/persistence/WalletRegistry.scala | 8 +- .../wallet/persistence/WalletStorage.scala | 23 +-- .../wallet/requests/BoxesRequest.scala | 5 +- .../wallet/requests/ExternalSecret.scala | 2 +- .../requests/GenerateCommitmentsRequest.scala | 2 +- .../wallet/requests/RequestsHolder.scala | 6 +- .../requests/TransactionSigningRequest.scala | 2 +- .../wallet/scanning/ScanningPredicate.scala | 8 +- .../ScanningPredicateJsonCodecs.scala | 2 +- .../ScanningPredicateSerializer.scala | 7 +- .../ergoplatform/settings/Parameters.scala | 2 +- .../http/routes/MiningApiRouteSpec.scala | 4 +- .../http/routes/TransactionApiRouteSpec.scala | 5 +- .../mining/CandidateGeneratorPropSpec.scala | 6 +- .../mining/CandidateGeneratorSpec.scala | 14 +- .../ergoplatform/mining/ErgoMinerSpec.scala | 18 +-- .../mempool/ErgoTransactionSpec.scala | 36 +++-- .../HeaderSerializationSpecification.scala | 5 +- .../extra/ExtraIndexerSpecification.scala | 22 +-- .../nodeView/mempool/ScriptsSpec.scala | 14 +- .../wallet/ErgoWalletServiceSpec.scala | 20 +-- .../nodeView/wallet/ErgoWalletSpec.scala | 14 +- .../persistence/WalletRegistryBenchmark.scala | 4 +- .../persistence/WalletStorageSpec.scala | 2 +- ...ningPredicateJsonCodecsSpecification.scala | 11 +- .../ScanningPredicateSpecification.scala | 17 +- .../reemission/ReemissionRulesSpec.scala | 23 +-- .../serialization/JsonSerializationSpec.scala | 8 +- .../ergoplatform/tools/ChainGenerator.scala | 2 +- .../org/ergoplatform/tools/FeeSimulator.scala | 8 +- .../utils/ErgoTestConstants.scala | 12 +- .../scala/org/ergoplatform/utils/Stubs.scala | 26 +-- .../ergoplatform/utils/WalletTestOps.scala | 20 +-- .../utils/generators/ErgoGenerators.scala | 22 +-- .../ErgoTransactionGenerators.scala | 18 +-- .../generators/ValidBlocksGenerators.scala | 2 +- 108 files changed, 465 insertions(+), 1134 deletions(-) delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/utils/ArithUtils.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/AssetUtils.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeParameters.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeStateContext.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/TransactionContext.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/DerivationPath.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedKey.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/Index.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretKey.scala delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/EncryptionSettings.scala diff --git a/build.sbt b/build.sbt index 70fd1c86a1..6d4627a551 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.7" +val sigmaStateVersion = "5.0.7-182-28cb73a6-SNAPSHOT" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala index 63070a1e9a..092fbc70ab 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala @@ -1,7 +1,7 @@ package org.ergoplatform.contracts import org.ergoplatform.ErgoBox.{R2, STokensRegType} -import org.ergoplatform.ErgoScriptPredef.{boxCreationHeight, expectedMinerOutScriptBytesVal} +import org.ergoplatform.ErgoTreePredef.{boxCreationHeight, expectedMinerOutScriptBytesVal} import org.ergoplatform.{Height, MinerPubkey, Outputs, Self} import org.ergoplatform.settings.MonetarySettings import sigmastate.{AND, EQ, GE, GT, LE, Minus, OR, SBox, SCollection, STuple} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/utils/ArithUtils.scala b/ergo-wallet/src/main/scala/org/ergoplatform/utils/ArithUtils.scala deleted file mode 100644 index 61a222a177..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/utils/ArithUtils.scala +++ /dev/null @@ -1,26 +0,0 @@ -package org.ergoplatform.utils - -object ArithUtils { - - /** - * Add longs, returning Long.Max value if there was any long overflow - */ - @inline def addExact(a: Long, b: Long): Long = { - val sum = a + b - if (((a ^ sum) & (b ^ sum)) < 0) Long.MaxValue else sum - } - - def addExact(a: Long, b: Long, c: Long*): Long = c.foldLeft(addExact(a,b))((x, y) => addExact(x, y)) - - /** - * Multiply longs, returning Long.Max value if there was any long overflow - */ - @inline def multiplyExact(e1: Long, e2: Long): Long = { - try { - java7.compat.Math.multiplyExact(e1, e2) - } catch { - case _: Throwable => Long.MaxValue - } - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/AssetUtils.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/AssetUtils.scala deleted file mode 100644 index eaa435f647..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/AssetUtils.scala +++ /dev/null @@ -1,55 +0,0 @@ -package org.ergoplatform.wallet - -import scorex.util.ModifierId - -import scala.collection.mutable - -object AssetUtils { - - @inline - def mergeAssetsMut(into: mutable.Map[ModifierId, Long], from: TokensMap*): Unit = { - from.foreach(_.foreach { - case (id, amount) => - into.put(id, java7.compat.Math.addExact(into.getOrElse(id, 0L), amount)) - }) - } - - @inline - def mergeAssets(initialMap: TokensMap, maps: TokensMap*): TokensMap = { - maps.foldLeft(initialMap) { case (to, map) => - map.foldLeft(to) { case (acc, (id, amount)) => - acc.updated(id, java7.compat.Math.addExact(acc.getOrElse(id, 0L), amount)) - } - } - } - - @inline - def subtractAssets(initialMap: TokensMap, subtractor: TokensMap*): TokensMap = { - subtractor.foldLeft(initialMap){ case (from, mapToSubtract) => - mapToSubtract.foldLeft(from) { case (acc, (id, amount)) => - val currAmt = acc.getOrElse(id, - throw new IllegalArgumentException(s"Cannot subtract $amount of token $id: token not found in $acc")) - require(amount >= 0, s"Cannot subtract negative amount from token $id: $amount") - require(currAmt >= amount, s"Not enough amount of token $id -> $currAmt to subtract $amount") - if (currAmt == amount) { - acc - id - } else { - acc.updated(id, currAmt - amount) - } - } - } - } - - @inline - def subtractAssetsMut(from: mutable.Map[ModifierId, Long], subtractor: TokensMap): Unit = { - subtractor.foreach { case (id, subtractAmt) => - val fromAmt = from(id) - if (fromAmt == subtractAmt) { - from.remove(id) - } else { - from.put(id, fromAmt - subtractAmt) - } - } - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala index 781db18ef7..b266280e13 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala @@ -1,6 +1,6 @@ package org.ergoplatform.wallet -import org.ergoplatform.wallet.secrets.DerivationPath +import org.ergoplatform.sdk.wallet.secrets.DerivationPath import supertagged.TaggedType object Constants { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala index 6faf194ca3..68e52a5b2e 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala @@ -1,8 +1,8 @@ package org.ergoplatform.wallet.boxes -import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder} +import org.ergoplatform.{ErgoBoxAssetsHolder, ErgoBoxAssets} import org.ergoplatform.SigmaConstants.MaxBoxSize -import org.ergoplatform.wallet.TokensMap +import org.ergoplatform.sdk.wallet.TokensMap import org.ergoplatform.wallet.boxes.BoxSelector.{BoxSelectionError, BoxSelectionResult} import scorex.util.ScorexLogging diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala index 534f55dc49..c9f4152205 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala @@ -1,10 +1,11 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.contracts.ReemissionContracts +import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} import scorex.util.ModifierId -import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder, ErgoBoxCandidate} +import org.ergoplatform.{ErgoBoxAssetsHolder, ErgoBoxCandidate, ErgoBoxAssets} import org.ergoplatform.wallet.Constants.MaxAssetsPerBox -import org.ergoplatform.wallet.{AssetUtils, TokensMap} + import scala.annotation.tailrec import scala.collection.mutable import org.ergoplatform.wallet.transactions.TransactionBuilder._ diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala index e7e55d5288..1e2867407b 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala @@ -1,7 +1,7 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.ErgoBoxCandidate -import sigmastate.eval.Extensions._ +import special.collection.Extensions._ import java7.compat.Math import scala.collection.compat.immutable.ArraySeq @@ -31,7 +31,7 @@ object ErgoBoxAssetExtractor { ) box.additionalTokens.foreach { case (assetId, amount) => - val aiWrapped = ArraySeq.unsafeWrapArray(assetId) + val aiWrapped = ArraySeq.unsafeWrapArray(assetId.toArray) val total = map.getOrElse(aiWrapped, 0L) map.put(aiWrapped, Math.addExact(total, amount)) } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala index 820f97ecf2..6f24e655c0 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala @@ -3,7 +3,7 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError import org.ergoplatform.ErgoBoxAssets -import org.ergoplatform.wallet.{AssetUtils, TokensMap} +import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} import scorex.util.ModifierId import org.ergoplatform.wallet.transactions.TransactionBuilder._ diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala index e6b3f91096..f38fb1e773 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala @@ -1,12 +1,13 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.{Constants, TokensMap} +import org.ergoplatform.wallet.{Constants} import org.ergoplatform.wallet.serialization.ErgoWalletSerializer import org.ergoplatform.{ErgoBox, ErgoLikeTransaction} import scorex.util.serialization.{Reader, Writer} -import scorex.util.{ModifierId, bytesToId, idToBytes} +import scorex.util.{idToBytes, bytesToId, ModifierId} import org.ergoplatform.ErgoBoxAssets +import org.ergoplatform.sdk.wallet.TokensMap /** * A box tracked by a wallet that contains Ergo box itself as well as @@ -67,7 +68,7 @@ case class TrackedBox(creationTxId: ModifierId, def isSpent: Boolean = spendingHeightOpt.isDefined lazy val tokens: TokensMap = box.additionalTokens.toArray.map { - case (id, amt) => bytesToId(id) -> amt + case (id, amt) => bytesToId(id.toArray) -> amt }.toMap override def equals(obj: Any): Boolean = obj match { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala index e5984dd1a8..61a7dd8377 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala @@ -1,9 +1,9 @@ package org.ergoplatform.wallet.crypto -import javax.crypto.spec.{GCMParameterSpec, PBEKeySpec, SecretKeySpec} -import javax.crypto.{Cipher, SecretKeyFactory} -import org.ergoplatform.wallet.settings.EncryptionSettings +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings +import javax.crypto.spec.{PBEKeySpec, SecretKeySpec, GCMParameterSpec} +import javax.crypto.{Cipher, SecretKeyFactory} import scala.util.Try object AES { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala index ef0c4a0d91..03f144ea2d 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/ErgoSignature.scala @@ -3,7 +3,7 @@ package org.ergoplatform.wallet.crypto import org.bouncycastle.util.BigIntegers import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base16 -import sigmastate.interpreter.CryptoConstants +import sigmastate.basics.CryptoConstants import sigmastate.serialization.GroupElementSerializer import scala.annotation.tailrec diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala index 449ff6cb9a..9391a921c0 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala @@ -1,13 +1,13 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters import org.ergoplatform.wallet.protocol.Constants -import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeInterpreter} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeInterpreter, ErgoLikeContext} import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree -import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} +import sigmastate.interpreter.Interpreter.{VerificationResult, ScriptEnv} import sigmastate.{AvlTreeData, AvlTreeFlags} import scala.util.Try diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala index 65539bb07a..848ff30068 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala @@ -1,24 +1,23 @@ package org.ergoplatform.wallet.interpreter import java.util - import org.ergoplatform._ -import org.ergoplatform.utils.ArithUtils.{addExact, multiplyExact} +import org.ergoplatform.sdk.utils.ArithUtils.{addExact, multiplyExact} +import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeStateContext, ErgoLikeParameters} +import org.ergoplatform.sdk.wallet.secrets.{SecretKey, ExtendedSecretKey, ExtendedPublicKey} import org.ergoplatform.validation.SigmaValidationSettings import sigmastate.AvlTreeData import sigmastate.Values.SigmaBoolean -import sigmastate.interpreter.{ContextExtension, ProverInterpreter} +import sigmastate.interpreter.{ProverInterpreter, ContextExtension} import org.ergoplatform.validation.ValidationRules import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor -import org.ergoplatform.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} -import org.ergoplatform.wallet.secrets.SecretKey +import scorex.crypto.authds.ADDigest import sigmastate.basics.SigmaProtocolPrivateInput -import org.ergoplatform.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey} import scorex.util.encode.Base16 import special.collection.Coll import special.sigma.{Header, PreHeader} -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * A class which is holding secrets and signing transactions. @@ -130,7 +129,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], inputsCostTry.flatMap { case (ins, totalCost) => val context = new ErgoLikeContext( - ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), + ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, dataBoxes, @@ -198,7 +197,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], val inputBox = boxesToSpend(inpIndex) val context = new ErgoLikeContext( - ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), + ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, dataBoxes, @@ -242,7 +241,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], val exp = box.ergoTree val proof = input.spendingProof.proof - val lastBlockUtxoRoot: AvlTreeData = ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest) + val lastBlockUtxoRoot: AvlTreeData = ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray) val headers: Coll[Header] = stateContext.sigmaLastHeaders val preHeader: PreHeader = stateContext.sigmaPreHeader val spendingTransaction = tx diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala index 7c7e9fcd82..9b36097517 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala @@ -1,7 +1,5 @@ package org.ergoplatform -import scorex.util.ModifierId package object wallet { - type TokensMap = Map[ModifierId, Long] } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeParameters.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeParameters.scala deleted file mode 100644 index 3f5f893ca7..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeParameters.scala +++ /dev/null @@ -1,63 +0,0 @@ -package org.ergoplatform.wallet.protocol.context - -/** - * Blockchain parameters readjustable via miners voting and voting-related data. - * All these fields are included into extension section of a first block of a voting epoch. - */ -trait ErgoLikeParameters { - - /** - * @return cost of storing 1 byte in UTXO for four years, in nanoErgs - */ - def storageFeeFactor: Int - - /** - * @return cost of a transaction output, in computation unit - */ - def minValuePerByte: Int - - /** - * @return max block size, in bytes - */ - def maxBlockSize: Int - - /** - * @return cost of a token contained in a transaction, in computation unit - */ - def tokenAccessCost: Int - - /** - * @return cost of a transaction input, in computation unit - */ - def inputCost: Int - - /** - * @return cost of a transaction data input, in computation unit - */ - def dataInputCost: Int - - /** - * @return cost of a transaction output, in computation unit - */ - def outputCost: Int - - /** - * @return computation units limit per block - */ - def maxBlockCost: Int - - /** - * @return height when voting for a soft-fork had been started - */ - def softForkStartingHeight: Option[Int] - - /** - * @return votes for soft-fork collected in previous epochs - */ - def softForkVotesCollected: Option[Int] - - /** - * @return Protocol version - */ - def blockVersion: Byte -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeStateContext.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeStateContext.scala deleted file mode 100644 index 9bf5829be9..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/ErgoLikeStateContext.scala +++ /dev/null @@ -1,26 +0,0 @@ -package org.ergoplatform.wallet.protocol.context - -import scorex.crypto.authds.ADDigest -import special.collection.Coll - -/** - * Blockchain context used in transaction validation. - */ -trait ErgoLikeStateContext { - - /** - * @return fixed number (10 in Ergo) of last block headers - */ - def sigmaLastHeaders: Coll[special.sigma.Header] - - // todo remove from ErgoLikeContext and from ErgoStateContext - /** - * @return UTXO set digest from a last header (of sigmaLastHeaders) - */ - def previousStateDigest: ADDigest - - /** - * @return returns pre-header (header without certain fields) of the current block - */ - def sigmaPreHeader: special.sigma.PreHeader -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/TransactionContext.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/TransactionContext.scala deleted file mode 100644 index b1539400bb..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/protocol/context/TransactionContext.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.ergoplatform.wallet.protocol.context - -import org.ergoplatform.{ErgoBox, ErgoLikeTransactionTemplate, UnsignedInput} - -/** - * Part of the execution context in regards with spending transaction - * - * @param boxesToSpend - inputs of the transaction - * @param dataBoxes - data (read-only) inputs of the transaction - * @param spendingTransaction - spending transaction - */ - -// TODO: it seems spendingTransaction is needed only to get output candidates in ErgoLikeContext. -// After ErgoLikeContext refactoring in sigma, this class can be simplified. -case class TransactionContext(boxesToSpend: IndexedSeq[ErgoBox], - dataBoxes: IndexedSeq[ErgoBox], - spendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput]) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/DerivationPath.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/DerivationPath.scala deleted file mode 100644 index 37425bd833..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/DerivationPath.scala +++ /dev/null @@ -1,150 +0,0 @@ -package org.ergoplatform.wallet.secrets - -import org.ergoplatform.wallet.Constants -import org.ergoplatform.wallet.serialization.ErgoWalletSerializer -import scorex.util.serialization.{Reader, Writer} - -import scala.util.{Failure, Success, Try} - -/** - * HD key derivation path (see: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) - */ -final case class DerivationPath(decodedPath: Seq[Int], publicBranch: Boolean) { - - import DerivationPath._ - - def depth: Int = decodedPath.length - - /** - * @return last element of the derivation path, e.g. 2 for m/1/2 - */ - def index: Int = decodedPath.last - - def isMaster: Boolean = depth == 1 - - /** Encode this DerivationPath as a parsable string. */ - def encoded: String = { - val masterPrefix = if (publicBranch) s"$PublicBranchMasterId/" else s"$PrivateBranchMasterId/" - val tailPath = decodedPath.tail - .map(x => if (Index.isHardened(x)) s"${x - Index.HardRangeStart}'" else x.toString) - .mkString("/") - masterPrefix + tailPath - } - - def extended(idx: Int): DerivationPath = DerivationPath(decodedPath :+ idx, publicBranch) - - /** - * @return path with last element of the derivation path being increased, e.g. m/1/2 -> m/1/3 - */ - def increased: DerivationPath = DerivationPath(decodedPath.dropRight(1) :+ (index + 1), publicBranch) - - /** - * Convert the derivation path to public branch. See BIP-32 for details. - * @return derivation path from the public branch - */ - def toPublicBranch: DerivationPath = this.copy(publicBranch = true) - - /** - * Convert the derivation path to private branch. See BIP-32 for details. - * @return derivation path from the private branch - */ - def toPrivateBranch: DerivationPath = this.copy(publicBranch = false) - - /** - * @return whether derivation path corresponds to EIP-3 - */ - def isEip3: Boolean = { - decodedPath.tail.startsWith(Constants.eip3DerivationPath.decodedPath.tail.take(3)) - } - - override def toString: String = encoded - - def bytes: Array[Byte] = DerivationPathSerializer.toBytes(this) -} - -object DerivationPath { - - val PublicBranchMasterId = "M" - val PrivateBranchMasterId = "m" - - val MasterPath: DerivationPath = DerivationPath(List(0), publicBranch = false) - - def fromEncoded(path: String): Try[DerivationPath] = { - val split = path.split("/") - if (!split.headOption.exists(Seq(PublicBranchMasterId, PrivateBranchMasterId).contains)) { - Failure(new Exception("Wrong path format")) - } else { - val pathTry = split.tail.foldLeft(Try(List(0))) { case (accTry, sym) => - accTry.flatMap { acc => - Try(if (sym.endsWith("'")) Index.hardIndex(sym.dropRight(1).toInt) else sym.toInt) - .map(acc :+ _) - } - } - val isPublicBranch = split.head == PublicBranchMasterId - pathTry.map(DerivationPath(_, isPublicBranch)) - } - } - - /** - * Finds next available path index for a new key. - * @param secrets - secrets previously generated - * @param usePreEip3Derivation - whether to use pre-EIP3 derivation or not - */ - def nextPath(secrets: IndexedSeq[ExtendedSecretKey], - usePreEip3Derivation: Boolean): Try[DerivationPath] = { - - def pathSequence(secret: ExtendedSecretKey): Seq[Int] = secret.path.decodedPath.tail - - @scala.annotation.tailrec - def nextPath(accPath: List[Int], remaining: Seq[Seq[Int]]): Try[DerivationPath] = { - if (!remaining.forall(_.isEmpty)) { - val maxChildIdx = remaining.flatMap(_.headOption).max - if (!Index.isHardened(maxChildIdx)) { - Success(DerivationPath(0 +: (accPath :+ maxChildIdx + 1), publicBranch = false)) - } else { - nextPath(accPath :+ maxChildIdx, remaining.map(_.drop(1))) - } - } else { - val exc = new Exception("Out of non-hardened index space. Try to derive hardened key specifying path manually") - Failure(exc) - } - } - - if (secrets.isEmpty || (secrets.size == 1 && secrets.head.path.isMaster)) { - // If pre-EIP3 generation, the first key generated after master's would be m/1, otherwise m/44'/429'/0'/0/0 - val path = if(usePreEip3Derivation) { - Constants.preEip3DerivationPath - } else { - Constants.eip3DerivationPath - } - Success(path) - } else { - // If last secret corresponds to EIP-3 path, do EIP-3 derivation, otherwise, old derivation - // For EIP-3 derivation, we increase last segment, m/44'/429'/0'/0/0 -> m/44'/429'/0'/0/1 and so on - // For old derivation, we increase last non-hardened segment, m/1/1 -> m/2 - if (secrets.last.path.isEip3) { - Success(secrets.last.path.increased) - } else { - nextPath(List.empty, secrets.map(pathSequence)) - } - } - } - -} - -object DerivationPathSerializer extends ErgoWalletSerializer[DerivationPath] { - - override def serialize(obj: DerivationPath, w: Writer): Unit = { - w.put(if (obj.publicBranch) 0x01 else 0x00) - w.putInt(obj.depth) - obj.decodedPath.foreach(i => w.putBytes(Index.serializeIndex(i))) - } - - override def parse(r: Reader): DerivationPath = { - val publicBranch = if (r.getByte() == 0x01) true else false - val depth = r.getInt() - val path = (0 until depth).map(_ => Index.parseIndex(r.getBytes(4))) - DerivationPath(path, publicBranch) - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala index 54b0ebca7a..3cfc2d2900 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala @@ -1,9 +1,9 @@ package org.ergoplatform.wallet.secrets import io.circe.syntax._ -import cats.syntax.either._ // don't remove, it is needed for scala 2.11 -import io.circe.{Decoder, Encoder, HCursor, Json} -import org.ergoplatform.wallet.settings.EncryptionSettings +import cats.syntax.either._ +import io.circe.{HCursor, Encoder, Json, Decoder} +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import scorex.util.encode.Base16 /** diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedKey.scala deleted file mode 100644 index fd11e311c2..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedKey.scala +++ /dev/null @@ -1,43 +0,0 @@ -package org.ergoplatform.wallet.secrets - -/** Description from https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki : - * - * We extend both private and public keys first with an extra 256 bits of entropy. - * This extension, called the chain code, is identical for corresponding private and public keys - * and consists of 32 bytes. - * We represent an extended private key as (k, c), with k the normal private key, - * and c the chain code. An extended public key is represented as (K, c), - * with K = point(k) and c the chain code. - * - * Each extended key has 2^31 normal child keys, and 2^31 hardened child keys. - * Each of these child keys has an index. The normal child keys use indices 0 through 2^31-1. - * The hardened child keys use indices 2^31 through `2^32-1`. - */ -trait ExtendedKey[T <: ExtendedKey[T]] { - - val path: DerivationPath - - /** Returns subtype reference. - */ - def selfReflection: T - - /** Given a parent extended key and an index `idx`, it is possible to compute the corresponding - * child extended key. The algorithm to do so depends on whether the child is a hardened key - * or not (or, equivalently, whether `idx ≥ 2^31`), and whether we're talking about private or - * public keys. - * - * @see implementations in derived classes - */ - def child(idx: Int): T - - def derive(upPath: DerivationPath): T = { - require( - upPath.depth >= path.depth && - upPath.decodedPath.take(path.depth).zip(path.decodedPath).forall { case (i1, i2) => i1 == i2 } && - upPath.publicBranch == path.publicBranch, - s"Incompatible paths: $upPath, $path" - ) - upPath.decodedPath.drop(path.depth).foldLeft(selfReflection)((parent, i) => parent.child(i)) - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala deleted file mode 100644 index 4fe572243c..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKey.scala +++ /dev/null @@ -1,98 +0,0 @@ -package org.ergoplatform.wallet.secrets - -import java.util -import org.bouncycastle.util.BigIntegers -import org.ergoplatform.wallet.Constants -import org.ergoplatform.wallet.crypto.HmacSHA512 -import org.ergoplatform.wallet.serialization.ErgoWalletSerializer -import scorex.util.serialization.{Reader, Writer} -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.crypto.CryptoFacade -import sigmastate.interpreter.CryptoConstants - -import scala.annotation.tailrec - -/** - * Public key, its chain code and path in key tree. - * (see: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) - */ -final class ExtendedPublicKey(private[secrets] val keyBytes: Array[Byte], - private[secrets] val chainCode: Array[Byte], - val path: DerivationPath) - extends ExtendedKey[ExtendedPublicKey] { - - def selfReflection: ExtendedPublicKey = this - - def key: ProveDlog = ProveDlog( - CryptoConstants.dlogGroup.ctx.decodePoint(keyBytes) - ) - - def child(idx: Int): ExtendedPublicKey = ExtendedPublicKey.deriveChildPublicKey(this, idx) - - override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { - case that: ExtendedPublicKey => - util.Arrays.equals(that.keyBytes, this.keyBytes) && - util.Arrays.equals(that.chainCode, this.chainCode) && - that.path == this.path - case _ => false - }) - - override def hashCode(): Int = { - var h = util.Arrays.hashCode(keyBytes) - h = 31 * h + util.Arrays.hashCode(chainCode) - h = 31 * h + path.hashCode() - h - } - - override def toString: String = s"ExtendedPublicKey($path : $key)" -} - -object ExtendedPublicKey { - - @tailrec - def deriveChildPublicKey(parentKey: ExtendedPublicKey, idx: Int): ExtendedPublicKey = { - require(!Index.isHardened(idx), "Hardened public keys derivation is not supported") - val (childKeyProto, childChainCode) = HmacSHA512 - .hash(parentKey.chainCode, parentKey.keyBytes ++ Index.serializeIndex(idx)) - .splitAt(Constants.SecretKeyLength) - val childKeyProtoDecoded = BigIntegers.fromUnsignedByteArray(childKeyProto) - val childKey = CryptoFacade.multiplyPoints( - DLogProverInput(childKeyProtoDecoded).publicImage.value, - parentKey.key.value) - if (childKeyProtoDecoded.compareTo(CryptoConstants.groupOrder) >= 0 || CryptoFacade.isInfinityPoint(childKey)) { - deriveChildPublicKey(parentKey, idx + 1) - } else { - new ExtendedPublicKey( - keyBytes = CryptoFacade.encodePoint(childKey, compressed = true), - chainCode = childChainCode, - path = parentKey.path.extended(idx) - ) - } - } - -} - -object ExtendedPublicKeySerializer extends ErgoWalletSerializer[ExtendedPublicKey] { - - import scorex.util.Extensions._ - - //ASN.1 encoding for secp256k1 points - 1 byte for sign + 32 bytes for x-coordinate of the point - val PublicKeyBytesSize: Int = Constants.SecretKeyLength + 1 - - override def serialize(obj: ExtendedPublicKey, w: Writer): Unit = { - w.putBytes(obj.keyBytes) - w.putBytes(obj.chainCode) - val pathBytes = DerivationPathSerializer.toBytes(obj.path) - w.putUInt(pathBytes.length) - w.putBytes(pathBytes) - } - - override def parse(r: Reader): ExtendedPublicKey = { - val keyBytes = r.getBytes(PublicKeyBytesSize) - val chainCode = r.getBytes(Constants.SecretKeyLength) - val pathLen = r.getUInt().toIntExact - val path = DerivationPathSerializer.parseBytes(r.getBytes(pathLen)) - new ExtendedPublicKey(keyBytes, chainCode, path) - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala deleted file mode 100644 index 1ac40239f4..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKey.scala +++ /dev/null @@ -1,131 +0,0 @@ -package org.ergoplatform.wallet.secrets - -import java.math.BigInteger -import java.util -import org.bouncycastle.util.BigIntegers -import org.ergoplatform.wallet.Constants -import org.ergoplatform.wallet.crypto.HmacSHA512 -import org.ergoplatform.wallet.serialization.ErgoWalletSerializer -import scorex.util.serialization.{Reader, Writer} -import sigmastate.basics.DLogProtocol -import sigmastate.basics.DLogProtocol.DLogProverInput -import sigmastate.crypto.CryptoFacade -import sigmastate.interpreter.CryptoConstants - -/** - * Secret, its chain code and path in key tree. - * (see: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) - */ -final class ExtendedSecretKey(private[secrets] val keyBytes: Array[Byte], - private[secrets] val chainCode: Array[Byte], - private[secrets] val usePre1627KeyDerivation: Boolean, - val path: DerivationPath) - extends ExtendedKey[ExtendedSecretKey] with SecretKey { - - def selfReflection: ExtendedSecretKey = this - - override def privateInput: DLogProverInput = DLogProverInput(BigIntegers.fromUnsignedByteArray(keyBytes)) - - def publicImage: DLogProtocol.ProveDlog = privateInput.publicImage - - def child(idx: Int): ExtendedSecretKey = ExtendedSecretKey.deriveChildSecretKey(this, idx) - - /** Returns extended public key corresponding to this secret key. */ - def publicKey: ExtendedPublicKey = - new ExtendedPublicKey( - keyBytes = CryptoFacade.encodePoint(privateInput.publicImage.value, compressed = true), - chainCode = chainCode, - path = path.toPublicBranch) - - def isErased: Boolean = keyBytes.forall(_ == 0x00) - - def zeroSecret(): Unit = util.Arrays.fill(keyBytes, 0: Byte) - - override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match { - case that: ExtendedSecretKey => - util.Arrays.equals(that.keyBytes, this.keyBytes) && - util.Arrays.equals(that.chainCode, this.chainCode) && - that.path == this.path - case _ => false - }) - - override def hashCode(): Int = { - var h = util.Arrays.hashCode(keyBytes) - h = 31 * h + util.Arrays.hashCode(chainCode) - h = 31 * h + path.hashCode() - h - } - -} - -object ExtendedSecretKey { - - @scala.annotation.tailrec - def deriveChildSecretKey(parentKey: ExtendedSecretKey, idx: Int): ExtendedSecretKey = { - val keyCoded: Array[Byte] = - if (Index.isHardened(idx)) (0x00: Byte) +: parentKey.keyBytes - else CryptoFacade.encodePoint(parentKey.privateInput.publicImage.value, compressed = true) - val (childKeyProto, childChainCode) = HmacSHA512 - .hash(parentKey.chainCode, keyCoded ++ Index.serializeIndex(idx)) - .splitAt(Constants.SecretKeyLength) - val childKeyProtoDecoded = BigIntegers.fromUnsignedByteArray(childKeyProto) - val childKey = childKeyProtoDecoded - .add(BigIntegers.fromUnsignedByteArray(parentKey.keyBytes)) - .mod(CryptoConstants.groupOrder) - if (childKeyProtoDecoded.compareTo(CryptoConstants.groupOrder) >= 0 || childKey.equals(BigInteger.ZERO)) - deriveChildSecretKey(parentKey, idx + 1) - else { - val keyBytes = if (parentKey.usePre1627KeyDerivation) { - // maybe less than 32 bytes if childKey is small enough while BIP32 requires 32 bytes. - // see https://github.com/ergoplatform/ergo/issues/1627 for details - BigIntegers.asUnsignedByteArray(childKey) - } else { - // padded with leading zeroes to 32 bytes - BigIntegers.asUnsignedByteArray(Constants.SecretKeyLength, childKey) - } - new ExtendedSecretKey(keyBytes, childChainCode, parentKey.usePre1627KeyDerivation, parentKey.path.extended(idx)) - } - } - - def deriveChildPublicKey(parentKey: ExtendedSecretKey, idx: Int): ExtendedPublicKey = { - val derivedSecret = deriveChildSecretKey(parentKey, idx) - val derivedPk = CryptoFacade.encodePoint( - derivedSecret.privateInput.publicImage.value, compressed = true) - val derivedPath = derivedSecret.path.copy(publicBranch = true) - new ExtendedPublicKey(derivedPk, derivedSecret.chainCode, derivedPath) - } - - - /** - * Derives master secret key from the seed - * @param seed - seed bytes - * @param usePre1627KeyDerivation - use incorrect(previous) BIP32 derivation, expected to be false for new wallets, and true for old pre-1627 wallets (see https://github.com/ergoplatform/ergo/issues/1627 for details) - */ - def deriveMasterKey(seed: Array[Byte], usePre1627KeyDerivation: Boolean): ExtendedSecretKey = { - val (masterKey, chainCode) = HmacSHA512.hash(Constants.BitcoinSeed, seed).splitAt(Constants.SecretKeyLength) - new ExtendedSecretKey(masterKey, chainCode, usePre1627KeyDerivation, DerivationPath.MasterPath) - } - -} - -object ExtendedSecretKeySerializer extends ErgoWalletSerializer[ExtendedSecretKey] { - - import scorex.util.Extensions._ - - override def serialize(obj: ExtendedSecretKey, w: Writer): Unit = { - w.putBytes(obj.keyBytes) - w.putBytes(obj.chainCode) - val pathBytes = DerivationPathSerializer.toBytes(obj.path) - w.putUInt(pathBytes.length) - w.putBytes(pathBytes) - } - - override def parse(r: Reader): ExtendedSecretKey = { - val keyBytes = r.getBytes(Constants.SecretKeyLength) - val chainCode = r.getBytes(Constants.SecretKeyLength) - val pathLen = r.getUInt().toIntExact - val path = DerivationPathSerializer.parseBytes(r.getBytes(pathLen)) - new ExtendedSecretKey(keyBytes, chainCode, false, path) - } - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/Index.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/Index.scala deleted file mode 100644 index 58d79e700e..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/Index.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.ergoplatform.wallet.secrets - -import scodec.bits.ByteVector - -object Index { - - val HardRangeStart = 0x80000000 - - def hardIndex(i: Int): Int = i | HardRangeStart - - def isHardened(i: Int): Boolean = (i & HardRangeStart) != 0 - - def serializeIndex(i: Int): Array[Byte] = ByteVector.fromInt(i).toArray - - def parseIndex(xs: Array[Byte]): Int = ByteVector(xs).toInt(signed = false) - -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala index a4db16771a..474f7a8597 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala @@ -1,17 +1,19 @@ package org.ergoplatform.wallet.secrets -import java.io.{File, FileNotFoundException, PrintWriter} +import java.io.{FileNotFoundException, File, PrintWriter} import java.util import java.util.UUID import io.circe.parser._ import io.circe.syntax._ +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import org.ergoplatform.wallet.crypto import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.settings.{EncryptionSettings, SecretStorageSettings} +import org.ergoplatform.wallet.settings.SecretStorageSettings import scorex.util.encode.Base16 -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * Secret storage backend. diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretKey.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretKey.scala deleted file mode 100644 index 19be5b82da..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretKey.scala +++ /dev/null @@ -1,39 +0,0 @@ -package org.ergoplatform.wallet.secrets - -import sigmastate.basics.DLogProtocol.DLogProverInput -import sigmastate.basics.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} - -/** - * Basic trait for secret data, encapsulating a corresponding private inputs for a Sigma protocol. - */ -trait SecretKey { - /** - * Private (secret) input of a sigma protocol - */ - def privateInput: SigmaProtocolPrivateInput[_, _] -} - -/** - * Basic trait for a secret which does not have a derivation scheme. - */ -sealed trait PrimitiveSecretKey extends SecretKey - -object PrimitiveSecretKey { - def apply(sigmaPrivateInput: SigmaProtocolPrivateInput[_, _]): PrimitiveSecretKey = sigmaPrivateInput match { - case dls: DLogProverInput => DlogSecretKey(dls) - case dhts: DiffieHellmanTupleProverInput => DhtSecretKey(dhts) - } -} - -/** - * Secret exponent of a group element, i.e. secret w such as h = g^^w, where g is group generator, h is a public key. - * @param privateInput - secret (in form of a sigma-protocol private input) - */ -case class DlogSecretKey(override val privateInput: DLogProverInput) extends PrimitiveSecretKey - -/** - * Secret exponent of a Diffie-Hellman tuple, i.e. secret w such as u = g^^w and v = h^^w, where g and h are group - * generators, (g,h,u,v) is a public input (public key). - * @param privateInput - secret (in form of a sigma-protocol private input) - */ -case class DhtSecretKey(override val privateInput: DiffieHellmanTupleProverInput) extends PrimitiveSecretKey diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretStorage.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretStorage.scala index 148c1001d0..1f3ee1394e 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretStorage.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/SecretStorage.scala @@ -1,5 +1,7 @@ package org.ergoplatform.wallet.secrets +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey + import java.io.File import scala.util.Try import org.ergoplatform.wallet.interface4j.SecretString diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala index df5807ce50..326b8a7377 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala @@ -1,9 +1,10 @@ package org.ergoplatform.wallet.serialization import java.nio.ByteBuffer - import scorex.util.ByteArrayBuilder import scorex.util.serialization._ +import sigmastate.serialization.{SigmaSerializer, ConstantStore} +import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} import scala.util.Try @@ -23,3 +24,17 @@ trait ErgoWalletSerializer[T] extends Serializer[T, T, Reader, Writer] { def parseBytesTry(bytes: Array[Byte]): Try[T] = Try(parseBytes(bytes)) } + +object ErgoWalletSerializer { + def fromSigmaSerializer[T](ss: SigmaSerializer[T, T]): ErgoWalletSerializer[T] = new ErgoWalletSerializer[T] { + override def serialize(obj: T, w: Writer): Unit = { + val sw = new SigmaByteWriter(w, None) + ss.serialize(obj, sw) + } + + override def parse(r: Reader): T = { + val sr = new SigmaByteReader(r, new ConstantStore(), resolvePlaceholdersToConstants = false) + ss.parse(sr) + } + } +} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/EncryptionSettings.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/EncryptionSettings.scala deleted file mode 100644 index e8369c275f..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/EncryptionSettings.scala +++ /dev/null @@ -1,40 +0,0 @@ -package org.ergoplatform.wallet.settings - -import io.circe.{Json, Encoder, Decoder, HCursor} -import io.circe.syntax._ -import cats.syntax.either._ // don't remove, it is needed for scala 2.11 - -/** - * Encryption parameters - * @param prf - pseudo-random function with output of length `dkLen` (PBKDF2 param) - * @param c - number of PBKDF2 iterations (PBKDF2 param) - * @param dkLen - desired bit-length of the derived key (PBKDF2 param) - */ -final case class EncryptionSettings(prf: String, c: Int, dkLen: Int) - -object EncryptionSettings { - - implicit object EncryptionSettingsEncoder extends Encoder[EncryptionSettings] { - - def apply(s: EncryptionSettings): Json = { - Json.obj( - "prf" -> s.prf.asJson, - "c" -> s.c.asJson, - "dkLen" -> s.dkLen.asJson - ) - } - - } - - implicit object EncryptionSettingsDecoder extends Decoder[EncryptionSettings] { - - def apply(cursor: HCursor): Decoder.Result[EncryptionSettings] = { - for { - prf <- cursor.downField("prf").as[String] - c <- cursor.downField("c").as[Int] - dkLen <- cursor.downField("dkLen").as[Int] - } yield EncryptionSettings(prf, c, dkLen) - } - - } -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/SecretStorageSettings.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/SecretStorageSettings.scala index 354b723099..d71c3ee02a 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/SecretStorageSettings.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/settings/SecretStorageSettings.scala @@ -1,3 +1,5 @@ package org.ergoplatform.wallet.settings +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings + final case class SecretStorageSettings(secretDir: String, encryption: EncryptionSettings) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala index e8dedb0c9e..bc7fc1aae7 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala @@ -1,25 +1,20 @@ package org.ergoplatform.wallet.transactions -import org.ergoplatform.ErgoBox -import org.ergoplatform.DataInput -import org.ergoplatform.ErgoBoxCandidate -import org.ergoplatform.ErgoAddress -import org.ergoplatform.ErgoScriptPredef -import org.ergoplatform.UnsignedErgoLikeTransaction -import org.ergoplatform.UnsignedInput +import org.ergoplatform.{UnsignedErgoLikeTransaction, UnsignedInput, ErgoBox, ErgoAddress, ErgoTreePredef, DataInput, ErgoBoxCandidate, ErgoScriptPredef} import sigmastate.eval.Extensions._ import scala.util.Try -import scorex.util.{ModifierId, bytesToId, idToBytes} +import scorex.util.{idToBytes, bytesToId, ModifierId} import special.collection.Coll import sigmastate.eval._ import org.ergoplatform.ErgoBox.TokenId +import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} import scorex.crypto.hash.Digest32 -import org.ergoplatform.wallet.{AssetUtils, TokensMap} import org.ergoplatform.wallet.boxes.BoxSelector import org.ergoplatform.wallet.boxes.DefaultBoxSelector import scorex.crypto.authds.ADKey import scorex.util.encode.Base16 + import scala.collection.JavaConverters._ object TransactionBuilder { @@ -52,7 +47,7 @@ object TransactionBuilder { currentHeight: Int): UnsignedErgoLikeTransaction = { val feeBox = new ErgoBoxCandidate( feeAmt, - ErgoScriptPredef.feeProposition(), + ErgoTreePredef.feeProposition(), currentHeight, Seq.empty[(ErgoBox.TokenId, Long)].toColl, Map.empty @@ -123,7 +118,7 @@ object TransactionBuilder { ) val fee = new ErgoBoxCandidate( feeAmt, - ErgoScriptPredef.feeProposition(), + ErgoTreePredef.feeProposition(), currentHeight, Seq.empty[(ErgoBox.TokenId, Long)].toColl, Map.empty @@ -164,10 +159,10 @@ object TransactionBuilder { } def collTokensToMap(tokens: Coll[(TokenId, Long)]): TokensMap = - tokens.toArray.map(t => bytesToId(t._1) -> t._2).toMap + tokens.toArray.map(t => bytesToId(t._1.toArray) -> t._2).toMap def tokensMapToColl(tokens: TokensMap): Coll[(TokenId, Long)] = - tokens.toSeq.map {t => (Digest32 @@ idToBytes(t._1)) -> t._2}.toArray.toColl + tokens.toSeq.map {t => (Digest32Coll @@ idToBytes(t._1).toColl) -> t._2}.toArray.toColl private def validateStatelessChecks(inputs: IndexedSeq[ErgoBox], dataInputs: IndexedSeq[DataInput], outputCandidates: Seq[ErgoBoxCandidate]): Unit = { @@ -258,7 +253,7 @@ object TransactionBuilder { val actualFee = if (changeGoesToFee) fee + changeAmt else fee new ErgoBoxCandidate( actualFee, - ErgoScriptPredef.feeProposition(minerRewardDelay), + ErgoTreePredef.feeProposition(minerRewardDelay), currentHeight ) } diff --git a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java index 2daa664862..6f842f5ed1 100644 --- a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java +++ b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java @@ -2,11 +2,11 @@ import org.ergoplatform.ErgoAddressEncoder; import org.ergoplatform.P2PKAddress; +import org.ergoplatform.sdk.wallet.secrets.DerivationPath; +import org.ergoplatform.sdk.wallet.secrets.ExtendedPublicKey; +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey; import org.ergoplatform.wallet.mnemonic.Mnemonic; import org.ergoplatform.wallet.interface4j.SecretString; -import org.ergoplatform.wallet.secrets.DerivationPath; -import org.ergoplatform.wallet.secrets.ExtendedPublicKey; -import org.ergoplatform.wallet.secrets.ExtendedSecretKey; import scala.Option; /** diff --git a/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java b/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java index a8f38ba40f..11ac65f6b0 100644 --- a/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java +++ b/ergo-wallet/src/test/java/org/ergoplatform/wallet/CreateTransactionDemo.java @@ -2,8 +2,8 @@ import io.circe.Json; import org.ergoplatform.*; +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey; import org.ergoplatform.wallet.interface4j.crypto.ErgoUnsafeProver; -import org.ergoplatform.wallet.secrets.ExtendedSecretKey; import org.ergoplatform.wallet.serialization.JsonCodecsWrapper; import org.ergoplatform.wallet.transactions.TransactionBuilder; import org.ergoplatform.wallet.transactions.TransactionBuilder.Payment; diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala index 4ec358b8e1..33fb0d0e35 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala @@ -1,11 +1,12 @@ package org.ergoplatform.wallet +import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} import org.ergoplatform.wallet.utils.WalletTestHelpers import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks class AssetUtilsSpec extends WalletTestHelpers with Matchers with TableDrivenPropertyChecks { - import AssetUtils._ + import org.ergoplatform.sdk.wallet.AssetUtils._ val tid1 = stringToId("t1") val tid2 = stringToId("t2") diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala index aee68d6250..fec8a7781d 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala @@ -8,12 +8,14 @@ import scorex.crypto.hash.{Blake2b256, Digest32} import sigmastate.Values import sigmastate.Values.SigmaPropValue import sigmastate.helpers.TestingHelpers._ -import scorex.util.{ModifierId, bytesToId, idToBytes} +import scorex.util.{idToBytes, bytesToId, ModifierId} import org.scalatest.EitherValues import org.ergoplatform.wallet.boxes.DefaultBoxSelector.NotEnoughErgsError import org.ergoplatform.wallet.boxes.DefaultBoxSelector.NotEnoughTokensError import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps import scala.util.Random @@ -28,7 +30,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues private val StartHeight: Int = 0 private def genTokens(count: Int) = { - (0 until count).map { i => Digest32 @@ idToBytes(bytesToId(Blake2b256(i.toString))) -> i.toLong } + (0 until count).map { i => Digest32Coll @@ idToBytes(bytesToId(Blake2b256(i.toString))).toColl -> i.toLong } } private val selector = new DefaultBoxSelector(None) @@ -98,9 +100,9 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val assetId2 = bytesToId(Blake2b256("world")) val parentTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq()) - val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32 @@ idToBytes(assetId1) -> 1)) - val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32 @@ idToBytes(assetId2) -> 10)) - val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32 @@ idToBytes(assetId1) -> 100)) + val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 1)) + val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId2).toColl -> 10)) + val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 100)) val uBox1 = TrackedBox(parentTx, 0, Some(100), box1, Set(PaymentsScanId)) val uBox2 = TrackedBox(parentTx, 1, None, box2, Set(PaymentsScanId)) @@ -147,16 +149,16 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val assetId8 = bytesToId(Blake2b256("8")) val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32 @@ idToBytes(assetId1) -> 1, Digest32 @@ idToBytes(assetId2) -> 1, - Digest32 @@ idToBytes(assetId3) -> 1, Digest32 @@ idToBytes(assetId4) -> 1)) + Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 1, Digest32Coll @@ idToBytes(assetId2).toColl -> 1, + Digest32Coll @@ idToBytes(assetId3).toColl -> 1, Digest32Coll @@ idToBytes(assetId4).toColl -> 1)) val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32 @@ idToBytes(assetId5) -> 10, Digest32 @@ idToBytes(assetId6) -> 10, - Digest32 @@ idToBytes(assetId7) -> 10, Digest32 @@ idToBytes(assetId8) -> 10)) + Seq(Digest32Coll @@ idToBytes(assetId5).toColl -> 10, Digest32Coll @@ idToBytes(assetId6).toColl -> 10, + Digest32Coll @@ idToBytes(assetId7).toColl -> 10, Digest32Coll @@ idToBytes(assetId8).toColl -> 10)) val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32 @@ idToBytes(assetId3) -> 100, Digest32 @@ idToBytes(assetId4) -> 100, - Digest32 @@ idToBytes(assetId5) -> 100, Digest32 @@ idToBytes(assetId6) -> 100)) + Seq(Digest32Coll @@ idToBytes(assetId3).toColl -> 100, Digest32Coll @@ idToBytes(assetId4).toColl -> 100, + Digest32Coll @@ idToBytes(assetId5).toColl -> 100, Digest32Coll @@ idToBytes(assetId6).toColl -> 100)) val uBox1 = TrackedBox(parentTx, 0, Some(100), box1, Set(PaymentsScanId)) val uBox2 = TrackedBox(parentTx, 1, None, box2, Set(PaymentsScanId)) @@ -193,7 +195,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues property("Size of a box with MaxAssetsPerBox tokens should not cross MaxBoxSize") { val tokens = (0 until MaxAssetsPerBox).map { _ => - (Digest32 @@ scorex.util.Random.randomBytes(TokenId.size), Random.nextInt(100000000).toLong) + (Digest32Coll @@ scorex.util.Random.randomBytes(TokenId.size).toColl, Random.nextInt(100000000).toLong) } val box = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, tokens) assert(box.bytes.length <= MaxBoxSize.value) @@ -222,7 +224,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val tokenData = genTokens(3).last tokenData._2 shouldBe 2 - val tokenId = ModifierId @@@ bytesToId(tokenData._1) + val tokenId = ModifierId @@@ bytesToId(tokenData._1.toArray) val ergValue = 10 * MinBoxValue @@ -254,7 +256,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val ts = genTokens(2) val reemissionNftId = ts(0)._1 val reemissionTokenId = ts(1)._1 - val selector = new DefaultBoxSelector(Some(ReemissionData(bytesToId(reemissionNftId), bytesToId(reemissionTokenId)))) + val selector = new DefaultBoxSelector(Some(ReemissionData(bytesToId(reemissionNftId.toArray), bytesToId(reemissionTokenId.toArray)))) val fullValue = 2000000000L val reemissionAmt = fullValue / 2 diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala index 123b70695e..b311824aa8 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala @@ -1,8 +1,8 @@ package org.ergoplatform.wallet.interpreter +import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DlogSecretKey} import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput} import org.ergoplatform.wallet.crypto.ErgoSignature -import org.ergoplatform.wallet.secrets.{DlogSecretKey, ExtendedSecretKey} import org.ergoplatform.wallet.utils.Generators import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -11,8 +11,8 @@ import scorex.util.ModifierId import scorex.util.encode.Base16 import scorex.util.Random import sigmastate.CTHRESHOLD -import sigmastate.Values.{GroupElementConstant, SigmaBoolean} -import sigmastate.interpreter.{ContextExtension, HintsBag} +import sigmastate.Values.{SigmaBoolean, GroupElementConstant} +import sigmastate.interpreter.{HintsBag, ContextExtension} import sigmastate.serialization.ErgoTreeSerializer diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala index e19690d8cb..c49445f83a 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoUnsafeProverSpec.scala @@ -1,7 +1,7 @@ package org.ergoplatform.wallet.interpreter +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.wallet.crypto.ErgoSignature -import org.ergoplatform.wallet.secrets.ExtendedSecretKey import org.ergoplatform.wallet.utils.Generators import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala index b1095e160e..1497ff84e2 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala @@ -1,10 +1,11 @@ package org.ergoplatform.wallet.interpreter -import org.ergoplatform.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} +import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeStateContext, ErgoLikeParameters} import scorex.crypto.authds.ADDigest import scorex.util.encode.Base16 -import sigmastate.eval.{CGroupElement, CPreHeader, Colls} -import sigmastate.interpreter.CryptoConstants +import sigmastate.basics.CryptoConstants +import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.{Colls, CGroupElement, CPreHeader} import special.collection.Coll import special.sigma.{Header, PreHeader} @@ -39,8 +40,8 @@ trait InterpreterSpecCommon { override def sigmaLastHeaders: Coll[Header] = Colls.emptyColl - override def previousStateDigest: ADDigest = Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") - .map(ADDigest @@ _) + override def previousStateDigest = Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") + .map(_.toColl) .getOrElse(throw new Error(s"Failed to parse genesisStateDigest")) override def sigmaPreHeader: PreHeader = CPreHeader( diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala index 0a598b32a4..4a392c59e1 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala @@ -1,5 +1,6 @@ package org.ergoplatform.wallet.secrets +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.wallet.Constants import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import org.ergoplatform.wallet.mnemonic.Mnemonic diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKeySpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKeySpec.scala index 2cd869c66e..dedfdc1c9d 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKeySpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedPublicKeySpec.scala @@ -1,5 +1,6 @@ package org.ergoplatform.wallet.secrets +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.wallet.utils.Generators import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala index f3ff7f401a..3467c2b13a 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala @@ -9,6 +9,7 @@ import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.util.encode.Base58 import org.ergoplatform.P2PKAddress import org.ergoplatform.ErgoAddressEncoder +import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, Index, DerivationPath} class ExtendedSecretKeySpec extends AnyPropSpec diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala index 50e473e284..6a5df08721 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala @@ -1,13 +1,14 @@ package org.ergoplatform.wallet.secrets +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import org.ergoplatform.wallet.settings.SecretStorageSettings -import org.ergoplatform.wallet.utils.{Generators, FileUtils} +import org.ergoplatform.wallet.utils.{FileUtils, Generators} import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import org.ergoplatform.wallet.interface4j.SecretString import org.scalacheck.Arbitrary -import org.ergoplatform.wallet.settings.EncryptionSettings + import java.io.{File, PrintWriter} import java.util.UUID diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala index b35bf51993..876bbe3561 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala @@ -1,7 +1,7 @@ package org.ergoplatform.wallet.serialization +import org.ergoplatform.sdk.wallet.secrets.{DerivationPathSerializer, ExtendedSecretKeySerializer, ExtendedPublicKeySerializer} import org.ergoplatform.wallet.boxes.TrackedBoxSerializer -import org.ergoplatform.wallet.secrets.{ExtendedPublicKeySerializer, ExtendedSecretKeySerializer, DerivationPathSerializer} import org.ergoplatform.wallet.utils.Generators import org.scalacheck.Gen import org.scalatest.Assertion @@ -26,7 +26,7 @@ class SerializationSpec } property("DerivationPath serialization") { - checkSerializationRoundtrip(derivationPathGen, DerivationPathSerializer) + checkSerializationRoundtrip(derivationPathGen, ErgoWalletSerializer.fromSigmaSerializer(DerivationPathSerializer)) } property("TrackedBox serialization") { @@ -34,11 +34,11 @@ class SerializationSpec } property("ExtendedSecretKey serialization") { - checkSerializationRoundtrip(extendedSecretGen, ExtendedSecretKeySerializer) + checkSerializationRoundtrip(extendedSecretGen, ErgoWalletSerializer.fromSigmaSerializer(ExtendedSecretKeySerializer)) } property("ExtendedPublicKey serialization") { - checkSerializationRoundtrip(extendedPubKeyGen, ExtendedPublicKeySerializer) + checkSerializationRoundtrip(extendedPubKeyGen, ErgoWalletSerializer.fromSigmaSerializer(ExtendedPublicKeySerializer)) } } diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala index 9dfaad23a0..15f526c702 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala @@ -8,13 +8,13 @@ import sigmastate.helpers.TestingHelpers._ import sigmastate.utils.Helpers._ import org.ergoplatform._ import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.wallet.TokensMap +import org.ergoplatform.sdk.wallet.TokensMap +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey -import scala.util.{Success, Try} +import scala.util.{Try, Success} import scorex.crypto.hash.Digest32 import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.secrets.ExtendedSecretKey import org.ergoplatform.wallet.boxes.BoxSelector import org.ergoplatform.wallet.utils.WalletTestHelpers import org.scalatest.matchers.should.Matchers @@ -74,7 +74,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("token minting") { val inputBox = box(minBoxValue * 2) - val tokenId = Digest32 @@@ inputBox.id + val tokenId = Digest32Coll @@@ inputBox.id.toColl val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox) @@ -86,8 +86,8 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { } property("token burning") { - val inputBox = box(minBoxValue * 3, Seq(Digest32 @@ idToBytes(tid1) -> 1000L, Digest32 @@ idToBytes(tid2) -> 2000L)) - val tokenId = Digest32 @@@ inputBox.id + val inputBox = box(minBoxValue * 3, Seq(Digest32Coll @@ idToBytes(tid1).toColl -> 1000L, Digest32Coll @@ idToBytes(tid2).toColl -> 2000L)) + val tokenId = Digest32Coll @@@ inputBox.id.toColl val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, burnTokens = Map(tid1 -> 400L, tid2 -> 800L)) @@ -105,7 +105,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("no fees") { val inputBox = box(minBoxValue) - val tokenId = Digest32 @@@ inputBox.id + val tokenId = Digest32Coll @@@ inputBox.id.toColl val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) @@ -117,7 +117,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("change goes to fee, but no outFee box") { val inputBox = box(minBoxValue + minBoxValue / 2) - val tokenId = Digest32 @@@ inputBox.id + val tokenId = Digest32Coll @@@ inputBox.id.toColl val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala index 0a515eb024..d9fb7698a1 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala @@ -4,27 +4,22 @@ import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId} import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.mnemonic.{Mnemonic, WordList} -import org.ergoplatform.wallet.secrets.ExtendedPublicKey -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey, Index, SecretKey} -import org.ergoplatform.wallet.settings.EncryptionSettings import org.scalacheck.Arbitrary.arbByte -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.{Gen, Arbitrary} import scorex.crypto.authds.ADKey -import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, EvaluatedValue, FalseLeaf, TrueLeaf} +import sigmastate.Values.{TrueLeaf, CollectionConstant, ByteArrayConstant, ErgoTree, FalseLeaf, EvaluatedValue} import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.{SByte, SType} -import org.ergoplatform.wallet.Constants.{ScanId, PaymentsScanId} +import sigmastate.{SType, SByte} +import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import scorex.util._ -import org.ergoplatform.ErgoBox -import org.ergoplatform.ErgoBoxCandidate -import org.ergoplatform.ErgoScriptPredef -import org.ergoplatform.UnsignedErgoLikeTransaction -import org.ergoplatform.UnsignedInput +import org.ergoplatform.{UnsignedErgoLikeTransaction, UnsignedInput, ErgoBox, ErgoTreePredef, ErgoBoxCandidate, ErgoScriptPredef} import sigmastate.eval.Extensions._ -import scorex.util.{ModifierId, bytesToId} +import scorex.util.{bytesToId, ModifierId} import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import org.ergoplatform.ErgoBox.TokenId +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey, ExtendedPublicKey, SecretKey, Index} +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import scorex.crypto.hash.Digest32 @@ -77,7 +72,7 @@ trait Generators { val assetGen: Gen[(TokenId, Long)] = for { id <- boxIdGen amt <- Gen.oneOf(1, 500, 20000, 10000000, Long.MaxValue) - } yield Digest32 @@@ id -> amt + } yield Digest32Coll @@@ id.toColl -> amt def additionalTokensGen(cnt: Int): Gen[Seq[(TokenId, Long)]] = Gen.listOfN(cnt, assetGen) @@ -189,7 +184,7 @@ trait Generators { h <- Gen.posNum[Int] out = new ErgoBoxCandidate( value, - ErgoScriptPredef.feeProposition(), + ErgoTreePredef.feeProposition(), h, Seq.empty[(ErgoBox.TokenId, Long)].toColl, Map.empty diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 0649507c17..92f0cc7a44 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -3,11 +3,11 @@ package org.ergoplatform.http.api import java.math.BigInteger import io.circe._ import org.bouncycastle.util.BigIntegers -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoLikeContext, ErgoLikeTransaction, JsonCodecs, UnsignedErgoLikeTransaction} +import org.ergoplatform.{UnsignedErgoLikeTransaction, ErgoAddressEncoder, ErgoLikeTransaction, ErgoLikeContext, ErgoBox, JsonCodecs} import org.ergoplatform.http.api.ApiEncoderOption.Detalization import org.ergoplatform.ErgoBox.RegisterId import org.ergoplatform.mining.{groupElemFromBytes, groupElemToBytes} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.nodeView.wallet.persistence.WalletDigest @@ -16,28 +16,28 @@ import org.ergoplatform.settings.Algos import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.wallet.secrets.{DhtSecretKey, DlogSecretKey} import scorex.core.validation.ValidationResult import scorex.util.encode.Base16 -import sigmastate.{CAND, COR, CTHRESHOLD, NodePosition, SigSerializer, TrivialProp} +import sigmastate.{SigSerializer, COR, TrivialProp, CAND, NodePosition, CTHRESHOLD} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.{DLogProverInput, FirstDLogProverMessage, ProveDlog} +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput, FirstDLogProverMessage} import sigmastate.basics.VerifierMessage.Challenge import sigmastate.basics._ import sigmastate.interpreter._ -import sigmastate.interpreter.CryptoConstants.EcPointType import io.circe.syntax._ -import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest, HintExtractionRequest} +import org.ergoplatform.http.api.requests.{ExecuteRequest, CryptoResult, HintExtractionRequest} import org.ergoplatform.nodeView.history.extra.ExtraIndexer.getAddress -import org.ergoplatform.nodeView.history.extra.{BalanceInfo, IndexedErgoBox, IndexedErgoTransaction, IndexedToken} +import org.ergoplatform.nodeView.history.extra.{BalanceInfo, IndexedErgoTransaction, IndexedErgoBox, IndexedToken} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.wallet.interface4j.SecretString -import scorex.crypto.authds.{LeafData, Side} +import scorex.crypto.authds.{Side, LeafData} import scorex.crypto.authds.merkle.MerkleProof import scorex.crypto.hash.Digest +import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.serialization.OpCodes import special.sigma.AnyValue -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} trait ApiCodecs extends JsonCodecs { diff --git a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala index 0922a83a8f..657c3d8ef2 100644 --- a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala @@ -3,7 +3,7 @@ package org.ergoplatform.http.api import akka.actor.ActorRefFactory import akka.http.scaladsl.server.Route import io.circe.{Encoder, Json} -import org.ergoplatform.{ErgoAddressEncoder, ErgoScriptPredef, Pay2SAddress} +import org.ergoplatform.{ErgoAddressEncoder, ErgoTreePredef, Pay2SAddress} import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.settings.{ErgoSettings, ReemissionSettings} import scorex.core.api.http.ApiResponse @@ -43,7 +43,7 @@ case class EmissionApiRoute(ergoSettings: ErgoSettings) ApiResponse( Json.obj( - "emission" -> Pay2SAddress(ErgoScriptPredef.emissionBoxProp(ms)).toString().asJson, + "emission" -> Pay2SAddress(ErgoTreePredef.emissionBoxProp(ms)).toString().asJson, "reemission" -> Pay2SAddress(reemissionSettings.reemissionRules.reemissionBoxProp(ms)).toString().asJson, "pay2Reemission" -> Pay2SAddress(reemissionSettings.reemissionRules.payToReemission).toString().asJson ) diff --git a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala index 62f862bab2..834abd9069 100644 --- a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala @@ -1,16 +1,16 @@ package org.ergoplatform.http.api -import akka.actor.{ActorRef, ActorRefFactory} +import akka.actor.{ActorRefFactory, ActorRef} import akka.http.scaladsl.server.Route import akka.pattern.ask import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform.mining.CandidateGenerator.Candidate -import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner} +import org.ergoplatform.mining.{AutolykosSolution, ErgoMiner, CandidateGenerator} import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.wallet.ErgoAddressJsonEncoder import org.ergoplatform.settings.ErgoSettings -import org.ergoplatform.{ErgoAddress, ErgoScriptPredef, Pay2SAddress} +import org.ergoplatform.{Pay2SAddress, ErgoAddress, ErgoTreePredef} import scorex.core.api.http.ApiResponse import scorex.core.settings.RESTApiSettings import sigmastate.basics.DLogProtocol.ProveDlog @@ -68,7 +68,7 @@ case class MiningApiRoute(miner: ActorRef, miner.askWithStatus(ErgoMiner.ReadMinerPk) .mapTo[ProveDlog] .map { pk => - val script = ErgoScriptPredef.rewardOutputScript(ergoSettings.chainSettings.monetary.minerRewardDelay, pk) + val script = ErgoTreePredef.rewardOutputScript(ergoSettings.chainSettings.monetary.minerRewardDelay, pk) Pay2SAddress(script)(ergoSettings.addressEncoder) } diff --git a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala index 3159f99f24..8ba9dd377a 100644 --- a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala @@ -1,29 +1,30 @@ package org.ergoplatform.http.api -import akka.actor.{ActorRef, ActorRefFactory} +import akka.actor.{ActorRefFactory, ActorRef} import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server.{Directive, Directive1, Route, ValidationRejection} +import akka.http.scaladsl.server.{Directive1, ValidationRejection, Directive, Route} import akka.pattern.ask import io.circe.Json import io.circe.syntax._ import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, ErgoTransactionSerializer, UnconfirmedTransaction} -import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} +import org.ergoplatform.modifiers.mempool.{ErgoTransactionSerializer, UnconfirmedTransaction, ErgoTransaction} +import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.mempool.HistogramStats.getFeeHistogram -import org.ergoplatform.nodeView.state.{ErgoStateReader, UtxoStateReader} +import org.ergoplatform.nodeView.state.{UtxoStateReader, ErgoStateReader} 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 scorex.crypto.authds.ADKey -import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 import sigmastate.SType import sigmastate.Values.EvaluatedValue +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps import scala.concurrent.Future -import scala.util.{Failure, Success} +import scala.util.{Success, Failure} case class TransactionsApiRoute(readersHolder: ActorRef, nodeViewActorRef: ActorRef, @@ -48,9 +49,9 @@ case class TransactionsApiRoute(readersHolder: ActorRef, val tokenId: Directive1[TokenId] = pathPrefix(Segment).flatMap(handleTokenId) private def handleTokenId(value: String): Directive1[TokenId] = { - Digest32 @@ Algos.decode(value) match { + Algos.decode(value) match { case Success(tokenId) => - provide(tokenId) + provide(Digest32Coll @@ tokenId.toColl) case _ => reject(ValidationRejection(s"tokenId $value is invalid, it should be 64 chars long hex string")) } @@ -244,7 +245,7 @@ case class TransactionsApiRoute(readersHolder: ActorRef, def getUnconfirmedOutputByTokenIdR: Route = (pathPrefix("unconfirmed" / "outputs" / "byTokenId") & get & tokenId) { tokenId => ApiResponse( - getMemPool.map(_.getAll.flatMap(_.transaction.outputs.filter(_.additionalTokens.exists(_._1.sameElements(tokenId))))) + getMemPool.map(_.getAll.flatMap(_.transaction.outputs.filter(_.additionalTokens.exists(_._1.toArray.sameElements(tokenId.toArray))))) ) } diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala index 25d0e40498..f7e465c0b0 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala @@ -1,15 +1,15 @@ package org.ergoplatform.mining import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} +import io.circe.{HCursor, Encoder, Decoder} import org.bouncycastle.util.BigIntegers import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.settings.Algos import org.ergoplatform.modifiers.history.header.Header.Version import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} -import sigmastate.interpreter.CryptoConstants -import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.basics.CryptoConstants +import sigmastate.basics.CryptoConstants.EcPointType /** * Solution for an Autolykos PoW puzzle. diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index d6b3dfb7b3..af66b3163a 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -1,6 +1,6 @@ package org.ergoplatform.mining -import akka.actor.{Actor, ActorRef, ActorRefFactory, Props} +import akka.actor.{ActorRefFactory, ActorRef, Actor, Props} import akka.pattern.StatusReply import com.google.common.primitives.Longs import org.ergoplatform.ErgoBox.TokenId @@ -11,22 +11,22 @@ import org.ergoplatform.modifiers.history._ 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.modifiers.mempool.{UnconfirmedTransaction, ErgoTransaction} import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages -import ReceivableMessages.{ChangedHistory, ChangedMempool, ChangedState, FullBlockApplied, NodeViewChange} -import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} +import ReceivableMessages.{ChangedMempool, NodeViewChange, ChangedHistory, ChangedState, FullBlockApplied} +import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} import org.ergoplatform.nodeView.history.ErgoHistory.Height import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader -import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoStateReader} -import org.ergoplatform.settings.{ErgoSettings, ErgoValidationSettingsUpdate, Parameters} +import org.ergoplatform.nodeView.state.{UtxoStateReader, StateType, ErgoState, ErgoStateContext} +import org.ergoplatform.settings.{ErgoValidationSettingsUpdate, Parameters, ErgoSettings} import org.ergoplatform.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoScriptPredef, Input} +import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, LocallyGeneratedModifier} import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 -import scorex.util.{ModifierId, ScorexLogging} +import scorex.util.{ScorexLogging, ModifierId} import sigmastate.SType.ErgoBoxRType import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade @@ -37,7 +37,7 @@ import special.collection.Coll import scala.annotation.tailrec import scala.concurrent.duration._ -import scala.util.{Failure, Random, Success, Try} +import scala.util.{Random, Try, Success, Failure} /** Responsible for generating block candidates and validating solutions. * It is observing changes of history, utxo state, mempool and newly applied blocks @@ -666,11 +666,11 @@ object CandidateGenerator extends ScorexLogging { val reemissionRules = reemissionSettings.reemissionRules val eip27ActivationHeight = reemissionSettings.activationHeight - val reemissionTokenId = Digest32 @@ reemissionSettings.reemissionTokenIdBytes + val reemissionTokenId = Digest32Coll @@ reemissionSettings.reemissionTokenIdBytes.toColl val nextHeight = currentHeight + 1 val minerProp = - ErgoScriptPredef.rewardOutputScript(emission.settings.minerRewardDelay, minerPk) + ErgoTreePredef.rewardOutputScript(emission.settings.minerRewardDelay, minerPk) val emissionTxOpt: Option[ErgoTransaction] = emissionBoxOpt.map { emissionBox => val prop = emissionBox.ergoTree diff --git a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala index 861dbbb39a..94d0a70349 100644 --- a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala @@ -3,10 +3,10 @@ package org.ergoplatform.mining import org.ergoplatform.modifiers.history.header.Header import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 -import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.basics.CryptoConstants.EcPointType import scala.math.BigInt -import scala.util.{Random, Success, Try} +import scala.util.{Random, Try, Success} /** * Fake Pow Scheme for tests. diff --git a/src/main/scala/org/ergoplatform/mining/mining.scala b/src/main/scala/org/ergoplatform/mining/mining.scala index 9b3a7fb3d3..3fc4be9f3e 100644 --- a/src/main/scala/org/ergoplatform/mining/mining.scala +++ b/src/main/scala/org/ergoplatform/mining/mining.scala @@ -2,10 +2,9 @@ package org.ergoplatform import org.bouncycastle.util.BigIntegers import scorex.crypto.hash.Blake2b256 -import sigmastate.basics.BcDlogGroup +import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.basics.{BcDlogGroup, CryptoConstants} import sigmastate.basics.DLogProtocol.DLogProverInput -import sigmastate.interpreter.CryptoConstants -import sigmastate.interpreter.CryptoConstants.EcPointType import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer} package object mining { diff --git a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala index 6208a182f1..217576f4f8 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/PreHeader.scala @@ -6,9 +6,9 @@ import org.ergoplatform.modifiers.history.header.Header._ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.settings.Constants import scorex.util._ +import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.eval.CGroupElement import sigmastate.eval.Extensions._ -import sigmastate.interpreter.CryptoConstants.EcPointType /** * Only header fields that can be predicted by a miner diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 13b5d0112b..9fe6cea654 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -1,13 +1,13 @@ package org.ergoplatform.modifiers.history.header import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor} +import io.circe.{HCursor, Encoder, Decoder} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.AutolykosSolution 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.modifiers.history.{ADProofs, PreHeader, BlockTransactions} +import org.ergoplatform.modifiers.{HeaderTypeId, BlockSection, NonHeaderBlockSection, NetworkObjectTypeId} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty import org.ergoplatform.settings.{Algos, Constants} @@ -16,9 +16,9 @@ import scorex.core.serialization.ErgoSerializer import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.util._ +import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.eval.Extensions._ -import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader} -import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.eval.{CGroupElement, CBigInt, CHeader, CAvlTree} import scala.concurrent.duration.FiniteDuration diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 80125bdaff..98f2d1753d 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -6,34 +6,35 @@ import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} import org.ergoplatform._ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.modifiers.{ErgoNodeViewModifier, NetworkObjectTypeId, TransactionTypeId} +import org.ergoplatform.modifiers.{TransactionTypeId, ErgoNodeViewModifier, NetworkObjectTypeId} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction.unresolvedIndices import org.ergoplatform.nodeView.ErgoContext import org.ergoplatform.nodeView.state.ErgoStateContext +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.ArithUtils._ import org.ergoplatform.utils.BoxUtils import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import org.ergoplatform.wallet.protocol.context.{InputContext, TransactionContext} +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 scorex.core.validation.{ValidationState, ModifierValidator, ValidationResult, InvalidModifier} import scorex.db.ByteArrayUtils import scorex.util.serialization.{Reader, Writer} -import scorex.util.{ModifierId, ScorexLogging, bytesToId} +import scorex.util.{ScorexLogging, bytesToId, ModifierId} import sigmastate.serialization.ConstantStore -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} import java.util import scala.collection.mutable -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * ErgoTransaction is an atomic state transition operation. It destroys Boxes from the state @@ -273,11 +274,11 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], val firstEmissionBoxTokenId = emissionOut.additionalTokens.apply(0)._1 val secondEmissionBoxTokenId = emissionOut.additionalTokens.apply(1)._1 require( - firstEmissionBoxTokenId.sameElements(emissionNftIdBytes), + firstEmissionBoxTokenId.toArray.sameElements(emissionNftIdBytes), "No emission box NFT in the emission box" ) require( - secondEmissionBoxTokenId.sameElements(reemissionTokenIdBytes), + secondEmissionBoxTokenId.toArray.sameElements(reemissionTokenIdBytes), "No re-emission token in the emission box" ) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala index 37a5820c5a..a96b58cc9e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala @@ -2,8 +2,10 @@ package org.ergoplatform.nodeView import org.ergoplatform.nodeView.state.ErgoStateContext import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import org.ergoplatform.wallet.protocol.context.{InputContext, TransactionContext} +import org.ergoplatform.wallet.protocol.context.InputContext import org.ergoplatform.ErgoLikeContext +import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext +import scorex.crypto.authds.ADDigest /** * Context to be used during transaction verification @@ -13,7 +15,7 @@ class ErgoContext(val stateContext: ErgoStateContext, inputContext: InputContext, override val costLimit: Long, override val initCost: Long) - extends ErgoLikeContext(ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), + extends ErgoLikeContext(ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, transactionContext.dataBoxes, 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 8f94495d73..5acf7a547f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala @@ -56,7 +56,7 @@ case class BalanceInfo() extends ScorexLogging { def add(box: ErgoBox): Unit = { nanoErgs += box.value cfor(0)(_ < box.additionalTokens.length, _ + 1) { i => - val id: ModifierId = bytesToId(box.additionalTokens(i)._1) + val id: ModifierId = bytesToId(box.additionalTokens(i)._1.toArray) index(id) match { case Some(n) => tokens(n) = Tuple2(id, tokens(n)._2 + box.additionalTokens(i)._2) case None => tokens += Tuple2(id, box.additionalTokens(i)._2) @@ -72,7 +72,7 @@ case class BalanceInfo() extends ScorexLogging { def subtract(box: ErgoBox)(implicit ae: ErgoAddressEncoder): Unit = { nanoErgs = math.max(nanoErgs - box.value, 0) cfor(0)(_ < box.additionalTokens.length, _ + 1) { i => - val id: ModifierId = bytesToId(box.additionalTokens(i)._1) + val id: ModifierId = bytesToId(box.additionalTokens(i)._1.toArray) index(id) match { case Some(n) => val newVal: Long = tokens(n)._2 - box.additionalTokens(i)._2 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 7cb23966a9..1803e294f8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala @@ -208,7 +208,7 @@ trait ExtraIndexerBase extends ScorexLogging { // check if box is creating new tokens, if yes record them cfor(0)(_ < box.additionalTokens.length, _ + 1) { j => - if (!tokens.exists(x => java.util.Arrays.equals(x._1, box.additionalTokens(j)._1))) + if (!tokens.exists(x => x._1 == box.additionalTokens(j)._1)) general += IndexedToken.fromBox(box, j) } 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 31c946fd82..2ee76a0405 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala @@ -123,7 +123,7 @@ object IndexedToken { case None => 0 } - IndexedToken(bytesToId(box.additionalTokens(tokenIndex)._1), + IndexedToken(bytesToId(box.additionalTokens(tokenIndex)._1.toArray), bytesToId(box.id), box.additionalTokens(tokenIndex)._2, name, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala index cfc95bba4b..d8161d432e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoState.scala @@ -230,7 +230,7 @@ object ErgoState extends ScorexLogging { val protection = AtLeast(IntConstant(2), pks) val protectionBytes = ValueSerializer.serialize(protection) val value = emission.foundersCoinsTotal - EmissionRules.CoinsInOneErgo - val prop = ErgoScriptPredef.foundationScript(settings.monetary) + val prop = ErgoTreePredef.foundationScript(settings.monetary) createGenesisBox(value, prop, Seq.empty, Map(R4 -> ByteArrayConstant(protectionBytes))) } diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index 37415bf9ec..9c835afb18 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -8,20 +8,21 @@ 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.storage.modifierprocessors.ExtensionValidator +import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ -import org.ergoplatform.wallet.protocol.context.ErgoLikeStateContext -import scorex.core.serialization.{BytesSerializable, ErgoSerializer} +import scorex.core.serialization.{ErgoSerializer, BytesSerializable} import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationState} +import scorex.core.validation.{ValidationState, ModifierValidator, InvalidModifier} import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} +import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval.SigmaDsl -import sigmastate.interpreter.CryptoConstants.EcPointType import special.collection.Coll -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * State context with predicted header. @@ -84,10 +85,10 @@ class ErgoStateContext(val lastHeaders: Seq[Header], // todo remove from ErgoLikeContext and from ErgoStateContext // State root hash before the last block - override def previousStateDigest: ADDigest = if (sigmaLastHeaders.toArray.nonEmpty) { - ADDigest @@ sigmaLastHeaders.toArray.head.stateRoot.digest.toArray + override def previousStateDigest: Coll[Byte] = if (sigmaLastHeaders.toArray.nonEmpty) { + sigmaLastHeaders.toArray.head.stateRoot.digest } else { - genesisStateDigest + genesisStateDigest.toColl } /* NOHF PROOF: @@ -275,7 +276,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], }.flatten override def toString: String = - s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest)}, $lastHeaders, $currentParameters)" + s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest.toArray)}, $lastHeaders, $currentParameters)" /** diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index 7bac961b68..cf8b686276 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -88,7 +88,9 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { // after EIP-27 we search for emission box NFT for efficiency's sake tx.outputs.size == 2 && !tx.outputs.head.additionalTokens.isEmpty && - java.util.Arrays.equals(tx.outputs.head.additionalTokens(0)._1, constants.settings.chainSettings.reemission.emissionNftIdBytes) + java.util.Arrays.equals( + tx.outputs.head.additionalTokens(0)._1.toArray, + constants.settings.chainSettings.reemission.emissionNftIdBytes) } else { tx.outputs.head.ergoTree == constants.settings.chainSettings.monetary.emissionBoxProposition } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/BalancesSnapshot.scala index d51b36e807..96a6814898 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.wallet.TokensMap +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/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index 18b422095a..9a78abaabc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -1,34 +1,34 @@ package org.ergoplatform.nodeView.wallet -import akka.actor.SupervisorStrategy.{Restart, Stop} +import akka.actor.SupervisorStrategy.{Stop, Restart} 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.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.history.{ErgoHistory, 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.scanning.{ScanRequest, Scan} import org.ergoplatform.settings._ import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.{BoxSelector, ChainStatus} import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.{ErgoAddressEncoder, ErgoApp, ErgoBox, GlobalConstants, P2PKAddress} +import org.ergoplatform.{GlobalConstants, ErgoAddressEncoder, P2PKAddress, ErgoBox, ErgoApp} import scorex.core.VersionTag -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedMempool, ChangedState} -import org.ergoplatform.wallet.secrets.DerivationPath +import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedState, ChangedMempool} +import org.ergoplatform.sdk.wallet.secrets.DerivationPath import scorex.core.utils.ScorexEncoding -import scorex.util.{ModifierId, ScorexLogging} +import scorex.util.{ScorexLogging, ModifierId} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} import scala.concurrent.duration._ -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} class ErgoWalletActor(settings: ErgoSettings, diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala index 7e10887d71..925fe9d213 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala @@ -6,18 +6,18 @@ import akka.pattern.ask import akka.util.Timeout import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform.{ErgoBox, P2PKAddress} -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.persistence.WalletDigest import org.ergoplatform.nodeView.wallet.scanning.ScanRequest -import org.ergoplatform.nodeView.wallet.requests.{BoxesRequest, ExternalSecret, TransactionGenerationRequest} +import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGenerationRequest, BoxesRequest} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, DerivationPath} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.boxes.ChainStatus -import org.ergoplatform.wallet.boxes.ChainStatus.{OffChain, OnChain} +import org.ergoplatform.wallet.boxes.ChainStatus.{OnChain, OffChain} import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedPublicKey} import scorex.core.NodeViewComponent import scorex.util.ModifierId import sigmastate.Values.SigmaBoolean diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala index 33885b6fcb..1215d49bf4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala @@ -4,29 +4,30 @@ import cats.implicits._ import org.ergoplatform.ErgoBox.BoxId 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.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.nodeView.state.{UtxoStateReader, ErgoStateContext} import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult -import org.ergoplatform.nodeView.wallet.models.{ChangeBox, CollectedBoxes} +import org.ergoplatform.nodeView.wallet.models.{CollectedBoxes, ChangeBox} 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.settings.{ErgoSettings, Parameters} +import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, Scan} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} +import org.ergoplatform.settings.{Parameters, ErgoSettings} import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.boxes.{BoxSelector, ErgoBoxSerializer} +import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, BoxSelector} import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.interpreter.{ErgoProvingInterpreter, TransactionHintsBag} +import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoProvingInterpreter} import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey, JsonSecretStorage} +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, bytesToId} +import scorex.util.{bytesToId, ModifierId} import sigmastate.Values.SigmaBoolean import sigmastate.basics.DLogProtocol.DLogProverInput import java.io.FileNotFoundException -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * Operations accessible from [[ErgoWalletActor]] @@ -446,7 +447,7 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg .map(tx => AugWalletTransaction(tx, fullHeight - tx.inclusionHeight)) override def collectBoxes(state: ErgoWalletState, boxSelector: BoxSelector, targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, Long]): Try[CollectedBoxes] = { - val assetsMap = targetAssets.map(t => bytesToId(t._1) -> t._2) + val assetsMap = targetAssets.map(t => bytesToId(t._1.toArray) -> t._2) val inputBoxes = state.getBoxesToSpend boxSelector .select(inputBoxes.iterator, state.walletFilter, targetBalance, assetsMap) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index cef8c8e37b..8e483550da 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -3,31 +3,31 @@ package org.ergoplatform.nodeView.wallet import cats.implicits._ import cats.Traverse import org.ergoplatform.ErgoBox.{BoxId, R4, R5, R6} -import org.ergoplatform.{DataInput, ErgoAddress, ErgoBox, ErgoBoxAssets, ErgoBoxCandidate, P2PKAddress, UnsignedInput} +import org.ergoplatform.{ErgoBoxAssets, UnsignedInput, P2PKAddress, ErgoBox, ErgoAddress, DataInput, ErgoBoxCandidate} import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.persistence.WalletStorage import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.nodeView.wallet.requests._ +import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, ExtendedPublicKey, DerivationPath} import org.ergoplatform.settings.Parameters import org.ergoplatform.utils.BoxUtils -import org.ergoplatform.wallet.{AssetUtils, Constants, TokensMap} +import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants.PaymentsScanId import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult -import org.ergoplatform.wallet.boxes.{BoxSelector, TrackedBox} +import org.ergoplatform.wallet.boxes.{TrackedBox, BoxSelector} import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedPublicKey, ExtendedSecretKey} import org.ergoplatform.wallet.transactions.TransactionBuilder -import scorex.crypto.hash.Digest32 import scorex.util.{ScorexLogging, idToBytes} import sigmastate.Values.ByteArrayConstant import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} trait ErgoWalletSupport extends ScorexLogging { @@ -212,7 +212,7 @@ trait ErgoWalletSupport extends ScorexLogging { def minimalErgoAmount: Long = BoxUtils.minimalErgoAmountSimulated( lockWithAddress.script, - Colls.fromItems((Digest32 @@@ assetId) -> amount), + Colls.fromItems((Digest32Coll @@@ assetId.toColl) -> amount), nonMandatoryRegisters, parameters ) @@ -221,7 +221,7 @@ trait ErgoWalletSupport extends ScorexLogging { valueOpt.getOrElse(minimalErgoAmount), lockWithAddress.script, fullHeight, - Colls.fromItems((Digest32 @@@ assetId) -> amount), + Colls.fromItems((Digest32Coll @@@ assetId.toColl) -> amount), nonMandatoryRegisters ) } @@ -255,7 +255,7 @@ trait ErgoWalletSupport extends ScorexLogging { case changeBox: ErgoBoxAssets => // todo: is this extra check needed ? val reemissionTokenId = ergoSettings.chainSettings.reemission.reemissionTokenId - val assets = changeBox.tokens.filterKeys(_ != reemissionTokenId).map(t => Digest32 @@ idToBytes(t._1) -> t._2).toIndexedSeq + val assets = changeBox.tokens.filterKeys(_ != reemissionTokenId).map(t => Digest32Coll @@ idToBytes(t._1).toColl -> t._2).toIndexedSeq new ErgoBoxCandidate(changeBox.value, changeAddressOpt.get, walletHeight, assets.toColl) } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/IdUtils.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/IdUtils.scala index f7d61ec97b..a3040f805c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/IdUtils.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/IdUtils.scala @@ -3,8 +3,9 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform.ErgoBox.{BoxId, TokenId} import org.ergoplatform.settings.Algos import scorex.crypto.authds.ADKey -import scorex.crypto.hash.Digest32 import scorex.util.ModifierId +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps import supertagged.TaggedType object IdUtils { @@ -22,7 +23,7 @@ object IdUtils { def encodedTokenId(id: TokenId): EncodedTokenId = ModifierId @@ Algos.encode(id) - def decodedTokenId(id: EncodedTokenId): TokenId = Digest32 @@ Algos.decode(id) - .getOrElse(throw new Error("Failed to decode token id")) + def decodedTokenId(id: EncodedTokenId): TokenId = + Digest32Coll @@ (Algos.decode(id).getOrElse(throw new Error("Failed to decode token id"))).toColl } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala index 9fd46cc0ca..b1951b12f5 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.wallet -import com.google.common.hash.{BloomFilter, Funnels} -import org.ergoplatform.{ErgoAddressEncoder, ErgoScriptPredef, P2PKAddress} +import com.google.common.hash.{Funnels, BloomFilter} +import org.ergoplatform.sdk.wallet.secrets.ExtendedPublicKey +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress, ErgoTreePredef} import org.ergoplatform.settings.ErgoSettings -import org.ergoplatform.wallet.secrets.ExtendedPublicKey import sigmastate.Values import sigmastate.eval._ @@ -49,7 +49,7 @@ object WalletCache { def miningScripts(trackedPubKeys: Seq[ExtendedPublicKey], settings: ErgoSettings): Seq[Values.ErgoTree] = { trackedPubKeys.map { pk => - ErgoScriptPredef.rewardOutputScript(settings.miningRewardDelay, pk.key) + ErgoTreePredef.rewardOutputScript(settings.miningRewardDelay, pk.key) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala index 9c5055e99f..a20e07d1ac 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala @@ -4,10 +4,10 @@ import com.google.common.hash.BloomFilter import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import org.ergoplatform.nodeView.wallet.persistence.WalletStorage import org.ergoplatform.nodeView.wallet.scanning.Scan -import org.ergoplatform.settings.{ErgoSettings, Parameters} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey} +import org.ergoplatform.settings.{Parameters, ErgoSettings} import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter -import org.ergoplatform.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey} import scorex.util.ScorexLogging import scala.util.Try 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 91b2af73cc..0062438726 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -4,10 +4,12 @@ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.settings.Constants import scorex.core.serialization.ErgoSerializer -import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} + import scala.collection.mutable import scorex.util.Extensions._ +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps /** * Holds aggregate wallet data (including off-chain) with no need fo re-processing it on each request. @@ -35,7 +37,7 @@ object WalletDigestSerializer extends ErgoSerializer[WalletDigest] { w.putUInt(obj.walletAssetBalances.size) obj.walletAssetBalances.foreach { case (id, amt) => - w.putBytes(decodedTokenId(id)) + w.putBytes(decodedTokenId(id).toArray) w.putULong(amt) } } @@ -48,7 +50,7 @@ object WalletDigestSerializer extends ErgoSerializer[WalletDigest] { val walletAssetBalances = mutable.LinkedHashMap.empty[EncodedTokenId, Long] (0 until walletAssetBalancesSize).foreach { _ => - val kv = encodedTokenId(Digest32 @@ r.getBytes(Constants.ModifierIdSize)) -> r.getULong() + val kv = encodedTokenId(Digest32Coll @@ r.getBytes(Constants.ModifierIdSize).toColl) -> r.getULong() walletAssetBalances += kv } 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 83addf40e3..20662e7fcc 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala @@ -1,24 +1,24 @@ package org.ergoplatform.nodeView.wallet.persistence import java.io.File - import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform.nodeView.wallet.IdUtils.{EncodedTokenId, encodedTokenId} import org.ergoplatform.nodeView.wallet.{WalletTransaction, WalletTransactionSerializer} import org.ergoplatform.settings.{Algos, ErgoSettings, WalletSettings} -import org.ergoplatform.wallet.{AssetUtils, Constants} +import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.boxes.{TrackedBox, TrackedBoxSerializer} import scorex.core.VersionTag import scorex.crypto.authds.ADKey -import scorex.util.{ModifierId, ScorexLogging, idToBytes} +import scorex.util.{ScorexLogging, idToBytes, ModifierId} import Constants.{PaymentsScanId, ScanId} import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.modifiers.history.header.PreGenesisHeader import scorex.db.LDBVersionedStore -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults +import org.ergoplatform.sdk.wallet.AssetUtils import org.ergoplatform.wallet.transactions.TransactionBuilder import scorex.util.encode.Base16 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 fe1cbb237b..78d6fb757f 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala @@ -1,19 +1,20 @@ package org.ergoplatform.nodeView.wallet.persistence import com.google.common.primitives.{Ints, Shorts} -import org.ergoplatform.nodeView.state.{ErgoStateContext, ErgoStateContextSerializer} -import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest, ScanSerializer} -import org.ergoplatform.settings.{Constants, ErgoSettings, Parameters} -import org.ergoplatform.wallet.secrets.{DerivationPath, DerivationPathSerializer, ExtendedPublicKey, ExtendedPublicKeySerializer} +import org.ergoplatform.nodeView.state.{ErgoStateContextSerializer, ErgoStateContext} +import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, ScanSerializer, Scan} +import org.ergoplatform.settings.{Parameters, ErgoSettings, Constants} import org.ergoplatform.P2PKAddress +import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKeySerializer, ExtendedPublicKey, DerivationPath, DerivationPathSerializer} import scorex.crypto.hash.Blake2b256 import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} -import scorex.db.{LDBFactory, LDBKVStore} +import scorex.db.{LDBKVStore, LDBFactory} import java.io.File import scorex.util.ScorexLogging +import sigmastate.serialization.SigmaSerializer -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} /** * Persists version-agnostic wallet actor's mutable state (which is not a subject to rollbacks in case of forks) @@ -38,7 +39,8 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco val qty = Ints.fromByteArray(r.take(4)) (0 until qty).foldLeft((Seq.empty[DerivationPath], r.drop(4))) { case ((acc, bytes), _) => val length = Ints.fromByteArray(bytes.take(4)) - val pathTry = DerivationPathSerializer.parseBytesTry(bytes.slice(4, 4 + length)) + val pathBytes = SigmaSerializer.startReader(bytes.slice(4, 4 + length)) + val pathTry = DerivationPathSerializer.parseTry(pathBytes) val newAcc = pathTry.map(acc :+ _).getOrElse(acc) val bytesTail = bytes.drop(4 + length) newAcc -> bytesTail @@ -69,8 +71,9 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco def getPublicKey(path: DerivationPath): Option[ExtendedPublicKey] = { store .get(pubKeyPrefixKey(path)) - .flatMap{bytes => - ExtendedPublicKeySerializer.parseBytesTry(bytes) match { + .flatMap { bytes => + val r = SigmaSerializer.startReader(bytes) + ExtendedPublicKeySerializer.parseTry(r) match { case Success(key) => Some(key) case Failure(t) => @@ -90,7 +93,7 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco */ def readAllKeys(): Seq[ExtendedPublicKey] = { store.getRange(FirstPublicKeyId, LastPublicKeyId).map { case (_, v) => - ExtendedPublicKeySerializer.parseBytes(v) + ExtendedPublicKeySerializer.fromBytes(v) } } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala index 64e8d43041..469304acbe 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala @@ -3,8 +3,9 @@ package org.ergoplatform.nodeView.wallet.requests import io.circe.generic.decoding.DerivedDecoder.deriveDecoder import io.circe.{Decoder, KeyDecoder} import org.ergoplatform.ErgoBox -import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps /** * A request for boxes with given balance and assets @@ -14,7 +15,7 @@ case class BoxesRequest(targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, object BoxesRequest { implicit val keyDecoder: KeyDecoder[ErgoBox.TokenId] = - KeyDecoder.instance(s => Base16.decode(s).toOption.map(Digest32 @@ _)) + KeyDecoder.instance(s => Base16.decode(s).toOption.map(Digest32Coll @@ _.toColl)) implicit val decoder: Decoder[BoxesRequest] = cursor => diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/ExternalSecret.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/ExternalSecret.scala index 466628e054..b242d8c38b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/ExternalSecret.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/ExternalSecret.scala @@ -1,6 +1,6 @@ package org.ergoplatform.nodeView.wallet.requests -import org.ergoplatform.wallet.secrets.PrimitiveSecretKey +import org.ergoplatform.sdk.wallet.secrets.PrimitiveSecretKey /** * Externally provided secret (to be used once for a transaction to sign) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala index 46be4b1dda..6da5d1a888 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.wallet.requests import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction -import org.ergoplatform.wallet.secrets.{DhtSecretKey, DlogSecretKey} +import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} /** * A request to generate commitments for unsigned transaction, useful for multi-party signing. 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 ee6593c61b..7ba3175292 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala @@ -1,11 +1,11 @@ package org.ergoplatform.nodeView.wallet.requests import io.circe.syntax._ -import io.circe.{Decoder, Encoder, HCursor, Json} +import io.circe.{HCursor, Encoder, Json, Decoder} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.nodeView.wallet.ErgoAddressJsonEncoder import org.ergoplatform.settings.ErgoSettings -import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder, ErgoScriptPredef, Pay2SAddress} +import org.ergoplatform.{ErgoAddressEncoder, Pay2SAddress, ErgoAddress, ErgoTreePredef} case class RequestsHolder(requests: Seq[TransactionGenerationRequest], @@ -17,7 +17,7 @@ case class RequestsHolder(requests: Seq[TransactionGenerationRequest], // Add separate payment request with fee. def withFee(): Seq[TransactionGenerationRequest] = { - val address = Pay2SAddress(ErgoScriptPredef.feeProposition(minerRewardDelay)) + val address = Pay2SAddress(ErgoTreePredef.feeProposition(minerRewardDelay)) val feeRequests = feeOpt .map(PaymentRequest(address, _, assets = Seq.empty, registers = Map.empty)) .toSeq diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala index c5bd413819..26296a061b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala @@ -1,8 +1,8 @@ package org.ergoplatform.nodeView.wallet.requests import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction +import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.wallet.secrets.{DhtSecretKey, DlogSecretKey} /** * A request to sign a transaction diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala index bd73242ec3..b585517b0a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala @@ -120,17 +120,17 @@ case class EqualsScanningPredicate(regId: ErgoBox.RegisterId, value: EvaluatedVa */ case class ContainsAssetPredicate(assetId: ErgoBox.TokenId) extends ScanningPredicate { override def filter(box: ErgoBox): Boolean = { - box.additionalTokens.exists(_._1.sameElements(assetId)) + box.additionalTokens.exists(_._1 == assetId) } override def equals(obj: Any): Boolean = obj match { - case other: ContainsAssetPredicate => other.assetId.sameElements(assetId) + case other: ContainsAssetPredicate => other.assetId == assetId case _ => false } - override def hashCode(): Int = assetId.toSeq.hashCode() + override def hashCode(): Int = assetId.hashCode() - override def toString: String = s"ContainsAssetPredicate(${Base16.encode(assetId)})" + override def toString: String = s"ContainsAssetPredicate(${Base16.encode(assetId.toArray)})" } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala index 1a29164c9c..1f6386709d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala @@ -19,7 +19,7 @@ object ScanningPredicateJsonCodecs extends ApiCodecs { case ep: EqualsScanningPredicate => Json.obj("predicate" -> "equals".asJson, "register" -> ep.regId.asJson, "value" -> ep.value.asJson) case cap: ContainsAssetPredicate => - Json.obj("predicate" -> "containsAsset".asJson, "assetId" -> Base16.encode(cap.assetId).asJson) + Json.obj("predicate" -> "containsAsset".asJson, "assetId" -> Base16.encode(cap.assetId.toArray).asJson) case and: AndScanningPredicate => Json.obj("predicate" -> "and".asJson, "args" -> and.subPredicates.asJson) case or: OrScanningPredicate => 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 a8cb34ccbf..e30772ed0c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala @@ -3,12 +3,13 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId import scorex.core.serialization.ErgoSerializer -import scorex.crypto.hash.Digest32 import scorex.util.serialization.{Reader, Writer} import sigmastate.SType import sigmastate.Values.EvaluatedValue import sigmastate.serialization.ValueSerializer import scorex.util.Extensions._ +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { @@ -34,7 +35,7 @@ object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { w.putBytes(valueBytes) case a: ContainsAssetPredicate => w.put(ContainsAssetPrefix) - w.putBytes(a.assetId) + w.putBytes(a.assetId.toArray) case AndScanningPredicate(subPredicates@_*) => w.put(AndPrefix) w.putInt(subPredicates.length) @@ -72,7 +73,7 @@ object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { ContainsScanningPredicate(reg, bs) case b: Byte if b == ContainsAssetPrefix => val bs = r.getBytes(32) - ContainsAssetPredicate(Digest32 @@ bs) + ContainsAssetPredicate(Digest32Coll @@ bs.toColl) case b: Byte if b == AndPrefix => AndScanningPredicate(parseArgs(r) :_*) case b: Byte if b == OrPrefix => diff --git a/src/main/scala/org/ergoplatform/settings/Parameters.scala b/src/main/scala/org/ergoplatform/settings/Parameters.scala index 90adba7592..f2f896335c 100644 --- a/src/main/scala/org/ergoplatform/settings/Parameters.scala +++ b/src/main/scala/org/ergoplatform/settings/Parameters.scala @@ -11,8 +11,8 @@ import scorex.util.Extensions._ import scala.util.Try import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.history.extension.{Extension, ExtensionCandidate} -import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters import Extension.SystemParametersPrefix +import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters /** * System parameters which could be readjusted via collective miners decision. diff --git a/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala index 0ea07612d7..942801417a 100644 --- a/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala @@ -11,7 +11,7 @@ import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.Stubs import org.ergoplatform.utils.generators.ErgoGenerators -import org.ergoplatform.{Pay2SAddress, ErgoScriptPredef} +import org.ergoplatform.{Pay2SAddress, ErgoTreePredef} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -48,7 +48,7 @@ class MiningApiRouteSpec it should "display miner pk" in { Get(prefix + "/rewardAddress") ~> route ~> check { status shouldBe StatusCodes.OK - val script = ErgoScriptPredef.rewardOutputScript(settings.chainSettings.monetary.minerRewardDelay, pk) + val script = ErgoTreePredef.rewardOutputScript(settings.chainSettings.monetary.minerRewardDelay, pk) val addressStr = Pay2SAddress(script)(settings.addressEncoder).toString() responseAs[Json].hcursor.downField("rewardAddress").as[String] shouldEqual Right(addressStr) } diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index ab267a4c33..36abac0bda 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -17,7 +17,6 @@ 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.crypto.hash.Digest32 import scorex.util.encode.Base16 import sigmastate.SType import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} @@ -44,7 +43,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec val dataInput = DataInput(input.boxId) val absentModifierId = "0000000000000000000000000000000000000000000000000000000000000000" - val tokens = List[(TokenId, Long)](Digest32 @@@ inputBox.id -> 10) + val tokens = List[(TokenId, Long)](Digest32Coll @@@ inputBox.id.toColl -> 10) val registers = Map( ErgoBox.R4 -> ByteArrayConstant("name".getBytes("UTF-8")), @@ -229,7 +228,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec } it should "return unconfirmed output by tokenId from mempool" in { - val searchedToken = Base16.encode(tokens.head._1) + val searchedToken = Base16.encode(tokens.head._1.toArray) Get(prefix + s"/unconfirmed/outputs/byTokenId/$searchedToken") ~> chainedRoute ~> check { status shouldBe StatusCodes.OK val actualOutputIds = responseAs[List[Json]].map(_.hcursor.downField("boxId").as[String].right.get) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala index a8c6db6a3f..a2269b863c 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorPropSpec.scala @@ -1,6 +1,6 @@ package org.ergoplatform.mining -import org.ergoplatform.ErgoScriptPredef +import org.ergoplatform.ErgoTreePredef import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.ErgoStateContext import org.ergoplatform.settings.MonetarySettings @@ -16,7 +16,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { val delta: Int = settings.chainSettings.monetary.minerRewardDelay private def expectedRewardOutputScriptBytes(pk: ProveDlog): Array[Byte] = - ErgoScriptPredef.rewardOutputScript(delta, pk).bytes + ErgoTreePredef.rewardOutputScript(delta, pk).bytes implicit private val verifier: ErgoInterpreter = ErgoInterpreter(parameters) @@ -195,7 +195,7 @@ class CandidateGeneratorPropSpec extends ErgoPropertyTest { val delta = 1 val inputsNum = 2 - val feeProposition = ErgoScriptPredef.feeProposition(delta) + val feeProposition = ErgoTreePredef.feeProposition(delta) val bh = boxesHolderGen.sample.get var us = createUtxoState(bh, parameters) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala index 0bc8866630..ad65d2dc34 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala @@ -1,21 +1,21 @@ package org.ergoplatform.mining -import akka.actor.{ActorRef, ActorSystem} +import akka.actor.{ActorSystem, ActorRef} import akka.pattern.{StatusReply, ask} -import akka.testkit.{TestKit, TestProbe} +import akka.testkit.{TestProbe, TestKit} import akka.util.Timeout import org.bouncycastle.util.BigIntegers 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.nodeView.ErgoReadersHolder.{GetReaders, Readers} +import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} +import org.ergoplatform.nodeView.{ErgoReadersHolderRef, ErgoNodeViewRef} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.ErgoTestHelpers -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoScriptPredef, Input} +import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied @@ -201,7 +201,7 @@ class CandidateGeneratorSpec extends AnyFlatSpec with ErgoTestHelpers with Event DLogProverInput(BigIntegers.fromUnsignedByteArray("test".getBytes())).publicImage val newlyMinedBlock = readers.h.bestFullBlockOpt.get val rewardBox: ErgoBox = newlyMinedBlock.transactions.last.outputs.last - rewardBox.propositionBytes shouldBe ErgoScriptPredef + rewardBox.propositionBytes shouldBe ErgoTreePredef .rewardOutputScript(emission.settings.minerRewardDelay, defaultMinerPk) .bytes val input = Input(rewardBox.id, emptyProverResult) diff --git a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala index d17b3da442..bd12ac62b3 100644 --- a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala @@ -1,31 +1,31 @@ package org.ergoplatform.mining -import akka.actor.{ActorRef, ActorSystem} +import akka.actor.{ActorSystem, ActorRef} import akka.pattern.{StatusReply, ask} -import akka.testkit.{TestKit, TestProbe} +import akka.testkit.{TestProbe, TestKit} import akka.util.Timeout import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate} 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.nodeView.ErgoReadersHolder.{GetReaders, Readers} +import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} 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.nodeView.{ErgoReadersHolderRef, ErgoNodeViewRef} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.utils.generators.ValidBlocksGenerators -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoScriptPredef, Input} +import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied import org.ergoplatform.wallet.interpreter.ErgoInterpreter import sigmastate.SigmaAnd -import sigmastate.Values.{ErgoTree, SigmaPropConstant} +import sigmastate.Values.{SigmaPropConstant, ErgoTree} import sigmastate.basics.DLogProtocol import sigmastate.basics.DLogProtocol.DLogProverInput @@ -85,7 +85,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen testProbe.expectMsgClass(newBlockDelay, newBlockSignal) val boxToSpend: ErgoBox = r.h.bestFullBlockOpt.get.transactions.last.outputs.last - boxToSpend.propositionBytes shouldBe ErgoScriptPredef.rewardOutputScript(emission.settings.minerRewardDelay, defaultMinerPk).bytes + boxToSpend.propositionBytes shouldBe ErgoTreePredef.rewardOutputScript(emission.settings.minerRewardDelay, defaultMinerPk).bytes val input = Input(boxToSpend.id, emptyProverResult) @@ -241,7 +241,7 @@ class ErgoMinerSpec extends AnyFlatSpec with ErgoTestHelpers with ValidBlocksGen val prop2: DLogProtocol.ProveDlog = DLogProverInput(BigIntegers.fromUnsignedByteArray("test2".getBytes())).publicImage val boxToDoubleSpend: ErgoBox = r.h.bestFullBlockOpt.get.transactions.last.outputs.last - boxToDoubleSpend.propositionBytes shouldBe ErgoScriptPredef.rewardOutputScript(emission.settings.minerRewardDelay, defaultMinerPk).bytes + boxToDoubleSpend.propositionBytes shouldBe ErgoTreePredef.rewardOutputScript(emission.settings.minerRewardDelay, defaultMinerPk).bytes val input = Input(boxToDoubleSpend.id, emptyProverResult) diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index b3f9ead789..4f9fd14efe 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -4,26 +4,28 @@ import io.circe.syntax._ import org.ergoplatform.ErgoBox._ import org.ergoplatform.nodeView.ErgoContext import org.ergoplatform.nodeView.state.{ErgoStateContext, VotingData} +import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext import org.ergoplatform.settings.Parameters.MaxBlockCostIncrease import org.ergoplatform.settings.ValidationRules.{bsBlockTransactionsCost, txAssetsInOneBox} import org.ergoplatform.settings._ -import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestConstants} +import org.ergoplatform.utils.{ErgoTestConstants, ErgoPropertyTest} import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor -import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, TransactionHintsBag} -import org.ergoplatform.wallet.protocol.context.{InputContext, TransactionContext} -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} +import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoInterpreter} +import org.ergoplatform.wallet.protocol.context.InputContext +import org.ergoplatform.{Input, ErgoBox, ErgoBoxCandidate} import org.scalacheck.Gen import scalan.util.BenchmarkUtil import scorex.crypto.authds.ADKey -import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.db.ByteArrayWrapper -import scorex.util.{ModifierId, bytesToId} +import scorex.crypto.hash.Blake2b256 +import scorex.util.{bytesToId, ModifierId} import scorex.util.encode.Base16 import sigmastate.AND -import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, LongArrayConstant, SigmaPropConstant, TrueLeaf} +import sigmastate.Values.{TrueLeaf, SigmaPropConstant, ByteArrayConstant, IntConstant, ByteConstant, LongArrayConstant} +import sigmastate.basics.CryptoConstants import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval._ -import sigmastate.interpreter.{ContextExtension, CryptoConstants, ProverResult} +import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.helpers.TestingHelpers._ import scala.util.{Random, Try} @@ -40,8 +42,8 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { if (modified) { (seq :+ ebc) -> true } else { - if (ebc.additionalTokens.nonEmpty && ebc.additionalTokens.exists(t => !java.util.Arrays.equals(t._1, from.head.id))) { - (seq :+ modifyAsset(ebc, deltaFn, Digest32 @@@ from.head.id)) -> true + if (ebc.additionalTokens.nonEmpty && ebc.additionalTokens.exists(t => !java.util.Arrays.equals(t._1.toArray, from.head.id))) { + (seq :+ modifyAsset(ebc, deltaFn, Digest32Coll @@@ from.head.id.toColl)) -> true } else { (seq :+ ebc) -> false } @@ -62,10 +64,10 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { private def modifyAsset(boxCandidate: ErgoBoxCandidate, deltaFn: Long => Long, idToskip: TokenId): ErgoBoxCandidate = { - val assetId = boxCandidate.additionalTokens.find(t => !java.util.Arrays.equals(t._1, idToskip)).get._1 + val assetId = boxCandidate.additionalTokens.find(t => t._1 != idToskip).get._1 val tokens = boxCandidate.additionalTokens.map { case (id, amount) => - if (java.util.Arrays.equals(id, assetId)) assetId -> deltaFn(amount) else assetId -> amount + if (id == assetId) assetId -> deltaFn(amount) else assetId -> amount } new ErgoBoxCandidate( @@ -205,7 +207,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { property("impossible to overflow an asset value") { val gen = validErgoTransactionGenTemplate(minAssets = 1, maxAssets = 1, maxInputs = 16, propositionGen = trueLeafGen) forAll(gen) { case (from, tx) => - val tokenOpt = tx.outputCandidates.flatMap(_.additionalTokens.toArray).map(t => ByteArrayWrapper.apply(t._1) -> t._2) + val tokenOpt = tx.outputCandidates.flatMap(_.additionalTokens.toArray) .groupBy(_._1).find(_._2.size >= 2) whenever(tokenOpt.nonEmpty) { @@ -215,7 +217,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { var modified = false val updCandidates = tx.outputCandidates.map { c => val updTokens = c.additionalTokens.map { case (id, amount) => - if (!modified && ByteArrayWrapper(id) == tokenId) { + if (!modified && id == tokenId) { modified = true id -> ((Long.MaxValue - tokenAmount) + amount + 1) } else { @@ -251,7 +253,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { // already existing token from one of the inputs val existingToken = from.flatMap(_.additionalTokens.toArray).toSet.head // completely new token - val randomToken = (Digest32 @@ scorex.util.Random.randomBytes(), Random.nextInt(100000000).toLong) + val randomToken = (Digest32Coll @@ scorex.util.Random.randomBytes().toColl, Random.nextInt(100000000).toLong) val in0 = from.last // new token added to the last input @@ -296,7 +298,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { property("spam simulation (transaction validation cost with too many tokens exceeds block limit)") { val bxsQty = 392 // with greater value test is failing with collection size exception val (inputs, tx) = validErgoTransactionGenTemplate(1, 1,16).sample.get // it takes too long to test with `forAll` - val tokens = (0 until 255).map(_ => (Digest32 @@ scorex.util.Random.randomBytes(), Random.nextLong)) + val tokens = (0 until 255).map(_ => (Digest32Coll @@ scorex.util.Random.randomBytes().toColl, Random.nextLong)) val (in, out) = { val in0 = inputs.head val out0 = tx.outputs.head diff --git a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala index bb80727f8e..2371e92875 100644 --- a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala @@ -1,9 +1,8 @@ package org.ergoplatform.network import java.nio.ByteBuffer - import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{AutolykosSolution, groupElemFromBytes} +import org.ergoplatform.mining.{groupElemFromBytes, AutolykosSolution} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.Algos import org.ergoplatform.utils.ErgoPropertyTest @@ -11,7 +10,7 @@ import scorex.crypto.authds.ADDigest import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.basics.CryptoConstants.EcPointType class HeaderSerializationSpecification extends ErgoPropertyTest with DecodingUtils { 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 d836c47696..817a48bd37 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -1,25 +1,25 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoBoxCandidate, ErgoScriptPredef, P2PKAddress, UnsignedInput} +import org.ergoplatform.{ErgoAddressEncoder, UnsignedInput, P2PKAddress, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{AutolykosPowScheme, CandidateBlock, CandidateGenerator} +import org.ergoplatform.mining.{CandidateBlock, AutolykosPowScheme, 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.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} +import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, txSegmentId, hashErgoTree} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UtxoState, UtxoStateReader} -import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} -import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} -import scorex.crypto.hash.Digest32 -import scorex.util.{ModifierId, bytesToId} +import org.ergoplatform.nodeView.state.{StateConstants, UtxoStateReader, ErgoStateContext, StateType, UtxoState, ErgoState} +import org.ergoplatform.settings.{NodeConfigurationSettings, NetworkType, ErgoSettings} +import org.ergoplatform.utils.{HistoryTestHelpers, ErgoPropertyTest, ErgoTestHelpers} +import scorex.util.{bytesToId, ModifierId} import sigmastate.Values import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval._ import special.collection.Coll import spire.implicits.cfor @@ -239,7 +239,7 @@ object ChainGenerator extends ErgoTestHelpers { val MaxTxsPerBlock: Int = 10 val minerPk: ProveDlog = defaultProver.hdKeys.head.publicImage val selfAddressScript: Values.ErgoTree = P2PKAddress(minerPk).script - val minerProp: Values.ErgoTree = ErgoScriptPredef.rewardOutputScript(RewardDelay, minerPk) + val minerProp: Values.ErgoTree = ErgoTreePredef.rewardOutputScript(RewardDelay, minerPk) val votingEpochLength: Height = votingSettings.votingLength val protocolVersion: Byte = initSettings.chainSettings.protocolVersion val minimalSuffix = 2 @@ -298,7 +298,7 @@ object ChainGenerator extends ErgoTestHelpers { val tokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] inOpt match { case Some(input) if cond => - tokens += Tuple2(Digest32 @@@ input.id, math.abs(Random.nextInt())) + tokens += Tuple2(Digest32Coll @@@ input.id.toColl, math.abs(Random.nextInt())) case Some(tokenBox) if !cond => tokenBox.additionalTokens.toArray.foreach(tokens += _) case _ => diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index 96c8aad776..7134154010 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -1,19 +1,19 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix -import org.ergoplatform.ErgoScriptPredef.boxCreationHeight -import org.ergoplatform.{ErgoBox, ErgoScriptPredef, Height, Self} -import org.ergoplatform.nodeView.state.{BoxHolder, ErgoState, UtxoState} +import org.ergoplatform.ErgoTreePredef.boxCreationHeight +import org.ergoplatform.{Self, ErgoBox, Height, ErgoTreePredef} +import org.ergoplatform.nodeView.state.{UtxoState, ErgoState, BoxHolder} import org.ergoplatform.settings.Algos import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} import scorex.crypto.authds.avltree.batch.Remove import sigmastate._ import sigmastate.Values._ +import sigmastate.basics.CryptoConstants.dlogGroup import sigmastate.lang.Terms._ import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.eval.{CompiletimeIRContext, IRContext} -import sigmastate.interpreter.CryptoConstants.dlogGroup -import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} +import sigmastate.eval.{IRContext, CompiletimeIRContext} +import sigmastate.lang.{TransformingSigmaBuilder, CompilerSettings, SigmaCompiler} import scala.util.Try @@ -55,7 +55,7 @@ class ScriptsSpec extends ErgoPropertyTest { delta shouldBe -1000 applyBlockSpendingScript(GE(Height, Plus(boxCreationHeight(Self), IntConstant(delta))).toSigmaProp) shouldBe 'success - applyBlockSpendingScript(ErgoScriptPredef.rewardOutputScript(delta, defaultMinerPk)) shouldBe 'success + applyBlockSpendingScript(ErgoTreePredef.rewardOutputScript(delta, defaultMinerPk)) shouldBe 'success // applyBlockSpendingScript(ErgoScriptPredef.feeProposition(delta)) shouldBe 'success } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala index 6b4b8fc76b..a01eac7e17 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala @@ -3,29 +3,29 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, R1} import org.ergoplatform._ import org.ergoplatform.db.DBSpec -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, ErgoTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults -import org.ergoplatform.nodeView.wallet.persistence.{OffChainRegistry, WalletRegistry, WalletStorage} -import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, PaymentRequest} -import org.ergoplatform.nodeView.wallet.scanning.{EqualsScanningPredicate, ScanRequest, ScanWalletInteraction} +import org.ergoplatform.nodeView.wallet.persistence.{WalletRegistry, OffChainRegistry, WalletStorage} +import org.ergoplatform.nodeView.wallet.requests.{PaymentRequest, AssetIssueRequest} +import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, EqualsScanningPredicate, ScanWalletInteraction} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DerivationPath} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.fixtures.WalletFixture import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import org.ergoplatform.utils.{ErgoPropertyTest, MempoolTestHelpers, WalletTestOps} +import org.ergoplatform.utils.{WalletTestOps, MempoolTestHelpers, ErgoPropertyTest} import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult -import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, ReplaceCompactCollectBoxSelector, TrackedBox} +import org.ergoplatform.wallet.boxes.{TrackedBox, ErgoBoxSerializer, ReplaceCompactCollectBoxSelector} import org.ergoplatform.wallet.crypto.ErgoSignature import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.scalacheck.Gen import org.scalatest.BeforeAndAfterAll import scorex.db.{LDBKVStore, LDBVersionedStore} import scorex.util.encode.Base16 -import sigmastate.ErgoTreeBenchmarks.traversableColl -import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} +import sigmastate.Values.{EvaluatedValue, ByteArrayConstant} +import sigmastate.eval.Extensions.ArrayOps import sigmastate.helpers.TestingHelpers.testBox import sigmastate.{SType, Values} @@ -148,7 +148,7 @@ class ErgoWalletServiceSpec ErgoBox.R5 -> ByteArrayConstant("test-description".getBytes("UTF-8")), ErgoBox.R6 -> ByteArrayConstant("4".getBytes("UTF-8")), ) - validCandidate.additionalTokens.toMap shouldBe Map(ergoBox.id -> 1) + validCandidate.additionalTokens.toArray.toMap shouldBe Map(ergoBox.id.toColl -> 1) validCandidate.creationHeight shouldBe startHeight validCandidate.ergoTree shouldBe pks.head.script } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala index 9c223aa364..846c030fab 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala @@ -1,27 +1,25 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform._ -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.state.{ErgoStateContext, VotingData} import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.nodeView.wallet.persistence.{WalletDigest, WalletDigestSerializer} -import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, BurnTokensRequest, ExternalSecret, PaymentRequest} +import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, PaymentRequest, BurnTokensRequest, AssetIssueRequest} +import org.ergoplatform.sdk.wallet.secrets.PrimitiveSecretKey import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.utils._ import org.ergoplatform.utils.fixtures.WalletFixture -import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, TransactionHintsBag} +import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoInterpreter} import scorex.util.encode.Base16 import sigmastate.eval._ import sigmastate.eval.Extensions._ - import org.ergoplatform.wallet.boxes.BoxSelector.MinBoxValue import org.ergoplatform.wallet.boxes.ErgoBoxSerializer -import org.ergoplatform.wallet.secrets.PrimitiveSecretKey import org.scalacheck.Gen import org.scalatest.concurrent.Eventually -import scorex.crypto.hash.Digest32 import scorex.util.ModifierId -import sigmastate.{CAND, CTHRESHOLD} +import sigmastate.{CTHRESHOLD, CAND} import sigmastate.basics.DLogProtocol.DLogProverInput import scala.concurrent.duration._ @@ -242,7 +240,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually property("whitelist set, preserve tokens from auto-burn") { val inputs = { val x = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) - Seq(encodedTokenId(Digest32 @@@ x.head.boxId)) + Seq(encodedTokenId(Digest32Coll @@@ x.head.boxId.toColl)) } implicit val ww: WalletFixture = new WalletFixture(settings diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala index db0f19b76e..b680edb1ad 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala @@ -1,15 +1,15 @@ package org.ergoplatform.nodeView.wallet.persistence -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, Input} +import org.ergoplatform.{ErgoAddressEncoder, Input, ErgoBox} import org.ergoplatform.ErgoBox.{AdditionalRegisters, TokenId} import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults import org.ergoplatform.nodeView.wallet.{WalletTransaction, WalletVars} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DerivationPath} import org.ergoplatform.utils.ErgoTestConstants import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey} import scorex.util.ModifierId import scorex.util.encode.Base16 import sigmastate.Values.ErgoTree diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala index a78cb1dd4d..c69e6d6437 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorageSpec.scala @@ -4,8 +4,8 @@ import com.google.common.primitives.Ints import org.ergoplatform.db.DBSpec import org.ergoplatform.nodeView.wallet.persistence.WalletStorage.SecretPathsKey import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, ScanWalletInteraction} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, DerivationPathSerializer} import org.ergoplatform.utils.generators.WalletGenerators -import org.ergoplatform.wallet.secrets.{DerivationPathSerializer, DerivationPath} import org.scalacheck.Gen import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala index 54991244fb..d8debd33b9 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala @@ -4,9 +4,10 @@ import io.circe.parser._ import org.ergoplatform.ErgoBox import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators -import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 import sigmastate.Values.ByteArrayConstant +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps import scala.language.implicitConversions @@ -19,13 +20,13 @@ class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with Wal private val complexOr = OrScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, ByteArrayConstant(Array.fill(32)(1: Byte))), EqualsScanningPredicate(ErgoBox.R4, ByteArrayConstant(Array.fill(32)(0: Byte))), - ContainsAssetPredicate(Digest32 @@ Array.fill(32)(0: Byte)) + ContainsAssetPredicate(Digest32Coll @@ Array.fill(32)(0: Byte).toColl) ) private val complexAnd = AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, ByteArrayConstant(Array.fill(32)(1: Byte))), EqualsScanningPredicate(ErgoBox.R4, ByteArrayConstant(Array.fill(32)(1: Byte))), - ContainsAssetPredicate(Digest32 @@ Array.fill(32)(1: Byte)) + ContainsAssetPredicate(Digest32Coll @@ Array.fill(32)(1: Byte).toColl) ) property("json roundtrip for generated predicate") { @@ -57,7 +58,7 @@ class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with Wal val bs = Base16.decode("02dada811a888cd0dc7a0a41739a3ad9b0f427741fe6ca19700cf1a51200c96bf7").get scanningPredicateDecoder.decodeJson(j).toTry.get == AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, bs), - ContainsAssetPredicate(Digest32 @@ bs) + ContainsAssetPredicate(Digest32Coll @@ bs.toColl) ) } @@ -71,7 +72,7 @@ class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with Wal val bs = Base16.decode("02dada811a888cd0dc7a0a41739a3ad9b0f427741fe6ca19700cf1a51200c96bf7").get scanningPredicateDecoder.decodeJson(j).toTry.get == AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R4, bs), - ContainsAssetPredicate(Digest32 @@ bs) + ContainsAssetPredicate(Digest32Coll @@ bs.toColl) ) } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala index 6138f216ad..738a411c38 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala @@ -1,10 +1,9 @@ package org.ergoplatform.nodeView.wallet.scanning -import org.ergoplatform.{ErgoScriptPredef, P2PKAddress} +import org.ergoplatform.{P2PKAddress, ErgoTreePredef} import org.ergoplatform.ErgoBox.R1 import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import scorex.crypto.hash.Digest32 import sigmastate.Values.ByteArrayConstant import sigmastate.helpers.TestingHelpers._ @@ -12,6 +11,8 @@ import scala.util.Random import scala.language.implicitConversions import io.circe.parser._ import org.ergoplatform.wallet.serialization.JsonCodecsWrapper +import sigmastate.eval.Digest32Coll +import sigmastate.eval.Extensions.ArrayOps class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransactionGenerators { @@ -45,7 +46,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti property("equals - miner prop") { forAll(proveDlogGen) { pk => - val minerProp = ErgoScriptPredef.rewardOutputScript(testDelay, pk) + val minerProp = ErgoTreePredef.rewardOutputScript(testDelay, pk) val mpBytes = minerProp.bytes //look for exact miner script bytes @@ -77,7 +78,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti property("contains - miner prop") { forAll(ergoAddressGen) { p2pkAddress => //look for exact p2pk script bytes - val minerProp = ErgoScriptPredef.rewardOutputScript(testDelay, p2pkAddress.asInstanceOf[P2PKAddress].pubkey) + val minerProp = ErgoTreePredef.rewardOutputScript(testDelay, p2pkAddress.asInstanceOf[P2PKAddress].pubkey) val box = testBox(value = 1, minerProp, creationHeight = 0) val pkBytes = p2pkAddress.asInstanceOf[P2PKAddress].pubkeyBytes @@ -101,7 +102,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti val emptyBox = testBox(value = 1, pk, creationHeight = 0) ContainsAssetPredicate(tokenId).filter(emptyBox) shouldBe false - ContainsAssetPredicate(Digest32 @@ mutateRandomByte(tokenId)).filter(box) shouldBe false + ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl).filter(box) shouldBe false } } } @@ -121,7 +122,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true AndScanningPredicate( - ContainsAssetPredicate(Digest32 @@ mutateRandomByte(tokenId)), + ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), ContainsScanningPredicate(R1, p2pk.contentBytes) ).filter(box) shouldBe false @@ -148,7 +149,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true OrScanningPredicate( - ContainsAssetPredicate(Digest32 @@ mutateRandomByte(tokenId)), + ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), ContainsScanningPredicate(R1, p2pk.contentBytes) ).filter(box) shouldBe true @@ -158,7 +159,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true OrScanningPredicate( - ContainsAssetPredicate(Digest32 @@ mutateRandomByte(tokenId)), + ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), ContainsScanningPredicate(R1, mutateRandomByte(p2pk.contentBytes)) ).filter(box) shouldBe false } diff --git a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala index 060e79c9ad..ace1b9f677 100644 --- a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala +++ b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala @@ -1,18 +1,19 @@ package org.ergoplatform.reemission import org.ergoplatform.settings.{MonetarySettings, ReemissionSettings} -import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestConstants} +import org.ergoplatform.utils.{ErgoTestConstants, ErgoPropertyTest} import org.ergoplatform._ -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId import sigmastate.AvlTreeData import sigmastate.TrivialProp.TrueProp -import sigmastate.eval.{Colls, Digest32RType} +import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.{Colls, Digest32Coll} import sigmastate.helpers.TestingHelpers.testBox -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigmastate.helpers.{ErgoLikeTestInterpreter, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting} import sigmastate.interpreter.Interpreter.emptyEnv -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} // done similarly to ErgoScriptPredefSpec in sigma repo class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { @@ -30,7 +31,7 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { private val rr = new ReemissionRules(rs) - private val reemissionBoxAssets = Colls.fromItems((Digest32 @@ rs.reemissionNftIdBytes) -> 1L) + private val reemissionBoxAssets = Colls.fromItems((Digest32Coll @@ rs.reemissionNftIdBytes.toColl) -> 1L) private val fakeMessage = Blake2b256("Hello World") @@ -67,7 +68,7 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { property("reemissionBoxProp - spending path") { val minerPk = prover.dlogSecrets.head.publicImage val pkBytes = minerPk.pkBytes - val minerProp = ErgoScriptPredef.rewardOutputScript(ms.minerRewardDelay, minerPk) + val minerProp = ErgoTreePredef.rewardOutputScript(ms.minerRewardDelay, minerPk) val currentHeight = rs.reemissionStartHeight val nextHeight = currentHeight + 1 @@ -122,7 +123,7 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { checkRewardsTx(nextHeight, pkBytes6, inputBoxes, spendingTransaction, false) // we modify reward delay here, not PK - val minerProp7 = ErgoScriptPredef.rewardOutputScript(ms.minerRewardDelay - 1, minerPk) + val minerProp7 = ErgoTreePredef.rewardOutputScript(ms.minerRewardDelay - 1, minerPk) val minerBox7 = new ErgoBoxCandidate(reemissionReward, minerProp7, nextHeight) val spendingTransaction7 = ErgoLikeTransaction(inputs, IndexedSeq(newReemissionBox, minerBox7)) checkRewardsTx(nextHeight, pkBytes, inputBoxes, spendingTransaction7, false) @@ -185,9 +186,11 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { // pay-2-reemission box can be spent only with a box with reemission NFT as input #0 val reemissionBoxAssets6 = Colls.fromItems( - (Digest32 @@ rs.reemissionNftIdBytes.reverse) -> 1L + (Digest32Coll @@ rs.reemissionNftIdBytes.reverse.toColl) -> 1L ) - val newReemissionBox6 = new ErgoBoxCandidate(reemissionBox.value + mergedValue - feeValue, prop, currentHeight, reemissionBoxAssets6) + val newReemissionBox6 = new ErgoBoxCandidate( + reemissionBox.value + mergedValue - feeValue, + prop, currentHeight, reemissionBoxAssets6) val spendingTransaction6 = ErgoLikeTransaction(inputs, IndexedSeq(newReemissionBox6, feeBox)) val ctx = ErgoLikeContextTesting( diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 48e9cb2732..90d8f2821b 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -1,25 +1,25 @@ package org.ergoplatform.serialization import io.circe.syntax._ -import io.circe.{ACursor, Decoder, Encoder, Json} +import io.circe.{Encoder, ACursor, Json, Decoder} import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.NonMandatoryRegisterId import org.ergoplatform.http.api.ApiEncoderOption.HideDetails.implicitValue -import org.ergoplatform.http.api.ApiEncoderOption.{Detalization, ShowDetails} +import org.ergoplatform.http.api.ApiEncoderOption.{ShowDetails, Detalization} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.modifiers.ErgoFullBlock 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.{DlogSecretKey, DhtSecretKey} import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.TrackedBox -import org.ergoplatform.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.scalatest.Inspectors import sigmastate.SType -import sigmastate.Values.{ErgoTree, EvaluatedValue} +import sigmastate.Values.{EvaluatedValue, ErgoTree} import scala.util.Random diff --git a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala index c234dab7e8..80a75939ea 100644 --- a/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala +++ b/src/test/scala/org/ergoplatform/tools/ChainGenerator.scala @@ -44,7 +44,7 @@ object ChainGenerator extends App with ErgoTestHelpers { val prover = defaultProver val minerPk = prover.hdKeys.head.publicImage val selfAddressScript = P2PKAddress(minerPk).script - val minerProp = ErgoScriptPredef.rewardOutputScript(RewardDelay, minerPk) + val minerProp = ErgoTreePredef.rewardOutputScript(RewardDelay, minerPk) val pow = new AutolykosPowScheme(powScheme.k, powScheme.n) val blockInterval = 2.minute diff --git a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala index 70b6bc8e9c..41306419e7 100644 --- a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala +++ b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala @@ -1,15 +1,15 @@ package org.ergoplatform.tools import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.{ErgoBoxCandidate, Input} +import org.ergoplatform.{Input, ErgoBoxCandidate} import scorex.crypto.authds.ADKey -import scorex.crypto.hash.Digest32 import scorex.utils.Random import sigmastate.basics.DLogProtocol.DLogProverInput -import sigmastate.interpreter.{ProverResult, ContextExtension} +import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.eval._ import org.ergoplatform.settings.LaunchParameters._ import org.ergoplatform.settings.Constants._ +import sigmastate.eval.Extensions.ArrayOps object FeeSimulator extends App { @@ -23,7 +23,7 @@ object FeeSimulator extends App { val input = Input(ADKey @@ Random.randomBytes(32), ProverResult(Random.randomBytes(65), ContextExtension(Map()))) val creationHeight: Int = 100000 - val box1 = new ErgoBoxCandidate(scala.util.Random.nextLong(), k1, creationHeight, Colls.fromItems((Digest32 @@ Random.randomBytes(32)) -> scala.util.Random.nextLong())) + val box1 = new ErgoBoxCandidate(scala.util.Random.nextLong(), k1, creationHeight, Colls.fromItems((Digest32Coll @@ Random.randomBytes(32).toColl) -> scala.util.Random.nextLong())) val box2 = new ErgoBoxCandidate(scala.util.Random.nextLong(), k2, creationHeight) val simpleTx = ErgoTransaction(IndexedSeq(input, input), IndexedSeq(box1, box2)) diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 707035bf79..1493f15bd4 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -6,7 +6,8 @@ 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.nodeView.state.{ErgoState, ErgoStateContext, StateConstants, StateType, UpcomingStateContext} +import org.ergoplatform.nodeView.state.{StateConstants, UpcomingStateContext, ErgoStateContext, StateType, ErgoState} +import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.Parameters.{MaxBlockCostIncrease, MinValuePerByteIncrease} import org.ergoplatform.settings.ValidationRules._ @@ -14,16 +15,15 @@ import org.ergoplatform.settings._ import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, ErgoProvingInterpreter} import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.ExtendedSecretKey -import org.ergoplatform.{DataInput, ErgoBox, ErgoScriptPredef} +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 import sigmastate.Values.ErgoTree -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} import sigmastate.interpreter.{ContextExtension, ProverResult} import scala.concurrent.duration._ @@ -60,7 +60,7 @@ trait ErgoTestConstants extends ScorexLogging { val coinsTotal: Long = emission.coinsTotal val stateConstants: StateConstants = StateConstants(settings) val genesisStateDigest: ADDigest = settings.chainSettings.genesisStateDigest - val feeProp: ErgoTree = ErgoScriptPredef.feeProposition(emission.settings.minerRewardDelay) + val feeProp: ErgoTree = ErgoTreePredef.feeProposition(emission.settings.minerRewardDelay) val emptyProverResult: ProverResult = ProverResult(Array.emptyByteArray, ContextExtension.empty) lazy val defaultSeed: Array[Byte] = Mnemonic.toSeed(settings.walletSettings.testMnemonic.fold[SecretString](SecretString.empty())(SecretString.create(_))) diff --git a/src/test/scala/org/ergoplatform/utils/Stubs.scala b/src/test/scala/org/ergoplatform/utils/Stubs.scala index 66ffffeea1..cd817a9c41 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -1,52 +1,52 @@ package org.ergoplatform.utils -import akka.actor.{Actor, ActorRef, ActorSystem, Props} +import akka.actor.{ActorSystem, ActorRef, Actor, Props} import akka.pattern.StatusReply import org.bouncycastle.util.BigIntegers import org.ergoplatform.P2PKAddress import org.ergoplatform.mining.CandidateGenerator.Candidate -import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner, WorkMessage} +import org.ergoplatform.mining.{AutolykosSolution, ErgoMiner, CandidateGenerator, WorkMessage} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, ErgoTransaction} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction -import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} +import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders, GetDataFromHistory} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.mempool.ErgoMemPool -import org.ergoplatform.nodeView.mempool.ErgoMemPool.{ProcessingOutcome, SortingOption} +import org.ergoplatform.nodeView.mempool.ErgoMemPool.{SortingOption, ProcessingOutcome} import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState -import org.ergoplatform.nodeView.state.{DigestState, ErgoStateContext, StateType} +import org.ergoplatform.nodeView.state.{StateType, DigestState, ErgoStateContext} import org.ergoplatform.nodeView.wallet.ErgoWalletActor._ import org.ergoplatform.nodeView.wallet.ErgoWalletService.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.utils.generators.{ChainGenerator, ErgoGenerators, ErgoTransactionGenerators} +import org.ergoplatform.utils.generators.{ErgoTransactionGenerators, ErgoGenerators, ChainGenerator} import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} -import org.ergoplatform.wallet.boxes.{ChainStatus, TrackedBox} +import org.ergoplatform.wallet.boxes.{TrackedBox, ChainStatus} import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.secrets.{DerivationPath, ExtendedSecretKey} 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.network.peer.PeerManager.ReceivableMessages.{GetBlacklistedPeers, GetAllPeers} +import scorex.core.network.{PeerSpec, Handshake} import scorex.core.settings.ScorexSettings import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.ByteArrayWrapper import scorex.util.Random -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} import scala.collection.mutable import scala.concurrent.duration._ -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with TestFileUtils { diff --git a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala index ac6df7067d..2dae438a70 100644 --- a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala @@ -6,16 +6,16 @@ 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.state.{ErgoState, UtxoState} +import org.ergoplatform.nodeView.state.{UtxoState, ErgoState} import org.ergoplatform.nodeView.wallet.ErgoWallet import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.nodeView.wallet.persistence.WalletDigest +import org.ergoplatform.sdk.wallet.TokensMap import org.ergoplatform.settings.Constants import org.ergoplatform.utils.fixtures.WalletFixture -import org.ergoplatform.wallet.TokensMap import scorex.crypto.authds.ADKey -import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.util.{ModifierId, bytesToId} +import scorex.crypto.hash.Blake2b256 +import scorex.util.{bytesToId, ModifierId} import sigmastate.Values.ErgoTree import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ @@ -24,7 +24,7 @@ import sigmastate.interpreter.ProverResult trait WalletTestOps extends NodeViewBaseOps { - def newAssetIdStub: TokenId = Blake2b256.hash("new_asset") + def newAssetIdStub: TokenId = Digest32Coll @@ Blake2b256.hash("new_asset").toColl def withFixture[T](test: WalletFixture => T): T = new WalletFixture(settings, parameters, getCurrentView(_).vault).apply(test) @@ -51,10 +51,10 @@ trait WalletTestOps extends NodeViewBaseOps { tx.outputs.filter(_.propositionBytes.containsSlice(org.ergoplatform.mining.groupElemToBytes(pk.value))) def assetAmount(boxes: Seq[ErgoBoxCandidate]): Seq[(ModifierId, Long)] = - assetsByTokenId(boxes).map { case (tokenId, sum) => (bytesToId(tokenId), sum) }.toArray[(ModifierId, Long)] + assetsByTokenId(boxes).map { case (tokenId, sum) => (bytesToId(tokenId.toArray), sum) }.toArray[(ModifierId, Long)] def toAssetMap(assetSeq: Seq[(TokenId, Long)]): TokensMap = - assetSeq.map { case (tokenId, sum) => (bytesToId(tokenId), sum) }.toMap + assetSeq.map { case (tokenId, sum) => (bytesToId(tokenId.toArray), sum) }.toMap def assetsByTokenId(boxes: Seq[ErgoBoxCandidate]): Map[TokenId, Long] = { boxes @@ -78,7 +78,7 @@ trait WalletTestOps extends NodeViewBaseOps { def makeGenesisTxWithAsset(publicKey: ProveDlog, issueAsset: Boolean): ErgoTransaction = { val inputs = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) val assets: Seq[(TokenId, Long)] = if (issueAsset) { - Seq((Digest32 @@@ inputs.head.boxId) -> 1L) + Seq((Digest32Coll @@@ inputs.head.boxId.toColl) -> 1L) } else { Seq.empty } @@ -125,10 +125,10 @@ trait WalletTestOps extends NodeViewBaseOps { } private def replaceNewAssetStub(assets: Seq[(TokenId, Long)], inputs: Seq[Input]): Seq[(TokenId, Long)] = { - def isNewAsset(tokenId: TokenId, value: Long): Boolean = java.util.Arrays.equals(tokenId, newAssetIdStub) + def isNewAsset(tokenId: TokenId, value: Long): Boolean = tokenId == newAssetIdStub val (newAsset, spentAssets) = assets.partition((isNewAsset _).tupled) - newAsset.map(Digest32 @@@ inputs.head.boxId -> _._2) ++ spentAssets + newAsset.map(Digest32Coll @@@ inputs.head.boxId.toColl -> _._2) ++ spentAssets } def randomNewAsset: Seq[(TokenId, Long)] = Seq(newAssetIdStub -> randomLong()) diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala index f24d6e3718..dd857204f0 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala @@ -3,30 +3,30 @@ package org.ergoplatform.utils.generators import com.google.common.primitives.Shorts import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{AutolykosSolution, genPk, q} +import org.ergoplatform.mining.{genPk, q, AutolykosSolution} import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.history.popow.{NipopowProof, PoPowParams} +import org.ergoplatform.modifiers.history.popow.{PoPowParams, NipopowProof} import org.ergoplatform.network.ModePeerFeature -import org.ergoplatform.nodeView.history.{ErgoSyncInfo, ErgoSyncInfoV1, ErgoSyncInfoV2} +import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfo, ErgoSyncInfoV2} import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.settings.{Constants, ErgoValidationSettings, ErgoValidationSettingsUpdate, ValidationRules} +import org.ergoplatform.settings.{ErgoValidationSettingsUpdate, ValidationRules, Constants, ErgoValidationSettings} import org.ergoplatform.utils.ErgoTestConstants -import org.ergoplatform.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule} +import org.ergoplatform.validation.{ReplacedRule, ChangedRule, DisabledRule, EnabledRule} import org.ergoplatform.wallet.utils.Generators import org.scalacheck.Arbitrary.arbByte -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.{Gen, Arbitrary} import org.scalatest.matchers.should.Matchers -import scorex.crypto.authds.{ADDigest, SerializedAdProof} +import scorex.crypto.authds.{SerializedAdProof, ADDigest} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.CoreGenerators import sigmastate.Values.ErgoTree -import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.basics.{DiffieHellmanTupleProverInput, ProveDHTuple} -import sigmastate.interpreter.CryptoConstants.EcPointType -import sigmastate.interpreter.{CryptoConstants, ProverResult} +import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.basics.{DiffieHellmanTupleProverInput, ProveDHTuple, CryptoConstants} +import sigmastate.interpreter.ProverResult import scala.util.Random diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 6edb2f67d0..4250fcb02d 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -3,24 +3,24 @@ package org.ergoplatform.utils.generators import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{BoxHolder, ErgoStateContext, VotingData} import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionSigningRequest} import org.ergoplatform.nodeView.wallet.{AugWalletTransaction, WalletTransaction} import org.ergoplatform.settings.Parameters._ -import org.ergoplatform.settings.{Constants, Parameters} -import org.ergoplatform.utils.{BoxUtils, RandomLike, RandomWrapper} +import org.ergoplatform.settings.{Parameters, Constants} +import org.ergoplatform.utils.{RandomLike, RandomWrapper, BoxUtils} import org.ergoplatform.wallet.Constants.{MaxAssetsPerBox, ScanId} -import org.ergoplatform.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.UnsignedInput import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} import org.ergoplatform.wallet.interpreter.TransactionHintsBag import org.ergoplatform.wallet.utils.Generators -import org.ergoplatform.{DataInput, ErgoAddress, ErgoAddressEncoder, ErgoBox, ErgoBoxCandidate, Input, P2PKAddress} +import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoAddress, DataInput, ErgoBoxCandidate, P2PKAddress, Input} import org.scalacheck.Gen -import scorex.crypto.hash.{Blake2b256, Digest32} +import scorex.crypto.hash.Blake2b256 import scorex.db.ByteArrayWrapper import scorex.util.encode.Base16 import sigmastate.Values.ErgoTree @@ -129,7 +129,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { val inputSum = boxesToSpend.map(_.value).reduce(Math.addExact(_, _)) val assetsMap: mutable.Map[ByteArrayWrapper, Long] = mutable.Map(boxesToSpend.flatMap(_.additionalTokens.toArray).map { case (bs, amt) => - ByteArrayWrapper(bs) -> amt + ByteArrayWrapper(bs.toArray) -> amt }: _*) //randomly creating a new asset @@ -195,7 +195,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { } val newBoxes = outputAmounts.zip(tokenAmounts.toIndexedSeq).map { case (amt, tokens) => - val normalizedTokens = tokens.toSeq.map(t => (Digest32 @@ t._1.data) -> t._2) + val normalizedTokens = tokens.toSeq.map(t => (Digest32Coll @@ t._1.data.toColl) -> t._2) testBox(amt, outputsProposition, 0, normalizedTokens) } val inputs = boxesToSpend.map(b => Input(b.id, emptyProverResult)) @@ -223,7 +223,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { def disperseTokens(inputsCount: Int, tokensCount: Byte): Gen[IndexedSeq[Seq[(TokenId, Long)]]] = { val tokensDistribution = mutable.IndexedSeq.fill(inputsCount)(Seq[(TokenId, Long)]()) (1 to tokensCount).foreach { i => - val (id, amt) = Blake2b256(s"$i" + Random.nextString(5)) -> (Random.nextInt(Int.MaxValue).toLong + 100) + val (id, amt) = Digest32Coll @@ Blake2b256(s"$i" + Random.nextString(5)).toColl -> (Random.nextInt(Int.MaxValue).toLong + 100) val idx = i % tokensDistribution.size val s = tokensDistribution(idx) tokensDistribution(idx) = s :+ (id -> amt) diff --git a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala index 31f60d02cb..f4874dfa36 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ValidBlocksGenerators.scala @@ -70,7 +70,7 @@ trait ValidBlocksGenerators val currentSize = acc.map(_.size).sum val averageSize = if (currentSize > 0) currentSize / acc.length else 1000 val customTokens = (stateBoxes ++ selfBoxes).flatMap(_.additionalTokens.toArray) - val customTokensNum = customTokens.map(ct => ByteArrayWrapper(ct._1)).toSet.size + val customTokensNum = customTokens.map(ct => ByteArrayWrapper(ct._1.toArray)).toSet.size val issueNew = customTokensNum == 0 stateBoxes.find(isEmissionBox) match { From ce431f94e4a99fd1c416472aa970caa0223563ef Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 24 May 2023 20:54:29 +0200 Subject: [PATCH 189/204] with-sigma-v5.0.8: incapsulate conversions into special methods --- build.sbt | 2 +- .../contracts/ReemissionContracts.scala | 11 ++-- .../wallet/boxes/BoxSelector.scala | 2 +- .../wallet/boxes/DefaultBoxSelector.scala | 14 ++--- .../wallet/boxes/ErgoBoxAssetExtractor.scala | 2 +- .../ReplaceCompactCollectBoxSelector.scala | 7 +-- .../wallet/boxes/TrackedBox.scala | 12 ++-- .../org/ergoplatform/wallet/crypto/AES.scala | 2 +- .../wallet/interpreter/ErgoInterpreter.scala | 4 +- .../interpreter/ErgoProvingInterpreter.scala | 23 ++++---- .../org/ergoplatform/wallet/package.scala | 5 -- .../wallet/secrets/EncryptedSecret.scala | 4 +- .../wallet/secrets/JsonSecretStorage.scala | 10 ++-- .../serialization/ErgoWalletSerializer.scala | 2 + .../transactions/TransactionBuilder.scala | 25 ++++----- .../wallet/AddressGenerationDemo.java | 2 +- .../ergoplatform/wallet/AssetUtilsSpec.scala | 2 +- .../wallet/boxes/DefaultBoxSelectorSpec.scala | 56 ++++++++++--------- .../ErgoProvingInterpreterSpec.scala | 11 ++-- .../interpreter/InterpreterSpecCommon.scala | 11 ++-- .../wallet/secrets/DerivationPathSpec.scala | 4 +- .../secrets/ExtendedSecretKeySpec.scala | 7 +-- .../secrets/JsonSecretStorageSpec.scala | 4 +- .../serialization/SerializationSpec.scala | 2 +- .../transactions/TransactionBuilderSpec.scala | 35 ++++++------ .../wallet/utils/Generators.scala | 23 ++++---- .../org/ergoplatform/http/api/ApiCodecs.scala | 37 ++++++------ .../http/api/EmissionApiRoute.scala | 4 +- .../http/api/MiningApiRoute.scala | 6 +- .../http/api/TransactionsApiRoute.scala | 15 ++--- .../mining/AutolykosSolution.scala | 6 +- .../mining/CandidateGenerator.scala | 23 ++++---- .../mining/DefaultFakePowScheme.scala | 3 +- .../modifiers/history/header/Header.scala | 10 ++-- .../modifiers/mempool/ErgoTransaction.scala | 14 ++--- .../ergoplatform/nodeView/ErgoContext.scala | 7 +-- .../nodeView/history/extra/ExtraIndexer.scala | 3 +- .../nodeView/history/extra/IndexedToken.scala | 4 +- .../nodeView/state/ErgoStateContext.scala | 13 ++--- .../nodeView/state/UtxoStateReader.scala | 7 +-- .../nodeView/wallet/ErgoWalletReader.scala | 16 +++--- .../nodeView/wallet/ErgoWalletService.scala | 27 +++++---- .../nodeView/wallet/ErgoWalletSupport.scala | 39 ++++++------- .../nodeView/wallet/WalletCache.scala | 4 +- .../nodeView/wallet/WalletVars.scala | 4 +- .../wallet/persistence/WalletDigest.scala | 6 +- .../wallet/persistence/WalletRegistry.scala | 21 ++++--- .../wallet/persistence/WalletStorage.scala | 21 ++++--- .../requests/GenerateCommitmentsRequest.scala | 2 +- .../wallet/requests/RequestsHolder.scala | 4 +- .../requests/TransactionSigningRequest.scala | 2 +- .../wallet/scanning/ScanningPredicate.scala | 4 +- .../ScanningPredicateJsonCodecs.scala | 9 ++- .../reemission/ReemissionRules.scala | 3 +- .../settings/ReemissionSettings.scala | 8 ++- .../http/routes/MiningApiRouteSpec.scala | 2 +- .../http/routes/TransactionApiRouteSpec.scala | 3 +- .../mining/CandidateGeneratorSpec.scala | 14 ++--- .../ergoplatform/mining/ErgoMinerSpec.scala | 20 +++---- .../mempool/ErgoTransactionSpec.scala | 12 ++-- .../HeaderSerializationSpecification.scala | 5 +- .../extra/ExtraIndexerSpecification.scala | 16 +++--- .../nodeView/mempool/ScriptsSpec.scala | 12 ++-- .../wallet/ErgoWalletServiceSpec.scala | 16 +++--- .../nodeView/wallet/ErgoWalletSpec.scala | 16 +++--- .../persistence/WalletRegistryBenchmark.scala | 4 +- ...ningPredicateJsonCodecsSpecification.scala | 13 ++--- .../ScanningPredicateSpecification.scala | 21 ++++--- .../reemission/ReemissionRulesSpec.scala | 13 ++--- .../serialization/JsonSerializationSpec.scala | 10 ++-- .../org/ergoplatform/tools/FeeSimulator.scala | 14 +++-- .../utils/ErgoTestConstants.scala | 4 +- .../scala/org/ergoplatform/utils/Stubs.scala | 26 ++++----- .../ergoplatform/utils/WalletTestOps.scala | 15 ++--- .../utils/generators/ErgoGenerators.scala | 18 +++--- .../ErgoTransactionGenerators.scala | 17 +++--- 76 files changed, 432 insertions(+), 443 deletions(-) delete mode 100644 ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala diff --git a/build.sbt b/build.sbt index 6d4627a551..8a97353999 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.7-182-28cb73a6-SNAPSHOT" +val sigmaStateVersion = "5.0.7-181-80a19714-20230524-1747-SNAPSHOT" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala index 092fbc70ab..55a70b4016 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/contracts/ReemissionContracts.scala @@ -2,12 +2,13 @@ package org.ergoplatform.contracts import org.ergoplatform.ErgoBox.{R2, STokensRegType} import org.ergoplatform.ErgoTreePredef.{boxCreationHeight, expectedMinerOutScriptBytesVal} -import org.ergoplatform.{Height, MinerPubkey, Outputs, Self} +import org.ergoplatform.mining.emission.EmissionRules.CoinsInOneErgo import org.ergoplatform.settings.MonetarySettings -import sigmastate.{AND, EQ, GE, GT, LE, Minus, OR, SBox, SCollection, STuple} +import org.ergoplatform.{Height, MinerPubkey, Outputs, Self} import sigmastate.Values.{ByteArrayConstant, ErgoTree, IntConstant, LongConstant, SigmaPropValue, Value} -import sigmastate.utxo.{ByIndex, ExtractAmount, ExtractRegisterAs, ExtractScriptBytes, OptionGet, SelectField, SizeOf} -import org.ergoplatform.mining.emission.EmissionRules.CoinsInOneErgo +import sigmastate.utxo._ +import sigmastate._ +import special.collection.Coll /** * Container for re-emission related contracts. Contains re-emission contract and pay-to-reemission contract. @@ -22,7 +23,7 @@ trait ReemissionContracts { /** * @return - ID of NFT token associated with re-emission contract */ - def reemissionNftIdBytes: Array[Byte] + def reemissionNftIdBytes: Coll[Byte] /** * @return - height when reemission starts diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala index 68e52a5b2e..f945b3e4bf 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/BoxSelector.scala @@ -1,9 +1,9 @@ package org.ergoplatform.wallet.boxes -import org.ergoplatform.{ErgoBoxAssetsHolder, ErgoBoxAssets} import org.ergoplatform.SigmaConstants.MaxBoxSize import org.ergoplatform.sdk.wallet.TokensMap import org.ergoplatform.wallet.boxes.BoxSelector.{BoxSelectionError, BoxSelectionResult} +import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder} import scorex.util.ScorexLogging diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala index c9f4152205..05a8e9c6d6 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala @@ -1,15 +1,14 @@ package org.ergoplatform.wallet.boxes -import org.ergoplatform.contracts.ReemissionContracts -import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} -import scorex.util.ModifierId -import org.ergoplatform.{ErgoBoxAssetsHolder, ErgoBoxCandidate, ErgoBoxAssets} +import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} import org.ergoplatform.wallet.Constants.MaxAssetsPerBox +import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError +import org.ergoplatform.wallet.transactions.TransactionBuilder._ +import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder} +import scorex.util.ModifierId import scala.annotation.tailrec import scala.collection.mutable -import org.ergoplatform.wallet.transactions.TransactionBuilder._ -import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError /** * Default implementation of the box selector. It simply picks boxes till sum of their monetary values @@ -20,9 +19,8 @@ import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError */ class DefaultBoxSelector(override val reemissionDataOpt: Option[ReemissionData]) extends BoxSelector { - import DefaultBoxSelector._ import BoxSelector._ - import scorex.util.idToBytes + import DefaultBoxSelector._ // helper function which returns count of assets in `initialMap` not fully spent in `subtractor` private def diffCount(initialMap: mutable.Map[ModifierId, Long], subtractor: TokensMap): Int = { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala index 1e2867407b..8c2fdf5384 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ErgoBoxAssetExtractor.scala @@ -1,8 +1,8 @@ package org.ergoplatform.wallet.boxes +import java7.compat.Math import org.ergoplatform.ErgoBoxCandidate import special.collection.Extensions._ -import java7.compat.Math import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala index 6f24e655c0..0e32ebcecf 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/ReplaceCompactCollectBoxSelector.scala @@ -1,11 +1,10 @@ package org.ergoplatform.wallet.boxes -import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult -import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError import org.ergoplatform.ErgoBoxAssets -import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} -import scorex.util.ModifierId +import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} +import org.ergoplatform.wallet.boxes.BoxSelector.{BoxSelectionError, BoxSelectionResult} import org.ergoplatform.wallet.transactions.TransactionBuilder._ +import scorex.util.ModifierId import scala.annotation.tailrec import scala.collection.mutable diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala index f38fb1e773..3b9e850a22 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala @@ -1,13 +1,13 @@ package org.ergoplatform.wallet.boxes +import org.ergoplatform.sdk.wallet.TokensMap +import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.{Constants} import org.ergoplatform.wallet.serialization.ErgoWalletSerializer -import org.ergoplatform.{ErgoBox, ErgoLikeTransaction} +import org.ergoplatform.{ErgoBox, ErgoBoxAssets, ErgoLikeTransaction} import scorex.util.serialization.{Reader, Writer} -import scorex.util.{idToBytes, bytesToId, ModifierId} -import org.ergoplatform.ErgoBoxAssets -import org.ergoplatform.sdk.wallet.TokensMap +import scorex.util.{ModifierId, bytesToId, idToBytes} +import special.collection.Extensions._ /** * A box tracked by a wallet that contains Ergo box itself as well as @@ -68,7 +68,7 @@ case class TrackedBox(creationTxId: ModifierId, def isSpent: Boolean = spendingHeightOpt.isDefined lazy val tokens: TokensMap = box.additionalTokens.toArray.map { - case (id, amt) => bytesToId(id.toArray) -> amt + case (id, amt) => id.toModifierId -> amt }.toMap override def equals(obj: Any): Boolean = obj match { diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala index 61a7dd8377..45a448c105 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/crypto/AES.scala @@ -2,7 +2,7 @@ package org.ergoplatform.wallet.crypto import org.ergoplatform.sdk.wallet.settings.EncryptionSettings -import javax.crypto.spec.{PBEKeySpec, SecretKeySpec, GCMParameterSpec} +import javax.crypto.spec.{GCMParameterSpec, PBEKeySpec, SecretKeySpec} import javax.crypto.{Cipher, SecretKeyFactory} import scala.util.Try diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala index 9391a921c0..fbed24ecfb 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoInterpreter.scala @@ -3,11 +3,11 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters import org.ergoplatform.wallet.protocol.Constants -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeInterpreter, ErgoLikeContext} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeInterpreter} import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree -import sigmastate.interpreter.Interpreter.{VerificationResult, ScriptEnv} +import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} import sigmastate.{AvlTreeData, AvlTreeFlags} import scala.util.Try diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala index 848ff30068..821ff5168a 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala @@ -1,23 +1,22 @@ package org.ergoplatform.wallet.interpreter -import java.util import org.ergoplatform._ import org.ergoplatform.sdk.utils.ArithUtils.{addExact, multiplyExact} -import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeStateContext, ErgoLikeParameters} -import org.ergoplatform.sdk.wallet.secrets.{SecretKey, ExtendedSecretKey, ExtendedPublicKey} -import org.ergoplatform.validation.SigmaValidationSettings -import sigmastate.AvlTreeData -import sigmastate.Values.SigmaBoolean -import sigmastate.interpreter.{ProverInterpreter, ContextExtension} -import org.ergoplatform.validation.ValidationRules +import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} +import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey, SecretKey} +import org.ergoplatform.validation.{SigmaValidationSettings, ValidationRules} import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor import scorex.crypto.authds.ADDigest -import sigmastate.basics.SigmaProtocolPrivateInput import scorex.util.encode.Base16 +import sigmastate.AvlTreeData +import sigmastate.Values.SigmaBoolean +import sigmastate.basics.SigmaProtocolPrivateInput +import sigmastate.interpreter.{ContextExtension, ProverInterpreter} import special.collection.Coll import special.sigma.{Header, PreHeader} -import scala.util.{Try, Success, Failure} +import java.util +import scala.util.{Failure, Success, Try} /** * A class which is holding secrets and signing transactions. @@ -129,7 +128,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], inputsCostTry.flatMap { case (ins, totalCost) => val context = new ErgoLikeContext( - ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), + ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, dataBoxes, @@ -241,7 +240,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], val exp = box.ergoTree val proof = input.spendingProof.proof - val lastBlockUtxoRoot: AvlTreeData = ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray) + val lastBlockUtxoRoot: AvlTreeData = ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest) val headers: Coll[Header] = stateContext.sigmaLastHeaders val preHeader: PreHeader = stateContext.sigmaPreHeader val spendingTransaction = tx diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala deleted file mode 100644 index 9b36097517..0000000000 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/package.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.ergoplatform - - -package object wallet { -} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala index 3cfc2d2900..4a0e23ff7e 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/EncryptedSecret.scala @@ -1,8 +1,8 @@ package org.ergoplatform.wallet.secrets import io.circe.syntax._ -import cats.syntax.either._ -import io.circe.{HCursor, Encoder, Json, Decoder} +import cats.syntax.either._ // don't remove, it is needed for scala 2.11 +import io.circe.{Decoder, Encoder, HCursor, Json} import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import scorex.util.encode.Base16 diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala index 474f7a8597..e75c9b951c 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/secrets/JsonSecretStorage.scala @@ -1,19 +1,19 @@ package org.ergoplatform.wallet.secrets -import java.io.{FileNotFoundException, File, PrintWriter} -import java.util -import java.util.UUID import io.circe.parser._ import io.circe.syntax._ import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import org.ergoplatform.wallet.crypto -import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.settings.SecretStorageSettings import scorex.util.encode.Base16 -import scala.util.{Try, Success, Failure} +import java.io.{File, FileNotFoundException, PrintWriter} +import java.util +import java.util.UUID +import scala.util.{Failure, Success, Try} /** * Secret storage backend. diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala index 326b8a7377..bc9bf9071b 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/serialization/ErgoWalletSerializer.scala @@ -26,6 +26,8 @@ trait ErgoWalletSerializer[T] extends Serializer[T, T, Reader, Writer] { } object ErgoWalletSerializer { + + /** Creates a new serializer which delegates to the given [[SigmaSerializer]]. */ def fromSigmaSerializer[T](ss: SigmaSerializer[T, T]): ErgoWalletSerializer[T] = new ErgoWalletSerializer[T] { override def serialize(obj: T, w: Writer): Unit = { val sw = new SigmaByteWriter(w, None) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala index bc7fc1aae7..6ad66b5423 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/transactions/TransactionBuilder.scala @@ -1,21 +1,20 @@ package org.ergoplatform.wallet.transactions -import org.ergoplatform.{UnsignedErgoLikeTransaction, UnsignedInput, ErgoBox, ErgoAddress, ErgoTreePredef, DataInput, ErgoBoxCandidate, ErgoScriptPredef} -import sigmastate.eval.Extensions._ - -import scala.util.Try -import scorex.util.{idToBytes, bytesToId, ModifierId} -import special.collection.Coll -import sigmastate.eval._ import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} -import scorex.crypto.hash.Digest32 -import org.ergoplatform.wallet.boxes.BoxSelector -import org.ergoplatform.wallet.boxes.DefaultBoxSelector +import org.ergoplatform._ +import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} +import org.ergoplatform.wallet.boxes.{BoxSelector, DefaultBoxSelector} import scorex.crypto.authds.ADKey import scorex.util.encode.Base16 +import scorex.util.{ModifierId, bytesToId} +import sigmastate.eval.Extensions._ +import sigmastate.eval._ +import sigmastate.utils.Extensions._ +import special.collection.Coll +import special.collection.Extensions._ import scala.collection.JavaConverters._ +import scala.util.Try object TransactionBuilder { @@ -159,10 +158,10 @@ object TransactionBuilder { } def collTokensToMap(tokens: Coll[(TokenId, Long)]): TokensMap = - tokens.toArray.map(t => bytesToId(t._1.toArray) -> t._2).toMap + tokens.toArray.map(t => t._1.toModifierId -> t._2).toMap def tokensMapToColl(tokens: TokensMap): Coll[(TokenId, Long)] = - tokens.toSeq.map {t => (Digest32Coll @@ idToBytes(t._1).toColl) -> t._2}.toArray.toColl + tokens.toArray.map {t => t._1.toTokenId -> t._2}.toColl private def validateStatelessChecks(inputs: IndexedSeq[ErgoBox], dataInputs: IndexedSeq[DataInput], outputCandidates: Seq[ErgoBoxCandidate]): Unit = { diff --git a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java index 6f842f5ed1..6e9c6d69ed 100644 --- a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java +++ b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java @@ -5,8 +5,8 @@ import org.ergoplatform.sdk.wallet.secrets.DerivationPath; import org.ergoplatform.sdk.wallet.secrets.ExtendedPublicKey; import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey; -import org.ergoplatform.wallet.mnemonic.Mnemonic; import org.ergoplatform.wallet.interface4j.SecretString; +import org.ergoplatform.wallet.mnemonic.Mnemonic; import scala.Option; /** diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala index 33fb0d0e35..4a8cec8b1b 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/AssetUtilsSpec.scala @@ -1,6 +1,6 @@ package org.ergoplatform.wallet -import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} +import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} import org.ergoplatform.wallet.utils.WalletTestHelpers import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala index fec8a7781d..1b4df37938 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala @@ -1,21 +1,21 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.ErgoBox.TokenId +import org.ergoplatform.ErgoLikeTransaction import org.ergoplatform.SigmaConstants.MaxBoxSize import org.ergoplatform.wallet.Constants.{MaxAssetsPerBox, PaymentsScanId} -import org.ergoplatform.ErgoLikeTransaction -import scorex.crypto.hash.{Blake2b256, Digest32} -import sigmastate.Values -import sigmastate.Values.SigmaPropValue -import sigmastate.helpers.TestingHelpers._ -import scorex.util.{idToBytes, bytesToId, ModifierId} +import org.ergoplatform.wallet.boxes.DefaultBoxSelector.{NotEnoughErgsError, NotEnoughTokensError} import org.scalatest.EitherValues -import org.ergoplatform.wallet.boxes.DefaultBoxSelector.NotEnoughErgsError -import org.ergoplatform.wallet.boxes.DefaultBoxSelector.NotEnoughTokensError import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import scorex.crypto.hash.Blake2b256 +import scorex.util.bytesToId +import sigmastate.Values +import sigmastate.Values.SigmaPropValue +import sigmastate.eval.Extensions._ +import sigmastate.helpers.TestingHelpers._ +import sigmastate.utils.Extensions._ +import special.collection.Extensions._ import scala.util.Random @@ -30,7 +30,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues private val StartHeight: Int = 0 private def genTokens(count: Int) = { - (0 until count).map { i => Digest32Coll @@ idToBytes(bytesToId(Blake2b256(i.toString))).toColl -> i.toLong } + (0 until count).map { i => Blake2b256(i.toString).toTokenId -> i.toLong } } private val selector = new DefaultBoxSelector(None) @@ -100,9 +100,9 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val assetId2 = bytesToId(Blake2b256("world")) val parentTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq()) - val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 1)) - val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId2).toColl -> 10)) - val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 100)) + val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, Seq(assetId1.toTokenId -> 1)) + val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, Seq(assetId2.toTokenId -> 10)) + val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, Seq(assetId1.toTokenId -> 100)) val uBox1 = TrackedBox(parentTx, 0, Some(100), box1, Set(PaymentsScanId)) val uBox2 = TrackedBox(parentTx, 1, None, box2, Set(PaymentsScanId)) @@ -148,17 +148,20 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val assetId7 = bytesToId(Blake2b256("7")) val assetId8 = bytesToId(Blake2b256("8")) - val box1 = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32Coll @@ idToBytes(assetId1).toColl -> 1, Digest32Coll @@ idToBytes(assetId2).toColl -> 1, - Digest32Coll @@ idToBytes(assetId3).toColl -> 1, Digest32Coll @@ idToBytes(assetId4).toColl -> 1)) + val box1 = testBox( + 1 * MinBoxValue, TrueLeaf, StartHeight, + Seq(assetId1.toTokenId -> 1, assetId2.toTokenId -> 1, + assetId3.toTokenId -> 1, assetId4.toTokenId -> 1)) - val box2 = testBox(10 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32Coll @@ idToBytes(assetId5).toColl -> 10, Digest32Coll @@ idToBytes(assetId6).toColl -> 10, - Digest32Coll @@ idToBytes(assetId7).toColl -> 10, Digest32Coll @@ idToBytes(assetId8).toColl -> 10)) + val box2 = testBox( + 10 * MinBoxValue, TrueLeaf, StartHeight, + Seq(assetId5.toTokenId -> 10, assetId6.toTokenId -> 10, + assetId7.toTokenId -> 10, assetId8.toTokenId -> 10)) - val box3 = testBox(100 * MinBoxValue, TrueLeaf, StartHeight, - Seq(Digest32Coll @@ idToBytes(assetId3).toColl -> 100, Digest32Coll @@ idToBytes(assetId4).toColl -> 100, - Digest32Coll @@ idToBytes(assetId5).toColl -> 100, Digest32Coll @@ idToBytes(assetId6).toColl -> 100)) + val box3 = testBox( + 100 * MinBoxValue, TrueLeaf, StartHeight, + Seq(assetId3.toTokenId -> 100, assetId4.toTokenId -> 100, + assetId5.toTokenId -> 100, assetId6.toTokenId -> 100)) val uBox1 = TrackedBox(parentTx, 0, Some(100), box1, Set(PaymentsScanId)) val uBox2 = TrackedBox(parentTx, 1, None, box2, Set(PaymentsScanId)) @@ -195,7 +198,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues property("Size of a box with MaxAssetsPerBox tokens should not cross MaxBoxSize") { val tokens = (0 until MaxAssetsPerBox).map { _ => - (Digest32Coll @@ scorex.util.Random.randomBytes(TokenId.size).toColl, Random.nextInt(100000000).toLong) + (scorex.util.Random.randomBytes(TokenId.size).toTokenId, Random.nextInt(100000000).toLong) } val box = testBox(1 * MinBoxValue, TrueLeaf, StartHeight, tokens) assert(box.bytes.length <= MaxBoxSize.value) @@ -224,7 +227,7 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val tokenData = genTokens(3).last tokenData._2 shouldBe 2 - val tokenId = ModifierId @@@ bytesToId(tokenData._1.toArray) + val tokenId = tokenData._1.toModifierId val ergValue = 10 * MinBoxValue @@ -256,7 +259,8 @@ class DefaultBoxSelectorSpec extends AnyPropSpec with Matchers with EitherValues val ts = genTokens(2) val reemissionNftId = ts(0)._1 val reemissionTokenId = ts(1)._1 - val selector = new DefaultBoxSelector(Some(ReemissionData(bytesToId(reemissionNftId.toArray), bytesToId(reemissionTokenId.toArray)))) + val selector = new DefaultBoxSelector( + Some(ReemissionData(reemissionNftId.toModifierId, reemissionTokenId.toModifierId))) val fullValue = 2000000000L val reemissionAmt = fullValue / 2 diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala index b311824aa8..0d96c4c8cc 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreterSpec.scala @@ -1,18 +1,17 @@ package org.ergoplatform.wallet.interpreter -import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DlogSecretKey} -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput} +import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, ExtendedSecretKey} import org.ergoplatform.wallet.crypto.ErgoSignature import org.ergoplatform.wallet.utils.Generators +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import scorex.util.ModifierId +import scorex.util.{ModifierId, Random} import scorex.util.encode.Base16 -import scorex.util.Random import sigmastate.CTHRESHOLD -import sigmastate.Values.{SigmaBoolean, GroupElementConstant} -import sigmastate.interpreter.{HintsBag, ContextExtension} +import sigmastate.Values.{GroupElementConstant, SigmaBoolean} +import sigmastate.interpreter.{ContextExtension, HintsBag} import sigmastate.serialization.ErgoTreeSerializer diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala index 1497ff84e2..d4af16e52d 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala @@ -1,11 +1,10 @@ package org.ergoplatform.wallet.interpreter -import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeStateContext, ErgoLikeParameters} +import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext} import scorex.crypto.authds.ADDigest import scorex.util.encode.Base16 import sigmastate.basics.CryptoConstants -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.eval.{Colls, CGroupElement, CPreHeader} +import sigmastate.eval.{CGroupElement, CPreHeader, Colls} import special.collection.Coll import special.sigma.{Header, PreHeader} @@ -40,9 +39,9 @@ trait InterpreterSpecCommon { override def sigmaLastHeaders: Coll[Header] = Colls.emptyColl - override def previousStateDigest = Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") - .map(_.toColl) - .getOrElse(throw new Error(s"Failed to parse genesisStateDigest")) + override def previousStateDigest: ADDigest = + ADDigest @@ Base16.decode("a5df145d41ab15a01e0cd3ffbab046f0d029e5412293072ad0f5827428589b9302") + .getOrElse(throw new Error(s"Failed to parse genesisStateDigest")) override def sigmaPreHeader: PreHeader = CPreHeader( version = 0, diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala index 4a392c59e1..72d708fe33 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala @@ -2,10 +2,10 @@ package org.ergoplatform.wallet.secrets import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.wallet.Constants -import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} -import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.utils.Generators +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala index 3467c2b13a..d59ff6c93a 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/ExtendedSecretKeySpec.scala @@ -1,15 +1,14 @@ package org.ergoplatform.wallet.secrets -import org.ergoplatform.wallet.mnemonic.Mnemonic +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey, Index} import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.wallet.mnemonic.Mnemonic import org.scalatest.Assertion import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.util.encode.Base58 -import org.ergoplatform.P2PKAddress -import org.ergoplatform.ErgoAddressEncoder -import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, Index, DerivationPath} class ExtendedSecretKeySpec extends AnyPropSpec diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala index 6a5df08721..39317270a6 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/JsonSecretStorageSpec.scala @@ -1,13 +1,13 @@ package org.ergoplatform.wallet.secrets import org.ergoplatform.sdk.wallet.settings.EncryptionSettings +import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.settings.SecretStorageSettings import org.ergoplatform.wallet.utils.{FileUtils, Generators} +import org.scalacheck.Arbitrary import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import org.ergoplatform.wallet.interface4j.SecretString -import org.scalacheck.Arbitrary import java.io.{File, PrintWriter} import java.util.UUID diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala index 876bbe3561..e49573dff0 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/serialization/SerializationSpec.scala @@ -1,6 +1,6 @@ package org.ergoplatform.wallet.serialization -import org.ergoplatform.sdk.wallet.secrets.{DerivationPathSerializer, ExtendedSecretKeySerializer, ExtendedPublicKeySerializer} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPathSerializer, ExtendedPublicKeySerializer, ExtendedSecretKeySerializer} import org.ergoplatform.wallet.boxes.TrackedBoxSerializer import org.ergoplatform.wallet.utils.Generators import org.scalacheck.Gen diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala index 15f526c702..f3bb1b4cbc 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/transactions/TransactionBuilderSpec.scala @@ -1,24 +1,23 @@ package org.ergoplatform.wallet.transactions -import sigmastate.Values -import sigmastate.Values.SigmaPropValue -import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate.helpers.TestingHelpers._ -import sigmastate.utils.Helpers._ -import org.ergoplatform._ import org.ergoplatform.ErgoBox.TokenId +import org.ergoplatform._ import org.ergoplatform.sdk.wallet.TokensMap import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey - -import scala.util.{Try, Success} -import scorex.crypto.hash.Digest32 -import org.ergoplatform.wallet.mnemonic.Mnemonic -import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.boxes.BoxSelector +import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.utils.WalletTestHelpers import org.scalatest.matchers.should.Matchers -import scorex.util.idToBytes +import sigmastate.Values +import sigmastate.Values.SigmaPropValue +import sigmastate.eval.Extensions._ +import sigmastate.eval._ +import sigmastate.helpers.TestingHelpers._ +import sigmastate.utils.Extensions._ +import sigmastate.utils.Helpers._ + +import scala.util.{Success, Try} class TransactionBuilderSpec extends WalletTestHelpers with Matchers { import TransactionBuilder.buildUnsignedTx @@ -74,7 +73,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("token minting") { val inputBox = box(minBoxValue * 2) - val tokenId = Digest32Coll @@@ inputBox.id.toColl + val tokenId = inputBox.id.toTokenId val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox) @@ -86,8 +85,8 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { } property("token burning") { - val inputBox = box(minBoxValue * 3, Seq(Digest32Coll @@ idToBytes(tid1).toColl -> 1000L, Digest32Coll @@ idToBytes(tid2).toColl -> 2000L)) - val tokenId = Digest32Coll @@@ inputBox.id.toColl + val inputBox = box(minBoxValue * 3, Seq(tid1.toTokenId -> 1000L, tid2.toTokenId -> 2000L)) + val tokenId = inputBox.id.toTokenId val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, burnTokens = Map(tid1 -> 400L, tid2 -> 800L)) @@ -105,7 +104,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("no fees") { val inputBox = box(minBoxValue) - val tokenId = Digest32Coll @@@ inputBox.id.toColl + val tokenId = inputBox.id.toTokenId val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) @@ -117,7 +116,7 @@ class TransactionBuilderSpec extends WalletTestHelpers with Matchers { property("change goes to fee, but no outFee box") { val inputBox = box(minBoxValue + minBoxValue / 2) - val tokenId = Digest32Coll @@@ inputBox.id.toColl + val tokenId = inputBox.id.toTokenId val outBox = boxCandidate(minBoxValue, Seq(tokenId -> 100L)) val res = transaction(inputBox, outBox, fee = None) diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala index d9fb7698a1..3253af80ce 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala @@ -1,26 +1,23 @@ package org.ergoplatform.wallet.utils -import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId} +import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} +import org.ergoplatform.sdk.wallet.secrets._ +import org.ergoplatform.sdk.wallet.settings.EncryptionSettings import org.ergoplatform.wallet.Constants +import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.mnemonic.{Mnemonic, WordList} +import org.ergoplatform._ import org.scalacheck.Arbitrary.arbByte -import org.scalacheck.{Gen, Arbitrary} +import org.scalacheck.{Arbitrary, Gen} import scorex.crypto.authds.ADKey -import sigmastate.Values.{TrueLeaf, CollectionConstant, ByteArrayConstant, ErgoTree, FalseLeaf, EvaluatedValue} -import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.{SType, SByte} -import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import scorex.util._ -import org.ergoplatform.{UnsignedErgoLikeTransaction, UnsignedInput, ErgoBox, ErgoTreePredef, ErgoBoxCandidate, ErgoScriptPredef} +import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, EvaluatedValue, FalseLeaf, TrueLeaf} +import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ -import scorex.util.{bytesToId, ModifierId} import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ -import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey, ExtendedPublicKey, SecretKey, Index} -import org.ergoplatform.sdk.wallet.settings.EncryptionSettings -import scorex.crypto.hash.Digest32 +import sigmastate.{SByte, SType} trait Generators { @@ -72,7 +69,7 @@ trait Generators { val assetGen: Gen[(TokenId, Long)] = for { id <- boxIdGen amt <- Gen.oneOf(1, 500, 20000, 10000000, Long.MaxValue) - } yield Digest32Coll @@@ id.toColl -> amt + } yield id.toTokenId -> amt def additionalTokensGen(cnt: Int): Gen[Seq[(TokenId, Long)]] = Gen.listOfN(cnt, assetGen) diff --git a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala index 92f0cc7a44..816a026305 100644 --- a/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala +++ b/src/main/scala/org/ergoplatform/http/api/ApiCodecs.scala @@ -1,43 +1,42 @@ package org.ergoplatform.http.api -import java.math.BigInteger import io.circe._ +import io.circe.syntax._ import org.bouncycastle.util.BigIntegers -import org.ergoplatform.{UnsignedErgoLikeTransaction, ErgoAddressEncoder, ErgoLikeTransaction, ErgoLikeContext, ErgoBox, JsonCodecs} -import org.ergoplatform.http.api.ApiEncoderOption.Detalization 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.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory.Difficulty -import org.ergoplatform.settings.ErgoAlgos +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.settings.Algos +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 scorex.crypto.authds.merkle.MerkleProof +import scorex.crypto.authds.{LeafData, Side} +import scorex.crypto.hash.Digest import scorex.util.encode.Base16 -import sigmastate.{SigSerializer, COR, TrivialProp, CAND, NodePosition, CTHRESHOLD} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput, FirstDLogProverMessage} +import sigmastate._ +import sigmastate.basics.CryptoConstants.EcPointType +import sigmastate.basics.DLogProtocol.{DLogProverInput, FirstDLogProverMessage, ProveDlog} import sigmastate.basics.VerifierMessage.Challenge import sigmastate.basics._ import sigmastate.interpreter._ -import io.circe.syntax._ -import org.ergoplatform.http.api.requests.{ExecuteRequest, CryptoResult, HintExtractionRequest} -import org.ergoplatform.nodeView.history.extra.ExtraIndexer.getAddress -import org.ergoplatform.nodeView.history.extra.{BalanceInfo, IndexedErgoTransaction, IndexedErgoBox, IndexedToken} -import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} -import org.ergoplatform.wallet.interface4j.SecretString -import scorex.crypto.authds.{Side, LeafData} -import scorex.crypto.authds.merkle.MerkleProof -import scorex.crypto.hash.Digest -import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.serialization.OpCodes import special.sigma.AnyValue -import scala.util.{Try, Success, Failure} +import java.math.BigInteger +import scala.util.{Failure, Success, Try} trait ApiCodecs extends JsonCodecs { diff --git a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala index 657c3d8ef2..0a3f370868 100644 --- a/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/EmissionApiRoute.scala @@ -2,13 +2,13 @@ package org.ergoplatform.http.api import akka.actor.ActorRefFactory import akka.http.scaladsl.server.Route +import io.circe.syntax._ import io.circe.{Encoder, Json} -import org.ergoplatform.{ErgoAddressEncoder, ErgoTreePredef, Pay2SAddress} import org.ergoplatform.mining.emission.EmissionRules import org.ergoplatform.settings.{ErgoSettings, ReemissionSettings} +import org.ergoplatform.{ErgoAddressEncoder, ErgoTreePredef, Pay2SAddress} import scorex.core.api.http.ApiResponse import scorex.core.settings.RESTApiSettings -import io.circe.syntax._ case class EmissionApiRoute(ergoSettings: ErgoSettings) (implicit val context: ActorRefFactory) extends ErgoBaseApiRoute { diff --git a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala index 834abd9069..03e7d543a2 100644 --- a/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/MiningApiRoute.scala @@ -1,16 +1,16 @@ package org.ergoplatform.http.api -import akka.actor.{ActorRefFactory, ActorRef} +import akka.actor.{ActorRef, ActorRefFactory} import akka.http.scaladsl.server.Route import akka.pattern.ask import io.circe.syntax._ import io.circe.{Encoder, Json} import org.ergoplatform.mining.CandidateGenerator.Candidate -import org.ergoplatform.mining.{AutolykosSolution, ErgoMiner, CandidateGenerator} +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.{Pay2SAddress, ErgoAddress, ErgoTreePredef} +import org.ergoplatform.{ErgoAddress, ErgoTreePredef, Pay2SAddress} import scorex.core.api.http.ApiResponse import scorex.core.settings.RESTApiSettings import sigmastate.basics.DLogProtocol.ProveDlog diff --git a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala index 8ba9dd377a..e536fe8ae9 100644 --- a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala @@ -1,17 +1,17 @@ package org.ergoplatform.http.api -import akka.actor.{ActorRefFactory, ActorRef} +import akka.actor.{ActorRef, ActorRefFactory} import akka.http.scaladsl.model.StatusCodes -import akka.http.scaladsl.server.{Directive1, ValidationRejection, Directive, Route} +import akka.http.scaladsl.server.{Directive, Directive1, Route, ValidationRejection} import akka.pattern.ask import io.circe.Json import io.circe.syntax._ import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} -import org.ergoplatform.modifiers.mempool.{ErgoTransactionSerializer, UnconfirmedTransaction, ErgoTransaction} -import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} +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.{UtxoStateReader, ErgoStateReader} +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} @@ -24,7 +24,7 @@ import sigmastate.eval.Digest32Coll import sigmastate.eval.Extensions.ArrayOps import scala.concurrent.Future -import scala.util.{Success, Failure} +import scala.util.{Failure, Success} case class TransactionsApiRoute(readersHolder: ActorRef, nodeViewActorRef: ActorRef, @@ -245,7 +245,8 @@ case class TransactionsApiRoute(readersHolder: ActorRef, def getUnconfirmedOutputByTokenIdR: Route = (pathPrefix("unconfirmed" / "outputs" / "byTokenId") & get & tokenId) { tokenId => ApiResponse( - getMemPool.map(_.getAll.flatMap(_.transaction.outputs.filter(_.additionalTokens.exists(_._1.toArray.sameElements(tokenId.toArray))))) + getMemPool.map(_.getAll.flatMap(unconfirmed => + unconfirmed.transaction.outputs.filter(_.additionalTokens.exists(_._1 == tokenId)))) ) } diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala index f7e465c0b0..13917b06a7 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosSolution.scala @@ -1,11 +1,11 @@ package org.ergoplatform.mining import io.circe.syntax._ -import io.circe.{HCursor, Encoder, Decoder} +import io.circe.{Decoder, Encoder, HCursor} import org.bouncycastle.util.BigIntegers import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.settings.Algos import org.ergoplatform.modifiers.history.header.Header.Version +import org.ergoplatform.settings.Algos import scorex.core.serialization.ErgoSerializer import scorex.util.serialization.{Reader, Writer} import sigmastate.basics.CryptoConstants @@ -90,7 +90,7 @@ class AutolykosV1SolutionSerializer extends ErgoSerializer[AutolykosSolution] { */ class AutolykosV2SolutionSerializer extends ErgoSerializer[AutolykosSolution] { - import AutolykosSolution.{wForV2, dForV2} + import AutolykosSolution.{dForV2, wForV2} override def serialize(obj: AutolykosSolution, w: Writer): Unit = { w.putBytes(groupElemToBytes(obj.pk)) diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index af66b3163a..1f07ae7c55 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -1,6 +1,6 @@ package org.ergoplatform.mining -import akka.actor.{ActorRefFactory, ActorRef, Actor, Props} +import akka.actor.{Actor, ActorRef, ActorRefFactory, Props} import akka.pattern.StatusReply import com.google.common.primitives.Longs import org.ergoplatform.ErgoBox.TokenId @@ -11,22 +11,21 @@ import org.ergoplatform.modifiers.history._ 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.{UnconfirmedTransaction, ErgoTransaction} -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages -import ReceivableMessages.{ChangedMempool, NodeViewChange, ChangedHistory, ChangedState, FullBlockApplied} -import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} +import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages._ +import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, LocallyGeneratedModifier} +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.mempool.ErgoMemPoolReader -import org.ergoplatform.nodeView.state.{UtxoStateReader, StateType, ErgoState, ErgoStateContext} -import org.ergoplatform.settings.{ErgoValidationSettingsUpdate, Parameters, ErgoSettings} +import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoStateReader} +import org.ergoplatform.settings.{ErgoSettings, ErgoValidationSettingsUpdate, Parameters} import org.ergoplatform.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.wallet.interpreter.ErgoInterpreter -import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.{EliminateTransactions, LocallyGeneratedModifier} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import scorex.crypto.hash.Digest32 import scorex.util.encode.Base16 -import scorex.util.{ScorexLogging, ModifierId} +import scorex.util.{ModifierId, ScorexLogging} import sigmastate.SType.ErgoBoxRType import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade @@ -37,7 +36,7 @@ import special.collection.Coll import scala.annotation.tailrec import scala.concurrent.duration._ -import scala.util.{Random, Try, Success, Failure} +import scala.util.{Failure, Random, Success, Try} /** Responsible for generating block candidates and validating solutions. * It is observing changes of history, utxo state, mempool and newly applied blocks @@ -666,7 +665,7 @@ object CandidateGenerator extends ScorexLogging { val reemissionRules = reemissionSettings.reemissionRules val eip27ActivationHeight = reemissionSettings.activationHeight - val reemissionTokenId = Digest32Coll @@ reemissionSettings.reemissionTokenIdBytes.toColl + val reemissionTokenId = Digest32Coll @@ reemissionSettings.reemissionTokenIdBytes val nextHeight = currentHeight + 1 val minerProp = diff --git a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala index 94d0a70349..f318a4f426 100644 --- a/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/DefaultFakePowScheme.scala @@ -5,8 +5,7 @@ import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import sigmastate.basics.CryptoConstants.EcPointType -import scala.math.BigInt -import scala.util.{Random, Try, Success} +import scala.util.{Random, Success, Try} /** * Fake Pow Scheme for tests. diff --git a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala index 9fe6cea654..2d82a70f74 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/header/Header.scala @@ -1,13 +1,13 @@ package org.ergoplatform.modifiers.history.header import io.circe.syntax._ -import io.circe.{HCursor, Encoder, Decoder} +import io.circe.{Decoder, Encoder, HCursor} import org.ergoplatform.http.api.ApiCodecs -import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.mining.AutolykosSolution +import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.history.extension.Extension -import org.ergoplatform.modifiers.history.{ADProofs, PreHeader, BlockTransactions} -import org.ergoplatform.modifiers.{HeaderTypeId, BlockSection, NonHeaderBlockSection, NetworkObjectTypeId} +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.settings.{Algos, Constants} @@ -18,7 +18,7 @@ import scorex.crypto.hash.Digest32 import scorex.util._ import sigmastate.basics.CryptoConstants.EcPointType import sigmastate.eval.Extensions._ -import sigmastate.eval.{CGroupElement, CBigInt, CHeader, CAvlTree} +import sigmastate.eval.{CAvlTree, CBigInt, CGroupElement, CHeader} import scala.concurrent.duration.FiniteDuration diff --git a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala index 98f2d1753d..55fa39b005 100644 --- a/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala +++ b/src/main/scala/org/ergoplatform/modifiers/mempool/ErgoTransaction.scala @@ -6,9 +6,9 @@ import org.ergoplatform.SigmaConstants.{MaxBoxSize, MaxPropositionBytes} import org.ergoplatform._ import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.mining.emission.EmissionRules -import org.ergoplatform.modifiers.{TransactionTypeId, ErgoNodeViewModifier, NetworkObjectTypeId} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.mempool.ErgoTransaction.unresolvedIndices +import org.ergoplatform.modifiers.{ErgoNodeViewModifier, NetworkObjectTypeId, TransactionTypeId} import org.ergoplatform.nodeView.ErgoContext import org.ergoplatform.nodeView.state.ErgoStateContext import org.ergoplatform.sdk.utils.ArithUtils.{addExact, multiplyExact} @@ -25,16 +25,16 @@ 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.{ValidationState, ModifierValidator, ValidationResult, InvalidModifier} +import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} import scorex.db.ByteArrayUtils import scorex.util.serialization.{Reader, Writer} -import scorex.util.{ScorexLogging, bytesToId, ModifierId} +import scorex.util.{ModifierId, ScorexLogging, bytesToId} import sigmastate.serialization.ConstantStore -import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import java.util import scala.collection.mutable -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} /** * ErgoTransaction is an atomic state transition operation. It destroys Boxes from the state @@ -274,11 +274,11 @@ case class ErgoTransaction(override val inputs: IndexedSeq[Input], val firstEmissionBoxTokenId = emissionOut.additionalTokens.apply(0)._1 val secondEmissionBoxTokenId = emissionOut.additionalTokens.apply(1)._1 require( - firstEmissionBoxTokenId.toArray.sameElements(emissionNftIdBytes), + firstEmissionBoxTokenId == emissionNftIdBytes, "No emission box NFT in the emission box" ) require( - secondEmissionBoxTokenId.toArray.sameElements(reemissionTokenIdBytes), + secondEmissionBoxTokenId == reemissionTokenIdBytes, "No re-emission token in the emission box" ) diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala index a96b58cc9e..132501df4b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoContext.scala @@ -1,11 +1,10 @@ package org.ergoplatform.nodeView +import org.ergoplatform.ErgoLikeContext import org.ergoplatform.nodeView.state.ErgoStateContext +import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.wallet.protocol.context.InputContext -import org.ergoplatform.ErgoLikeContext -import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext -import scorex.crypto.authds.ADDigest /** * Context to be used during transaction verification @@ -15,7 +14,7 @@ class ErgoContext(val stateContext: ErgoStateContext, inputContext: InputContext, override val costLimit: Long, override val initCost: Long) - extends ErgoLikeContext(ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), + extends ErgoLikeContext(ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, transactionContext.dataBoxes, 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 1803e294f8..e7547aa3b8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexer.scala @@ -208,8 +208,9 @@ trait ExtraIndexerBase extends ScorexLogging { // check if box is creating new tokens, if yes record them cfor(0)(_ < box.additionalTokens.length, _ + 1) { j => - if (!tokens.exists(x => x._1 == box.additionalTokens(j)._1)) + if (!tokens.exists(x => x._1 == box.additionalTokens(j)._1)) { general += IndexedToken.fromBox(box, j) + } } globalBoxIndex += 1 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 2ee76a0405..a4efb95485 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.extra.ExtraIndexer.{ExtraIndexTypeId, f import org.ergoplatform.nodeView.history.extra.IndexedTokenSerializer.{ByteColl, uniqueId} import org.ergoplatform.settings.Algos import scorex.core.serialization.ErgoSerializer -import scorex.util.{ModifierId, bytesToId} +import scorex.util.{ByteArrayOps, ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.CollectionConstant import sigmastate.SByte @@ -124,7 +124,7 @@ object IndexedToken { } IndexedToken(bytesToId(box.additionalTokens(tokenIndex)._1.toArray), - bytesToId(box.id), + box.id.toModifierId, box.additionalTokens(tokenIndex)._2, name, description, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index 9c835afb18..e4ee9457a2 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -11,18 +11,17 @@ import org.ergoplatform.nodeView.history.storage.modifierprocessors.ExtensionVal import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ -import scorex.core.serialization.{ErgoSerializer, BytesSerializable} +import scorex.core.serialization.{BytesSerializable, ErgoSerializer} import scorex.core.utils.ScorexEncoding -import scorex.core.validation.{ValidationState, ModifierValidator, InvalidModifier} +import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationState} import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval.SigmaDsl import special.collection.Coll -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} /** * State context with predicted header. @@ -85,10 +84,10 @@ class ErgoStateContext(val lastHeaders: Seq[Header], // todo remove from ErgoLikeContext and from ErgoStateContext // State root hash before the last block - override def previousStateDigest: Coll[Byte] = if (sigmaLastHeaders.toArray.nonEmpty) { - sigmaLastHeaders.toArray.head.stateRoot.digest + override def previousStateDigest = if (sigmaLastHeaders.toArray.nonEmpty) { + ADDigest @@ sigmaLastHeaders.toArray.head.stateRoot.digest.toArray } else { - genesisStateDigest.toColl + genesisStateDigest } /* NOHF PROOF: diff --git a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala index cf8b686276..2e723ab0ba 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/UtxoStateReader.scala @@ -86,11 +86,10 @@ trait UtxoStateReader extends ErgoStateReader with TransactionValidation { def hasEmissionBox(tx: ErgoTransaction): Boolean = if(fb.height > constants.settings.chainSettings.reemission.activationHeight) { // after EIP-27 we search for emission box NFT for efficiency's sake + val emissionNftId = constants.settings.chainSettings.reemission.emissionNftIdBytes + val outTokens = tx.outputs.head.additionalTokens tx.outputs.size == 2 && - !tx.outputs.head.additionalTokens.isEmpty && - java.util.Arrays.equals( - tx.outputs.head.additionalTokens(0)._1.toArray, - constants.settings.chainSettings.reemission.emissionNftIdBytes) + !outTokens.isEmpty && outTokens(0)._1 == emissionNftId } else { tx.outputs.head.ergoTree == constants.settings.chainSettings.monetary.emissionBoxProposition } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala index 925fe9d213..dab82fe87b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletReader.scala @@ -1,28 +1,28 @@ package org.ergoplatform.nodeView.wallet -import java.util.concurrent.TimeUnit import akka.actor.ActorRef import akka.pattern.ask import akka.util.Timeout import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.{ErgoBox, P2PKAddress} -import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} +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.persistence.WalletDigest +import org.ergoplatform.nodeView.wallet.requests.{BoxesRequest, ExternalSecret, TransactionGenerationRequest} import org.ergoplatform.nodeView.wallet.scanning.ScanRequest -import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGenerationRequest, BoxesRequest} -import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, DerivationPath} -import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.boxes.ChainStatus -import org.ergoplatform.wallet.boxes.ChainStatus.{OnChain, OffChain} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedPublicKey} import org.ergoplatform.wallet.Constants.ScanId +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 scorex.util.ModifierId import sigmastate.Values.SigmaBoolean import sigmastate.basics.DLogProtocol.DLogProverInput +import java.util.concurrent.TimeUnit import scala.concurrent.Future import scala.util.Try diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala index 1215d49bf4..b4d5869d0a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletService.scala @@ -4,30 +4,31 @@ import cats.implicits._ import org.ergoplatform.ErgoBox.BoxId import org.ergoplatform._ import org.ergoplatform.modifiers.ErgoFullBlock -import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} -import org.ergoplatform.nodeView.state.{UtxoStateReader, ErgoStateContext} +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.models.{CollectedBoxes, ChangeBox} +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.{ScanRequest, Scan} +import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest} import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} -import org.ergoplatform.settings.{Parameters, ErgoSettings} +import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.wallet.Constants.ScanId -import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, BoxSelector} +import org.ergoplatform.wallet.boxes.{BoxSelector, ErgoBoxSerializer} import org.ergoplatform.wallet.interface4j.SecretString -import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoProvingInterpreter} +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.{bytesToId, ModifierId} +import scorex.util.{ModifierId} import sigmastate.Values.SigmaBoolean import sigmastate.basics.DLogProtocol.DLogProverInput +import special.collection.Extensions.CollBytesOps import java.io.FileNotFoundException -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} /** * Operations accessible from [[ErgoWalletActor]] @@ -446,8 +447,12 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg registry.getTx(txId) .map(tx => AugWalletTransaction(tx, fullHeight - tx.inclusionHeight)) - override def collectBoxes(state: ErgoWalletState, boxSelector: BoxSelector, targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, Long]): Try[CollectedBoxes] = { - val assetsMap = targetAssets.map(t => bytesToId(t._1.toArray) -> t._2) + override def collectBoxes( + state: ErgoWalletState, + boxSelector: BoxSelector, + targetBalance: Long, + targetAssets: Map[ErgoBox.TokenId, Long]): Try[CollectedBoxes] = { + val assetsMap = targetAssets.map(t => t._1.toModifierId -> t._2) val inputBoxes = state.getBoxesToSpend boxSelector .select(inputBoxes.iterator, state.walletFilter, targetBalance, assetsMap) diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index 8e483550da..2a4deeb4a8 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -1,33 +1,33 @@ package org.ergoplatform.nodeView.wallet -import cats.implicits._ import cats.Traverse +import cats.implicits._ import org.ergoplatform.ErgoBox.{BoxId, R4, R5, R6} -import org.ergoplatform.{ErgoBoxAssets, UnsignedInput, P2PKAddress, ErgoBox, ErgoAddress, DataInput, ErgoBoxCandidate} +import org.ergoplatform._ import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction import org.ergoplatform.nodeView.wallet.ErgoWalletService.DeriveNextKeyResult import org.ergoplatform.nodeView.wallet.persistence.WalletStorage -import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.nodeView.wallet.requests._ -import org.ergoplatform.sdk.wallet.{TokensMap, AssetUtils} -import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, ExtendedPublicKey, DerivationPath} -import org.ergoplatform.settings.Parameters +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedPublicKey, ExtendedSecretKey} +import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} +import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.utils.BoxUtils import org.ergoplatform.wallet.Constants -import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants.PaymentsScanId import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult -import org.ergoplatform.wallet.boxes.{TrackedBox, BoxSelector} +import org.ergoplatform.wallet.boxes.{BoxSelector, TrackedBox} +import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.transactions.TransactionBuilder -import scorex.util.{ScorexLogging, idToBytes} +import scorex.util.ScorexLogging import sigmastate.Values.ByteArrayConstant import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ +import sigmastate.utils.Extensions._ -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} trait ErgoWalletSupport extends ScorexLogging { @@ -248,17 +248,14 @@ trait ErgoWalletSupport extends ScorexLogging { val p2r = ergoSettings.chainSettings.reemission.reemissionRules.payToReemission new ErgoBoxCandidate(eip27OutputAssets.value, p2r, walletHeight) } - } ++ selectionResult.changeBoxes.map { changeBoxAssets => - changeBoxAssets match { - case candidate: ErgoBoxCandidate => - candidate - case changeBox: ErgoBoxAssets => - // todo: is this extra check needed ? - val reemissionTokenId = ergoSettings.chainSettings.reemission.reemissionTokenId - val assets = changeBox.tokens.filterKeys(_ != reemissionTokenId).map(t => Digest32Coll @@ idToBytes(t._1).toColl -> t._2).toIndexedSeq - - new ErgoBoxCandidate(changeBox.value, changeAddressOpt.get, walletHeight, assets.toColl) - } + } ++ selectionResult.changeBoxes.map { + case candidate: ErgoBoxCandidate => + candidate + case changeBox: ErgoBoxAssets => + // todo: is this extra check needed ? + val reemissionTokenId = ergoSettings.chainSettings.reemission.reemissionTokenId + val assets = changeBox.tokens.filterKeys(_ != reemissionTokenId).map(t => t._1.toTokenId -> t._2).toIndexedSeq + new ErgoBoxCandidate(changeBox.value, changeAddressOpt.get, walletHeight, assets.toColl) } val inputBoxes = selectionResult.inputBoxes.toIndexedSeq new UnsignedErgoTransaction( diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala index b1951b12f5..4ead415b3b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletCache.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.wallet -import com.google.common.hash.{Funnels, BloomFilter} +import com.google.common.hash.{BloomFilter, Funnels} import org.ergoplatform.sdk.wallet.secrets.ExtendedPublicKey -import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress, ErgoTreePredef} import org.ergoplatform.settings.ErgoSettings +import org.ergoplatform.{ErgoAddressEncoder, ErgoTreePredef, P2PKAddress} import sigmastate.Values import sigmastate.eval._ diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala index a20e07d1ac..a6c3a0303a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/WalletVars.scala @@ -1,13 +1,13 @@ package org.ergoplatform.nodeView.wallet import com.google.common.hash.BloomFilter -import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import org.ergoplatform.nodeView.wallet.persistence.WalletStorage import org.ergoplatform.nodeView.wallet.scanning.Scan import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKey, ExtendedSecretKey} -import org.ergoplatform.settings.{Parameters, ErgoSettings} +import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter +import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} import scorex.util.ScorexLogging import scala.util.Try 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 0062438726..0b8ee6a23b 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -4,13 +4,13 @@ import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.settings.Constants import scorex.core.serialization.ErgoSerializer -import scorex.util.serialization.{Reader, Writer} - -import scala.collection.mutable import scorex.util.Extensions._ +import scorex.util.serialization.{Reader, Writer} import sigmastate.eval.Digest32Coll import sigmastate.eval.Extensions.ArrayOps +import scala.collection.mutable + /** * Holds aggregate wallet data (including off-chain) with no need fo re-processing it on each request. * 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 20662e7fcc..75fee08b84 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistry.scala @@ -1,28 +1,27 @@ package org.ergoplatform.nodeView.wallet.persistence -import java.io.File +import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.BoxId +import org.ergoplatform.ErgoLikeContext.Height +import org.ergoplatform.modifiers.history.header.PreGenesisHeader import org.ergoplatform.nodeView.wallet.IdUtils.{EncodedTokenId, encodedTokenId} +import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults import org.ergoplatform.nodeView.wallet.{WalletTransaction, WalletTransactionSerializer} +import org.ergoplatform.sdk.wallet.AssetUtils import org.ergoplatform.settings.{Algos, ErgoSettings, WalletSettings} 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 scorex.crypto.authds.ADKey -import scorex.util.{ScorexLogging, idToBytes, ModifierId} -import Constants.{PaymentsScanId, ScanId} -import org.ergoplatform.ErgoBox -import org.ergoplatform.ErgoLikeContext.Height -import org.ergoplatform.modifiers.history.header.PreGenesisHeader import scorex.db.LDBVersionedStore - -import scala.util.{Try, Success, Failure} -import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults -import org.ergoplatform.sdk.wallet.AssetUtils -import org.ergoplatform.wallet.transactions.TransactionBuilder import scorex.util.encode.Base16 +import scorex.util.{ModifierId, ScorexLogging, idToBytes} +import java.io.File import scala.collection.mutable +import scala.util.{Failure, Success, Try} /** * Provides an access to version-sensitive wallet-specific indexes: 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 78d6fb757f..99a0d54f34 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletStorage.scala @@ -1,20 +1,19 @@ package org.ergoplatform.nodeView.wallet.persistence import com.google.common.primitives.{Ints, Shorts} -import org.ergoplatform.nodeView.state.{ErgoStateContextSerializer, ErgoStateContext} -import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, ScanSerializer, Scan} -import org.ergoplatform.settings.{Parameters, ErgoSettings, Constants} import org.ergoplatform.P2PKAddress -import org.ergoplatform.sdk.wallet.secrets.{ExtendedPublicKeySerializer, ExtendedPublicKey, DerivationPath, DerivationPathSerializer} -import scorex.crypto.hash.Blake2b256 +import org.ergoplatform.nodeView.state.{ErgoStateContext, ErgoStateContextSerializer} +import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest, ScanSerializer} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, DerivationPathSerializer, ExtendedPublicKey, ExtendedPublicKeySerializer} +import org.ergoplatform.settings.{Constants, ErgoSettings, Parameters} import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} -import scorex.db.{LDBKVStore, LDBFactory} - -import java.io.File +import scorex.crypto.hash.Blake2b256 +import scorex.db.{LDBFactory, LDBKVStore} import scorex.util.ScorexLogging import sigmastate.serialization.SigmaSerializer -import scala.util.{Try, Success, Failure} +import java.io.File +import scala.util.{Failure, Success, Try} /** * Persists version-agnostic wallet actor's mutable state (which is not a subject to rollbacks in case of forks) @@ -39,8 +38,8 @@ final class WalletStorage(store: LDBKVStore, settings: ErgoSettings) extends Sco val qty = Ints.fromByteArray(r.take(4)) (0 until qty).foldLeft((Seq.empty[DerivationPath], r.drop(4))) { case ((acc, bytes), _) => val length = Ints.fromByteArray(bytes.take(4)) - val pathBytes = SigmaSerializer.startReader(bytes.slice(4, 4 + length)) - val pathTry = DerivationPathSerializer.parseTry(pathBytes) + val r = SigmaSerializer.startReader(bytes.slice(4, 4 + length)) + val pathTry = DerivationPathSerializer.parseTry(r) val newAcc = pathTry.map(acc :+ _).getOrElse(acc) val bytesTail = bytes.drop(4 + length) newAcc -> bytesTail diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala index 6da5d1a888..66d2d36d7e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/GenerateCommitmentsRequest.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.wallet.requests import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction -import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} /** * A request to generate commitments for unsigned transaction, useful for multi-party signing. 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 7ba3175292..39890f4562 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/RequestsHolder.scala @@ -1,11 +1,11 @@ package org.ergoplatform.nodeView.wallet.requests import io.circe.syntax._ -import io.circe.{HCursor, Encoder, Json, Decoder} +import io.circe.{Decoder, Encoder, HCursor, Json} import org.ergoplatform.http.api.ApiCodecs import org.ergoplatform.nodeView.wallet.ErgoAddressJsonEncoder import org.ergoplatform.settings.ErgoSettings -import org.ergoplatform.{ErgoAddressEncoder, Pay2SAddress, ErgoAddress, ErgoTreePredef} +import org.ergoplatform.{ErgoAddress, ErgoAddressEncoder, ErgoTreePredef, Pay2SAddress} case class RequestsHolder(requests: Seq[TransactionGenerationRequest], diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala index 26296a061b..5b3b0c4d90 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/TransactionSigningRequest.scala @@ -1,7 +1,7 @@ package org.ergoplatform.nodeView.wallet.requests import org.ergoplatform.modifiers.mempool.UnsignedErgoTransaction -import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.wallet.interpreter.TransactionHintsBag /** diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala index b585517b0a..13449663f4 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala @@ -1,9 +1,9 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.ErgoBox -import scorex.util.encode.Base16 import sigmastate.Values.EvaluatedValue import sigmastate.{SType, Values} +import special.collection.Extensions._ /** * Basic interface for box scanning predicate functionality @@ -130,7 +130,7 @@ case class ContainsAssetPredicate(assetId: ErgoBox.TokenId) extends ScanningPred override def hashCode(): Int = assetId.hashCode() - override def toString: String = s"ContainsAssetPredicate(${Base16.encode(assetId.toArray)})" + override def toString: String = s"ContainsAssetPredicate(${assetId.toHex})" } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala index 1f6386709d..e8305fb331 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecs.scala @@ -1,14 +1,13 @@ package org.ergoplatform.nodeView.wallet.scanning -import io.circe.Json +import io.circe.syntax._ +import io.circe.{Decoder, Encoder, Json} import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId -import scorex.util.encode.Base16 -import io.circe.{Decoder, Encoder} -import io.circe.syntax._ import org.ergoplatform.http.api.ApiCodecs import sigmastate.SType import sigmastate.Values.EvaluatedValue +import special.collection.Extensions._ object ScanningPredicateJsonCodecs extends ApiCodecs { @@ -19,7 +18,7 @@ object ScanningPredicateJsonCodecs extends ApiCodecs { case ep: EqualsScanningPredicate => Json.obj("predicate" -> "equals".asJson, "register" -> ep.regId.asJson, "value" -> ep.value.asJson) case cap: ContainsAssetPredicate => - Json.obj("predicate" -> "containsAsset".asJson, "assetId" -> Base16.encode(cap.assetId.toArray).asJson) + Json.obj("predicate" -> "containsAsset".asJson, "assetId" -> cap.assetId.toHex.asJson) case and: AndScanningPredicate => Json.obj("predicate" -> "and".asJson, "args" -> and.subPredicates.asJson) case or: OrScanningPredicate => diff --git a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala b/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala index 201b350ac2..7dc87a66b0 100644 --- a/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala +++ b/src/main/scala/org/ergoplatform/reemission/ReemissionRules.scala @@ -9,6 +9,7 @@ import sigmastate.SBoolean import sigmastate.Values.Value import sigmastate.eval.CompiletimeIRContext import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} +import special.collection.Coll import scala.util.Try @@ -18,7 +19,7 @@ import scala.util.Try */ class ReemissionRules(reemissionSettings: ReemissionSettings) extends ReemissionContracts { - override val reemissionNftIdBytes: Array[Byte] = reemissionSettings.reemissionNftIdBytes + override val reemissionNftIdBytes: Coll[Byte] = reemissionSettings.reemissionNftIdBytes override val reemissionStartHeight: Height = reemissionSettings.reemissionStartHeight /** diff --git a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala index 41f90aeaea..7925cc9449 100644 --- a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala @@ -5,6 +5,8 @@ import org.ergoplatform.reemission.ReemissionRules import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.util.ModifierId import scorex.util.encode.Base16 +import sigmastate.eval.Extensions.ArrayOps +import special.collection.Coll /** * Configuration section for re-emission (EIP27) parameters @@ -19,9 +21,9 @@ case class ReemissionSettings(checkReemissionRules: Boolean, reemissionStartHeight: Int, injectionBoxBytesEncoded: String) { - val emissionNftIdBytes: Array[Byte] = Algos.decode(emissionNftId).get - val reemissionNftIdBytes: Array[Byte] = Algos.decode(reemissionNftId).get - val reemissionTokenIdBytes: Array[Byte] = Algos.decode(reemissionTokenId).get + val emissionNftIdBytes: Coll[Byte] = emissionNftId.toBytes.toColl + val reemissionNftIdBytes: Coll[Byte] = reemissionNftId.toBytes.toColl + val reemissionTokenIdBytes: Coll[Byte] = reemissionTokenId.toBytes.toColl lazy val InjectionBoxBytes: Array[Byte] = Base16.decode(injectionBoxBytesEncoded).get lazy val injectionBox: ErgoBox = ErgoBoxSerializer.parseBytes(InjectionBoxBytes) diff --git a/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala index 942801417a..dee8e45a3f 100644 --- a/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/MiningApiRouteSpec.scala @@ -11,7 +11,7 @@ import org.ergoplatform.mining.AutolykosSolution import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.Stubs import org.ergoplatform.utils.generators.ErgoGenerators -import org.ergoplatform.{Pay2SAddress, ErgoTreePredef} +import org.ergoplatform.{ErgoTreePredef, Pay2SAddress} import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index 36abac0bda..ea0dbad8a3 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -22,6 +22,7 @@ import sigmastate.SType import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} import sigmastate.eval.Extensions._ import sigmastate.eval._ +import special.collection.Extensions._ import java.net.InetSocketAddress import scala.concurrent.duration._ @@ -228,7 +229,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec } it should "return unconfirmed output by tokenId from mempool" in { - val searchedToken = Base16.encode(tokens.head._1.toArray) + val searchedToken = tokens.head._1.toHex Get(prefix + s"/unconfirmed/outputs/byTokenId/$searchedToken") ~> chainedRoute ~> check { status shouldBe StatusCodes.OK val actualOutputIds = responseAs[List[Json]].map(_.hcursor.downField("boxId").as[String].right.get) diff --git a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala index ad65d2dc34..0fddaf5750 100644 --- a/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/CandidateGeneratorSpec.scala @@ -1,24 +1,24 @@ package org.ergoplatform.mining -import akka.actor.{ActorSystem, ActorRef} +import akka.actor.{ActorRef, ActorSystem} import akka.pattern.{StatusReply, ask} -import akka.testkit.{TestProbe, TestKit} +import akka.testkit.{TestKit, TestProbe} import akka.util.Timeout import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} -import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} +import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied +import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistoryReader import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.nodeView.{ErgoReadersHolderRef, ErgoNodeViewRef} +import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.ErgoTestHelpers -import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied import sigmastate.basics.DLogProtocol import sigmastate.basics.DLogProtocol.DLogProverInput diff --git a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala index bd12ac62b3..16e83f5d00 100644 --- a/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala +++ b/src/test/scala/org/ergoplatform/mining/ErgoMinerSpec.scala @@ -1,31 +1,31 @@ package org.ergoplatform.mining -import akka.actor.{ActorSystem, ActorRef} +import akka.actor.{ActorRef, ActorSystem} import akka.pattern.{StatusReply, ask} -import akka.testkit.{TestProbe, TestKit} +import akka.testkit.{TestKit, TestProbe} import akka.util.Timeout import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining.CandidateGenerator.{Candidate, GenerateCandidate} import org.ergoplatform.mining.ErgoMiner.StartMining import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, UnsignedErgoTransaction, ErgoTransaction} -import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction, UnsignedErgoTransaction} +import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.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.{ErgoReadersHolderRef, ErgoNodeViewRef} +import org.ergoplatform.nodeView.{ErgoNodeViewRef, ErgoReadersHolderRef} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.ErgoTestHelpers import org.ergoplatform.utils.generators.ValidBlocksGenerators -import org.ergoplatform.{Input, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} +import org.ergoplatform.wallet.interpreter.ErgoInterpreter +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import org.scalatest.concurrent.Eventually import org.scalatest.flatspec.AnyFlatSpec -import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.FullBlockApplied -import org.ergoplatform.wallet.interpreter.ErgoInterpreter import sigmastate.SigmaAnd -import sigmastate.Values.{SigmaPropConstant, ErgoTree} +import sigmastate.Values.{ErgoTree, SigmaPropConstant} import sigmastate.basics.DLogProtocol import sigmastate.basics.DLogProtocol.DLogProverInput diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index 4f9fd14efe..d9a50fb587 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -8,25 +8,25 @@ import org.ergoplatform.sdk.wallet.protocol.context.TransactionContext import org.ergoplatform.settings.Parameters.MaxBlockCostIncrease import org.ergoplatform.settings.ValidationRules.{bsBlockTransactionsCost, txAssetsInOneBox} import org.ergoplatform.settings._ -import org.ergoplatform.utils.{ErgoTestConstants, ErgoPropertyTest} +import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestConstants} import org.ergoplatform.wallet.boxes.ErgoBoxAssetExtractor -import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoInterpreter} +import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, TransactionHintsBag} import org.ergoplatform.wallet.protocol.context.InputContext -import org.ergoplatform.{Input, ErgoBox, ErgoBoxCandidate} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, Input} import org.scalacheck.Gen import scalan.util.BenchmarkUtil import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 -import scorex.util.{bytesToId, ModifierId} import scorex.util.encode.Base16 +import scorex.util.{ModifierId, bytesToId} import sigmastate.AND -import sigmastate.Values.{TrueLeaf, SigmaPropConstant, ByteArrayConstant, IntConstant, ByteConstant, LongArrayConstant} +import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, LongArrayConstant, SigmaPropConstant, TrueLeaf} import sigmastate.basics.CryptoConstants import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval._ -import sigmastate.interpreter.{ContextExtension, ProverResult} import sigmastate.helpers.TestingHelpers._ +import sigmastate.interpreter.{ContextExtension, ProverResult} import scala.util.{Random, Try} diff --git a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala index 2371e92875..1196482d4c 100644 --- a/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala +++ b/src/test/scala/org/ergoplatform/network/HeaderSerializationSpecification.scala @@ -1,8 +1,7 @@ package org.ergoplatform.network -import java.nio.ByteBuffer import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{groupElemFromBytes, AutolykosSolution} +import org.ergoplatform.mining.{AutolykosSolution, groupElemFromBytes} import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.settings.Algos import org.ergoplatform.utils.ErgoPropertyTest @@ -12,6 +11,8 @@ import scorex.util.ModifierId import scorex.util.encode.Base16 import sigmastate.basics.CryptoConstants.EcPointType +import java.nio.ByteBuffer + class HeaderSerializationSpecification extends ErgoPropertyTest with DecodingUtils { private def base16ToEcPoint(pointEncoded: String): EcPointType = { 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 817a48bd37..974ad52115 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -1,22 +1,22 @@ package org.ergoplatform.nodeView.history.extra import org.ergoplatform.ErgoBox.TokenId -import org.ergoplatform.{ErgoAddressEncoder, UnsignedInput, P2PKAddress, ErgoBox, ErgoTreePredef, ErgoBoxCandidate} import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{CandidateBlock, AutolykosPowScheme, CandidateGenerator} +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.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, txSegmentId, hashErgoTree} +import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId} import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption -import org.ergoplatform.nodeView.state.{StateConstants, UtxoStateReader, ErgoStateContext, StateType, UtxoState, ErgoState} -import org.ergoplatform.settings.{NodeConfigurationSettings, NetworkType, ErgoSettings} -import org.ergoplatform.utils.{HistoryTestHelpers, ErgoPropertyTest, ErgoTestHelpers} -import scorex.util.{bytesToId, ModifierId} +import org.ergoplatform.nodeView.state._ +import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} +import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} +import org.ergoplatform._ +import scorex.util.{ModifierId, bytesToId} import sigmastate.Values import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions.ArrayOps diff --git a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala index 7134154010..46d0fcd6d6 100644 --- a/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/mempool/ScriptsSpec.scala @@ -2,18 +2,18 @@ package org.ergoplatform.nodeView.mempool import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform.ErgoTreePredef.boxCreationHeight -import org.ergoplatform.{Self, ErgoBox, Height, ErgoTreePredef} -import org.ergoplatform.nodeView.state.{UtxoState, ErgoState, BoxHolder} +import org.ergoplatform.nodeView.state.{BoxHolder, ErgoState, UtxoState} import org.ergoplatform.settings.Algos import org.ergoplatform.utils.{ErgoPropertyTest, RandomWrapper} +import org.ergoplatform.{ErgoBox, ErgoTreePredef, Height, Self} import scorex.crypto.authds.avltree.batch.Remove -import sigmastate._ import sigmastate.Values._ +import sigmastate._ import sigmastate.basics.CryptoConstants.dlogGroup -import sigmastate.lang.Terms._ import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.eval.{IRContext, CompiletimeIRContext} -import sigmastate.lang.{TransformingSigmaBuilder, CompilerSettings, SigmaCompiler} +import sigmastate.eval.{CompiletimeIRContext, IRContext} +import sigmastate.lang.Terms._ +import sigmastate.lang.{CompilerSettings, SigmaCompiler, TransformingSigmaBuilder} import scala.util.Try diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala index a01eac7e17..e188e76649 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletServiceSpec.scala @@ -3,20 +3,20 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, R1} import org.ergoplatform._ import org.ergoplatform.db.DBSpec -import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults -import org.ergoplatform.nodeView.wallet.persistence.{WalletRegistry, OffChainRegistry, WalletStorage} -import org.ergoplatform.nodeView.wallet.requests.{PaymentRequest, AssetIssueRequest} -import org.ergoplatform.nodeView.wallet.scanning.{ScanRequest, EqualsScanningPredicate, ScanWalletInteraction} -import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DerivationPath} +import org.ergoplatform.nodeView.wallet.persistence.{OffChainRegistry, WalletRegistry, WalletStorage} +import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, PaymentRequest} +import org.ergoplatform.nodeView.wallet.scanning.{EqualsScanningPredicate, ScanRequest, ScanWalletInteraction} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.settings.ErgoSettings import org.ergoplatform.utils.fixtures.WalletFixture import org.ergoplatform.utils.generators.ErgoTransactionGenerators -import org.ergoplatform.utils.{WalletTestOps, MempoolTestHelpers, ErgoPropertyTest} +import org.ergoplatform.utils.{ErgoPropertyTest, MempoolTestHelpers, WalletTestOps} import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult -import org.ergoplatform.wallet.boxes.{TrackedBox, ErgoBoxSerializer, ReplaceCompactCollectBoxSelector} +import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, ReplaceCompactCollectBoxSelector, TrackedBox} import org.ergoplatform.wallet.crypto.ErgoSignature import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.mnemonic.Mnemonic @@ -24,7 +24,7 @@ import org.scalacheck.Gen import org.scalatest.BeforeAndAfterAll import scorex.db.{LDBKVStore, LDBVersionedStore} import scorex.util.encode.Base16 -import sigmastate.Values.{EvaluatedValue, ByteArrayConstant} +import sigmastate.Values.{ByteArrayConstant, EvaluatedValue} import sigmastate.eval.Extensions.ArrayOps import sigmastate.helpers.TestingHelpers.testBox import sigmastate.{SType, Values} diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala index 846c030fab..9659837cb8 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSpec.scala @@ -1,26 +1,26 @@ package org.ergoplatform.nodeView.wallet import org.ergoplatform._ -import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.state.{ErgoStateContext, VotingData} import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.nodeView.wallet.persistence.{WalletDigest, WalletDigestSerializer} -import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, PaymentRequest, BurnTokensRequest, AssetIssueRequest} +import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, BurnTokensRequest, ExternalSecret, PaymentRequest} import org.ergoplatform.sdk.wallet.secrets.PrimitiveSecretKey import org.ergoplatform.settings.{Algos, Constants} import org.ergoplatform.utils._ import org.ergoplatform.utils.fixtures.WalletFixture -import org.ergoplatform.wallet.interpreter.{TransactionHintsBag, ErgoInterpreter} -import scorex.util.encode.Base16 -import sigmastate.eval._ -import sigmastate.eval.Extensions._ import org.ergoplatform.wallet.boxes.BoxSelector.MinBoxValue import org.ergoplatform.wallet.boxes.ErgoBoxSerializer +import org.ergoplatform.wallet.interpreter.{ErgoInterpreter, TransactionHintsBag} import org.scalacheck.Gen import org.scalatest.concurrent.Eventually import scorex.util.ModifierId -import sigmastate.{CTHRESHOLD, CAND} +import scorex.util.encode.Base16 import sigmastate.basics.DLogProtocol.DLogProverInput +import sigmastate.eval.Extensions._ +import sigmastate.eval._ +import sigmastate.{CAND, CTHRESHOLD} import scala.concurrent.duration._ @@ -240,7 +240,7 @@ class ErgoWalletSpec extends ErgoPropertyTest with WalletTestOps with Eventually property("whitelist set, preserve tokens from auto-burn") { val inputs = { val x = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) - Seq(encodedTokenId(Digest32Coll @@@ x.head.boxId.toColl)) + Seq(encodedTokenId(x.head.boxId.toTokenId)) } implicit val ww: WalletFixture = new WalletFixture(settings diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala index b680edb1ad..70093e7fab 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/persistence/WalletRegistryBenchmark.scala @@ -1,15 +1,15 @@ package org.ergoplatform.nodeView.wallet.persistence -import org.ergoplatform.{ErgoAddressEncoder, Input, ErgoBox} import org.ergoplatform.ErgoBox.{AdditionalRegisters, TokenId} import org.ergoplatform.modifiers.mempool.ErgoTransaction import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults import org.ergoplatform.nodeView.wallet.{WalletTransaction, WalletVars} -import org.ergoplatform.sdk.wallet.secrets.{ExtendedSecretKey, DerivationPath} +import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.utils.ErgoTestConstants import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.boxes.TrackedBox import org.ergoplatform.wallet.interpreter.ErgoProvingInterpreter +import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, Input} import scorex.util.ModifierId import scorex.util.encode.Base16 import sigmastate.Values.ErgoTree diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala index d8debd33b9..4d015ac027 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateJsonCodecsSpecification.scala @@ -6,27 +6,26 @@ import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators import scorex.util.encode.Base16 import sigmastate.Values.ByteArrayConstant -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.Extensions.ArrayByteOps import scala.language.implicitConversions class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with WalletGenerators { - import ScanningPredicateJsonCodecs.{scanningPredicateEncoder, scanningPredicateDecoder} + import ScanningPredicateJsonCodecs.{scanningPredicateDecoder, scanningPredicateEncoder} private implicit def bacFromBytes(bs: Array[Byte]) = ByteArrayConstant(bs) private val complexOr = OrScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, ByteArrayConstant(Array.fill(32)(1: Byte))), EqualsScanningPredicate(ErgoBox.R4, ByteArrayConstant(Array.fill(32)(0: Byte))), - ContainsAssetPredicate(Digest32Coll @@ Array.fill(32)(0: Byte).toColl) + ContainsAssetPredicate(Array.fill(32)(0: Byte).toTokenId) ) private val complexAnd = AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, ByteArrayConstant(Array.fill(32)(1: Byte))), EqualsScanningPredicate(ErgoBox.R4, ByteArrayConstant(Array.fill(32)(1: Byte))), - ContainsAssetPredicate(Digest32Coll @@ Array.fill(32)(1: Byte).toColl) + ContainsAssetPredicate(Array.fill(32)(1: Byte).toTokenId) ) property("json roundtrip for generated predicate") { @@ -58,7 +57,7 @@ class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with Wal val bs = Base16.decode("02dada811a888cd0dc7a0a41739a3ad9b0f427741fe6ca19700cf1a51200c96bf7").get scanningPredicateDecoder.decodeJson(j).toTry.get == AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R1, bs), - ContainsAssetPredicate(Digest32Coll @@ bs.toColl) + ContainsAssetPredicate(bs.toTokenId) ) } @@ -72,7 +71,7 @@ class ScanningPredicateJsonCodecsSpecification extends ErgoPropertyTest with Wal val bs = Base16.decode("02dada811a888cd0dc7a0a41739a3ad9b0f427741fe6ca19700cf1a51200c96bf7").get scanningPredicateDecoder.decodeJson(j).toTry.get == AndScanningPredicate( ContainsScanningPredicate(ErgoBox.R4, bs), - ContainsAssetPredicate(Digest32Coll @@ bs.toColl) + ContainsAssetPredicate(bs.toTokenId) ) } diff --git a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala index 738a411c38..dcfc049dbe 100644 --- a/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSpecification.scala @@ -1,18 +1,17 @@ package org.ergoplatform.nodeView.wallet.scanning -import org.ergoplatform.{P2PKAddress, ErgoTreePredef} +import io.circe.parser._ import org.ergoplatform.ErgoBox.R1 import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.ErgoTransactionGenerators +import org.ergoplatform.wallet.serialization.JsonCodecsWrapper +import org.ergoplatform.{ErgoTreePredef, P2PKAddress} import sigmastate.Values.ByteArrayConstant +import sigmastate.eval.Extensions.ArrayByteOps import sigmastate.helpers.TestingHelpers._ -import scala.util.Random import scala.language.implicitConversions -import io.circe.parser._ -import org.ergoplatform.wallet.serialization.JsonCodecsWrapper -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import scala.util.Random class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransactionGenerators { @@ -20,7 +19,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti private implicit def bacFromBytes(bs: Array[Byte]) = ByteArrayConstant(bs) - //helper function to change random byte + /** Helper function to create a new array with changed random byte. */ private def mutateRandomByte(source: Array[Byte]): Array[Byte] = { val sourceModified = source.clone() val idx = Random.nextInt(sourceModified.length) @@ -102,7 +101,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti val emptyBox = testBox(value = 1, pk, creationHeight = 0) ContainsAssetPredicate(tokenId).filter(emptyBox) shouldBe false - ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl).filter(box) shouldBe false + ContainsAssetPredicate(mutateRandomByte(tokenId.toArray).toTokenId).filter(box) shouldBe false } } } @@ -122,7 +121,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true AndScanningPredicate( - ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), + ContainsAssetPredicate(mutateRandomByte(tokenId.toArray).toTokenId), ContainsScanningPredicate(R1, p2pk.contentBytes) ).filter(box) shouldBe false @@ -149,7 +148,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true OrScanningPredicate( - ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), + ContainsAssetPredicate(mutateRandomByte(tokenId.toArray).toTokenId), ContainsScanningPredicate(R1, p2pk.contentBytes) ).filter(box) shouldBe true @@ -159,7 +158,7 @@ class ScanningPredicateSpecification extends ErgoPropertyTest with ErgoTransacti ).filter(box) shouldBe true OrScanningPredicate( - ContainsAssetPredicate(Digest32Coll @@ mutateRandomByte(tokenId.toArray).toColl), + ContainsAssetPredicate(mutateRandomByte(tokenId.toArray).toTokenId), ContainsScanningPredicate(R1, mutateRandomByte(p2pk.contentBytes)) ).filter(box) shouldBe false } diff --git a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala index ace1b9f677..2d5eca8bf7 100644 --- a/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala +++ b/src/test/scala/org/ergoplatform/reemission/ReemissionRulesSpec.scala @@ -1,19 +1,18 @@ package org.ergoplatform.reemission -import org.ergoplatform.settings.{MonetarySettings, ReemissionSettings} -import org.ergoplatform.utils.{ErgoTestConstants, ErgoPropertyTest} import org.ergoplatform._ +import org.ergoplatform.settings.{MonetarySettings, ReemissionSettings} +import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestConstants} import scorex.crypto.hash.Blake2b256 import scorex.util.ModifierId import sigmastate.AvlTreeData import sigmastate.TrivialProp.TrueProp -import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval.{Colls, Digest32Coll} import sigmastate.helpers.TestingHelpers.testBox -import sigmastate.helpers.{ErgoLikeTestInterpreter, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting} +import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.interpreter.Interpreter.emptyEnv -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} // done similarly to ErgoScriptPredefSpec in sigma repo class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { @@ -31,7 +30,7 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { private val rr = new ReemissionRules(rs) - private val reemissionBoxAssets = Colls.fromItems((Digest32Coll @@ rs.reemissionNftIdBytes.toColl) -> 1L) + private val reemissionBoxAssets = Colls.fromItems((Digest32Coll @@ rs.reemissionNftIdBytes) -> 1L) private val fakeMessage = Blake2b256("Hello World") @@ -186,7 +185,7 @@ class ReemissionRulesSpec extends ErgoPropertyTest with ErgoTestConstants { // pay-2-reemission box can be spent only with a box with reemission NFT as input #0 val reemissionBoxAssets6 = Colls.fromItems( - (Digest32Coll @@ rs.reemissionNftIdBytes.reverse.toColl) -> 1L + (Digest32Coll @@ rs.reemissionNftIdBytes.reverse) -> 1L ) val newReemissionBox6 = new ErgoBoxCandidate( reemissionBox.value + mergedValue - feeValue, diff --git a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala index 90d8f2821b..63908e2dc5 100644 --- a/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala +++ b/src/test/scala/org/ergoplatform/serialization/JsonSerializationSpec.scala @@ -1,17 +1,17 @@ package org.ergoplatform.serialization import io.circe.syntax._ -import io.circe.{Encoder, ACursor, Json, Decoder} +import io.circe.{ACursor, Decoder, Encoder, Json} import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.NonMandatoryRegisterId -import org.ergoplatform.http.api.ApiEncoderOption.HideDetails.implicitValue -import org.ergoplatform.http.api.ApiEncoderOption.{ShowDetails, Detalization} import org.ergoplatform.http.api.ApiCodecs +import org.ergoplatform.http.api.ApiEncoderOption.HideDetails.implicitValue +import org.ergoplatform.http.api.ApiEncoderOption.{Detalization, ShowDetails} import org.ergoplatform.modifiers.ErgoFullBlock 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.{DlogSecretKey, DhtSecretKey} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.settings.{Algos, ErgoSettings} import org.ergoplatform.utils.ErgoPropertyTest import org.ergoplatform.utils.generators.WalletGenerators @@ -19,7 +19,7 @@ import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.TrackedBox import org.scalatest.Inspectors import sigmastate.SType -import sigmastate.Values.{EvaluatedValue, ErgoTree} +import sigmastate.Values.{ErgoTree, EvaluatedValue} import scala.util.Random diff --git a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala index 41306419e7..c09e9c6bd9 100644 --- a/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala +++ b/src/test/scala/org/ergoplatform/tools/FeeSimulator.scala @@ -1,15 +1,15 @@ package org.ergoplatform.tools import org.ergoplatform.modifiers.mempool.ErgoTransaction -import org.ergoplatform.{Input, ErgoBoxCandidate} +import org.ergoplatform.settings.Constants._ +import org.ergoplatform.settings.LaunchParameters._ +import org.ergoplatform.{ErgoBoxCandidate, Input} import scorex.crypto.authds.ADKey import scorex.utils.Random import sigmastate.basics.DLogProtocol.DLogProverInput -import sigmastate.interpreter.{ContextExtension, ProverResult} +import sigmastate.eval.Extensions.ArrayByteOps import sigmastate.eval._ -import org.ergoplatform.settings.LaunchParameters._ -import org.ergoplatform.settings.Constants._ -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.interpreter.{ContextExtension, ProverResult} object FeeSimulator extends App { @@ -23,7 +23,9 @@ object FeeSimulator extends App { val input = Input(ADKey @@ Random.randomBytes(32), ProverResult(Random.randomBytes(65), ContextExtension(Map()))) val creationHeight: Int = 100000 - val box1 = new ErgoBoxCandidate(scala.util.Random.nextLong(), k1, creationHeight, Colls.fromItems((Digest32Coll @@ Random.randomBytes(32).toColl) -> scala.util.Random.nextLong())) + val box1 = new ErgoBoxCandidate( + scala.util.Random.nextLong(), k1, creationHeight, + Colls.fromItems(Random.randomBytes(32).toTokenId -> scala.util.Random.nextLong())) val box2 = new ErgoBoxCandidate(scala.util.Random.nextLong(), k2, creationHeight) val simpleTx = ErgoTransaction(IndexedSeq(input, input), IndexedSeq(box1, box2)) diff --git a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala index 1493f15bd4..4e7f839dc3 100644 --- a/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala +++ b/src/test/scala/org/ergoplatform/utils/ErgoTestConstants.scala @@ -6,7 +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.nodeView.state.{StateConstants, UpcomingStateContext, ErgoStateContext, StateType, ErgoState} +import org.ergoplatform.nodeView.state._ import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.Parameters.{MaxBlockCostIncrease, MinValuePerByteIncrease} @@ -23,7 +23,7 @@ import scorex.crypto.hash.Digest32 import scorex.util.ScorexLogging import sigmastate.Values.ErgoTree import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} import sigmastate.interpreter.{ContextExtension, ProverResult} 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 cd817a9c41..b259d0166b 100644 --- a/src/test/scala/org/ergoplatform/utils/Stubs.scala +++ b/src/test/scala/org/ergoplatform/utils/Stubs.scala @@ -1,21 +1,21 @@ package org.ergoplatform.utils -import akka.actor.{ActorSystem, ActorRef, Actor, Props} +import akka.actor.{Actor, ActorRef, ActorSystem, Props} import akka.pattern.StatusReply import org.bouncycastle.util.BigIntegers import org.ergoplatform.P2PKAddress import org.ergoplatform.mining.CandidateGenerator.Candidate -import org.ergoplatform.mining.{AutolykosSolution, ErgoMiner, CandidateGenerator, WorkMessage} +import org.ergoplatform.mining.{AutolykosSolution, CandidateGenerator, ErgoMiner, WorkMessage} import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.mempool.{UnconfirmedTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction} import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction -import org.ergoplatform.nodeView.ErgoReadersHolder.{Readers, GetReaders, GetDataFromHistory} +import org.ergoplatform.nodeView.ErgoReadersHolder.{GetDataFromHistory, GetReaders, Readers} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.mempool.ErgoMemPool -import org.ergoplatform.nodeView.mempool.ErgoMemPool.{SortingOption, ProcessingOutcome} +import org.ergoplatform.nodeView.mempool.ErgoMemPool.{ProcessingOutcome, SortingOption} import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState -import org.ergoplatform.nodeView.state.{StateType, DigestState, ErgoStateContext} +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._ @@ -25,28 +25,28 @@ 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.utils.generators.{ErgoTransactionGenerators, ErgoGenerators, ChainGenerator} -import org.ergoplatform.wallet.interface4j.SecretString +import org.ergoplatform.utils.generators.{ChainGenerator, ErgoGenerators, ErgoTransactionGenerators} import org.ergoplatform.wallet.Constants.{PaymentsScanId, ScanId} -import org.ergoplatform.wallet.boxes.{TrackedBox, ChainStatus} +import org.ergoplatform.wallet.boxes.{ChainStatus, TrackedBox} +import org.ergoplatform.wallet.interface4j.SecretString 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.{GetBlacklistedPeers, GetAllPeers} -import scorex.core.network.{PeerSpec, Handshake} +import scorex.core.network.peer.PeerManager.ReceivableMessages.{GetAllPeers, GetBlacklistedPeers} +import scorex.core.network.{Handshake, PeerSpec} import scorex.core.settings.ScorexSettings import scorex.crypto.authds.ADDigest import scorex.crypto.hash.Digest32 import scorex.db.ByteArrayWrapper import scorex.util.Random -import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} import scala.collection.mutable import scala.concurrent.duration._ -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} trait Stubs extends ErgoGenerators with ErgoTestHelpers with ChainGenerator with TestFileUtils { diff --git a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala index 2dae438a70..b76ac7485b 100644 --- a/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala +++ b/src/test/scala/org/ergoplatform/utils/WalletTestOps.scala @@ -6,7 +6,7 @@ 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.state.{UtxoState, ErgoState} +import org.ergoplatform.nodeView.state.{ErgoState, UtxoState} import org.ergoplatform.nodeView.wallet.ErgoWallet import org.ergoplatform.nodeView.wallet.IdUtils._ import org.ergoplatform.nodeView.wallet.persistence.WalletDigest @@ -15,16 +15,17 @@ import org.ergoplatform.settings.Constants import org.ergoplatform.utils.fixtures.WalletFixture import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 -import scorex.util.{bytesToId, ModifierId} +import scorex.util.ModifierId import sigmastate.Values.ErgoTree import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.interpreter.ProverResult +import special.collection.Extensions._ trait WalletTestOps extends NodeViewBaseOps { - def newAssetIdStub: TokenId = Digest32Coll @@ Blake2b256.hash("new_asset").toColl + def newAssetIdStub: TokenId = Blake2b256.hash("new_asset").toTokenId def withFixture[T](test: WalletFixture => T): T = new WalletFixture(settings, parameters, getCurrentView(_).vault).apply(test) @@ -51,10 +52,10 @@ trait WalletTestOps extends NodeViewBaseOps { tx.outputs.filter(_.propositionBytes.containsSlice(org.ergoplatform.mining.groupElemToBytes(pk.value))) def assetAmount(boxes: Seq[ErgoBoxCandidate]): Seq[(ModifierId, Long)] = - assetsByTokenId(boxes).map { case (tokenId, sum) => (bytesToId(tokenId.toArray), sum) }.toArray[(ModifierId, Long)] + assetsByTokenId(boxes).map { case (tokenId, sum) => (tokenId.toModifierId, sum) }.toArray[(ModifierId, Long)] def toAssetMap(assetSeq: Seq[(TokenId, Long)]): TokensMap = - assetSeq.map { case (tokenId, sum) => (bytesToId(tokenId.toArray), sum) }.toMap + assetSeq.map { case (tokenId, sum) => (tokenId.toModifierId, sum) }.toMap def assetsByTokenId(boxes: Seq[ErgoBoxCandidate]): Map[TokenId, Long] = { boxes @@ -78,7 +79,7 @@ trait WalletTestOps extends NodeViewBaseOps { def makeGenesisTxWithAsset(publicKey: ProveDlog, issueAsset: Boolean): ErgoTransaction = { val inputs = IndexedSeq(new Input(genesisEmissionBox.id, emptyProverResult)) val assets: Seq[(TokenId, Long)] = if (issueAsset) { - Seq((Digest32Coll @@@ inputs.head.boxId.toColl) -> 1L) + Seq(inputs.head.boxId.toTokenId -> 1L) } else { Seq.empty } @@ -128,7 +129,7 @@ trait WalletTestOps extends NodeViewBaseOps { def isNewAsset(tokenId: TokenId, value: Long): Boolean = tokenId == newAssetIdStub val (newAsset, spentAssets) = assets.partition((isNewAsset _).tupled) - newAsset.map(Digest32Coll @@@ inputs.head.boxId.toColl -> _._2) ++ spentAssets + newAsset.map(inputs.head.boxId.toTokenId -> _._2) ++ spentAssets } def randomNewAsset: Seq[(TokenId, Long)] = Seq(newAssetIdStub -> randomLong()) diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala index dd857204f0..2053f11e0f 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoGenerators.scala @@ -3,29 +3,29 @@ package org.ergoplatform.utils.generators import com.google.common.primitives.Shorts import org.bouncycastle.util.BigIntegers import org.ergoplatform.mining.difficulty.DifficultySerializer -import org.ergoplatform.mining.{genPk, q, AutolykosSolution} +import org.ergoplatform.mining.{AutolykosSolution, genPk, q} import org.ergoplatform.modifiers.history.ADProofs import org.ergoplatform.modifiers.history.extension.Extension import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.modifiers.history.popow.{PoPowParams, NipopowProof} +import org.ergoplatform.modifiers.history.popow.{NipopowProof, PoPowParams} import org.ergoplatform.network.ModePeerFeature -import org.ergoplatform.nodeView.history.{ErgoSyncInfoV1, ErgoSyncInfo, ErgoSyncInfoV2} +import org.ergoplatform.nodeView.history.{ErgoSyncInfo, ErgoSyncInfoV1, ErgoSyncInfoV2} import org.ergoplatform.nodeView.mempool.ErgoMemPool import org.ergoplatform.nodeView.state.StateType -import org.ergoplatform.settings.{ErgoValidationSettingsUpdate, ValidationRules, Constants, ErgoValidationSettings} +import org.ergoplatform.settings.{Constants, ErgoValidationSettings, ErgoValidationSettingsUpdate, ValidationRules} import org.ergoplatform.utils.ErgoTestConstants -import org.ergoplatform.validation.{ReplacedRule, ChangedRule, DisabledRule, EnabledRule} +import org.ergoplatform.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule} import org.ergoplatform.wallet.utils.Generators import org.scalacheck.Arbitrary.arbByte -import org.scalacheck.{Gen, Arbitrary} +import org.scalacheck.{Arbitrary, Gen} import org.scalatest.matchers.should.Matchers -import scorex.crypto.authds.{SerializedAdProof, ADDigest} +import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.Digest32 import scorex.testkit.generators.CoreGenerators import sigmastate.Values.ErgoTree import sigmastate.basics.CryptoConstants.EcPointType -import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} -import sigmastate.basics.{DiffieHellmanTupleProverInput, ProveDHTuple, CryptoConstants} +import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.basics.{CryptoConstants, DiffieHellmanTupleProverInput, ProveDHTuple} import sigmastate.interpreter.ProverResult import scala.util.Random diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 4250fcb02d..3e9c514b94 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -3,22 +3,21 @@ package org.ergoplatform.utils.generators import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history.BlockTransactions -import org.ergoplatform.modifiers.mempool.{UnsignedErgoTransaction, ErgoTransaction} +import org.ergoplatform.modifiers.history.header.Header +import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnsignedErgoTransaction} import org.ergoplatform.nodeView.history.ErgoHistory import org.ergoplatform.nodeView.state.wrapped.WrappedUtxoState import org.ergoplatform.nodeView.state.{BoxHolder, ErgoStateContext, VotingData} import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionSigningRequest} import org.ergoplatform.nodeView.wallet.{AugWalletTransaction, WalletTransaction} +import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.settings.Parameters._ -import org.ergoplatform.settings.{Parameters, Constants} -import org.ergoplatform.utils.{RandomLike, RandomWrapper, BoxUtils} +import org.ergoplatform.settings.{Constants, Parameters} +import org.ergoplatform.utils.{BoxUtils, RandomLike, RandomWrapper} import org.ergoplatform.wallet.Constants.{MaxAssetsPerBox, ScanId} -import org.ergoplatform.UnsignedInput -import org.ergoplatform.modifiers.history.header.Header -import org.ergoplatform.sdk.wallet.secrets.{DlogSecretKey, DhtSecretKey} import org.ergoplatform.wallet.interpreter.TransactionHintsBag import org.ergoplatform.wallet.utils.Generators -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoAddress, DataInput, ErgoBoxCandidate, P2PKAddress, Input} +import org.ergoplatform._ import org.scalacheck.Gen import scorex.crypto.hash.Blake2b256 import scorex.db.ByteArrayWrapper @@ -195,7 +194,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { } val newBoxes = outputAmounts.zip(tokenAmounts.toIndexedSeq).map { case (amt, tokens) => - val normalizedTokens = tokens.toSeq.map(t => (Digest32Coll @@ t._1.data.toColl) -> t._2) + val normalizedTokens = tokens.toSeq.map(t => t._1.data.toTokenId -> t._2) testBox(amt, outputsProposition, 0, normalizedTokens) } val inputs = boxesToSpend.map(b => Input(b.id, emptyProverResult)) @@ -223,7 +222,7 @@ trait ErgoTransactionGenerators extends ErgoGenerators with Generators { def disperseTokens(inputsCount: Int, tokensCount: Byte): Gen[IndexedSeq[Seq[(TokenId, Long)]]] = { val tokensDistribution = mutable.IndexedSeq.fill(inputsCount)(Seq[(TokenId, Long)]()) (1 to tokensCount).foreach { i => - val (id, amt) = Digest32Coll @@ Blake2b256(s"$i" + Random.nextString(5)).toColl -> (Random.nextInt(Int.MaxValue).toLong + 100) + val (id, amt) = Blake2b256(s"$i" + Random.nextString(5)).toTokenId -> (Random.nextInt(Int.MaxValue).toLong + 100) val idx = i % tokensDistribution.size val s = tokensDistribution(idx) tokensDistribution(idx) = s :+ (id -> amt) From b373459e0bf07bcc90af2347b794958187783ccd Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 24 May 2023 21:36:58 +0200 Subject: [PATCH 190/204] with-sigma-v5.0.8: update sigma --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 8a97353999..d79da2ce63 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.7-181-80a19714-20230524-1747-SNAPSHOT" +val sigmaStateVersion = "5.0.7-183-fc6d62c4-SNAPSHOT" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) From b2053c8cc767add613cd28b88ba507e103c6d050 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 24 May 2023 22:05:05 +0200 Subject: [PATCH 191/204] with-sigma-v5.0.8: removed duplicated constants --- .../org/ergoplatform/wallet/Constants.scala | 40 ------------------- .../wallet/boxes/DefaultBoxSelector.scala | 2 +- .../wallet/boxes/TrackedBox.scala | 5 ++- .../wallet/mnemonic/Mnemonic.scala | 5 +-- .../wallet/AddressGenerationDemo.java | 4 +- .../wallet/boxes/DefaultBoxSelectorSpec.scala | 3 +- .../wallet/secrets/DerivationPathSpec.scala | 10 ++--- .../wallet/utils/Generators.scala | 9 +++-- .../mining/AutolykosPowScheme.scala | 5 +-- .../mining/CandidateGenerator.scala | 2 +- .../modifiers/history/popow/PoPowHeader.scala | 2 +- .../nodeView/wallet/ErgoWalletSupport.scala | 3 +- .../ErgoTransactionGenerators.scala | 3 +- 13 files changed, 28 insertions(+), 65 deletions(-) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala index b266280e13..5e3aab6ff3 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/Constants.scala @@ -1,22 +1,11 @@ package org.ergoplatform.wallet -import org.ergoplatform.sdk.wallet.secrets.DerivationPath import supertagged.TaggedType object Constants { object ScanId extends TaggedType[Short] type ScanId = ScanId.Type - /** part of the protocol, do not change */ - val SecretKeyLength = 32 - - /** part of the protocol, do not change */ - val ModifierIdLength = 32 - - val Encoding = "UTF-8" - - val BitcoinSeed: Array[Byte] = "Bitcoin seed".getBytes(Encoding) - // Identifiers for system applications of Ergo node // Ids below 9 are reserved. Ids from 11 (inclusive) are for user scans @@ -26,33 +15,4 @@ object Constants { /** Scan which is checking mining rewards */ val MiningScanId: ScanId = ScanId @@ 9.toShort - - /** - * [See EIP-3 https://github.com/ergoplatform/eips/blob/master/eip-0003.md ] - * - * For coin type, we suggest consider "ergo" word in ASCII and calculate coin_type number as - * - * 101 + 114 + 103 + 111 = 429 - * - * Following this idea we should use next scheme - * - * m / 44' / 429' / account' / change / address_index - */ - val CoinType = 429 - - /** - * There needs to be reasonable amount of tokens in a box due to a byte size limit for ergo box - * */ - val MaxAssetsPerBox = 100 - - /** - * Pre - EIP3 derivation path - */ - val preEip3DerivationPath: DerivationPath = DerivationPath.fromEncoded("m/1").get - - /** - * Post - EIP3 derivation path - */ - val eip3DerivationPath: DerivationPath = DerivationPath.fromEncoded("m/44'/429'/0'/0/0").get - } diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala index 05a8e9c6d6..8de1ec7d49 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelector.scala @@ -1,7 +1,7 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} -import org.ergoplatform.wallet.Constants.MaxAssetsPerBox +import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionError import org.ergoplatform.wallet.transactions.TransactionBuilder._ import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder} diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala index 3b9e850a22..2cfec5ad29 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/boxes/TrackedBox.scala @@ -1,5 +1,6 @@ package org.ergoplatform.wallet.boxes +import org.ergoplatform.sdk.wallet import org.ergoplatform.sdk.wallet.TokensMap import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.ScanId @@ -122,10 +123,10 @@ object TrackedBoxSerializer extends ErgoWalletSerializer[TrackedBox] { } override def parse(r: Reader): TrackedBox = { - val creationTxId = bytesToId(r.getBytes(Constants.ModifierIdLength)) + val creationTxId = bytesToId(r.getBytes(wallet.Constants.ModifierIdLength)) val creationOutIndex = r.getShort() val inclusionHeightOpt = r.getOption(r.getInt()) - val spendingTxIdOpt = r.getOption(r.getBytes(Constants.ModifierIdLength)).map(bytesToId) + val spendingTxIdOpt = r.getOption(r.getBytes(wallet.Constants.ModifierIdLength)).map(bytesToId) val spendingHeightOpt = r.getOption(r.getInt()) val appsCount = r.getShort() diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/mnemonic/Mnemonic.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/mnemonic/Mnemonic.scala index 88db0570c4..46871b6bdb 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/mnemonic/Mnemonic.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/mnemonic/Mnemonic.scala @@ -2,12 +2,11 @@ package org.ergoplatform.wallet.mnemonic import java.text.Normalizer.Form.NFKD import java.text.Normalizer.normalize - import javax.crypto.SecretKeyFactory import javax.crypto.spec.PBEKeySpec -import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.interface4j.SecretString import scodec.bits.BitVector +import sigmastate.crypto.CryptoFacade import scala.util.{Failure, Try} @@ -73,7 +72,7 @@ object Mnemonic { val spec = new PBEKeySpec( normalizedMnemonic, - normalizedPass.getBytes(Constants.Encoding), + normalizedPass.getBytes(CryptoFacade.Encoding), Pbkdf2Iterations, Pbkdf2KeyLength ) diff --git a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java index 6e9c6d69ed..35944bbf3b 100644 --- a/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java +++ b/ergo-wallet/src/test/java/org/ergoplatform/wallet/AddressGenerationDemo.java @@ -9,6 +9,8 @@ import org.ergoplatform.wallet.mnemonic.Mnemonic; import scala.Option; +import static org.ergoplatform.sdk.wallet.Constants.eip3DerivationPath; + /** * This runnable example is showing how to derive change address and other addresses according to EIP-3 */ @@ -40,7 +42,7 @@ public static void main(String[] args) { ExtendedSecretKey rootSecret = masterSecretFromSeed(seed); // Let's use "m/44'/429'/0'/0/0" path for change (this path is from EIP-3 which is BIP-44 for Ergo) - DerivationPath changePath = Constants.eip3DerivationPath(); + DerivationPath changePath = eip3DerivationPath(); ExtendedSecretKey changeSecretKey = deriveSecretKey(rootSecret, changePath); ExtendedPublicKey changePubkey = changeSecretKey.publicKey(); P2PKAddress changeAddress = P2PKAddress.apply(changePubkey.key(), addressEncoder); diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala index 1b4df37938..311275b06f 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/boxes/DefaultBoxSelectorSpec.scala @@ -3,7 +3,8 @@ package org.ergoplatform.wallet.boxes import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.ErgoLikeTransaction import org.ergoplatform.SigmaConstants.MaxBoxSize -import org.ergoplatform.wallet.Constants.{MaxAssetsPerBox, PaymentsScanId} +import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox +import org.ergoplatform.wallet.Constants.PaymentsScanId import org.ergoplatform.wallet.boxes.DefaultBoxSelector.{NotEnoughErgsError, NotEnoughTokensError} import org.scalatest.EitherValues import org.scalatest.matchers.should.Matchers diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala index 72d708fe33..5b944630e7 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/secrets/DerivationPathSpec.scala @@ -1,7 +1,7 @@ package org.ergoplatform.wallet.secrets +import org.ergoplatform.sdk.wallet import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} -import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.mnemonic.Mnemonic import org.ergoplatform.wallet.utils.Generators @@ -95,7 +95,7 @@ class DerivationPathSpec property("equality of old derivation") { // Check that hardcoded path from old codebase corresponds to the new string form (Constants.usePreEip3Derivation) - DerivationPath(Array(0, 1), publicBranch = false) shouldBe Constants.preEip3DerivationPath + DerivationPath(Array(0, 1), publicBranch = false) shouldBe wallet.Constants.preEip3DerivationPath } property("master key derivation") { @@ -121,12 +121,12 @@ class DerivationPathSpec } property("isEip3 correctly distinguishing") { - Constants.eip3DerivationPath.isEip3 shouldBe true - Constants.eip3DerivationPath.toPublicBranch.isEip3 shouldBe true + wallet.Constants.eip3DerivationPath.isEip3 shouldBe true + wallet.Constants.eip3DerivationPath.toPublicBranch.isEip3 shouldBe true DerivationPath.fromEncoded("m/44'/429'/0'/0/1").get.isEip3 shouldBe true DerivationPath.fromEncoded("M/44'/429'/0'/0/1").get.isEip3 shouldBe true DerivationPath.fromEncoded("m/44'/429'/0'/1/1").get.isEip3 shouldBe true - Constants.preEip3DerivationPath.isEip3 shouldBe false + wallet.Constants.preEip3DerivationPath.isEip3 shouldBe false DerivationPath.fromEncoded("m/44'/429'/1'/0/1").get.isEip3 shouldBe false } diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala index 3253af80ce..fc8d6d3ab3 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/utils/Generators.scala @@ -14,6 +14,7 @@ import scorex.crypto.authds.ADKey import scorex.util._ import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, EvaluatedValue, FalseLeaf, TrueLeaf} import sigmastate.basics.DLogProtocol.ProveDlog +import sigmastate.crypto.CryptoFacade.SecretKeyLength import sigmastate.eval.Extensions._ import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ @@ -60,11 +61,11 @@ trait Generators { def genExactSizeBytes(size: Int): Gen[Array[Byte]] = genLimitedSizedBytes(size, size) val boxIdGen: Gen[BoxId] = { - val x = ADKey @@ genExactSizeBytes(Constants.ModifierIdLength) + val x = ADKey @@ genExactSizeBytes(sdk.wallet.Constants.ModifierIdLength) x } - val modIdGen: Gen[ModifierId] = genExactSizeBytes(Constants.ModifierIdLength).map(bytesToId) + val modIdGen: Gen[ModifierId] = genExactSizeBytes(sdk.wallet.Constants.ModifierIdLength).map(bytesToId) val assetGen: Gen[(TokenId, Long)] = for { id <- boxIdGen @@ -112,7 +113,7 @@ trait Generators { heightGen: Gen[Int] = heightGen): Gen[ErgoBox] = for { h <- heightGen prop <- propGen - transactionId: Array[Byte] <- genExactSizeBytes(Constants.ModifierIdLength) + transactionId: Array[Byte] <- genExactSizeBytes(sdk.wallet.Constants.ModifierIdLength) boxId: Short <- boxIndexGen ar <- additionalRegistersGen tokens <- tokensGen @@ -135,7 +136,7 @@ trait Generators { } yield DerivationPath(0 +: indices, isPublic) def extendedSecretGen: Gen[ExtendedSecretKey] = for { - seed <- Gen.const(Constants.SecretKeyLength).map(scorex.utils.Random.randomBytes) + seed <- Gen.const(SecretKeyLength).map(scorex.utils.Random.randomBytes) } yield ExtendedSecretKey.deriveMasterKey(seed, usePre1627KeyDerivation = false) def extendedPubKeyGen: Gen[ExtendedPublicKey] = extendedSecretGen.map(_.publicKey) diff --git a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala index fd9b853a9c..858bc51d7b 100644 --- a/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala +++ b/src/main/scala/org/ergoplatform/mining/AutolykosPowScheme.scala @@ -7,11 +7,11 @@ import org.ergoplatform.mining.difficulty.DifficultySerializer import org.ergoplatform.modifiers.ErgoFullBlock import org.ergoplatform.modifiers.history._ 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.mempool.TransactionMembershipProof -import org.ergoplatform.modifiers.history.header.Header.{Timestamp, Version} import scorex.crypto.authds.{ADDigest, SerializedAdProof} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.{ModifierId, ScorexLogging} @@ -19,7 +19,6 @@ import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.crypto.CryptoFacade import scala.annotation.tailrec -import scala.math.BigInt import scala.util.Try /** @@ -399,7 +398,7 @@ class AutolykosPowScheme(val k: Int, val n: Int) extends ScorexLogging { val proofs = if (mandatoryTxIds.nonEmpty) { // constructs fake block transactions section (BlockTransactions instance) to get proofs from it - val fakeHeaderId = scorex.util.bytesToId(Array.fill(org.ergoplatform.wallet.Constants.ModifierIdLength)(0: Byte)) + val fakeHeaderId = scorex.util.bytesToId(Array.fill(org.ergoplatform.sdk.wallet.Constants.ModifierIdLength)(0: Byte)) val bt = BlockTransactions(fakeHeaderId, blockCandidate.version, blockCandidate.transactions) val ps = mandatoryTxIds.flatMap { txId => bt.proofFor(txId).map(mp => TransactionMembershipProof(txId, mp)) } Some(ProofOfUpcomingTransactions(headerCandidate, ps)) diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 1f07ae7c55..91fc5c3ad4 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -20,7 +20,7 @@ import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader} import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader import org.ergoplatform.nodeView.state.{ErgoState, ErgoStateContext, StateType, UtxoStateReader} import org.ergoplatform.settings.{ErgoSettings, ErgoValidationSettingsUpdate, Parameters} -import org.ergoplatform.wallet.Constants.MaxAssetsPerBox +import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.wallet.interpreter.ErgoInterpreter import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, Input} import scorex.crypto.hash.Digest32 diff --git a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala index c45fcdbfda..95aad0a2c9 100644 --- a/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala +++ b/src/main/scala/org/ergoplatform/modifiers/history/popow/PoPowHeader.scala @@ -137,7 +137,7 @@ object PoPowHeader { * Binary serializer for PoPowHeader */ object PoPowHeaderSerializer extends ErgoSerializer[PoPowHeader] { - import org.ergoplatform.wallet.Constants.ModifierIdLength + import org.ergoplatform.sdk.wallet.Constants.ModifierIdLength implicit val hf: HF = Algos.hash val merkleProofSerializer = new BatchMerkleProofSerializer[Digest32, HF] diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index 2a4deeb4a8..b9214909a6 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -12,7 +12,6 @@ import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedPublicKey, E import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} import org.ergoplatform.settings.{ErgoSettings, Parameters} import org.ergoplatform.utils.BoxUtils -import org.ergoplatform.wallet.Constants import org.ergoplatform.wallet.Constants.PaymentsScanId import org.ergoplatform.wallet.boxes.BoxSelector.BoxSelectionResult import org.ergoplatform.wallet.boxes.{BoxSelector, TrackedBox} @@ -161,7 +160,7 @@ trait ErgoWalletSupport extends ScorexLogging { } } else { if (pubKeys.size == 1 && - pubKeys.head.path == Constants.eip3DerivationPath.toPublicBranch && + pubKeys.head.path == sdk.wallet.Constants.eip3DerivationPath.toPublicBranch && state.storage.readChangeAddress.isEmpty) { val changeAddress = P2PKAddress(pubKeys.head.key)(addressEncoder) log.info(s"Update change address to $changeAddress") diff --git a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala index 3e9c514b94..e743e5bef1 100644 --- a/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala +++ b/src/test/scala/org/ergoplatform/utils/generators/ErgoTransactionGenerators.scala @@ -14,10 +14,11 @@ import org.ergoplatform.sdk.wallet.secrets.{DhtSecretKey, DlogSecretKey} import org.ergoplatform.settings.Parameters._ import org.ergoplatform.settings.{Constants, Parameters} import org.ergoplatform.utils.{BoxUtils, RandomLike, RandomWrapper} -import org.ergoplatform.wallet.Constants.{MaxAssetsPerBox, ScanId} +import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.wallet.interpreter.TransactionHintsBag import org.ergoplatform.wallet.utils.Generators import org.ergoplatform._ +import org.ergoplatform.wallet.Constants.ScanId import org.scalacheck.Gen import scorex.crypto.hash.Blake2b256 import scorex.db.ByteArrayWrapper From ef7e807f8f6d5f7c71980a180e2a5333edce67a3 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 24 May 2023 23:58:25 +0200 Subject: [PATCH 192/204] with-sigma-v5.0.8: more systematic use of toTokenId --- .../interpreter/ErgoProvingInterpreter.scala | 2 +- .../http/api/TransactionsApiRoute.scala | 5 ++--- .../nodeView/history/extra/BalanceInfo.scala | 8 ++++---- .../nodeView/history/extra/IndexedToken.scala | 3 ++- .../nodeView/state/ErgoStateContext.scala | 4 ++-- .../nodeView/wallet/ErgoWalletActor.scala | 20 +++++++++---------- .../nodeView/wallet/ErgoWalletSupport.scala | 4 ++-- .../wallet/persistence/WalletDigest.scala | 5 ++--- .../wallet/requests/BoxesRequest.scala | 5 ++--- .../ScanningPredicateSerializer.scala | 7 +++---- .../http/routes/TransactionApiRouteSpec.scala | 2 +- .../mempool/ErgoTransactionSpec.scala | 9 ++++----- .../extra/ExtraIndexerSpecification.scala | 6 +++--- 13 files changed, 38 insertions(+), 42 deletions(-) diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala index 821ff5168a..4d1d62ed6f 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala @@ -196,7 +196,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], val inputBox = boxesToSpend(inpIndex) val context = new ErgoLikeContext( - ErgoInterpreter.avlTreeFromDigest(ADDigest @@ stateContext.previousStateDigest.toArray), + ErgoInterpreter.avlTreeFromDigest(stateContext.previousStateDigest), stateContext.sigmaLastHeaders, stateContext.sigmaPreHeader, dataBoxes, diff --git a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala index e536fe8ae9..1765be3911 100644 --- a/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/TransactionsApiRoute.scala @@ -20,8 +20,7 @@ import scorex.crypto.authds.ADKey import scorex.util.encode.Base16 import sigmastate.SType import sigmastate.Values.EvaluatedValue -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.Extensions.ArrayByteOps import scala.concurrent.Future import scala.util.{Failure, Success} @@ -51,7 +50,7 @@ case class TransactionsApiRoute(readersHolder: ActorRef, private def handleTokenId(value: String): Directive1[TokenId] = { Algos.decode(value) match { case Success(tokenId) => - provide(Digest32Coll @@ tokenId.toColl) + provide(tokenId.toTokenId) case _ => reject(ValidationRejection(s"tokenId $value is invalid, it should be 64 chars long hex string")) } 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 5acf7a547f..a6002c1b0c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/BalanceInfo.scala @@ -8,7 +8,7 @@ import scorex.core.serialization.ErgoSerializer import scorex.util.{ModifierId, ScorexLogging, bytesToId} import scorex.util.serialization.{Reader, Writer} import spire.implicits.cfor - +import special.collection.Extensions._ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -56,7 +56,7 @@ case class BalanceInfo() extends ScorexLogging { def add(box: ErgoBox): Unit = { nanoErgs += box.value cfor(0)(_ < box.additionalTokens.length, _ + 1) { i => - val id: ModifierId = bytesToId(box.additionalTokens(i)._1.toArray) + val id: ModifierId = box.additionalTokens(i)._1.toModifierId index(id) match { case Some(n) => tokens(n) = Tuple2(id, tokens(n)._2 + box.additionalTokens(i)._2) case None => tokens += Tuple2(id, box.additionalTokens(i)._2) @@ -72,11 +72,11 @@ case class BalanceInfo() extends ScorexLogging { def subtract(box: ErgoBox)(implicit ae: ErgoAddressEncoder): Unit = { nanoErgs = math.max(nanoErgs - box.value, 0) cfor(0)(_ < box.additionalTokens.length, _ + 1) { i => - val id: ModifierId = bytesToId(box.additionalTokens(i)._1.toArray) + val id: ModifierId = box.additionalTokens(i)._1.toModifierId index(id) match { case Some(n) => val newVal: Long = tokens(n)._2 - box.additionalTokens(i)._2 - if(newVal == 0) + if (newVal == 0) tokens.remove(n) else tokens(n) = (id, newVal) 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 a4efb95485..19c6d18d8e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/extra/IndexedToken.scala @@ -10,6 +10,7 @@ import scorex.util.{ByteArrayOps, ModifierId, bytesToId} import scorex.util.serialization.{Reader, Writer} import sigmastate.Values.CollectionConstant import sigmastate.SByte +import special.collection.Extensions._ /** * Index of a token containing creation information. @@ -123,7 +124,7 @@ object IndexedToken { case None => 0 } - IndexedToken(bytesToId(box.additionalTokens(tokenIndex)._1.toArray), + IndexedToken(box.additionalTokens(tokenIndex)._1.toModifierId, box.id.toModifierId, box.additionalTokens(tokenIndex)._2, name, diff --git a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index e4ee9457a2..49c5ac70d7 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -84,7 +84,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], // todo remove from ErgoLikeContext and from ErgoStateContext // State root hash before the last block - override def previousStateDigest = if (sigmaLastHeaders.toArray.nonEmpty) { + override def previousStateDigest: ADDigest = if (sigmaLastHeaders.toArray.nonEmpty) { ADDigest @@ sigmaLastHeaders.toArray.head.stateRoot.digest.toArray } else { genesisStateDigest @@ -275,7 +275,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], }.flatten override def toString: String = - s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest.toArray)}, $lastHeaders, $currentParameters)" + s"ErgoStateContext($currentHeight, ${encoder.encode(previousStateDigest)}, $lastHeaders, $currentParameters)" /** diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala index 9a78abaabc..0d9732964d 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala @@ -1,34 +1,34 @@ package org.ergoplatform.nodeView.wallet -import akka.actor.SupervisorStrategy.{Stop, Restart} +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.{UnsignedErgoTransaction, ErgoTransaction} +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.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.{ScanRequest, Scan} +import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest} +import org.ergoplatform.sdk.wallet.secrets.DerivationPath import org.ergoplatform.settings._ -import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.Constants.ScanId import org.ergoplatform.wallet.boxes.{BoxSelector, ChainStatus} +import org.ergoplatform.wallet.interface4j.SecretString import org.ergoplatform.wallet.interpreter.TransactionHintsBag -import org.ergoplatform.{GlobalConstants, ErgoAddressEncoder, P2PKAddress, ErgoBox, ErgoApp} +import org.ergoplatform._ import scorex.core.VersionTag -import org.ergoplatform.network.ErgoNodeViewSynchronizer.ReceivableMessages.{ChangedState, ChangedMempool} -import org.ergoplatform.sdk.wallet.secrets.DerivationPath import scorex.core.utils.ScorexEncoding -import scorex.util.{ScorexLogging, ModifierId} +import scorex.util.{ModifierId, ScorexLogging} import sigmastate.Values.SigmaBoolean -import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} +import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog} import scala.concurrent.duration._ -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} class ErgoWalletActor(settings: ErgoSettings, diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala index b9214909a6..cdb1981c3e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletSupport.scala @@ -211,7 +211,7 @@ trait ErgoWalletSupport extends ScorexLogging { def minimalErgoAmount: Long = BoxUtils.minimalErgoAmountSimulated( lockWithAddress.script, - Colls.fromItems((Digest32Coll @@@ assetId.toColl) -> amount), + Colls.fromItems(assetId.toTokenId -> amount), nonMandatoryRegisters, parameters ) @@ -220,7 +220,7 @@ trait ErgoWalletSupport extends ScorexLogging { valueOpt.getOrElse(minimalErgoAmount), lockWithAddress.script, fullHeight, - Colls.fromItems((Digest32Coll @@@ assetId.toColl) -> amount), + Colls.fromItems(assetId.toTokenId -> amount), nonMandatoryRegisters ) } 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 0b8ee6a23b..9223a4b91e 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/persistence/WalletDigest.scala @@ -6,8 +6,7 @@ import org.ergoplatform.settings.Constants import scorex.core.serialization.ErgoSerializer import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.Extensions.ArrayByteOps import scala.collection.mutable @@ -50,7 +49,7 @@ object WalletDigestSerializer extends ErgoSerializer[WalletDigest] { val walletAssetBalances = mutable.LinkedHashMap.empty[EncodedTokenId, Long] (0 until walletAssetBalancesSize).foreach { _ => - val kv = encodedTokenId(Digest32Coll @@ r.getBytes(Constants.ModifierIdSize).toColl) -> r.getULong() + val kv = encodedTokenId(r.getBytes(Constants.ModifierIdSize).toTokenId) -> r.getULong() walletAssetBalances += kv } diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala index 469304acbe..7f4818f040 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/requests/BoxesRequest.scala @@ -4,8 +4,7 @@ import io.circe.generic.decoding.DerivedDecoder.deriveDecoder import io.circe.{Decoder, KeyDecoder} import org.ergoplatform.ErgoBox import scorex.util.encode.Base16 -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.Extensions.ArrayByteOps /** * A request for boxes with given balance and assets @@ -15,7 +14,7 @@ case class BoxesRequest(targetBalance: Long, targetAssets: Map[ErgoBox.TokenId, object BoxesRequest { implicit val keyDecoder: KeyDecoder[ErgoBox.TokenId] = - KeyDecoder.instance(s => Base16.decode(s).toOption.map(Digest32Coll @@ _.toColl)) + KeyDecoder.instance(s => Base16.decode(s).toOption.map(_.toTokenId)) implicit val decoder: Decoder[BoxesRequest] = cursor => 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 e30772ed0c..2c70437053 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicateSerializer.scala @@ -3,13 +3,12 @@ package org.ergoplatform.nodeView.wallet.scanning import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId import scorex.core.serialization.ErgoSerializer +import scorex.util.Extensions._ import scorex.util.serialization.{Reader, Writer} import sigmastate.SType import sigmastate.Values.EvaluatedValue +import sigmastate.eval.Extensions.ArrayByteOps import sigmastate.serialization.ValueSerializer -import scorex.util.Extensions._ -import sigmastate.eval.Digest32Coll -import sigmastate.eval.Extensions.ArrayOps object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { @@ -73,7 +72,7 @@ object ScanningPredicateSerializer extends ErgoSerializer[ScanningPredicate] { ContainsScanningPredicate(reg, bs) case b: Byte if b == ContainsAssetPrefix => val bs = r.getBytes(32) - ContainsAssetPredicate(Digest32Coll @@ bs.toColl) + ContainsAssetPredicate(bs.toTokenId) case b: Byte if b == AndPrefix => AndScanningPredicate(parseArgs(r) :_*) case b: Byte if b == OrPrefix => diff --git a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala index ea0dbad8a3..c13bd6cb0c 100644 --- a/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala +++ b/src/test/scala/org/ergoplatform/http/routes/TransactionApiRouteSpec.scala @@ -44,7 +44,7 @@ class TransactionApiRouteSpec extends AnyFlatSpec val dataInput = DataInput(input.boxId) val absentModifierId = "0000000000000000000000000000000000000000000000000000000000000000" - val tokens = List[(TokenId, Long)](Digest32Coll @@@ inputBox.id.toColl -> 10) + val tokens = List[(TokenId, Long)](inputBox.id.toTokenId -> 10) val registers = Map( ErgoBox.R4 -> ByteArrayConstant("name".getBytes("UTF-8")), diff --git a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala index d9a50fb587..ee110f90fc 100644 --- a/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala +++ b/src/test/scala/org/ergoplatform/modifiers/mempool/ErgoTransactionSpec.scala @@ -23,11 +23,10 @@ import sigmastate.AND import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, LongArrayConstant, SigmaPropConstant, TrueLeaf} import sigmastate.basics.CryptoConstants import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.eval.Extensions.ArrayOps import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.{ContextExtension, ProverResult} - +import sigmastate.eval.Extensions._ import scala.util.{Random, Try} class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { @@ -43,7 +42,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { (seq :+ ebc) -> true } else { if (ebc.additionalTokens.nonEmpty && ebc.additionalTokens.exists(t => !java.util.Arrays.equals(t._1.toArray, from.head.id))) { - (seq :+ modifyAsset(ebc, deltaFn, Digest32Coll @@@ from.head.id.toColl)) -> true + (seq :+ modifyAsset(ebc, deltaFn, from.head.id.toTokenId)) -> true } else { (seq :+ ebc) -> false } @@ -253,7 +252,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { // already existing token from one of the inputs val existingToken = from.flatMap(_.additionalTokens.toArray).toSet.head // completely new token - val randomToken = (Digest32Coll @@ scorex.util.Random.randomBytes().toColl, Random.nextInt(100000000).toLong) + val randomToken = (scorex.util.Random.randomBytes().toTokenId, Random.nextInt(100000000).toLong) val in0 = from.last // new token added to the last input @@ -298,7 +297,7 @@ class ErgoTransactionSpec extends ErgoPropertyTest with ErgoTestConstants { property("spam simulation (transaction validation cost with too many tokens exceeds block limit)") { val bxsQty = 392 // with greater value test is failing with collection size exception val (inputs, tx) = validErgoTransactionGenTemplate(1, 1,16).sample.get // it takes too long to test with `forAll` - val tokens = (0 until 255).map(_ => (Digest32Coll @@ scorex.util.Random.randomBytes().toColl, Random.nextLong)) + val tokens = (0 until 255).map(_ => (scorex.util.Random.randomBytes().toTokenId, Random.nextLong)) val (in, out) = { val in0 = inputs.head val out0 = tx.outputs.head 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 974ad52115..80f28360e3 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/extra/ExtraIndexerSpecification.scala @@ -2,6 +2,7 @@ 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 @@ -15,11 +16,10 @@ import org.ergoplatform.nodeView.mempool.ErgoMemPool.SortingOption import org.ergoplatform.nodeView.state._ import org.ergoplatform.settings.{ErgoSettings, NetworkType, NodeConfigurationSettings} import org.ergoplatform.utils.{ErgoPropertyTest, ErgoTestHelpers, HistoryTestHelpers} -import org.ergoplatform._ import scorex.util.{ModifierId, bytesToId} import sigmastate.Values import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.eval.Extensions._ import sigmastate.eval._ import special.collection.Coll import spire.implicits.cfor @@ -298,7 +298,7 @@ object ChainGenerator extends ErgoTestHelpers { val tokens: ArrayBuffer[(TokenId, Long)] = ArrayBuffer.empty[(TokenId, Long)] inOpt match { case Some(input) if cond => - tokens += Tuple2(Digest32Coll @@@ input.id.toColl, math.abs(Random.nextInt())) + tokens += Tuple2(input.id.toTokenId, math.abs(Random.nextInt())) case Some(tokenBox) if !cond => tokenBox.additionalTokens.toArray.foreach(tokens += _) case _ => From 33b86bdf37bfb1e7cfb6581fd4a1a5ea6637e935 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 30 May 2023 11:32:25 +0200 Subject: [PATCH 193/204] with-sigma-v5.0.8: update sigma dep --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index d79da2ce63..17cfbdde6d 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.7-183-fc6d62c4-SNAPSHOT" +val sigmaStateVersion = "5.0.7-186-21c924e4-SNAPSHOT" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) From 901b1fd3c44c84f406f3c9e1304d0a9f73293a8f Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 May 2023 20:14:43 +0300 Subject: [PATCH 194/204] increased delivery timeout for utxo set chunks, logging bootstrapping time --- .../network/ErgoNodeViewSynchronizer.scala | 17 ++++++----------- .../UtxoSetSnapshotDownloadPlan.scala | 5 ++++- .../UtxoSetSnapshotProcessor.scala | 4 ++++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 6ddb56123e..5937d47ca9 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -172,7 +172,9 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, */ private val availableManifests = mutable.Map[ModifierId, (Height, Seq[ConnectedPeer])]() - // + /** + * How many peers should have a utxo set snapshot to start downloading it + */ private lazy val MinSnapshots = settings.nodeSettings.utxoSettings.p2pUtxoSnapshots /** @@ -565,15 +567,6 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, * Private helper methods to request UTXO set snapshots metadata and related data (manifests, chunks) from peers */ - /* - // todo: register? - - val snapshotsInfoId = ModifierId @@ Algos.encode(Algos.hash("snapshots info")) - deliveryTracker.setRequested(ManifestTypeId.value, ModifierId @@ Algos.encode(snapshotsInfoId), peer) { deliveryCheck => - context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) - } - */ - private def requestSnapshotsInfo(): Unit = { // ask all the peers supporting UTXO set snapshots for snapshots they have val msg = Message(GetSnapshotsInfoSpec, Right(()), None) @@ -595,8 +588,10 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, } private def requestUtxoSetChunk(subtreeId: SubtreeId, peer: ConnectedPeer): Unit = { + // as we download multiple chunks in parallel and they can be quite large, timeout increased + val chunkDeliveryTimeout = 4 * deliveryTimeout deliveryTracker.setRequested(UtxoSnapshotChunkTypeId.value, ModifierId @@ Algos.encode(subtreeId), peer) { deliveryCheck => - context.system.scheduler.scheduleOnce(deliveryTimeout, self, deliveryCheck) + context.system.scheduler.scheduleOnce(chunkDeliveryTimeout, self, deliveryCheck) } val msg = Message(GetUtxoSnapshotChunkSpec, Right(subtreeId), None) networkControllerRef ! SendToNetwork(msg, SendToPeer(peer)) diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala index 9a4e26b865..1310762d4c 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala @@ -10,6 +10,7 @@ import scorex.crypto.hash.Digest32 /** * Entity which stores information about state of UTXO set snapshots downloading * + * @param createdTime - time when snapshot was created * @param latestUpdateTime - latest time when anything was updated for the state of UTXO set snapshot * @param snapshotHeight - height of a block UTXO set snapshot is corresponding to (UTXO set if after the block applied) * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snasphot @@ -20,7 +21,8 @@ import scorex.crypto.hash.Digest32 * @param downloadingChunks - number of UTXO set shapshot chunks the node is currently downloading * @param peersToDownload - peers UTXO set snapshot chunks can be downloaded from */ -case class UtxoSetSnapshotDownloadPlan(latestUpdateTime: Long, +case class UtxoSetSnapshotDownloadPlan(createdTime: Long, + latestUpdateTime: Long, snapshotHeight: Height, utxoSetRootHash: Digest32, utxoSetTreeHeight: Byte, @@ -61,6 +63,7 @@ object UtxoSetSnapshotDownloadPlan { // it is safe to call .toByte below, as the whole tree has height <= 127, and manifest even less UtxoSetSnapshotDownloadPlan( + createdTime = now, latestUpdateTime = now, snapshotHeight = blockHeight, utxoSetRootHash = manifest.id, 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 68ac944c2b..498aad07b2 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 @@ -60,6 +60,10 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { * after. */ def utxoSnapshotApplied(height: Height): Unit = { + val utxoPhaseTime = { + _cachedDownloadPlan.map(_.latestUpdateTime).getOrElse(0L) - _cachedDownloadPlan.map(_.createdTime).getOrElse(0L) + } + log.info(s"UTXO set downloading and application time: $utxoPhaseTime") // remove downloaded utxo set snapshots chunks val ts0 = System.currentTimeMillis() _cachedDownloadPlan.foreach { plan => From 8d9fca5e14181c6784c4c31089042c91ce84a6c7 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 May 2023 22:12:28 +0300 Subject: [PATCH 195/204] chainisstuck processing fix --- .../ergoplatform/network/ErgoNodeViewSynchronizer.scala | 8 ++++++-- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 5937d47ca9..5d91ed0a17 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -1408,8 +1408,12 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, logger.debug("Chain is good") case ChainIsStuck(error) => - log.warn(s"Chain is stuck! $error\nDelivery tracker State:\n$deliveryTracker\nSync tracker state:\n$syncTracker") - deliveryTracker.reset() + if (historyReader.fullBlockHeight > 0) { + log.warn(s"Chain is stuck! $error\nDelivery tracker State:\n$deliveryTracker\nSync tracker state:\n$syncTracker") + deliveryTracker.reset() + } else { + log.debug("Got ChainIsStuck signal when no full-blocks applied yet") + } } /** handlers of messages coming from peers */ 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 498aad07b2..d86c9a517b 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 @@ -63,7 +63,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { val utxoPhaseTime = { _cachedDownloadPlan.map(_.latestUpdateTime).getOrElse(0L) - _cachedDownloadPlan.map(_.createdTime).getOrElse(0L) } - log.info(s"UTXO set downloading and application time: $utxoPhaseTime") + log.info(s"UTXO set downloading and application time: ${utxoPhaseTime / 1000.0} s.") // remove downloaded utxo set snapshots chunks val ts0 = System.currentTimeMillis() _cachedDownloadPlan.foreach { plan => From 560098137aeaad5179e781aa077e54933c2cfdab Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 31 May 2023 22:24:27 +0300 Subject: [PATCH 196/204] new mainnet epoch length --- papers/utxo.md | 4 ++-- src/main/scala/org/ergoplatform/settings/Constants.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/papers/utxo.md b/papers/utxo.md index dc8cbbc6cc..e4b0e0f982 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -23,9 +23,9 @@ UTXO set is authenticated via AVL+ tree. Design principles for tree construction [https://eprint.iacr.org/2016/994.pdf](https://eprint.iacr.org/2016/994.pdf), the implementation of the tree is available in [the Scrypto framework](https://github.com/input-output-hk/scrypto). -Time is broken into epochs, 1 epoch = 51,200 blocks (~72 days). +Time is broken into epochs, 1 epoch = 52,224 blocks (~72.5 days). Snapshot is taken after last block of an epoch, namely, after processing a block with -height *h % 51200 == 51199*. +height *h % 51200 == 52,224*. Chunk format ------------ diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 4fd2c06fd2..9eddb19961 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -82,7 +82,7 @@ object Constants { * So for the Ergo mainnet the value should be divisible by 1024 (for testnet, 128, any number divisible by * 1024 is divisible by 128 also. */ - // todo: change before deployment to 51200 + // todo: change before deployment to 52224 val MakeSnapshotEvery = 1024 /** From 4bc93516f11e93d19a21284abbc821065eb2758c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 1 Jun 2023 13:43:21 +0300 Subject: [PATCH 197/204] min full block height persistence --- .../network/ErgoNodeViewSynchronizer.scala | 1 + .../nodeView/history/ErgoHistoryReader.scala | 2 +- .../FullBlockPruningProcessor.scala | 23 +++++++------- .../modifierprocessors/HeadersProcessor.scala | 31 ++++++++++++++++--- .../MinimalFullBlockHeightFunctions.scala | 15 +++++++++ .../UtxoSetSnapshotProcessor.scala | 12 ++----- .../BlockSectionValidationSpecification.scala | 4 +-- ...txoSetSnapshotProcessorSpecification.scala | 6 +++- .../VerifyADHistorySpecification.scala | 2 +- 9 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 5d91ed0a17..1c4420849c 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -974,6 +974,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, hr.utxoSetSnapshotDownloadPlan() match { case Some(downloadPlan) => if (downloadPlan.fullyDownloaded) { + log.info("All the UTXO set snapshot chunks downloaded") // if all the chunks of snapshot are downloaded, initialize UTXO set state with it if (!hr.isUtxoSnapshotApplied) { val h = downloadPlan.snapshotHeight diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala index 47b5dc6c43..43f1a9c013 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistoryReader.scala @@ -618,7 +618,7 @@ trait ErgoHistoryReader */ def estimatedTip(): Option[Height] = { Try { //error may happen if history not initialized - if(isHeadersChainSynced) { + if (isHeadersChainSynced) { Some(headersHeight) } else { None 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 3381b78ccd..d77b9e0def 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 @@ -8,7 +8,7 @@ import org.ergoplatform.settings.ErgoSettings * A class that keeps and calculates minimal height for full blocks starting from which we need to download these full * blocks from the network and keep them in our history. */ -trait FullBlockPruningProcessor { +trait FullBlockPruningProcessor extends MinimalFullBlockHeightFunctions { protected def settings: ErgoSettings @@ -18,7 +18,6 @@ trait FullBlockPruningProcessor { private def VotingEpochLength = chainSettings.voting.votingLength @volatile private[history] var isHeadersChainSyncedVar: Boolean = false - @volatile private[history] var minimalFullBlockHeightVar: Int = ErgoHistory.GenesisHeight private def extensionWithParametersHeight(height: Int): Int = { require(height >= VotingEpochLength) @@ -33,7 +32,7 @@ trait FullBlockPruningProcessor { /** Start height to download full blocks from */ - def minimalFullBlockHeight: Int = minimalFullBlockHeightVar + def minimalFullBlockHeight: Int = readMinimalFullBlockHeight() /** Check if headers chain is synchronized with the network and modifier is not too old */ @@ -47,15 +46,16 @@ trait FullBlockPruningProcessor { * @return minimal height to process best full block */ def updateBestFullBlock(header: Header): Int = { - minimalFullBlockHeightVar = if (!nodeConfig.isFullBlocksPruned) { - ErgoHistory.GenesisHeight // keep all blocks in history - } else if (!isHeadersChainSynced && !nodeConfig.stateType.requireProofs) { - // just synced with the headers chain - determine first full block to apply - //TODO start with the height of UTXO snapshot applied. For now we start from genesis until this is implemented - ErgoHistory.GenesisHeight + val minimalFullBlockHeight = if (nodeConfig.blocksToKeep < 0) { + if (nodeConfig.utxoSettings.utxoBootstrap) { + // 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 + } } else { // Start from config.blocksToKeep blocks back - val h = Math.max(minimalFullBlockHeight, header.height - nodeConfig.blocksToKeep + 1) + val h = Math.max(readMinimalFullBlockHeight(), header.height - nodeConfig.blocksToKeep + 1) // ... but not later than the beginning of a voting epoch if (h > VotingEpochLength) { Math.min(h, extensionWithParametersHeight(h)) @@ -64,7 +64,8 @@ trait FullBlockPruningProcessor { } } if (!isHeadersChainSynced) isHeadersChainSyncedVar = true - minimalFullBlockHeightVar + writeMinimalFullBlockHeight(minimalFullBlockHeight) + minimalFullBlockHeight } } 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 5216f06662..45af35094c 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 @@ -10,7 +10,7 @@ import org.ergoplatform.modifiers.history._ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.nodeView.history.ErgoHistory -import org.ergoplatform.nodeView.history.ErgoHistory.{Difficulty, GenesisHeight} +import org.ergoplatform.nodeView.history.ErgoHistory.{Difficulty, GenesisHeight, Height} import org.ergoplatform.nodeView.history.storage.HistoryStorage import org.ergoplatform.settings.Constants.HashLength import org.ergoplatform.settings.ValidationRules._ @@ -21,6 +21,7 @@ import scorex.core.utils.ScorexEncoding import scorex.core.validation.{InvalidModifier, ModifierValidator, ValidationResult, ValidationState} import scorex.db.ByteArrayWrapper import scorex.util._ +import scorex.util.encode.Base16 import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer @@ -31,6 +32,22 @@ import scala.util.{Failure, Success, Try} */ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with ScorexEncoding { + /** + * Key for database record storing ID of best block header + */ + protected val BestHeaderKey: ByteArrayWrapper = ByteArrayWrapper(Array.fill(HashLength)(Header.modifierTypeId)) + + /** + * Key for database record storing ID of best full block + */ + protected val BestFullBlockKey: ByteArrayWrapper = ByteArrayWrapper(Array.fill(HashLength)(-1)) + + protected val MinFullBlockHeightKey = { + // hash of "minfullheight" UTF-8 string + ByteArrayWrapper(Base16.decode("4987eb6a8fecbed88a6f733f456cdf4e334b944f4436be4cab50cacb442e15e6").get) + } + + protected val historyStorage: HistoryStorage protected val settings: ErgoSettings @@ -56,6 +73,14 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score protected[history] def validityKey(id: ModifierId): ByteArrayWrapper = ByteArrayWrapper(Algos.hash("validity".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) + override def writeMinimalFullBlockHeight(height: Height): Unit = { + historyStorage.insert(Array(MinFullBlockHeightKey -> Ints.toByteArray(height)), Array.empty[BlockSection]) + } + + override def readMinimalFullBlockHeight(): Height = { + historyStorage.getIndex(MinFullBlockHeightKey).map(Ints.fromByteArray).getOrElse(ErgoHistory.GenesisHeight) + } + def bestHeaderIdOpt: Option[ModifierId] = historyStorage.getIndex(BestHeaderKey).map(bytesToId) /** @@ -158,10 +183,6 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score */ protected def validate(header: Header): Try[Unit] = HeaderValidator.validate(header).toTry - protected val BestHeaderKey: ByteArrayWrapper = ByteArrayWrapper(Array.fill(HashLength)(Header.modifierTypeId)) - - protected val BestFullBlockKey: ByteArrayWrapper = ByteArrayWrapper(Array.fill(HashLength)(-1)) - /** * @param id - header id * @return score of header with such id if is in History 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 new file mode 100644 index 0000000000..53e5931991 --- /dev/null +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/MinimalFullBlockHeightFunctions.scala @@ -0,0 +1,15 @@ +package org.ergoplatform.nodeView.history.storage.modifierprocessors + +import org.ergoplatform.nodeView.history.ErgoHistory.Height + +trait MinimalFullBlockHeightFunctions { + // minimal height to applu full blocks from + // its value depends on node settings, + // if download with UTXO set snapshot is used, the value is being set to a first block after the snapshot, + // if blockToKeep > 0, the value is being set to a first block of blockchain suffix after headers downloaded + + def writeMinimalFullBlockHeight(height: Height): Unit + + def readMinimalFullBlockHeight(): Height + +} 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 d86c9a517b..d9504a24ce 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 @@ -24,7 +24,7 @@ import scorex.crypto.authds.avltree.batch.{BatchAVLProver, PersistentBatchAVLPro * * Stores UTXO set snapshots manifests and chunks for incomplete snapshots. */ -trait UtxoSetSnapshotProcessor extends ScorexLogging { +trait UtxoSetSnapshotProcessor extends MinimalFullBlockHeightFunctions with ScorexLogging { import org.ergoplatform.settings.ErgoAlgos.HF @@ -34,12 +34,6 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { // database to read history-related objects here and in descendants protected val historyStorage: HistoryStorage - // minimal height to applu full blocks from - // its value depends on node settings, - // if download with UTXO set snapshot is used, the value is being set to a first block after the snapshot, - // if blockToKeep > 0, the value is being set to a first block of blockchain suffix after headers downloaded - private[history] var minimalFullBlockHeightVar: Int - private val downloadedChunksPrefix = Blake2b256.hash("downloaded chunk").drop(4) private var _manifest: Option[BatchAVLProverManifest[Digest32]] = None @@ -52,7 +46,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { * After first full-block block application not needed anymore. */ def isUtxoSnapshotApplied: Boolean = { - minimalFullBlockHeightVar > ErgoHistory.GenesisHeight + readMinimalFullBlockHeight() > ErgoHistory.GenesisHeight } /** @@ -78,7 +72,7 @@ trait UtxoSetSnapshotProcessor extends ScorexLogging { log.info(s"Imported UTXO set snapshots chunks removed in ${ts - ts0} ms") // set height of first full block to be downloaded - minimalFullBlockHeightVar = height + 1 + writeMinimalFullBlockHeight(height + 1) } private def updateUtxoSetSnashotDownloadPlan(plan: UtxoSetSnapshotDownloadPlan): Unit = { diff --git a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala index 2abc601436..40feb58cef 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/BlockSectionValidationSpecification.scala @@ -72,10 +72,10 @@ class BlockSectionValidationSpecification extends HistoryTestHelpers { // should not be able to apply when blocks at this height are already pruned history.applicableTry(section) shouldBe 'success - history.minimalFullBlockHeightVar = history.bestHeaderOpt.get.height + 1 + history.writeMinimalFullBlockHeight(history.bestHeaderOpt.get.height + 1) history.isHeadersChainSyncedVar = true history.applicableTry(section) shouldBe 'failure - history.minimalFullBlockHeightVar = ErgoHistory.GenesisHeight + history.writeMinimalFullBlockHeight(ErgoHistory.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/UtxoSetSnapshotProcessorSpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala index dc128d7084..3903251054 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/UtxoSetSnapshotProcessorSpecification.scala @@ -19,10 +19,14 @@ class UtxoSetSnapshotProcessorSpecification extends HistoryTestHelpers { val epochLength = 20 val utxoSetSnapshotProcessor = new UtxoSetSnapshotProcessor { + var minimalFullBlockHeightVar = ErgoHistory.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) - override private[history] var minimalFullBlockHeightVar = ErgoHistory.GenesisHeight + override def readMinimalFullBlockHeight() = minimalFullBlockHeightVar + override def writeMinimalFullBlockHeight(height: Int): Unit = { + minimalFullBlockHeightVar = height + } } var history = generateHistory( diff --git a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala index 72a8431768..0240d00afb 100644 --- a/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala +++ b/src/test/scala/org/ergoplatform/nodeView/history/VerifyADHistorySpecification.scala @@ -22,7 +22,7 @@ class VerifyADHistorySpecification extends HistoryTestHelpers with NoShrink { minFullHeight: Option[Int] = Some(ErgoHistory.GenesisHeight)): (ErgoHistory, Seq[ErgoFullBlock]) = { val inHistory = generateHistory(verifyTransactions = true, StateType.Digest, PoPoWBootstrap = false, BlocksToKeep) minFullHeight.foreach { h => - inHistory.minimalFullBlockHeightVar = h + inHistory.writeMinimalFullBlockHeight(h) inHistory.isHeadersChainSyncedVar = true } From 07d544b8ef282e0ef9ce5b8d9dd3fc4e877af4c2 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Thu, 1 Jun 2023 15:32:02 +0300 Subject: [PATCH 198/204] scaladoc --- .../serialization/ManifestSerializer.scala | 4 ++++ src/main/resources/application.conf | 1 + .../network/ErgoNodeViewSynchronizer.scala | 2 +- .../modifierprocessors/HeadersProcessor.scala | 3 +++ .../MinimalFullBlockHeightFunctions.scala | 21 +++++++++++++------ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index 9dc05e0cea..857841dd50 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -11,6 +11,9 @@ import scorex.util.serialization.{Reader, Writer} class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLProverManifest[DigestType]] { private val nodeSerializer = VersionedLDBAVLStorage.noStoreSerializer + /** + * Serialize manifest provided as top subtree and height separately. Used in tests. + */ def serialize(rootNode: ProverNodes[DigestType], rootNodeHeight: Byte, w: Writer): Unit = { w.put(rootNodeHeight) w.put(manifestDepth) @@ -29,6 +32,7 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro loop(rootNode, level = 1) } + override def serialize(manifest: BatchAVLProverManifest[DigestType], w: Writer): Unit = { serialize(manifest.root, manifest.rootHeight.toByte, w) } diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 1766eeb6f9..a1a1b8e7a3 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -23,6 +23,7 @@ ergo { utxo { # Download and apply UTXO set snapshot and full-blocks after that + # Done in 5.0.12, but better to bootstrap since version 5.0.13 utxoBootstrap = false # how many utxo set snapshots to store, 0 means that they are not stored at all diff --git a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala index 1c4420849c..ede79994ba 100644 --- a/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala +++ b/src/main/scala/org/ergoplatform/network/ErgoNodeViewSynchronizer.scala @@ -575,7 +575,7 @@ class ErgoNodeViewSynchronizer(networkControllerRef: ActorRef, if (peersCount >= MinSnapshots) { networkControllerRef ! SendToNetwork(msg, SendToPeers(peers)) } else { - log.info(s"Less UTXO-snapshot supporting peers found than required mininum ($peersCount < $MinSnapshots)") + log.warn(s"Less UTXO-snapshot supporting peers found than required mininum ($peersCount < $MinSnapshots)") } } 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 45af35094c..91a9113471 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 @@ -42,6 +42,9 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score */ protected val BestFullBlockKey: ByteArrayWrapper = ByteArrayWrapper(Array.fill(HashLength)(-1)) + /** + * Key for database record storing height of first full block stored + */ protected val MinFullBlockHeightKey = { // hash of "minfullheight" UTF-8 string ByteArrayWrapper(Base16.decode("4987eb6a8fecbed88a6f733f456cdf4e334b944f4436be4cab50cacb442e15e6").get) 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 53e5931991..fc53aca3c0 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 @@ -2,14 +2,23 @@ package org.ergoplatform.nodeView.history.storage.modifierprocessors import org.ergoplatform.nodeView.history.ErgoHistory.Height +/** + * Interface to methods providing updating and reading height of first full block stored in local database + */ trait MinimalFullBlockHeightFunctions { - // minimal height to applu full blocks from - // its value depends on node settings, - // if download with UTXO set snapshot is used, the value is being set to a first block after the snapshot, - // if blockToKeep > 0, the value is being set to a first block of blockchain suffix after headers downloaded - - def writeMinimalFullBlockHeight(height: Height): Unit + /** + * @return minimal height to applu full blocks from. Its value depends on node settings, + * if bootstrapping with UTXO set snapshot is used, the value is being set to a first block after the snapshot, + * for other modes, if blockToKeep > 0, the value is being set to a first block of blockchain suffix + * after headers downloaded, and the value is updated when new blocks added to the chain. If blockToKeep == 0, + * min full block height is set to genesis block height (1). + */ def readMinimalFullBlockHeight(): Height + /** + * Update min full block height, see `readMinimalFullBlockHeight` scaladoc on how it should be done + */ + def writeMinimalFullBlockHeight(height: Height): Unit + } From c73336305ef9c1404453d0684e213275c9846032 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 2 Jun 2023 16:38:34 +0200 Subject: [PATCH 199/204] v5.0.12-ops: some optimizations and improvements --- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 2 +- avldb/src/main/scala/scorex/db/LDBKVStore.scala | 1 + .../nodeView/history/storage/HistoryStorage.scala | 5 ++++- .../storage/modifierprocessors/HeadersProcessor.scala | 5 ++++- .../org/ergoplatform/nodeView/state/SnapshotsInfo.scala | 4 ++-- .../nodeView/wallet/scanning/ScanningPredicate.scala | 7 ------- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 2895772ba7..7bf291a8ed 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -244,7 +244,7 @@ object VersionedLDBAVLStorage { * splits 33-byte digest into 32-bytes hash and 1-byte tree height */ def splitDigest(digest: ADDigest): (Array[Byte], Byte) = { - digest.dropRight(1) -> digest.takeRight(1).head + digest.dropRight(1) -> digest.last } } diff --git a/avldb/src/main/scala/scorex/db/LDBKVStore.scala b/avldb/src/main/scala/scorex/db/LDBKVStore.scala index 9c95193e6d..035e43510a 100644 --- a/avldb/src/main/scala/scorex/db/LDBKVStore.scala +++ b/avldb/src/main/scala/scorex/db/LDBKVStore.scala @@ -13,6 +13,7 @@ import spire.syntax.all.cfor * Both keys and values are var-sized byte arrays. */ class LDBKVStore(protected val db: DB) extends KVStoreReader with ScorexLogging { + /** Immutable empty array can be shared to avoid allocations. */ private val emptyArrayOfByteArray = Array.empty[Array[Byte]] /** 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 afcd274dd3..3385322366 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/HistoryStorage.scala @@ -116,7 +116,10 @@ class HistoryStorage private(indexStore: LDBKVStore, objectsStore: LDBKVStore, e /** * @return object with `id` if it is in the objects database */ - def get(id: ModifierId): Option[Array[Byte]] = objectsStore.get(idToBytes(id)).orElse(extraStore.get(idToBytes(id))) + def get(id: ModifierId): Option[Array[Byte]] = { + val idBytes = idToBytes(id) + objectsStore.get(idBytes).orElse(extraStore.get(idBytes)) + } def get(id: Array[Byte]): Option[Array[Byte]] = objectsStore.get(id).orElse(extraStore.get(id)) /** 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 91a9113471..cf386c14bb 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 @@ -24,6 +24,7 @@ import scorex.util._ import scorex.util.encode.Base16 import scala.annotation.tailrec +import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable.ArrayBuffer import scala.util.{Failure, Success, Try} @@ -77,7 +78,9 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score ByteArrayWrapper(Algos.hash("validity".getBytes(ErgoHistory.CharsetName) ++ idToBytes(id))) override def writeMinimalFullBlockHeight(height: Height): Unit = { - historyStorage.insert(Array(MinFullBlockHeightKey -> Ints.toByteArray(height)), Array.empty[BlockSection]) + historyStorage.insert( + indexesToInsert = Array(MinFullBlockHeightKey -> Ints.toByteArray(height)), + objectsToInsert = ArraySeq.empty[BlockSection].unsafeArray) } override def readMinimalFullBlockHeight(): Height = { diff --git a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala index f93645eaa6..575e5bdf7a 100644 --- a/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala +++ b/src/main/scala/org/ergoplatform/nodeView/state/SnapshotsInfo.scala @@ -39,7 +39,7 @@ object SnapshotsInfoSerializer extends ErgoSerializer[SnapshotsInfo] { override def serialize(snapshotsInfo: SnapshotsInfo, w: Writer): Unit = { w.putUInt(snapshotsInfo.availableManifests.size) - snapshotsInfo.availableManifests.foreach{case (height, manifestId) => + snapshotsInfo.availableManifests.foreach { case (height, manifestId) => w.putUInt(height) w.putBytes(manifestId) } @@ -47,7 +47,7 @@ object SnapshotsInfoSerializer extends ErgoSerializer[SnapshotsInfo] { override def parse(r: Reader): SnapshotsInfo = { val manifestsCount = r.getUInt().toInt // we read from trusted source, no need for extra checks - val manifests = (1 to manifestsCount).map{_ => + val manifests = (1 to manifestsCount).map { _ => val h = r.getUInt().toInt val manifestId = Digest32 @@ r.getBytes(Constants.HashLength) h -> manifestId diff --git a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala index 13449663f4..402475a8aa 100644 --- a/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala +++ b/src/main/scala/org/ergoplatform/nodeView/wallet/scanning/ScanningPredicate.scala @@ -123,13 +123,6 @@ case class ContainsAssetPredicate(assetId: ErgoBox.TokenId) extends ScanningPred box.additionalTokens.exists(_._1 == assetId) } - override def equals(obj: Any): Boolean = obj match { - case other: ContainsAssetPredicate => other.assetId == assetId - case _ => false - } - - override def hashCode(): Int = assetId.hashCode() - override def toString: String = s"ContainsAssetPredicate(${assetId.toHex})" } From 9f8375211f1e561711a2d93c952a1074cba97d7e Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 2 Jun 2023 17:09:24 +0200 Subject: [PATCH 200/204] v5.0.12-ops: simplification --- .../org/ergoplatform/settings/ReemissionSettings.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala index 7925cc9449..1acc476597 100644 --- a/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala +++ b/src/main/scala/org/ergoplatform/settings/ReemissionSettings.scala @@ -5,9 +5,8 @@ import org.ergoplatform.reemission.ReemissionRules import org.ergoplatform.wallet.boxes.ErgoBoxSerializer import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.eval.Extensions.ArrayOps +import sigmastate.utils.Extensions.ModifierIdOps import special.collection.Coll - /** * Configuration section for re-emission (EIP27) parameters * @@ -21,9 +20,9 @@ case class ReemissionSettings(checkReemissionRules: Boolean, reemissionStartHeight: Int, injectionBoxBytesEncoded: String) { - val emissionNftIdBytes: Coll[Byte] = emissionNftId.toBytes.toColl - val reemissionNftIdBytes: Coll[Byte] = reemissionNftId.toBytes.toColl - val reemissionTokenIdBytes: Coll[Byte] = reemissionTokenId.toBytes.toColl + val emissionNftIdBytes: Coll[Byte] = emissionNftId.toColl + val reemissionNftIdBytes: Coll[Byte] = reemissionNftId.toColl + val reemissionTokenIdBytes: Coll[Byte] = reemissionTokenId.toColl lazy val InjectionBoxBytes: Array[Byte] = Base16.decode(injectionBoxBytesEncoded).get lazy val injectionBox: ErgoBox = ErgoBoxSerializer.parseBytes(InjectionBoxBytes) From 3136dc7b039eb672719746e68ea2aeeba6cd827d Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 2 Jun 2023 17:28:29 +0200 Subject: [PATCH 201/204] v5.0.12-ops: BlockSection.emptyArray --- .../org/ergoplatform/modifiers/BlockSection.scala | 2 ++ .../ergoplatform/nodeView/history/ErgoHistory.scala | 12 ++++++------ .../modifierprocessors/HeadersProcessor.scala | 3 +-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala b/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala index 3a65907c5e..fea7be63e3 100644 --- a/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala +++ b/src/main/scala/org/ergoplatform/modifiers/BlockSection.scala @@ -22,4 +22,6 @@ object BlockSection { case other => throw new Exception(s"Unknown block section type: $other") } + /** Immutable empty array can be shared to avoid allocations. */ + val emptyArray: Array[BlockSection] = Array.empty[BlockSection] } diff --git a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala index f214fbf743..1d1b47fe09 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/ErgoHistory.scala @@ -102,14 +102,14 @@ trait ErgoHistory if (nonMarkedIds.nonEmpty) { historyStorage.insert( nonMarkedIds.map(id => validityKey(id) -> Array(1.toByte)), - Array.empty[BlockSection]).map(_ => this) + BlockSection.emptyArray).map(_ => this) } else { Success(this) } case _ => historyStorage.insert( Array(validityKey(modifier.id) -> Array(1.toByte)), - Array.empty[BlockSection]).map(_ => this) + BlockSection.emptyArray).map(_ => this) } } @@ -136,7 +136,7 @@ trait ErgoHistory (bestHeaderIsInvalidated, bestFullIsInvalidated) match { case (false, false) => // Modifiers from best header and best full chain are not involved, no rollback and links change required - historyStorage.insert(validityRow, Array.empty[BlockSection]).map { _ => + historyStorage.insert(validityRow, BlockSection.emptyArray).map { _ => this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } case _ => @@ -147,7 +147,7 @@ trait ErgoHistory //Only headers chain involved historyStorage.insert( newBestHeaderOpt.map(h => BestHeaderKey -> idToBytes(h.id)).toArray, - Array.empty[BlockSection] + BlockSection.emptyArray ).map { _ => this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } @@ -175,7 +175,7 @@ trait ErgoHistory val changedLinks = validHeadersChain.lastOption.map(b => BestFullBlockKey -> idToBytes(b.id)) ++ newBestHeaderOpt.map(h => BestHeaderKey -> idToBytes(h.id)).toSeq val toInsert = validityRow ++ changedLinks ++ chainStatusRow - historyStorage.insert(toInsert, Array.empty[BlockSection]).map { _ => + historyStorage.insert(toInsert, BlockSection.emptyArray).map { _ => val toRemove = if (genesisInvalidated) invalidatedChain else invalidatedChain.tail this -> ProgressInfo(Some(branchPointHeader.id), toRemove, validChain, Seq.empty) } @@ -184,7 +184,7 @@ trait ErgoHistory case None => //No headers become invalid. Just mark this modifier as invalid log.warn(s"Modifier ${modifier.encodedId} of type ${modifier.modifierTypeId} is missing corresponding header") - historyStorage.insert(Array(validityKey(modifier.id) -> Array(0.toByte)), Array.empty[BlockSection]).map { _ => + historyStorage.insert(Array(validityKey(modifier.id) -> Array(0.toByte)), BlockSection.emptyArray).map { _ => this -> ProgressInfo[BlockSection](None, Seq.empty, Seq.empty, Seq.empty) } } 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 cf386c14bb..97dfd8fb4c 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 @@ -24,7 +24,6 @@ import scorex.util._ import scorex.util.encode.Base16 import scala.annotation.tailrec -import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable.ArrayBuffer import scala.util.{Failure, Success, Try} @@ -80,7 +79,7 @@ trait HeadersProcessor extends ToDownloadProcessor with ScorexLogging with Score override def writeMinimalFullBlockHeight(height: Height): Unit = { historyStorage.insert( indexesToInsert = Array(MinFullBlockHeightKey -> Ints.toByteArray(height)), - objectsToInsert = ArraySeq.empty[BlockSection].unsafeArray) + objectsToInsert = BlockSection.emptyArray) } override def readMinimalFullBlockHeight(): Height = { From b9d615c83d6b2b7496a67b479ee6db27ace691f2 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 3 Jun 2023 17:19:28 +0200 Subject: [PATCH 202/204] v5.0.12-ops: update sigma to v5.0.8 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 17cfbdde6d..e880b0fa32 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ val circeVersion = "0.13.0" val akkaVersion = "2.6.10" val akkaHttpVersion = "10.2.4" -val sigmaStateVersion = "5.0.7-186-21c924e4-SNAPSHOT" +val sigmaStateVersion = "5.0.8" // for testing current sigmastate build (see sigmastate-ergo-it jenkins job) val effectiveSigmaStateVersion = Option(System.getenv().get("SIGMASTATE_VERSION")).getOrElse(sigmaStateVersion) From a77e58eba4d3b15c476804b2feaee43f1bde2b07 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Mon, 5 Jun 2023 13:12:59 +0300 Subject: [PATCH 203/204] minimizing branches in ManifestSerializer.serialize.loop --- .../scala/scorex/core/serialization/ManifestSerializer.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala index 857841dd50..70bced053d 100644 --- a/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala +++ b/avldb/src/main/scala/scorex/core/serialization/ManifestSerializer.scala @@ -21,12 +21,11 @@ class ManifestSerializer(manifestDepth: Byte) extends ErgoSerializer[BatchAVLPro def loop(node: ProverNodes[DigestType], level: Int): Unit = { nodeSerializer.serialize(node, w) node match { - case _: ProverLeaf[DigestType] => case n: ProxyInternalNode[DigestType] if n.isEmpty => case i: InternalProverNode[DigestType] if level < manifestDepth => loop(i.left, level + 1) loop(i.right, level + 1) - case _: InternalProverNode[DigestType] => + case _: InternalProverNode[DigestType] | _: ProverLeaf[DigestType] => } } From 418d45dfd7b1e737f35ce5fc0324fb238aedaa9c Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Wed, 7 Jun 2023 13:20:09 +0300 Subject: [PATCH 204/204] config polishing --- .../authds/avltree/batch/VersionedLDBAVLStorage.scala | 2 +- papers/utxo.md | 2 +- src/main/resources/application.conf | 3 --- src/main/resources/mainnet.conf | 4 ++-- src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala | 1 + .../org/ergoplatform/nodeView/ErgoNodeViewHolder.scala | 2 +- .../storage/modifierprocessors/ToDownloadProcessor.scala | 1 + .../modifierprocessors/UtxoSetSnapshotDownloadPlan.scala | 6 +++--- .../modifierprocessors/UtxoSetSnapshotProcessor.scala | 2 +- src/main/scala/org/ergoplatform/settings/Constants.scala | 3 +-- 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala index 7bf291a8ed..b7e5eb3512 100644 --- a/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala +++ b/avldb/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedLDBAVLStorage.scala @@ -231,7 +231,7 @@ object VersionedLDBAVLStorage { * Calculate tree digest, given root node label(hash) and root node height, by appending height to the hash */ def digest[D <: hash.Digest](rootNodeLabel: D, rootNodeHeight: Int): ADDigest = { - assert(rootNodeHeight >= 0 && rootNodeHeight <= 127) + assert(rootNodeHeight >= 0 && rootNodeHeight < 256) // rootNodeHeight should never be more than 255, so the toByte conversion is safe (though it may cause an incorrect // sign on the signed byte if rootHeight>127, but we handle that case correctly on decoding the byte back to int in the // verifier, by adding 256 if it's negative). diff --git a/papers/utxo.md b/papers/utxo.md index e4b0e0f982..d61085d057 100644 --- a/papers/utxo.md +++ b/papers/utxo.md @@ -25,7 +25,7 @@ tree is available in [the Scrypto framework](https://github.com/input-output-hk/ Time is broken into epochs, 1 epoch = 52,224 blocks (~72.5 days). Snapshot is taken after last block of an epoch, namely, after processing a block with -height *h % 51200 == 52,224*. +height *h % 52224 == 52,223*. Chunk format ------------ diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index a1a1b8e7a3..deaaa32dab 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -41,9 +41,6 @@ ergo { # Minimal suffix size for PoPoW proof (may be pre-defined constant or settings parameter) minimalSuffix = 10 - # how many utxo set snapshots to store, 0 means that they are not stored at all - storingUtxoSnapshots = 0 - # Is the node is doing mining mining = false diff --git a/src/main/resources/mainnet.conf b/src/main/resources/mainnet.conf index 69d47b2ad9..42683d7314 100644 --- a/src/main/resources/mainnet.conf +++ b/src/main/resources/mainnet.conf @@ -63,8 +63,8 @@ ergo { # # To validate all the scripts for all the blocks, set checkpoint = null. checkpoint = { - height = 856800 - blockId = "13335f1ec71a0b5511f30f03b283ca8c3549803c9b84a3a92106245b6f6282fb" + height = 1020454 + blockId = "7829c6513c5b319b86e87253ed29d51fed61597c65e32f5197434461ccc4c905" } # List with hex-encoded identifiers of transactions banned from getting into memory pool diff --git a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala index 531245a73f..129d0bf809 100644 --- a/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/UtxoApiRoute.scala @@ -94,4 +94,5 @@ case class UtxoApiRoute(readersHolder: ActorRef, override val settings: RESTApiS case _ => None }) } + } diff --git a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala index 3bc7924c1a..7978c3ed31 100644 --- a/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala +++ b/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala @@ -287,7 +287,7 @@ abstract class ErgoNodeViewHolder[State <: ErgoState[State]](settings: ErgoSetti history().createPersistentProver(store, history(), height, blockId) match { case Success(pp) => log.info(s"Restoring state from prover with digest ${pp.digest} reconstructed for height $height") - history().utxoSnapshotApplied(height) + history().onUtxoSnapshotApplied(height) val newState = new UtxoState(pp, version = VersionTag @@@ blockId, store, settings) updateNodeView(updatedState = Some(newState.asInstanceOf[State])) case Failure(t) => 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 b666f73130..de74ea9af2 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 @@ -87,6 +87,7 @@ trait ToDownloadProcessor val minHeight = Math.max(1, fb.header.height - 100) continuation(minHeight, Map.empty, maxHeight = Int.MaxValue) case None if (nodeSettings.utxoSettings.utxoBootstrap && !isUtxoSnapshotApplied) => + // if bootstrapping with UTXO set snapshot is chosen, and no snapshot applied yet, ask peers for snapshots if (utxoSetSnapshotDownloadPlan().isEmpty) { Map(SnapshotsInfoTypeId.value -> Seq.empty) } else { diff --git a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala index 1310762d4c..0de26e2545 100644 --- a/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala +++ b/src/main/scala/org/ergoplatform/nodeView/history/storage/modifierprocessors/UtxoSetSnapshotDownloadPlan.scala @@ -13,9 +13,9 @@ import scorex.crypto.hash.Digest32 * @param createdTime - time when snapshot was created * @param latestUpdateTime - latest time when anything was updated for the state of UTXO set snapshot * @param snapshotHeight - height of a block UTXO set snapshot is corresponding to (UTXO set if after the block applied) - * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snasphot - * @param utxoSetTreeHeight - tree height of AVL+ tree which is authenticating UTXO set snasphot - * @param expectedChunkIds - ids of UTXO set snasphot chunks to be downloaded + * @param utxoSetRootHash - root hash of AVL+ tree which is authenticating UTXO set snapshot + * @param utxoSetTreeHeight - tree height of AVL+ tree which is authenticating UTXO set snapshot + * @param expectedChunkIds - ids of UTXO set snapshot chunks to be downloaded * @param downloadedChunkIds - shapshot chunks already downloaded, in form of boolean map over * `expectedChunkIds` (true = downloaded) * @param downloadingChunks - number of UTXO set shapshot chunks the node is currently downloading 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 d9504a24ce..66f614da64 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 @@ -53,7 +53,7 @@ trait UtxoSetSnapshotProcessor extends MinimalFullBlockHeightFunctions with Scor * Writes that UTXO set snapshot applied at height `height`. Starts full blocks applications since the next block * after. */ - def utxoSnapshotApplied(height: Height): Unit = { + def onUtxoSnapshotApplied(height: Height): Unit = { val utxoPhaseTime = { _cachedDownloadPlan.map(_.latestUpdateTime).getOrElse(0L) - _cachedDownloadPlan.map(_.createdTime).getOrElse(0L) } diff --git a/src/main/scala/org/ergoplatform/settings/Constants.scala b/src/main/scala/org/ergoplatform/settings/Constants.scala index 9eddb19961..6a47a36088 100644 --- a/src/main/scala/org/ergoplatform/settings/Constants.scala +++ b/src/main/scala/org/ergoplatform/settings/Constants.scala @@ -82,8 +82,7 @@ object Constants { * So for the Ergo mainnet the value should be divisible by 1024 (for testnet, 128, any number divisible by * 1024 is divisible by 128 also. */ - // todo: change before deployment to 52224 - val MakeSnapshotEvery = 1024 + val MakeSnapshotEvery = 52224 /** * AVL+ tree node parameters. The tree is used to authenticate UTXO set.