From 748c0a4980ae67a184c92878f02d305f36cdffee Mon Sep 17 00:00:00 2001 From: Zihe Huang Date: Mon, 5 Jun 2017 13:44:00 -0500 Subject: [PATCH 1/2] making box value to be a generic type --- .../scala/scorex/core/NodeViewHolder.scala | 4 ++-- .../scala/scorex/core/app/Application.scala | 2 +- .../core/transaction/BoxTransaction.scala | 4 ++-- .../account/PublicKeyNoncedBox.scala | 4 ++-- .../scorex/core/transaction/box/Box.scala | 9 ++------- .../core/transaction/box/BoxUnlocker.scala | 2 +- .../scorex/core/transaction/proof/Proof.scala | 4 ++-- .../transaction/proof/Signature25519.scala | 4 ++-- .../core/transaction/state/MinimalState.scala | 10 +++++----- .../core/transaction/state/SecretHolder.scala | 4 ++-- .../core/transaction/state/StateChanges.scala | 20 +++++++++---------- .../state/authenticated/BoxMinimalState.scala | 12 +++++------ .../core/transaction/wallet/Wallet.scala | 18 ++++++++--------- .../mid/wallet/DefaultWallet25519.scala | 4 ++-- src/main/scala/scorex/temp/mining/Miner.scala | 4 ++-- .../scorex/temp/mining/MiningController.scala | 6 +++--- 16 files changed, 53 insertions(+), 58 deletions(-) diff --git a/src/main/scala/scorex/core/NodeViewHolder.scala b/src/main/scala/scorex/core/NodeViewHolder.scala index 6594b36ff..e01f57aed 100644 --- a/src/main/scala/scorex/core/NodeViewHolder.scala +++ b/src/main/scala/scorex/core/NodeViewHolder.scala @@ -30,14 +30,14 @@ import scala.util.{Failure, Success} * @tparam P * @tparam TX */ -trait NodeViewHolder[P <: Proposition, TX <: Transaction[P], PMOD <: PersistentNodeViewModifier[P, TX]] +trait NodeViewHolder[T, P <: Proposition, TX <: Transaction[P], PMOD <: PersistentNodeViewModifier[P, TX]] extends Actor with ScorexLogging { import NodeViewHolder._ type SI <: SyncInfo type HIS <: History[P, TX, PMOD, SI, HIS] - type MS <: MinimalState[P, _, TX, PMOD, MS] + type MS <: MinimalState[T, P, _, TX, PMOD, MS] type VL <: Vault[P, TX, PMOD, VL] type MP <: MemoryPool[TX, MP] diff --git a/src/main/scala/scorex/core/app/Application.scala b/src/main/scala/scorex/core/app/Application.scala index b38cd787c..0b260d77b 100644 --- a/src/main/scala/scorex/core/app/Application.scala +++ b/src/main/scala/scorex/core/app/Application.scala @@ -21,7 +21,7 @@ trait Application extends ScorexLogging { type P <: Proposition type TX <: Transaction[P] type PMOD <: PersistentNodeViewModifier[P, TX] - type NVHT <: NodeViewHolder[P, TX, PMOD] + type NVHT <: NodeViewHolder[_, P, TX, PMOD] val ApplicationNameLimit = 50 diff --git a/src/main/scala/scorex/core/transaction/BoxTransaction.scala b/src/main/scala/scorex/core/transaction/BoxTransaction.scala index e36b3b022..88773089f 100644 --- a/src/main/scala/scorex/core/transaction/BoxTransaction.scala +++ b/src/main/scala/scorex/core/transaction/BoxTransaction.scala @@ -2,10 +2,10 @@ package scorex.core.transaction import com.google.common.primitives.{Bytes, Longs} import scorex.core.transaction.box.proposition.Proposition -import scorex.core.transaction.box.{Box, BoxUnlocker} +import scorex.core.transaction.box.{BoxUnlocker, Box} -abstract class BoxTransaction[P <: Proposition, BX <: Box[P]] extends Transaction[P] { +abstract class BoxTransaction[P <: Proposition, T, BX <: Box[P, T]] extends Transaction[P] { val unlockers: Traversable[BoxUnlocker[P]] val newBoxes: Traversable[BX] diff --git a/src/main/scala/scorex/core/transaction/account/PublicKeyNoncedBox.scala b/src/main/scala/scorex/core/transaction/account/PublicKeyNoncedBox.scala index 707fc14aa..8bd4a80c4 100644 --- a/src/main/scala/scorex/core/transaction/account/PublicKeyNoncedBox.scala +++ b/src/main/scala/scorex/core/transaction/account/PublicKeyNoncedBox.scala @@ -5,7 +5,7 @@ import scorex.core.crypto.hash.FastCryptographicHash import scorex.core.transaction.box.Box import scorex.core.transaction.box.proposition.PublicKey25519Proposition -trait PublicKeyNoncedBox[PKP <: PublicKey25519Proposition] extends Box[PKP] { +trait PublicKeyNoncedBox[PKP <: PublicKey25519Proposition, T] extends Box[PKP, T] { val nonce: Long lazy val id: Array[Byte] = PublicKeyNoncedBox.idFromBox(proposition, nonce) @@ -13,7 +13,7 @@ trait PublicKeyNoncedBox[PKP <: PublicKey25519Proposition] extends Box[PKP] { lazy val publicKey = proposition override def equals(obj: Any): Boolean = obj match { - case acc: PublicKeyNoncedBox[PKP] => (acc.id sameElements this.id) && acc.value == this.value + case acc: PublicKeyNoncedBox[PKP, T] => (acc.id sameElements this.id) && acc.value == this.value case _ => false } diff --git a/src/main/scala/scorex/core/transaction/box/Box.scala b/src/main/scala/scorex/core/transaction/box/Box.scala index 0ea8231d9..83c8ded0f 100644 --- a/src/main/scala/scorex/core/transaction/box/Box.scala +++ b/src/main/scala/scorex/core/transaction/box/Box.scala @@ -6,14 +6,9 @@ import scorex.core.transaction.box.proposition.Proposition /** * Box is a state element locked by some proposition. */ -trait Box[P <: Proposition] extends BytesSerializable { - val value: Box.Amount +trait Box[P <: Proposition, T] extends BytesSerializable { + val value: T val proposition: P val id: Array[Byte] } - -object Box { - type Amount = Long -} - diff --git a/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala b/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala index f5cff800b..e4a31e93b 100644 --- a/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala +++ b/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala @@ -4,7 +4,7 @@ import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.proof.Proof import scorex.crypto.encode.Base58 -trait BoxUnlocker[P <: Proposition] { +trait BoxUnlocker[+P <: Proposition] { val closedBoxId: Array[Byte] val boxKey: Proof[P] diff --git a/src/main/scala/scorex/core/transaction/proof/Proof.scala b/src/main/scala/scorex/core/transaction/proof/Proof.scala index fdf4b669a..b50115b6a 100644 --- a/src/main/scala/scorex/core/transaction/proof/Proof.scala +++ b/src/main/scala/scorex/core/transaction/proof/Proof.scala @@ -11,8 +11,8 @@ import scorex.core.transaction.state.Secret * A proof is non-interactive and thus serializable */ -trait Proof[P <: Proposition] extends BytesSerializable { - def isValid(proposition: P, message: Array[Byte]): Boolean +trait Proof[+P <: Proposition] extends BytesSerializable { + def isValid(proposition: Proposition, message: Array[Byte]): Boolean } trait ProofOfKnowledge[S <: Secret, P <: ProofOfKnowledgeProposition[S]] extends Proof[P] diff --git a/src/main/scala/scorex/core/transaction/proof/Signature25519.scala b/src/main/scala/scorex/core/transaction/proof/Signature25519.scala index c913a5b5e..04f8e934e 100644 --- a/src/main/scala/scorex/core/transaction/proof/Signature25519.scala +++ b/src/main/scala/scorex/core/transaction/proof/Signature25519.scala @@ -1,7 +1,7 @@ package scorex.core.transaction.proof import scorex.core.serialization.Serializer -import scorex.core.transaction.box.proposition.PublicKey25519Proposition +import scorex.core.transaction.box.proposition.{Proposition, PublicKey25519Proposition} import scorex.core.transaction.state.PrivateKey25519 import scorex.crypto.encode.Base58 import scorex.crypto.signatures.Curve25519 @@ -15,7 +15,7 @@ case class Signature25519(signature: Array[Byte]) extends ProofOfKnowledge[Priva require(signature.isEmpty || signature.length == Curve25519.SignatureLength, s"${signature.length} != ${Curve25519.SignatureLength}") - override def isValid(proposition: PublicKey25519Proposition, message: Array[Byte]): Boolean = + override def isValid(proposition: Proposition, message: Array[Byte]): Boolean = Curve25519.verify(signature, message, proposition.bytes) override type M = Signature25519 diff --git a/src/main/scala/scorex/core/transaction/state/MinimalState.scala b/src/main/scala/scorex/core/transaction/state/MinimalState.scala index 60aa3b5ae..ca346c784 100644 --- a/src/main/scala/scorex/core/transaction/state/MinimalState.scala +++ b/src/main/scala/scorex/core/transaction/state/MinimalState.scala @@ -12,11 +12,11 @@ import scala.util.Try * Abstract functional interface of state which is a result of a sequential blocks applying */ -trait MinimalState[P <: Proposition, -BX <: Box[P], +trait MinimalState[T, P <: Proposition, +BX <: Box[P, T], TX <: Transaction[P], M <: PersistentNodeViewModifier[P, TX], -MS <: MinimalState[P, BX, TX, M, MS]] extends NodeViewComponent { +MS <: MinimalState[T, P, BX, TX, M, MS]] extends NodeViewComponent { self: MS => def version: VersionTag @@ -33,9 +33,9 @@ MS <: MinimalState[P, BX, TX, M, MS]] extends NodeViewComponent { def boxesOf(proposition: P): Seq[BX] - def changes(mod: M): Try[StateChanges[P, BX]] + def changes(mod: M): Try[StateChanges[T, P, BX]] - def applyChanges(changes: StateChanges[P, BX], newVersion: VersionTag): Try[MS] + def applyChanges(changes: StateChanges[T, P, BX], newVersion: VersionTag): Try[MS] def applyModifier(mod: M): Try[MS] = { validate(mod) flatMap {_ => diff --git a/src/main/scala/scorex/core/transaction/state/SecretHolder.scala b/src/main/scala/scorex/core/transaction/state/SecretHolder.scala index fc6675338..b2c9f9a19 100644 --- a/src/main/scala/scorex/core/transaction/state/SecretHolder.scala +++ b/src/main/scala/scorex/core/transaction/state/SecretHolder.scala @@ -27,7 +27,7 @@ trait SecretCompanion[S <: Secret] { type PR <: ProofOfKnowledge[S, _ <: ProofOfKnowledgeProposition[S]] - def owns(secret: S, box: Box[_]): Boolean + def owns(secret: S, box: Box[_, Any]): Boolean def sign(secret: S, message: Array[Byte]): PR @@ -63,7 +63,7 @@ object PrivateKey25519Companion extends SecretCompanion[PrivateKey25519] { override type PR = Signature25519 - override def owns(secret: PrivateKey25519, box: Box[_]): Boolean = { + override def owns(secret: PrivateKey25519, box: Box[_, Any]): Boolean = { box.proposition match { case p: PublicKey25519Proposition => p.pubKeyBytes sameElements secret.publicKeyBytes case _ => false diff --git a/src/main/scala/scorex/core/transaction/state/StateChanges.scala b/src/main/scala/scorex/core/transaction/state/StateChanges.scala index 92b7ed2d3..5724003ff 100644 --- a/src/main/scala/scorex/core/transaction/state/StateChanges.scala +++ b/src/main/scala/scorex/core/transaction/state/StateChanges.scala @@ -3,22 +3,22 @@ package scorex.core.transaction.state import scorex.core.transaction.box.Box import scorex.core.transaction.box.proposition.Proposition -abstract class StateChangeOperation[P <: Proposition, BX <: Box[P]] -case class Removal[P <: Proposition, BX <: Box[P]](boxId: Array[Byte]) extends StateChangeOperation[P, BX] -case class Insertion[P <: Proposition, BX <: Box[P]](box: BX) extends StateChangeOperation[P, BX] +abstract class StateChangeOperation[T, P <: Proposition, BX <: Box[P, T]] +case class Removal[T, P <: Proposition, BX <: Box[P, T]](boxId: Array[Byte]) extends StateChangeOperation[T, P, BX] +case class Insertion[T, P <: Proposition, BX <: Box[P, T]](box: BX) extends StateChangeOperation[T, P, BX] -case class StateChanges[P <: Proposition, BX <: Box[P]] (operations: Seq[StateChangeOperation[P, BX]]){ - lazy val toAppend: Seq[Insertion[P, BX]] = operations.filter {op => +case class StateChanges[T, P <: Proposition, BX <: Box[P, T]] (operations: Seq[StateChangeOperation[T, P, BX]]){ + lazy val toAppend: Seq[Insertion[T, P, BX]] = operations.filter {op => op match { - case _: Insertion[P, BX] => true + case _: Insertion[T, P, BX] => true case _ => false } - }.asInstanceOf[Seq[Insertion[P, BX]]] + }.asInstanceOf[Seq[Insertion[T, P, BX]]] - lazy val toRemove: Seq[Removal[P, BX]] = operations.filter {op => + lazy val toRemove: Seq[Removal[T, P, BX]] = operations.filter {op => op match { - case _: Removal[P, BX] => true + case _: Removal[T, P, BX] => true case _ => false } - }.asInstanceOf[Seq[Removal[P, BX]]] + }.asInstanceOf[Seq[Removal[T, P, BX]]] } diff --git a/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala b/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala index 553ea0702..766aaabf3 100644 --- a/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala +++ b/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala @@ -1,7 +1,7 @@ package scorex.core.transaction.state.authenticated import scorex.core.PersistentNodeViewModifier -import scorex.core.transaction.box.Box +import scorex.core.transaction.box.{Box} import scorex.core.transaction.BoxTransaction import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.state.MinimalState @@ -10,8 +10,8 @@ import scorex.utils._ import scala.util.{Failure, Success, Try} -trait BoxMinimalState[P <: Proposition, BX <: Box[P], BTX <: BoxTransaction[P, BX], M <: PersistentNodeViewModifier[P, BTX], BMS <: BoxMinimalState[P, BX, BTX, M, BMS]] - extends MinimalState[P, BX, BTX, M, BMS] { +trait BoxMinimalState[T, P <: Proposition, BX <: Box[P, T], BTX <: BoxTransaction[P, T, BX], M <: PersistentNodeViewModifier[P, BTX], BMS <: BoxMinimalState[T, P, BX, BTX, M, BMS]] + extends MinimalState[T, P, BX, BTX, M, BMS] { self: BMS => /** @@ -27,14 +27,14 @@ trait BoxMinimalState[P <: Proposition, BX <: Box[P], BTX <: BoxTransaction[P, B * @param tx - transaction to check against the state * @return */ - override def validate(tx: BTX): Try[Unit] = { + /*override def validate(tx: BTX): Try[Unit] = { val statefulValid = { val boxesSumTry = tx.unlockers.foldLeft[Try[Long]](Success(0L)) { case (partialRes, unlocker) => partialRes.flatMap { partialSum => closedBox(unlocker.closedBoxId) match { case Some(box) => unlocker.boxKey.isValid(box.proposition, tx.messageToSign) match { - case true => Success(partialSum + box.value) + case true => Success(partialSum + box.value.asInstanceOf[Long]) case false => Failure(new Exception("Incorrect unlocker")) } case None => Failure(new Exception(s"Box for unlocker $unlocker is not in the state")) @@ -50,7 +50,7 @@ trait BoxMinimalState[P <: Proposition, BX <: Box[P], BTX <: BoxTransaction[P, B } } statefulValid.flatMap(_ => semanticValidity(tx)) - } + }*/ def semanticValidity(tx: BTX): Try[Unit] } diff --git a/src/main/scala/scorex/core/transaction/wallet/Wallet.scala b/src/main/scala/scorex/core/transaction/wallet/Wallet.scala index 06e6f2187..2052da56d 100644 --- a/src/main/scala/scorex/core/transaction/wallet/Wallet.scala +++ b/src/main/scala/scorex/core/transaction/wallet/Wallet.scala @@ -12,28 +12,28 @@ import scorex.crypto.encode.Base58 import scala.util.Try //TODO why do we need transactionId and createdAt -case class WalletBox[P <: Proposition, B <: Box[P]](box: B, transactionId: Array[Byte], createdAt: Long) +case class WalletBox[T, P <: Proposition, B <: Box[P, T]](box: B, transactionId: Array[Byte], createdAt: Long) (subclassDeser: Serializer[B]) extends BytesSerializable { - override type M = WalletBox[P, B] + override type M = WalletBox[T, P, B] - override def serializer: Serializer[WalletBox[P, B]] = new WalletBoxSerializer(subclassDeser) + override def serializer: Serializer[WalletBox[T, P, B]] = new WalletBoxSerializer(subclassDeser) override def toString: String = s"WalletBox($box, ${Base58.encode(transactionId)}, $createdAt)" } -class WalletBoxSerializer[P <: Proposition, B <: Box[P]](subclassDeser: Serializer[B]) extends Serializer[WalletBox[P, B]] { - override def toBytes(box: WalletBox[P, B]): Array[Byte] = { +class WalletBoxSerializer[T, P <: Proposition, B <: Box[P, T]](subclassDeser: Serializer[B]) extends Serializer[WalletBox[T, P, B]] { + override def toBytes(box: WalletBox[T, P, B]): Array[Byte] = { Bytes.concat(box.transactionId, Longs.toByteArray(box.createdAt), box.box.bytes) } - override def parseBytes(bytes: Array[Byte]): Try[WalletBox[P, B]] = Try { + override def parseBytes(bytes: Array[Byte]): Try[WalletBox[T, P, B]] = Try { val txId = bytes.slice(0, NodeViewModifier.ModifierIdSize) val createdAt = Longs.fromByteArray( bytes.slice(NodeViewModifier.ModifierIdSize, NodeViewModifier.ModifierIdSize + 8)) val boxB = bytes.slice(NodeViewModifier.ModifierIdSize + 8, bytes.length) val box: B = subclassDeser.parseBytes(boxB).get - WalletBox[P, B](box, txId, createdAt)(subclassDeser) + WalletBox[T, P, B](box, txId, createdAt)(subclassDeser) } } @@ -90,7 +90,7 @@ object WalletTransaction { * @tparam P * @tparam TX */ -trait Wallet[P <: Proposition, TX <: Transaction[P], PMOD <: PersistentNodeViewModifier[P, TX], W <: Wallet[P, TX, PMOD, W]] +trait Wallet[T, P <: Proposition, TX <: Transaction[P], PMOD <: PersistentNodeViewModifier[P, TX], W <: Wallet[T, P, TX, PMOD, W]] extends Vault[P, TX, PMOD, W] { self: W => @@ -102,7 +102,7 @@ trait Wallet[P <: Proposition, TX <: Transaction[P], PMOD <: PersistentNodeViewM def historyTransactions: Seq[WalletTransaction[P, TX]] - def boxes(): Seq[WalletBox[P, _ <: Box[P]]] + def boxes(): Seq[WalletBox[T, P, _ <: Box[P, T]]] def publicKeys: Set[PI] diff --git a/src/main/scala/scorex/mid/wallet/DefaultWallet25519.scala b/src/main/scala/scorex/mid/wallet/DefaultWallet25519.scala index 570b432fd..62c3f8708 100644 --- a/src/main/scala/scorex/mid/wallet/DefaultWallet25519.scala +++ b/src/main/scala/scorex/mid/wallet/DefaultWallet25519.scala @@ -23,7 +23,7 @@ import scala.util.Try case class DefaultWallet25519[TX <: Transaction[PublicKey25519Proposition], PMOD <: PersistentNodeViewModifier[PublicKey25519Proposition, TX]] (settings: Settings) - extends Wallet[PublicKey25519Proposition, TX, PMOD, DefaultWallet25519[TX, PMOD]] { + extends Wallet[Any, PublicKey25519Proposition, TX, PMOD, DefaultWallet25519[TX, PMOD]] { override type S = PrivateKey25519 override type PI = PublicKey25519Proposition @@ -51,7 +51,7 @@ PMOD <: PersistentNodeViewModifier[PublicKey25519Proposition, TX]] override def historyTransactions: Seq[WalletTransaction[PublicKey25519Proposition, TX]] = ??? - override def boxes(): Seq[WalletBox[PublicKey25519Proposition, _ <: PublicKeyNoncedBox[PublicKey25519Proposition]]] = ??? + override def boxes(): Seq[WalletBox[Any, PublicKey25519Proposition, _ <: PublicKeyNoncedBox[PublicKey25519Proposition, Any]]] = ??? override def publicKeys: Set[PublicKey25519Proposition] = dbSecrets.keySet.map(PublicKey25519Proposition.apply).toSet diff --git a/src/main/scala/scorex/temp/mining/Miner.scala b/src/main/scala/scorex/temp/mining/Miner.scala index 94642481e..203373c98 100644 --- a/src/main/scala/scorex/temp/mining/Miner.scala +++ b/src/main/scala/scorex/temp/mining/Miner.scala @@ -12,10 +12,10 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.util.{Failure, Try} -class Miner[P <: Proposition, TX <: Transaction[P]] +class Miner[T, P <: Proposition, TX <: Transaction[P]] (settings: Settings, historySynchronizer: ActorRef, - viewHolder: NodeViewHolder[P, TX, _]) + viewHolder: NodeViewHolder[T, P, TX, _]) extends Actor with ScorexLogging { // BlockGenerator is trying to generate a new block every $blockGenerationDelay. diff --git a/src/main/scala/scorex/temp/mining/MiningController.scala b/src/main/scala/scorex/temp/mining/MiningController.scala index 62fd8e20f..456388187 100644 --- a/src/main/scala/scorex/temp/mining/MiningController.scala +++ b/src/main/scala/scorex/temp/mining/MiningController.scala @@ -16,10 +16,10 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ import scala.util.Success -class MiningController[P <: Proposition, TX <: Transaction[P]] +class MiningController[T, P <: Proposition, TX <: Transaction[P]] (settings: Settings, historySynchronizer: ActorRef, - viewHolder: NodeViewHolder[P, TX, _] + viewHolder: NodeViewHolder[T, P, TX, _] ) extends Actor with ScorexLogging { @@ -87,7 +87,7 @@ class MiningController[P <: Proposition, TX <: Transaction[P]] } def newWorkers(count: Int): Seq[ActorRef] = (1 to count).map { i => - context.watch(context.actorOf(Props(classOf[Miner[P, TX]], + context.watch(context.actorOf(Props(classOf[Miner[T, P, TX]], settings, historySynchronizer, viewHolder From ee690bf976eb87eaf707b6dcd07f2665b4d0b4f7 Mon Sep 17 00:00:00 2001 From: Zihe Huang Date: Mon, 5 Jun 2017 15:35:40 -0500 Subject: [PATCH 2/2] made examples and tests compatible with generic box and its derivatives --- .../commons/SimpleBoxTransaction.scala | 2 +- .../curvepos/SimpleNodeViewHolder.scala | 2 +- .../transaction/PublicKey25519NoncedBox.scala | 2 +- .../curvepos/transaction/SimpleState.scala | 14 ++--- .../curvepos/transaction/SimpleWallet.scala | 4 +- .../hybrid/HybridNodeViewHolder.scala | 2 +- .../hybrid/state/HBoxStoredState.scala | 51 ++++++++++++++----- .../examples/hybrid/wallet/HWallet.scala | 8 +-- .../spv/simulation/SimulatorFuctions.scala | 4 +- .../examples/trimchain/TNodeViewHolder.scala | 2 +- .../InMemoryAuthenticatedUtxo.scala | 33 ++++++++++-- .../simulation/OneMinerSimulation.scala | 4 +- .../simulation/ValidationSimulator.scala | 4 +- .../utxo/PersistentAuthenticatedUtxo.scala | 45 ++++++++++++---- .../test/scala/hybrid/HybridGenerators.scala | 8 +-- .../src/test/scala/hybrid/HybridSanity.scala | 6 +-- .../serialization/SerializationTests.scala | 4 +- .../core/transaction/box/BoxUnlocker.scala | 2 +- .../scorex/core/transaction/proof/Proof.scala | 4 +- .../transaction/proof/Signature25519.scala | 2 +- .../core/transaction/state/SecretHolder.scala | 4 +- .../state/authenticated/BoxMinimalState.scala | 24 --------- .../testkit/BlockchainPerformance.scala | 6 +-- .../scorex/testkit/BlockchainSanity.scala | 12 ++--- .../properties/StateApplyChangesTest.scala | 8 +-- .../StateChangesGenerationTest.scala | 12 ++--- .../properties/StateRollbackTest.scala | 8 +-- .../testkit/properties/StateTests.scala | 6 +-- .../properties/WalletSecretsTest.scala | 2 +- 29 files changed, 169 insertions(+), 116 deletions(-) diff --git a/examples/src/main/scala/examples/commons/SimpleBoxTransaction.scala b/examples/src/main/scala/examples/commons/SimpleBoxTransaction.scala index cefb867dd..0812b73be 100644 --- a/examples/src/main/scala/examples/commons/SimpleBoxTransaction.scala +++ b/examples/src/main/scala/examples/commons/SimpleBoxTransaction.scala @@ -29,7 +29,7 @@ case class SimpleBoxTransaction(from: IndexedSeq[(PublicKey25519Proposition, Non signatures: IndexedSeq[Signature25519], override val fee: Long, override val timestamp: Long) extends - BoxTransaction[PublicKey25519Proposition, PublicKey25519NoncedBox] { + BoxTransaction[PublicKey25519Proposition, Long, PublicKey25519NoncedBox] { override type M = SimpleBoxTransaction diff --git a/examples/src/main/scala/examples/curvepos/SimpleNodeViewHolder.scala b/examples/src/main/scala/examples/curvepos/SimpleNodeViewHolder.scala index 6201670c9..e11e4776f 100644 --- a/examples/src/main/scala/examples/curvepos/SimpleNodeViewHolder.scala +++ b/examples/src/main/scala/examples/curvepos/SimpleNodeViewHolder.scala @@ -12,7 +12,7 @@ import scorex.crypto.signatures.Curve25519 import scala.util.{Failure, Success} class SimpleNodeViewHolder(settings: Settings) - extends NodeViewHolder[PublicKey25519Proposition, SimpleTransaction, SimpleBlock] { + extends NodeViewHolder[Long, PublicKey25519Proposition, SimpleTransaction, SimpleBlock] { override val networkChunkSize: Int = settings.networkChunkSize diff --git a/examples/src/main/scala/examples/curvepos/transaction/PublicKey25519NoncedBox.scala b/examples/src/main/scala/examples/curvepos/transaction/PublicKey25519NoncedBox.scala index 3ba7ceec5..4d7dd9b5e 100644 --- a/examples/src/main/scala/examples/curvepos/transaction/PublicKey25519NoncedBox.scala +++ b/examples/src/main/scala/examples/curvepos/transaction/PublicKey25519NoncedBox.scala @@ -15,7 +15,7 @@ import scorex.crypto.signatures.Curve25519 case class PublicKey25519NoncedBox(override val proposition: PublicKey25519Proposition, override val nonce: Long, override val value: Long - ) extends PublicKeyNoncedBox[PublicKey25519Proposition] with JsonSerializable { + ) extends PublicKeyNoncedBox[PublicKey25519Proposition, Long] with JsonSerializable { override def json: Json = Map( "id" -> Base58.encode(id).asJson, diff --git a/examples/src/main/scala/examples/curvepos/transaction/SimpleState.scala b/examples/src/main/scala/examples/curvepos/transaction/SimpleState.scala index 436e3871b..846e52507 100644 --- a/examples/src/main/scala/examples/curvepos/transaction/SimpleState.scala +++ b/examples/src/main/scala/examples/curvepos/transaction/SimpleState.scala @@ -12,11 +12,11 @@ import scorex.crypto.encode.Base58 import scala.util.{Failure, Try} -case class TransactionChanges[P <: Proposition, BX <: Box[P]](toRemove: Set[BX], toAppend: Set[BX], minerReward: Long) +case class TransactionChanges[P <: Proposition, BX <: Box[P, Long]](toRemove: Set[BX], toAppend: Set[BX], minerReward: Long) case class SimpleState(override val version: VersionTag = EmptyVersion, storage: Map[ByteBuffer, PublicKey25519NoncedBox] = Map()) extends ScorexLogging - with MinimalState[PublicKey25519Proposition, PublicKey25519NoncedBox, SimpleTransaction, SimpleBlock, SimpleState] { + with MinimalState[Long, PublicKey25519Proposition, PublicKey25519NoncedBox, SimpleTransaction, SimpleBlock, SimpleState] { def isEmpty: Boolean = version sameElements EmptyVersion @@ -37,7 +37,7 @@ case class SimpleState(override val version: VersionTag = EmptyVersion, Try(this) } - override def applyChanges(change: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox], newVersion: VersionTag): Try[SimpleState] = Try { + override def applyChanges(change: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox], newVersion: VersionTag): Try[SimpleState] = Try { val rmap = change.toRemove.foldLeft(storage) { case (m, r) => m - ByteBuffer.wrap(r.boxId) } val amap = change.toAppend.foldLeft(rmap) { case (m, a) => @@ -83,13 +83,13 @@ case class SimpleState(override val version: VersionTag = EmptyVersion, } } - override def changes(block: SimpleBlock): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = Try { + override def changes(block: SimpleBlock): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = Try { val generatorReward = block.txs.map(_.fee).sum val gen = block.generator val txChanges = block.txs.map(tx => changes(tx)).map(_.get) val toRemove = txChanges.flatMap(_.toRemove).map(_.id).map(id => - Removal[PublicKey25519Proposition, PublicKey25519NoncedBox](id)) + Removal[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](id)) val toAppendFrom = txChanges.flatMap(_.toAppend) val (generator, withoutGenerator) = toAppendFrom.partition(_.proposition.address == gen.address) val generatorBox: PublicKey25519NoncedBox = (generator ++ boxesOf(gen)).headOption match { @@ -99,10 +99,10 @@ case class SimpleState(override val version: VersionTag = EmptyVersion, PublicKey25519NoncedBox(gen, 1, generatorReward) } val toAppend = (withoutGenerator ++ Seq(generatorBox)).map(b => - Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](b)) + Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](b)) assert(toAppend.forall(_.box.value >= 0)) - StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox](toRemove ++ toAppend) + StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](toRemove ++ toAppend) } } diff --git a/examples/src/main/scala/examples/curvepos/transaction/SimpleWallet.scala b/examples/src/main/scala/examples/curvepos/transaction/SimpleWallet.scala index de6d79f4c..af4b7ad85 100644 --- a/examples/src/main/scala/examples/curvepos/transaction/SimpleWallet.scala +++ b/examples/src/main/scala/examples/curvepos/transaction/SimpleWallet.scala @@ -15,7 +15,7 @@ case class SimpleWallet(seed: Array[Byte] = Random.randomBytes(PrivKeyLength), chainTransactions: Map[TransactionId, SimpleTransaction] = Map(), offchainTransactions: Map[TransactionId, SimpleTransaction] = Map(), currentBalance: Long = 0) - extends Wallet[PublicKey25519Proposition, SimpleTransaction, SimpleBlock, SimpleWallet] { + extends Wallet[Long, PublicKey25519Proposition, SimpleTransaction, SimpleBlock, SimpleWallet] { override type S = PrivateKey25519 override type PI = PublicKey25519Proposition @@ -43,7 +43,7 @@ case class SimpleWallet(seed: Array[Byte] = Random.randomBytes(PrivKeyLength), override def historyTransactions: Seq[WalletTransaction[PublicKey25519Proposition, SimpleTransaction]] = ??? - override def boxes(): Seq[WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox]] = Seq() + override def boxes(): Seq[WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = Seq() override def scanOffchain(tx: SimpleTransaction): SimpleWallet = tx match { case sp: SimplePayment => diff --git a/examples/src/main/scala/examples/hybrid/HybridNodeViewHolder.scala b/examples/src/main/scala/examples/hybrid/HybridNodeViewHolder.scala index f25611935..2f860248d 100644 --- a/examples/src/main/scala/examples/hybrid/HybridNodeViewHolder.scala +++ b/examples/src/main/scala/examples/hybrid/HybridNodeViewHolder.scala @@ -16,7 +16,7 @@ import scorex.core.{NodeViewHolder, NodeViewModifier} import scorex.crypto.encode.Base58 -class HybridNodeViewHolder(settings: MiningSettings) extends NodeViewHolder[PublicKey25519Proposition, +class HybridNodeViewHolder(settings: MiningSettings) extends NodeViewHolder[Long, PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock] { override val networkChunkSize: Int = settings.networkChunkSize diff --git a/examples/src/main/scala/examples/hybrid/state/HBoxStoredState.scala b/examples/src/main/scala/examples/hybrid/state/HBoxStoredState.scala index d5d7f13aa..ded117e8d 100644 --- a/examples/src/main/scala/examples/hybrid/state/HBoxStoredState.scala +++ b/examples/src/main/scala/examples/hybrid/state/HBoxStoredState.scala @@ -8,18 +8,20 @@ import examples.curvepos.transaction.{PublicKey25519NoncedBox, PublicKey25519Non import examples.hybrid.blocks.{HybridBlock, PosBlock, PowBlock} import io.iohk.iodb.{ByteArrayWrapper, LSMStore} import scorex.core.settings.Settings -import scorex.core.transaction.box.proposition.PublicKey25519Proposition +import scorex.core.transaction.BoxTransaction +import scorex.core.transaction.box.Box +import scorex.core.transaction.box.proposition.{Proposition, PublicKey25519Proposition} import scorex.core.transaction.state.MinimalState.VersionTag import scorex.core.transaction.state.{Insertion, Removal, StateChangeOperation, StateChanges} import scorex.core.transaction.state.authenticated.BoxMinimalState import scorex.core.utils.ScorexLogging import scorex.crypto.encode.Base58 -import scala.util.{Success, Try} +import scala.util.{Failure, Success, Try} case class HBoxStoredState(store: LSMStore, override val version: VersionTag) extends - BoxMinimalState[PublicKey25519Proposition, + BoxMinimalState[Long, PublicKey25519Proposition, PublicKey25519NoncedBox, SimpleBoxTransaction, HybridBlock, @@ -42,7 +44,7 @@ case class HBoxStoredState(store: LSMStore, override val version: VersionTag) ex //there's no easy way to know boxes associated with a proposition, without an additional index override def boxesOf(proposition: PublicKey25519Proposition): Seq[PublicKey25519NoncedBox] = ??? - override def changes(mod: HPMOD): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = + override def changes(mod: HPMOD): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = HBoxStoredState.changes(mod) //Validate transactions in block and generator box @@ -64,7 +66,32 @@ case class HBoxStoredState(store: LSMStore, override val version: VersionTag) ex } } - override def applyChanges(changes: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox], + override def validate(tx: SimpleBoxTransaction): Try[Unit] = { + val statefulValid = { + val boxesSumTry = tx.unlockers.foldLeft[Try[Long]](Success(0L)) { case (partialRes, unlocker) => + partialRes.flatMap { partialSum => + closedBox(unlocker.closedBoxId) match { + case Some(box) => + unlocker.boxKey.isValid(box.proposition, tx.messageToSign) match { + case true => Success(partialSum + box.value) + case false => Failure(new Exception("Incorrect unlocker")) + } + case None => Failure(new Exception(s"Box for unlocker $unlocker is not in the state")) + } + } + } + + boxesSumTry flatMap { openSum => + tx.newBoxes.map(_.value).sum == openSum - tx.fee match { + case true => Success[Unit](Unit) + case false => Failure(new Exception("Negative fee")) + } + } + } + statefulValid.flatMap(_ => semanticValidity(tx)) + } + + override def applyChanges(changes: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox], newVersion: VersionTag): Try[HBoxStoredState] = Try { val boxIdsToRemove = changes.toRemove.view.map(_.boxId).map(ByteArrayWrapper.apply) val boxesToAdd = changes.toAppend.view.map(_.box).map(b => ByteArrayWrapper(b.id) -> ByteArrayWrapper(b.bytes)) @@ -97,14 +124,14 @@ case class HBoxStoredState(store: LSMStore, override val version: VersionTag) ex object HBoxStoredState { def semanticValidity(tx: SimpleBoxTransaction): Try[Unit] = tx.semanticValidity - def changes(mod: HybridBlock): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = { + def changes(mod: HybridBlock): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = { mod match { case pb: PowBlock => val proposition: PublicKey25519Proposition = pb.generatorProposition val nonce: Long = SimpleBoxTransaction.nonceFromDigest(mod.id) val value: Long = 1 val minerBox = PublicKey25519NoncedBox(proposition, nonce, value) - Success(StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox](Seq(Insertion(minerBox)))) + Success(StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](Seq(Insertion(minerBox)))) case ps: PosBlock => Try { val initial = (Seq(): Seq[Array[Byte]], Seq(): Seq[PublicKey25519NoncedBox], 0L) @@ -118,11 +145,11 @@ object HBoxStoredState { val forgerNonce = Longs.fromByteArray(ps.id.take(8)) val forgerBox = PublicKey25519NoncedBox(ps.generatorBox.proposition, forgerNonce, reward) - val ops: Seq[StateChangeOperation[PublicKey25519Proposition, PublicKey25519NoncedBox]] = - toRemove.map(id => Removal[PublicKey25519Proposition, PublicKey25519NoncedBox](id)) ++ - toAdd.map(b => Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](b)) ++ - Seq(Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](forgerBox)) - StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox](ops) + val ops: Seq[StateChangeOperation[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = + toRemove.map(id => Removal[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](id)) ++ + toAdd.map(b => Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](b)) ++ + Seq(Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](forgerBox)) + StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](ops) } } } diff --git a/examples/src/main/scala/examples/hybrid/wallet/HWallet.scala b/examples/src/main/scala/examples/hybrid/wallet/HWallet.scala index b566005fe..69ba088c8 100644 --- a/examples/src/main/scala/examples/hybrid/wallet/HWallet.scala +++ b/examples/src/main/scala/examples/hybrid/wallet/HWallet.scala @@ -20,7 +20,7 @@ import scala.util.Try case class HWallet(seed: Array[Byte], store: LSMStore) - extends Wallet[PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock, HWallet] + extends Wallet[Long, PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock, HWallet] with ScorexLogging { override type S = PrivateKey25519 @@ -35,12 +35,12 @@ case class HWallet(seed: Array[Byte], store: LSMStore) } private lazy val walletBoxSerializer = - new WalletBoxSerializer[PublicKey25519Proposition, PublicKey25519NoncedBox](PublicKey25519NoncedBoxSerializer) + new WalletBoxSerializer[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](PublicKey25519NoncedBoxSerializer) //not implemented intentionally for now override def historyTransactions: Seq[WalletTransaction[PublicKey25519Proposition, SimpleBoxTransaction]] = ??? - override def boxes(): Seq[WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox]] = { + override def boxes(): Seq[WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = { boxIds .flatMap(id => store.get(ByteArrayWrapper(id))) .map(_.data) @@ -84,7 +84,7 @@ case class HWallet(seed: Array[Byte], store: LSMStore) .find(t => t.newBoxes.exists(tb => tb.id sameElements box.id)) val txId = boxTransaction.map(_.id).getOrElse(Array.fill(32)(0: Byte)) val ts = boxTransaction.map(_.timestamp).getOrElse(modifier.timestamp) - val wb = WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox](box, txId, ts)(PublicKey25519NoncedBoxSerializer) + val wb = WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](box, txId, ts)(PublicKey25519NoncedBoxSerializer) ByteArrayWrapper(box.id) -> ByteArrayWrapper(wb.bytes) } diff --git a/examples/src/main/scala/examples/spv/simulation/SimulatorFuctions.scala b/examples/src/main/scala/examples/spv/simulation/SimulatorFuctions.scala index 10428452b..8f15c21da 100644 --- a/examples/src/main/scala/examples/spv/simulation/SimulatorFuctions.scala +++ b/examples/src/main/scala/examples/spv/simulation/SimulatorFuctions.scala @@ -23,8 +23,8 @@ trait SimulatorFuctions { 10000000000L ) } - val genesisChanges: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox] = - StateChanges(genesisBoxes.map(box => Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](box))) + val genesisChanges: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox] = + StateChanges(genesisBoxes.map(box => Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](box))) InMemoryAuthenticatedUtxo(genesisBoxes.size, None, defaultId).applyChanges(genesisChanges, defaultId).get } diff --git a/examples/src/main/scala/examples/trimchain/TNodeViewHolder.scala b/examples/src/main/scala/examples/trimchain/TNodeViewHolder.scala index 6cf9b8e03..c94957651 100644 --- a/examples/src/main/scala/examples/trimchain/TNodeViewHolder.scala +++ b/examples/src/main/scala/examples/trimchain/TNodeViewHolder.scala @@ -16,7 +16,7 @@ import scorex.core.{NodeViewHolder, NodeViewModifier} import scorex.crypto.encode.Base58 -class TNodeViewHolder(settings: MiningSettings) extends NodeViewHolder[PublicKey25519Proposition, +class TNodeViewHolder(settings: MiningSettings) extends NodeViewHolder[Long, PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock] { override val networkChunkSize: Int = settings.networkChunkSize diff --git a/examples/src/main/scala/examples/trimchain/simulation/InMemoryAuthenticatedUtxo.scala b/examples/src/main/scala/examples/trimchain/simulation/InMemoryAuthenticatedUtxo.scala index f5d61d649..846346f84 100644 --- a/examples/src/main/scala/examples/trimchain/simulation/InMemoryAuthenticatedUtxo.scala +++ b/examples/src/main/scala/examples/trimchain/simulation/InMemoryAuthenticatedUtxo.scala @@ -11,7 +11,7 @@ import scorex.core.transaction.state.authenticated.BoxMinimalState import scorex.core.utils.ScorexLogging import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Remove} -import scala.util.Try +import scala.util.{Failure, Success, Try} import examples.trimchain.utxo.PersistentAuthenticatedUtxo.ProverType /** @@ -19,7 +19,7 @@ import examples.trimchain.utxo.PersistentAuthenticatedUtxo.ProverType */ case class InMemoryAuthenticatedUtxo(size: Int, proverOpt: Option[ProverType], override val version: VersionTag) extends - BoxMinimalState[PublicKey25519Proposition, + BoxMinimalState[Long, PublicKey25519Proposition, PublicKey25519NoncedBox, SimpleBoxTransaction, TModifier, @@ -53,7 +53,7 @@ case class InMemoryAuthenticatedUtxo(size: Int, proverOpt: Option[ProverType], o //there's no easy way to know boxes associated with a proposition, without an additional index override def boxesOf(proposition: PublicKey25519Proposition): Seq[PublicKey25519NoncedBox] = ??? - override def changes(mod: TModifier): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = + override def changes(mod: TModifier): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = PersistentAuthenticatedUtxo.changes(mod) //Validate transactions in block and generator box @@ -66,8 +66,33 @@ case class InMemoryAuthenticatedUtxo(size: Int, proverOpt: Option[ProverType], o super.validate(mod).get } + override def validate(tx: SimpleBoxTransaction): Try[Unit] = { + val statefulValid = { + val boxesSumTry = tx.unlockers.foldLeft[Try[Long]](Success(0L)) { case (partialRes, unlocker) => + partialRes.flatMap { partialSum => + closedBox(unlocker.closedBoxId) match { + case Some(box) => + unlocker.boxKey.isValid(box.proposition, tx.messageToSign) match { + case true => Success(partialSum + box.value) + case false => Failure(new Exception("Incorrect unlocker")) + } + case None => Failure(new Exception(s"Box for unlocker $unlocker is not in the state")) + } + } + } + + boxesSumTry flatMap { openSum => + tx.newBoxes.map(_.value).sum == openSum - tx.fee match { + case true => Success[Unit](Unit) + case false => Failure(new Exception("Negative fee")) + } + } + } + statefulValid.flatMap(_ => semanticValidity(tx)) + } + //todo: newVersion is not used - override def applyChanges(changes: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox], + override def applyChanges(changes: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox], newVersion: VersionTag): Try[InMemoryAuthenticatedUtxo] = Try { changes.operations foreach { op => diff --git a/examples/src/main/scala/examples/trimchain/simulation/OneMinerSimulation.scala b/examples/src/main/scala/examples/trimchain/simulation/OneMinerSimulation.scala index c865cc2fb..ac0502898 100644 --- a/examples/src/main/scala/examples/trimchain/simulation/OneMinerSimulation.scala +++ b/examples/src/main/scala/examples/trimchain/simulation/OneMinerSimulation.scala @@ -70,8 +70,8 @@ object OneMinerSimulation extends App with Simulators { ) } - val genesisChanges: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox] = - StateChanges(genesisBoxes.map(box => Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](box))) + val genesisChanges: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox] = + StateChanges(genesisBoxes.map(box => Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](box))) var currentUtxo = InMemoryAuthenticatedUtxo(genesisBoxes.size, None, defaultId).applyChanges(genesisChanges, defaultId).get diff --git a/examples/src/main/scala/examples/trimchain/simulation/ValidationSimulator.scala b/examples/src/main/scala/examples/trimchain/simulation/ValidationSimulator.scala index 21a625818..46fe38bbd 100644 --- a/examples/src/main/scala/examples/trimchain/simulation/ValidationSimulator.scala +++ b/examples/src/main/scala/examples/trimchain/simulation/ValidationSimulator.scala @@ -25,8 +25,8 @@ object ValidationSimulator extends App with Simulators { ) } - val genesisChanges: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox] = - StateChanges(genesisBoxes.map(box => Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](box))) + val genesisChanges: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox] = + StateChanges(genesisBoxes.map(box => Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](box))) val genesisUtxo = InMemoryAuthenticatedUtxo(genesisBoxes.size, None, defaultId).applyChanges(genesisChanges, defaultId).get val rootHash = genesisUtxo.rootHash diff --git a/examples/src/main/scala/examples/trimchain/utxo/PersistentAuthenticatedUtxo.scala b/examples/src/main/scala/examples/trimchain/utxo/PersistentAuthenticatedUtxo.scala index daeb95e3b..3b8979e3a 100644 --- a/examples/src/main/scala/examples/trimchain/utxo/PersistentAuthenticatedUtxo.scala +++ b/examples/src/main/scala/examples/trimchain/utxo/PersistentAuthenticatedUtxo.scala @@ -16,7 +16,7 @@ import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Lookup, Remov import scorex.crypto.encode.Base58 import scorex.crypto.hash.Blake2b256Unsafe -import scala.util.{Random, Success, Try} +import scala.util.{Failure, Random, Success, Try} import PersistentAuthenticatedUtxo.ProverType trait AuthenticatedUtxo { @@ -49,7 +49,7 @@ case class PersistentAuthenticatedUtxo(store: LSMStore, size: Int, proverOpt: Option[ProverType], //todo: externalize the type with the parameter override val version: VersionTag) extends - BoxMinimalState[PublicKey25519Proposition, + BoxMinimalState[Long, PublicKey25519Proposition, PublicKey25519NoncedBox, SimpleBoxTransaction, TModifier, @@ -87,7 +87,7 @@ case class PersistentAuthenticatedUtxo(store: LSMStore, //there's no easy way to know boxes associated with a proposition, without an additional index override def boxesOf(proposition: PublicKey25519Proposition): Seq[PublicKey25519NoncedBox] = ??? - override def changes(mod: TModifier): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = + override def changes(mod: TModifier): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = PersistentAuthenticatedUtxo.changes(mod) //Validate transactions in block and generator box @@ -101,8 +101,33 @@ case class PersistentAuthenticatedUtxo(store: LSMStore, super.validate(mod).get } + override def validate(tx: SimpleBoxTransaction): Try[Unit] = { + val statefulValid = { + val boxesSumTry = tx.unlockers.foldLeft[Try[Long]](Success(0L)) { case (partialRes, unlocker) => + partialRes.flatMap { partialSum => + closedBox(unlocker.closedBoxId) match { + case Some(box) => + unlocker.boxKey.isValid(box.proposition, tx.messageToSign) match { + case true => Success(partialSum + box.value) + case false => Failure(new Exception("Incorrect unlocker")) + } + case None => Failure(new Exception(s"Box for unlocker $unlocker is not in the state")) + } + } + } + + boxesSumTry flatMap { openSum => + tx.newBoxes.map(_.value).sum == openSum - tx.fee match { + case true => Success[Unit](Unit) + case false => Failure(new Exception("Negative fee")) + } + } + } + statefulValid.flatMap(_ => semanticValidity(tx)) + } + //todo: newVersion is not used - override def applyChanges(changes: StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox], + override def applyChanges(changes: StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox], newVersion: VersionTag): Try[PersistentAuthenticatedUtxo] = Try { val (boxIdsToRemove, boxesToAdd) = changes.operations @@ -170,9 +195,9 @@ object PersistentAuthenticatedUtxo { } //todo: fees - def changes(txs: Seq[SimpleBoxTransaction]): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = + def changes(txs: Seq[SimpleBoxTransaction]): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = Try { - type SC = Seq[StateChangeOperation[PublicKey25519Proposition, PublicKey25519NoncedBox]] + type SC = Seq[StateChangeOperation[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] val initial = (Seq(): SC, 0L) //no reward additional to tx fees @@ -180,15 +205,15 @@ object PersistentAuthenticatedUtxo { val (ops, reward) = txs.foldLeft(initial) { case ((os, f), tx) => (os ++ - (tx.boxIdsToOpen.map(id => Removal[PublicKey25519Proposition, PublicKey25519NoncedBox](id)): SC) - ++ tx.newBoxes.map(b => Insertion[PublicKey25519Proposition, PublicKey25519NoncedBox](b)): SC, + (tx.boxIdsToOpen.map(id => Removal[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](id)): SC) + ++ tx.newBoxes.map(b => Insertion[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](b)): SC, f + tx.fee) } - StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox](ops) + StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](ops) } - def changes(mod: TModifier): Try[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = { + def changes(mod: TModifier): Try[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = { mod match { case h: BlockHeader => diff --git a/examples/src/test/scala/hybrid/HybridGenerators.scala b/examples/src/test/scala/hybrid/HybridGenerators.scala index 2a8a59052..eb2da6654 100644 --- a/examples/src/test/scala/hybrid/HybridGenerators.scala +++ b/examples/src/test/scala/hybrid/HybridGenerators.scala @@ -88,14 +88,14 @@ trait HybridGenerators extends ExamplesCommonGenerators { value <- positiveLongGen } yield PublicKey25519NoncedBox(proposition, nonce, value) - lazy val stateChangesGen: Gen[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = noncedBoxGen - .map(b => StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox](Seq(Insertion(b)))) + lazy val stateChangesGen: Gen[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = noncedBoxGen + .map(b => StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](Seq(Insertion(b)))) - lazy val walletBoxGen: Gen[WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox]] = for { + lazy val walletBoxGen: Gen[WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = for { createdAt <- positiveLongGen txId <- genBytesList(NodeViewModifier.ModifierIdSize) box: PublicKey25519NoncedBox <- noncedBoxGen - } yield WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox](box, txId, createdAt)(PublicKey25519NoncedBoxSerializer) + } yield WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](box, txId, createdAt)(PublicKey25519NoncedBoxSerializer) def generateHistory: HybridHistory = { val dataDir = s"/tmp/scorex/scorextest-${Random.nextInt(10000000)}" diff --git a/examples/src/test/scala/hybrid/HybridSanity.scala b/examples/src/test/scala/hybrid/HybridSanity.scala index 043dcb58f..ab64f2f57 100644 --- a/examples/src/test/scala/hybrid/HybridSanity.scala +++ b/examples/src/test/scala/hybrid/HybridSanity.scala @@ -13,14 +13,14 @@ import scorex.core.transaction.box.proposition.PublicKey25519Proposition import scorex.core.transaction.state.{PrivateKey25519, StateChanges} import scorex.testkit.{BlockchainPerformance, BlockchainSanity} -class HybridSanity extends BlockchainSanity[PublicKey25519Proposition, +class HybridSanity extends BlockchainSanity[Long, PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock, HybridSyncInfo, PublicKey25519NoncedBox, SimpleBoxTransactionMemPool, HBoxStoredState, - HybridHistory] with BlockchainPerformance[PublicKey25519Proposition, + HybridHistory] with BlockchainPerformance[Long, PublicKey25519Proposition, SimpleBoxTransaction, HybridBlock, HybridSyncInfo, @@ -38,7 +38,7 @@ class HybridSanity extends BlockchainSanity[PublicKey25519Proposition, //Generators override val transactionGenerator: Gen[SimpleBoxTransaction] = simpleBoxTransactionGen - override val stateChangesGenerator: Gen[StateChanges[PublicKey25519Proposition, PublicKey25519NoncedBox]] = + override val stateChangesGenerator: Gen[StateChanges[Long, PublicKey25519Proposition, PublicKey25519NoncedBox]] = stateChangesGen override def genValidModifier(curHistory: HybridHistory): HybridBlock = { diff --git a/examples/src/test/scala/hybrid/serialization/SerializationTests.scala b/examples/src/test/scala/hybrid/serialization/SerializationTests.scala index 176a33141..39686e0ea 100644 --- a/examples/src/test/scala/hybrid/serialization/SerializationTests.scala +++ b/examples/src/test/scala/hybrid/serialization/SerializationTests.scala @@ -18,8 +18,8 @@ class SerializationTests extends PropSpec property("WalletBox serialization") { val walletBoxSerializer = - new WalletBoxSerializer[PublicKey25519Proposition, PublicKey25519NoncedBox](PublicKey25519NoncedBoxSerializer) - forAll(walletBoxGen) { b: WalletBox[PublicKey25519Proposition, PublicKey25519NoncedBox] => + new WalletBoxSerializer[Long, PublicKey25519Proposition, PublicKey25519NoncedBox](PublicKey25519NoncedBoxSerializer) + forAll(walletBoxGen) { b: WalletBox[Long, PublicKey25519Proposition, PublicKey25519NoncedBox] => val parsed = walletBoxSerializer.parseBytes(walletBoxSerializer.toBytes(b)).get walletBoxSerializer.toBytes(parsed) shouldEqual walletBoxSerializer.toBytes(b) } diff --git a/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala b/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala index e4a31e93b..f5cff800b 100644 --- a/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala +++ b/src/main/scala/scorex/core/transaction/box/BoxUnlocker.scala @@ -4,7 +4,7 @@ import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.proof.Proof import scorex.crypto.encode.Base58 -trait BoxUnlocker[+P <: Proposition] { +trait BoxUnlocker[P <: Proposition] { val closedBoxId: Array[Byte] val boxKey: Proof[P] diff --git a/src/main/scala/scorex/core/transaction/proof/Proof.scala b/src/main/scala/scorex/core/transaction/proof/Proof.scala index b50115b6a..fdf4b669a 100644 --- a/src/main/scala/scorex/core/transaction/proof/Proof.scala +++ b/src/main/scala/scorex/core/transaction/proof/Proof.scala @@ -11,8 +11,8 @@ import scorex.core.transaction.state.Secret * A proof is non-interactive and thus serializable */ -trait Proof[+P <: Proposition] extends BytesSerializable { - def isValid(proposition: Proposition, message: Array[Byte]): Boolean +trait Proof[P <: Proposition] extends BytesSerializable { + def isValid(proposition: P, message: Array[Byte]): Boolean } trait ProofOfKnowledge[S <: Secret, P <: ProofOfKnowledgeProposition[S]] extends Proof[P] diff --git a/src/main/scala/scorex/core/transaction/proof/Signature25519.scala b/src/main/scala/scorex/core/transaction/proof/Signature25519.scala index 04f8e934e..6753d7533 100644 --- a/src/main/scala/scorex/core/transaction/proof/Signature25519.scala +++ b/src/main/scala/scorex/core/transaction/proof/Signature25519.scala @@ -15,7 +15,7 @@ case class Signature25519(signature: Array[Byte]) extends ProofOfKnowledge[Priva require(signature.isEmpty || signature.length == Curve25519.SignatureLength, s"${signature.length} != ${Curve25519.SignatureLength}") - override def isValid(proposition: Proposition, message: Array[Byte]): Boolean = + override def isValid(proposition: PublicKey25519Proposition, message: Array[Byte]): Boolean = Curve25519.verify(signature, message, proposition.bytes) override type M = Signature25519 diff --git a/src/main/scala/scorex/core/transaction/state/SecretHolder.scala b/src/main/scala/scorex/core/transaction/state/SecretHolder.scala index b2c9f9a19..f172317da 100644 --- a/src/main/scala/scorex/core/transaction/state/SecretHolder.scala +++ b/src/main/scala/scorex/core/transaction/state/SecretHolder.scala @@ -27,7 +27,7 @@ trait SecretCompanion[S <: Secret] { type PR <: ProofOfKnowledge[S, _ <: ProofOfKnowledgeProposition[S]] - def owns(secret: S, box: Box[_, Any]): Boolean + def owns[T](secret: S, box: Box[_, T]): Boolean def sign(secret: S, message: Array[Byte]): PR @@ -63,7 +63,7 @@ object PrivateKey25519Companion extends SecretCompanion[PrivateKey25519] { override type PR = Signature25519 - override def owns(secret: PrivateKey25519, box: Box[_, Any]): Boolean = { + override def owns[T](secret: PrivateKey25519, box: Box[_, T]): Boolean = { box.proposition match { case p: PublicKey25519Proposition => p.pubKeyBytes sameElements secret.publicKeyBytes case _ => false diff --git a/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala b/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala index 766aaabf3..dabac2d18 100644 --- a/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala +++ b/src/main/scala/scorex/core/transaction/state/authenticated/BoxMinimalState.scala @@ -27,30 +27,6 @@ trait BoxMinimalState[T, P <: Proposition, BX <: Box[P, T], BTX <: BoxTransactio * @param tx - transaction to check against the state * @return */ - /*override def validate(tx: BTX): Try[Unit] = { - val statefulValid = { - val boxesSumTry = tx.unlockers.foldLeft[Try[Long]](Success(0L)) { case (partialRes, unlocker) => - partialRes.flatMap { partialSum => - closedBox(unlocker.closedBoxId) match { - case Some(box) => - unlocker.boxKey.isValid(box.proposition, tx.messageToSign) match { - case true => Success(partialSum + box.value.asInstanceOf[Long]) - case false => Failure(new Exception("Incorrect unlocker")) - } - case None => Failure(new Exception(s"Box for unlocker $unlocker is not in the state")) - } - } - } - - boxesSumTry flatMap { openSum => - tx.newBoxes.map(_.value).sum == openSum - tx.fee match { - case true => Success[Unit](Unit) - case false => Failure(new Exception("Negative fee")) - } - } - } - statefulValid.flatMap(_ => semanticValidity(tx)) - }*/ def semanticValidity(tx: BTX): Try[Unit] } diff --git a/testkit/src/main/scala/scorex/testkit/BlockchainPerformance.scala b/testkit/src/main/scala/scorex/testkit/BlockchainPerformance.scala index dea83fbd0..5fa4809aa 100644 --- a/testkit/src/main/scala/scorex/testkit/BlockchainPerformance.scala +++ b/testkit/src/main/scala/scorex/testkit/BlockchainPerformance.scala @@ -11,11 +11,11 @@ import scorex.testkit.properties._ /** * Performance test for implementations */ -trait BlockchainPerformance[P <: Proposition, +trait BlockchainPerformance[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], SI <: SyncInfo, -B <: Box[P], +B <: Box[P, T], MPool <: MemoryPool[TX, MPool], -ST <: MinimalState[P, B, TX, PM, ST], +ST <: MinimalState[T, P, B, TX, PM, ST], HT <: History[P, TX, PM, SI, HT]] extends MempoolFilterPerformanceTest[P, TX, MPool] diff --git a/testkit/src/main/scala/scorex/testkit/BlockchainSanity.scala b/testkit/src/main/scala/scorex/testkit/BlockchainSanity.scala index 37d249471..8e9fcec69 100644 --- a/testkit/src/main/scala/scorex/testkit/BlockchainSanity.scala +++ b/testkit/src/main/scala/scorex/testkit/BlockchainSanity.scala @@ -11,20 +11,20 @@ import scorex.testkit.properties._ /** * The idea of this class is to get some generators and test some situations, common for all blockchains */ -trait BlockchainSanity[P <: Proposition, +trait BlockchainSanity[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], SI <: SyncInfo, -B <: Box[P], +B <: Box[P, T], MPool <: MemoryPool[TX, MPool], -ST <: MinimalState[P, B, TX, PM, ST], +ST <: MinimalState[T, P, B, TX, PM, ST], HT <: History[P, TX, PM, SI, HT]] extends HistoryAppendBlockTest[P, TX, PM, SI, HT] - with StateApplyChangesTest[P, TX, PM, B, ST] + with StateApplyChangesTest[T, P, TX, PM, B, ST] with WalletSecretsTest[P, TX, PM] - with StateRollbackTest[P, TX, PM, B, ST] + with StateRollbackTest[T, P, TX, PM, B, ST] with MempoolTransactionsTest[P, TX, MPool] with MempoolFilterPerformanceTest[P, TX, MPool] - with StateChangesGenerationTest[P, TX, PM, B, ST, SI, HT] { + with StateChangesGenerationTest[T, P, TX, PM, B, ST, SI, HT] { } diff --git a/testkit/src/main/scala/scorex/testkit/properties/StateApplyChangesTest.scala b/testkit/src/main/scala/scorex/testkit/properties/StateApplyChangesTest.scala index a20b32353..36a95634a 100644 --- a/testkit/src/main/scala/scorex/testkit/properties/StateApplyChangesTest.scala +++ b/testkit/src/main/scala/scorex/testkit/properties/StateApplyChangesTest.scala @@ -9,13 +9,13 @@ import scorex.core.transaction.state.{MinimalState, StateChanges} import scala.util.Random -trait StateApplyChangesTest[P <: Proposition, +trait StateApplyChangesTest[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], -B <: Box[P], -ST <: MinimalState[P, B, TX, PM, ST]] extends StateTests[P, TX, PM, B, ST] { +B <: Box[P, T], +ST <: MinimalState[T, P, B, TX, PM, ST]] extends StateTests[T, P, TX, PM, B, ST] { - val stateChangesGenerator: Gen[StateChanges[P, B]] + val stateChangesGenerator: Gen[StateChanges[T, P, B]] property("State should be able to add a box") { forAll(stateChangesGenerator) { c => diff --git a/testkit/src/main/scala/scorex/testkit/properties/StateChangesGenerationTest.scala b/testkit/src/main/scala/scorex/testkit/properties/StateChangesGenerationTest.scala index 517b80fd5..b41f4c977 100644 --- a/testkit/src/main/scala/scorex/testkit/properties/StateChangesGenerationTest.scala +++ b/testkit/src/main/scala/scorex/testkit/properties/StateChangesGenerationTest.scala @@ -8,13 +8,13 @@ import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.state.{MinimalState, Removal, StateChanges} import scorex.testkit.TestkitHelpers -trait StateChangesGenerationTest[P <: Proposition, +trait StateChangesGenerationTest[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], -B <: Box[P], -ST <: MinimalState[P, B, TX, PM, ST], +B <: Box[P, T], +ST <: MinimalState[T, P, B, TX, PM, ST], SI <: SyncInfo, -HT <: History[P, TX, PM, SI, HT]] extends StateTests[P, TX, PM, B, ST] with TestkitHelpers { +HT <: History[P, TX, PM, SI, HT]] extends StateTests[T, P, TX, PM, B, ST] with TestkitHelpers { val history: HT @@ -25,9 +25,9 @@ HT <: History[P, TX, PM, SI, HT]] extends StateTests[P, TX, PM, B, ST] with Test val block = genValidModifier(history) val blockChanges = state.changes(block).get - val changes: StateChanges[P, B] = StateChanges(blockChanges.operations.flatMap{op => + val changes: StateChanges[T, P, B] = StateChanges(blockChanges.operations.flatMap{op => op match { - case rm: Removal[P, B] if state.closedBox(rm.boxId).isEmpty => None + case rm: Removal[T, P, B] if state.closedBox(rm.boxId).isEmpty => None case _ => Some(op) } }) diff --git a/testkit/src/main/scala/scorex/testkit/properties/StateRollbackTest.scala b/testkit/src/main/scala/scorex/testkit/properties/StateRollbackTest.scala index 10799af62..35afefa00 100644 --- a/testkit/src/main/scala/scorex/testkit/properties/StateRollbackTest.scala +++ b/testkit/src/main/scala/scorex/testkit/properties/StateRollbackTest.scala @@ -7,13 +7,13 @@ import scorex.core.transaction.box.Box import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.state.{MinimalState, StateChanges} -trait StateRollbackTest[P <: Proposition, +trait StateRollbackTest[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], -B <: Box[P], -ST <: MinimalState[P, B, TX, PM, ST]] extends StateTests[P, TX, PM, B, ST] { +B <: Box[P, T], +ST <: MinimalState[T, P, B, TX, PM, ST]] extends StateTests[T, P, TX, PM, B, ST] { - val stateChangesGenerator: Gen[StateChanges[P, B]] + val stateChangesGenerator: Gen[StateChanges[T, P, B]] property("State version updates as expected") { diff --git a/testkit/src/main/scala/scorex/testkit/properties/StateTests.scala b/testkit/src/main/scala/scorex/testkit/properties/StateTests.scala index da3c53580..151acb66a 100644 --- a/testkit/src/main/scala/scorex/testkit/properties/StateTests.scala +++ b/testkit/src/main/scala/scorex/testkit/properties/StateTests.scala @@ -9,11 +9,11 @@ import scorex.core.transaction.box.proposition.Proposition import scorex.core.transaction.state.MinimalState import scorex.testkit.{CoreGenerators, TestkitHelpers} -trait StateTests[P <: Proposition, +trait StateTests[T, P <: Proposition, TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX], -B <: Box[P], -ST <: MinimalState[P, B, TX, PM, ST]] extends PropSpec with GeneratorDrivenPropertyChecks with Matchers +B <: Box[P, T], +ST <: MinimalState[T, P, B, TX, PM, ST]] extends PropSpec with GeneratorDrivenPropertyChecks with Matchers with PropertyChecks with CoreGenerators { val state: ST diff --git a/testkit/src/main/scala/scorex/testkit/properties/WalletSecretsTest.scala b/testkit/src/main/scala/scorex/testkit/properties/WalletSecretsTest.scala index 46125fa74..5fa33c29d 100644 --- a/testkit/src/main/scala/scorex/testkit/properties/WalletSecretsTest.scala +++ b/testkit/src/main/scala/scorex/testkit/properties/WalletSecretsTest.scala @@ -12,7 +12,7 @@ TX <: Transaction[P], PM <: PersistentNodeViewModifier[P, TX]] extends PropSpec with GeneratorDrivenPropertyChecks with Matchers with PropertyChecks { - val wallet: Wallet[P, TX, PM, _] + val wallet: Wallet[_, P, TX, PM, _] property("Wallet should contain secrets for all it's public propositions") { val publicImages = wallet.publicKeys