diff --git a/build.sbt b/build.sbt index 8e58a05..b98c5f9 100644 --- a/build.sbt +++ b/build.sbt @@ -1,12 +1,19 @@ +import org.apache.ivy.core.module.descriptor.ExcludeRule + name := "encry-common" -version := "0.9.0" +version := "0.9.1" scalaVersion := "2.12.6" organization := "org.encry" val circeVersion = "0.9.3" +val excludeRule = ExclusionRule("ove") + +lazy val root = project.in(file(".")).dependsOn(blake2b) +lazy val blake2b = ProjectRef(uri("git://github.com/alphazero/Blake2b.git#master"), "blake2b") + libraryDependencies ++= Seq( - "org.encry" %% "prism" % "0.8.5", + ("org.encry" %% "prism" % "0.8.6").excludeAll(excludeRule), "io.circe" %% "circe-core" % circeVersion, "io.circe" %% "circe-generic" % circeVersion, "io.circe" %% "circe-parser" % circeVersion, diff --git a/project/build.properties b/project/build.properties index 84d4f68..a723771 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.2.0 \ No newline at end of file +sbt.version = 1.3.0 \ No newline at end of file diff --git a/src/main/scala/org/encryfoundation/common/modifiers/ModifierWithDigest.scala b/src/main/scala/org/encryfoundation/common/modifiers/ModifierWithDigest.scala index 042c076..a864bc8 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/ModifierWithDigest.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/ModifierWithDigest.scala @@ -1,18 +1,13 @@ package org.encryfoundation.common.modifiers -import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} +import org.encryfoundation.prismlang.utils.{BouncyCastleHasher, Hasher} -trait ModifierWithDigest extends PersistentNodeViewModifier { +trait ModifierWithDigest extends PersistentNodeViewModifier with BouncyCastleHasher { - override lazy val id: ModifierId = ModifierWithDigest.computeId(modifierTypeId, headerId, digest) + override lazy val id: ModifierId = ModifierId @@ prefixedHash(modifierTypeId, headerId, digest).repr def digest: Array[Byte] def headerId: Array[Byte] -} - -object ModifierWithDigest { - def computeId(modifierType: ModifierTypeId, headerId: Array[Byte], digest: Array[Byte]): ModifierId = - ModifierId @@ Algos.hash.prefixedHash(modifierType, headerId, digest).repr -} +} \ No newline at end of file diff --git a/src/main/scala/org/encryfoundation/common/modifiers/history/Header.scala b/src/main/scala/org/encryfoundation/common/modifiers/history/Header.scala index 6936246..5ac0906 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/history/Header.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/history/Header.scala @@ -11,10 +11,12 @@ import org.encryfoundation.common.utils.TaggedTypes.{ADDigest, Difficulty, Modif import scorex.crypto.hash.Digest32 import io.circe.{Decoder, Encoder, HCursor} import io.circe.syntax._ + import scala.util.Try import Header._ import org.encryfoundation.common.utils.constants.TestNetConstants import org.encryfoundation.common.utils.Algos +import org.encryfoundation.prismlang.utils.BouncyCastleHasher case class Header(version: Byte, override val parentId: ModifierId, @@ -23,7 +25,7 @@ case class Header(version: Byte, height: Int, nonce: Long, difficulty: Difficulty, - equihashSolution: EquihashSolution) extends PersistentModifier { + equihashSolution: EquihashSolution) extends PersistentModifier with BouncyCastleHasher { override type M = Header @@ -38,7 +40,10 @@ case class Header(version: Byte, lazy val isGenesis: Boolean = height == TestNetConstants.GenesisHeight lazy val payloadId: ModifierId = - ModifierWithDigest.computeId(Payload.modifierTypeId, id, transactionsRoot) + computeId(Payload.modifierTypeId, id, transactionsRoot) + + def computeId(modifierType: ModifierTypeId, headerId: Array[Byte], digest: Array[Byte]): ModifierId = + ModifierId @@ prefixedHash(modifierType, headerId, digest).repr def toHeaderProto: HeaderProtoMessage = HeaderProtoSerializer.toProto(this) diff --git a/src/main/scala/org/encryfoundation/common/modifiers/history/Payload.scala b/src/main/scala/org/encryfoundation/common/modifiers/history/Payload.scala index 20bad8a..d1b2a29 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/history/Payload.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/history/Payload.scala @@ -9,9 +9,13 @@ import org.apache.commons.lang.ArrayUtils import org.encryfoundation.common.modifiers.mempool.transaction.{Transaction, TransactionProtoSerializer, TransactionSerializer} import org.encryfoundation.common.modifiers.{ModifierWithDigest, PersistentModifier} import org.encryfoundation.common.serialization.Serializer +import org.encryfoundation.common.utils.Algos.emptyMerkleTreeRoot import org.encryfoundation.common.utils.{Algos, TaggedTypes} -import org.encryfoundation.common.utils.TaggedTypes.{LeafData, ModifierId, ModifierTypeId} +import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} +import scorex.crypto.authds.{LeafData => ScorexLeaf} +import scorex.crypto.authds.merkle.MerkleTree import scorex.crypto.hash.Digest32 + import scala.util.{Success, Try} case class Payload(override val headerId: ModifierId, @@ -23,7 +27,7 @@ case class Payload(override val headerId: ModifierId, override val modifierTypeId: ModifierTypeId = Payload.modifierTypeId - override lazy val digest: Digest32 = Payload.rootHash(txs.map(_.id)) + override lazy val digest: Digest32 = rootHash(txs.map(_.id)) override def serializer: Serializer[Payload] = PayloadSerializer @@ -33,10 +37,20 @@ case class Payload(override val headerId: ModifierId, s" txsQty=${txs.size}, id = ${Algos.encode(id)})" override def parentId: ModifierId = null + + def rootHash(ids: Seq[ModifierId]): Digest32 = merkleTreeRoot(ScorexLeaf !@@ ids) + + def merkleTreeRoot(elements: Seq[ScorexLeaf]): Digest32 = + if (elements.isEmpty) emptyMerkleTreeRoot else MerkleTree(elements.map(ScorexLeaf @@ _.repr))(this).rootHash } object Payload { + def rootHash(ids: Seq[ModifierId]): Digest32 = merkleTreeRoot(ScorexLeaf !@@ ids) //for tests only + + def merkleTreeRoot(elements: Seq[ScorexLeaf]): Digest32 = + if (elements.isEmpty) emptyMerkleTreeRoot else MerkleTree(elements.map(ScorexLeaf @@ _.repr))(Algos.hash).rootHash + implicit val jsonEncoder: Encoder[Payload] = (p: Payload) => Map( "headerId" -> Algos.encode(p.headerId).asJson, "transactions" -> p.txs.map(_.asJson).asJson @@ -51,9 +65,6 @@ object Payload { ) val modifierTypeId: ModifierTypeId = ModifierTypeId @@ (102: Byte) - - def rootHash(ids: Seq[ModifierId]): Digest32 = Algos.merkleTreeRoot(LeafData !@@ ids) - } object PayloadProtoSerializer { diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/AssetIssuingDirective.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/AssetIssuingDirective.scala index a0bf6b5..8697287 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/AssetIssuingDirective.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/AssetIssuingDirective.scala @@ -26,9 +26,9 @@ case class AssetIssuingDirective(contractHash: ContractHash, amount: Amount) ext override def boxes(digest: Digest32, idx: Int): Seq[EncryBaseBox] = Seq(TokenIssuingBox( EncryProposition(contractHash), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), + nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount, - Algos.hash(Ints.toByteArray(idx) ++ digest) + blake2b.digest(Ints.toByteArray(idx) ++ digest) )) override def serializer: Serializer[M] = AssetIssuingDirectiveSerializer diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/DataDirective.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/DataDirective.scala index d8733d7..09949bd 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/DataDirective.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/DataDirective.scala @@ -25,7 +25,7 @@ case class DataDirective(contractHash: ContractHash, data: Array[Byte]) extends override lazy val isValid: Boolean = data.length <= TestNetConstants.MaxDataLength override def boxes(digest: Digest32, idx: Int): Seq[EncryBaseBox] = - Seq(DataBox(EncryProposition(contractHash), Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), data)) + Seq(DataBox(EncryProposition(contractHash), nonceFromDigest(digest ++ Ints.toByteArray(idx)), data)) override def serializer: Serializer[M] = DataDirectiveSerializer diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/Directive.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/Directive.scala index becd917..b71a73c 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/Directive.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/Directive.scala @@ -1,13 +1,15 @@ package org.encryfoundation.common.modifiers.mempool.directive import TransactionProto.TransactionProtoMessage.DirectiveProtoMessage +import com.google.common.primitives.Longs import io.circe._ import org.encryfoundation.common.modifiers.mempool.directive.Directive.DTypeId import org.encryfoundation.common.modifiers.state.box.EncryBaseBox import org.encryfoundation.common.serialization.BytesSerializable +import org.encryfoundation.prismlang.utils.Hasher import scorex.crypto.hash.Digest32 -trait Directive extends BytesSerializable { +trait Directive extends BytesSerializable with Hasher { val typeId: DTypeId @@ -16,6 +18,8 @@ trait Directive extends BytesSerializable { def boxes(digest: Digest32, idx: Int): Seq[EncryBaseBox] def toDirectiveProto: DirectiveProtoMessage + + def nonceFromDigest(digest: Array[Byte]): Long = Longs.fromByteArray(blake2b.digest(digest).take(8)) } object Directive { diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/ScriptedAssetDirective.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/ScriptedAssetDirective.scala index ec25d61..168bdce 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/ScriptedAssetDirective.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/ScriptedAssetDirective.scala @@ -29,7 +29,7 @@ case class ScriptedAssetDirective(contractHash: ContractHash, override lazy val isValid: Boolean = amount > 0 override def boxes(digest: Digest32, idx: Int): Seq[EncryBaseBox] = - Seq(AssetBox(EncryProposition(contractHash), Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount)) + Seq(AssetBox(EncryProposition(contractHash), nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount)) override def serializer: Serializer[M] = ScriptedAssetDirectiveSerializer diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/TransferDirective.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/TransferDirective.scala index f54e0b7..26d7d93 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/TransferDirective.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/directive/TransferDirective.scala @@ -32,7 +32,7 @@ case class TransferDirective(address: Address, override def boxes(digest: Digest32, idx: Int): Seq[EncryBaseBox] = Seq(AssetBox(EncryProposition.addressLocked(address), - Utils.nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount, tokenIdOpt)) + nonceFromDigest(digest ++ Ints.toByteArray(idx)), amount, tokenIdOpt)) override def serializer: Serializer[M] = TransferDirectiveSerializer diff --git a/src/main/scala/org/encryfoundation/common/modifiers/mempool/transaction/Transaction.scala b/src/main/scala/org/encryfoundation/common/modifiers/mempool/transaction/Transaction.scala index 5104ad2..b979bb0 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/mempool/transaction/Transaction.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/mempool/transaction/Transaction.scala @@ -21,12 +21,14 @@ import org.encryfoundation.common.utils.TaggedTypes.{ModifierId, ModifierTypeId} import org.encryfoundation.common.utils.constants.TestNetConstants import org.encryfoundation.common.validation.{ModifierValidator, ValidationResult} import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} +import org.encryfoundation.prismlang.utils.Hasher case class Transaction(fee: Amount, timestamp: Long, inputs: IndexedSeq[Input], directives: IndexedSeq[Directive], - defaultProofOpt: Option[Proof]) extends NodeViewModifier with ModifierValidator with PConvertible { + defaultProofOpt: Option[Proof]) + extends NodeViewModifier with ModifierValidator with PConvertible with Hasher with BytesToSign { override val modifierTypeId: ModifierTypeId = Transaction.modifierTypeId @@ -36,9 +38,9 @@ case class Transaction(fee: Amount, def toTransactionProto: TransactionProtoMessage = TransactionProtoSerializer.toProto(this) - val messageToSign: Array[Byte] = UnsignedTransaction.bytesToSign(fee, timestamp, inputs, directives) + val messageToSign: Array[Byte] = bytesToSign(fee, timestamp, inputs, directives) - val id: ModifierId = ModifierId !@@ Algos.hash(messageToSign) + val id: ModifierId = ModifierId !@@ blake2b.digest(messageToSign) val size: Int = this.bytes.length @@ -182,9 +184,9 @@ object TransactionSerializer extends Serializer[Transaction] { case class UnsignedTransaction(fee: Amount, timestamp: Long, inputs: IndexedSeq[Input], - directives: IndexedSeq[Directive]) { + directives: IndexedSeq[Directive]) extends BytesToSign with Hasher { - val messageToSign: Array[Byte] = UnsignedTransaction.bytesToSign(fee, timestamp, inputs, directives) + val messageToSign: Array[Byte] = bytesToSign(fee, timestamp, inputs, directives) def toSigned(proofs: IndexedSeq[Seq[Proof]], defaultProofOpt: Option[Proof]): Transaction = { val signedInputs: IndexedSeq[Input] = inputs.zipWithIndex.map { case (input, idx) => @@ -194,13 +196,14 @@ case class UnsignedTransaction(fee: Amount, } } -object UnsignedTransaction { +trait BytesToSign { + this: Hasher => def bytesToSign(fee: Amount, timestamp: Long, unlockers: IndexedSeq[Input], directives: IndexedSeq[Directive]): Digest32 = - Algos.hash(Bytes.concat( + Digest32 @@ blake2b.digest(Bytes.concat( unlockers.map(_.bytesWithoutProof).foldLeft(Array[Byte]())(_ ++ _), directives.map(_.bytes).foldLeft(Array[Byte]())(_ ++ _), Longs.toByteArray(timestamp), diff --git a/src/main/scala/org/encryfoundation/common/modifiers/state/box/EncryBaseBox.scala b/src/main/scala/org/encryfoundation/common/modifiers/state/box/EncryBaseBox.scala index b404686..4538171 100644 --- a/src/main/scala/org/encryfoundation/common/modifiers/state/box/EncryBaseBox.scala +++ b/src/main/scala/org/encryfoundation/common/modifiers/state/box/EncryBaseBox.scala @@ -4,13 +4,14 @@ import BoxesProto.BoxProtoMessage import com.google.common.primitives.Longs import io.circe.{Decoder, DecodingFailure, Encoder} import org.encryfoundation.common.modifiers.state.box.EncryBox.BxTypeId -import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ADKey import org.encryfoundation.prismlang.core.wrapped.{PObject, PValue} import org.encryfoundation.prismlang.core.{PConvertible, Types} +import org.encryfoundation.prismlang.utils.Hasher + import scala.util.Try -trait EncryBaseBox extends Box[EncryProposition] with PConvertible { +trait EncryBaseBox extends Box[EncryProposition] with PConvertible with Hasher { val typeId: BxTypeId @@ -18,7 +19,7 @@ trait EncryBaseBox extends Box[EncryProposition] with PConvertible { def serializeToProto: BoxProtoMessage - override lazy val id: ADKey = ADKey @@ Algos.hash(Longs.toByteArray(nonce)).updated(0, typeId) + override lazy val id: ADKey = ADKey @@ blake2b.digest(Longs.toByteArray(nonce)).updated(0, typeId) def isAmountCarrying: Boolean = this.isInstanceOf[MonetaryBox] diff --git a/src/main/scala/org/encryfoundation/common/utils/Algos.scala b/src/main/scala/org/encryfoundation/common/utils/Algos.scala index f83b23e..a2a981c 100644 --- a/src/main/scala/org/encryfoundation/common/utils/Algos.scala +++ b/src/main/scala/org/encryfoundation/common/utils/Algos.scala @@ -2,10 +2,8 @@ package org.encryfoundation.common.utils import java.nio.charset.Charset import org.encryfoundation.common.utils.TaggedTypes.LeafData -import scorex.crypto.authds.merkle.MerkleTree import scorex.crypto.encode.Base16 import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.crypto.authds.{LeafData => ScorexLeaf} import scala.util.Try object Algos { @@ -22,8 +20,5 @@ object Algos { val charset: Charset = Charset.defaultCharset() - def merkleTreeRoot(elements: Seq[LeafData]): Digest32 = - if (elements.isEmpty) emptyMerkleTreeRoot else MerkleTree(elements.map(ScorexLeaf @@ _.repr))(hash).rootHash - lazy val emptyMerkleTreeRoot: Digest32 = Algos.hash(LeafData @@ Array[Byte]()) } \ No newline at end of file diff --git a/src/main/scala/org/encryfoundation/common/utils/Utils.scala b/src/main/scala/org/encryfoundation/common/utils/Utils.scala index 3e31f50..d4fc2e7 100644 --- a/src/main/scala/org/encryfoundation/common/utils/Utils.scala +++ b/src/main/scala/org/encryfoundation/common/utils/Utils.scala @@ -1,7 +1,6 @@ package org.encryfoundation.common.utils import java.nio.{ByteBuffer, ByteOrder} -import com.google.common.primitives.Longs import org.bouncycastle.crypto.Digest object Utils { @@ -32,6 +31,4 @@ object Utils { } def validateSolution(solution: Array[Byte], target: Double): Boolean = countLeadingZeroes(solution) >= target - - def nonceFromDigest(digest: Array[Byte]): Long = Longs.fromByteArray(Algos.hash(digest).take(8)) } \ No newline at end of file