diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38d6d8d5..cf8d0b3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,13 +15,13 @@ env: jobs: build: - name: Test and publish a snapshot + name: JVM - Test and publish a snapshot env: HAS_SECRETS: ${{ secrets.SONATYPE_PASSWORD != '' }} strategy: matrix: os: [ubuntu-latest] - scala: [2.12.10, 2.11.12, 2.13.1] + scala: [2.13.8, 2.12.15, 2.11.12] java: [adopt@1.8] runs-on: ${{ matrix.os }} steps: @@ -48,11 +48,60 @@ jobs: key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - name: Runs tests - run: sbt ++${{ matrix.scala }} test + run: sbt ++${{ matrix.scala }} scryptoJVM/test - name: Publish a snapshot ${{ github.ref }} if: env.HAS_SECRETS == 'true' - run: sbt ++${{ matrix.scala }} publish + run: sbt ++${{ matrix.scala }} scryptoJVM/publish + env: + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + + buildJs: + name: JS - Test and publish a snapshot + env: + HAS_SECRETS: ${{ secrets.SONATYPE_PASSWORD != '' }} + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.13.8, 2.12.15] + java: [adopt@1.8] + node-version: [16.x] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + + - name: Setup NPM + uses: pnpm/action-setup@v2 + with: + version: 7.21.0 + node-version: ${{ matrix.node-version }} + cache: "pnpm" + - run: pnpm install + + - name: Setup Java and Scala + uses: olafurpg/setup-scala@v10 + with: + java-version: ${{ matrix.java }} + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Runs tests + run: sbt ++${{ matrix.scala }} scryptoJS/test + + - name: Publish a snapshot ${{ github.ref }} + if: env.HAS_SECRETS == 'true' + run: sbt ++${{ matrix.scala }} scryptoJS/publish env: SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/README.md b/README.md index 934fc17a..a484b388 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Here are code examples for generating proofs and checking them. In this example * First, we create a prover and get an initial digest from it (in a real application, this value is a public constant because anyone, including verifiers, can compute it by using the same two lines of code) ```scala - import com.google.common.primitives.Longs + import scorex.utils.Longs import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.authds.avltree.batch._ import scorex.crypto.hash.{Blake2b256, Digest32} @@ -190,4 +190,30 @@ The code is under Public Domain CC0 license means you can do anything with it. F # Contributing -Your contributions are always welcome! Please submit a pull request or create an issue to add a new cryptographic primitives or better implementations. +Clone the repository +``` +$git clone scrypto +$cd scrypto +``` + +The code uses [Scalablytyped](https://scalablytyped.org/docs/readme) to generate Scala.js bindings +for `@noble/hashes`. This [video](https://youtu.be/hWUAVrNj65c?t=1341) explains how the +environment for ScalablyTyped is configured in this repository. + +Before compiling the library with SBT, you need to install JS dependencies for ScalablyTyped. +The configuration is in `package.json`. +``` +$npm install +added 285 packages, and audited 286 packages in 20s +found 0 vulnerabilities +``` + +Then you can compile the library with SBT and run tests. +``` +$sbt +sbt:scrypto> compile +sbt:scrypto> test +``` + +Your contributions are always welcome! +Please submit a pull request or create an issue to add a new cryptographic primitives or better implementations. diff --git a/benchmarks/src/main/scala/scorex.benchmarks/Base16Benchmark.scala b/benchmarks/src/main/scala/scorex.benchmarks/Base16Benchmark.scala index a7e8ff90..98c6b996 100644 --- a/benchmarks/src/main/scala/scorex.benchmarks/Base16Benchmark.scala +++ b/benchmarks/src/main/scala/scorex.benchmarks/Base16Benchmark.scala @@ -45,12 +45,12 @@ object Base16Benchmark { .view .map(_ => Random.nextString(200).getBytes("UTF-8")) .map(Base16.encode) - .force + .force.toSeq val xab: Seq[Array[Byte]] = (1 to 1000) .view .map(_ => Random.nextString(200).getBytes("UTF-8")) - .force + .force.toSeq } } \ No newline at end of file diff --git a/benchmarks/src/main/scala/scorex.benchmarks/ByteArrayComparePerformance.scala b/benchmarks/src/main/scala/scorex.benchmarks/ByteArrayComparePerformance.scala index ad9eb15c..a0971ab9 100644 --- a/benchmarks/src/main/scala/scorex.benchmarks/ByteArrayComparePerformance.scala +++ b/benchmarks/src/main/scala/scorex.benchmarks/ByteArrayComparePerformance.scala @@ -2,7 +2,7 @@ package scorex.benchmarks import java.util.concurrent.TimeUnit -import com.google.common.primitives.Shorts +import scorex.utils.Shorts import org.openjdk.jmh.annotations._ import org.openjdk.jmh.infra.Blackhole import scorex.utils.ByteArray diff --git a/benchmarks/src/main/scala/scorex.benchmarks/Helpers.scala b/benchmarks/src/main/scala/scorex.benchmarks/Helpers.scala index 94ee2223..0f8b804a 100644 --- a/benchmarks/src/main/scala/scorex.benchmarks/Helpers.scala +++ b/benchmarks/src/main/scala/scorex.benchmarks/Helpers.scala @@ -1,6 +1,6 @@ package scorex.benchmarks -import com.google.common.primitives.Longs +import scorex.utils.Longs import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Operation, Remove} import scorex.crypto.hash.{Blake2b256, Digest32} diff --git a/build.sbt b/build.sbt index 0fda21c6..93710647 100644 --- a/build.sbt +++ b/build.sbt @@ -3,12 +3,9 @@ import sbt.Keys.{homepage, scalaVersion} name := "scrypto" description := "Cryptographic primitives for Scala" +lazy val scala213 = "2.13.8" lazy val scala212 = "2.12.15" lazy val scala211 = "2.11.12" -lazy val scala213 = "2.13.7" - -crossScalaVersions := Seq(scala212, scala211, scala213) -scalaVersion := scala212 javacOptions ++= "-source" :: "1.8" :: @@ -34,38 +31,64 @@ lazy val commonSettings = Seq( "scm:git@github.com:input-output-hk/scrypto.git" ) ), + libraryDependencies ++= Seq( + "org.rudogma" %%% "supertagged" % "2.0-RC2", + "org.scorexfoundation" %%% "scorex-util" % "0.1.8-20-565873cd-SNAPSHOT", + "org.scalatest" %%% "scalatest" % "3.3.0-SNAP3" % Test, + "org.scalatest" %%% "scalatest-propspec" % "3.3.0-SNAP3" % Test, + "org.scalatest" %%% "scalatest-shouldmatchers" % "3.3.0-SNAP3" % Test, + "org.scalatestplus" %%% "scalacheck-1-15" % "3.3.0.0-SNAP3" % Test, + "org.scalacheck" %%% "scalacheck" % "1.15.2" % Test + ), + publishMavenStyle := true, + publishTo := sonatypePublishToBundle.value ) -libraryDependencies ++= Seq( - "org.rudogma" %% "supertagged" % "1.5", - "com.google.guava" % "guava" % "23.0", - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2", - "org.whispersystems" % "curve25519-java" % "0.5.0", - "org.bouncycastle" % "bcprov-jdk15to18" % "1.66", - "org.scorexfoundation" %% "scorex-util" % "0.1.8" -) - -libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % "3.1.+" % Test, - "org.scalacheck" %% "scalacheck" % "1.14.+" % Test, - // https://mvnrepository.com/artifact/org.scalatestplus/scalatestplus-scalacheck - "org.scalatestplus" %% "scalatestplus-scalacheck" % "3.1.0.0-RC2" % Test -) - -publishMavenStyle := true -publishArtifact in Test := false +Test / publishArtifact := false -publishTo := sonatypePublishToBundle.value pomIncludeRepository := { _ => false } -lazy val scrypto = (project in file(".")).settings(commonSettings: _*) - -lazy val benchmarks = (project in file("benchmarks")) - .settings(commonSettings, name := "scrypto-benchmarks") - .dependsOn(scrypto) - .enablePlugins(JmhPlugin) +lazy val scrypto = crossProject(JVMPlatform, JSPlatform) + .in(file(".")) + .settings(commonSettings: _*) + .jvmSettings( + libraryDependencies ++= Seq( + "org.bouncycastle" % "bcprov-jdk15to18" % "1.66" + ), + scalaVersion := scala213, + crossScalaVersions := Seq(scala211, scala212, scala213) + ) + +lazy val scryptoJS = scrypto.js + .enablePlugins(ScalaJSBundlerPlugin) + .enablePlugins(ScalablyTypedConverterExternalNpmPlugin) + .settings( + scalaVersion := scala213, + crossScalaVersions := Seq(scala212, scala213), + libraryDependencies ++= Seq( + "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0", + ("org.scala-js" %%% "scalajs-java-securerandom" % "1.0.0").cross(CrossVersion.for3Use2_13) + ), + Test / parallelExecution := false, + // how to setup ScalablyTyped https://youtu.be/hWUAVrNj65c?t=1341 + externalNpm := { file(s"${baseDirectory.value}/..") }, + Compile / npmDependencies ++= Seq( + "@noble/hashes" -> "^1.1.4" + ), + useYarn := true + ) + +lazy val benchmarks = project + .in(file("benchmarks")) + .dependsOn(scrypto.jvm) + .settings( + moduleName := "scrypto-benchmarks", + crossScalaVersions := Seq(scala211, scala212, scala213), + scalaVersion := scala213, + ) + .enablePlugins(JmhPlugin) credentials ++= (for { username <- Option(System.getenv().get("SONATYPE_USERNAME")) @@ -73,9 +96,9 @@ credentials ++= (for { } yield Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", username, password)).toSeq // prefix version with "-SNAPSHOT" for builds without a git tag -dynverSonatypeSnapshots in ThisBuild := true +ThisBuild / dynverSonatypeSnapshots := true // use "-" instead of default "+" -dynverSeparator in ThisBuild := "-" +ThisBuild / dynverSeparator := "-" // PGP key for signing a release build published to sonatype // signing is done by sbt-pgp plugin diff --git a/js/src/main/scala/scorex/crypto/hash/Platform.scala b/js/src/main/scala/scorex/crypto/hash/Platform.scala new file mode 100644 index 00000000..fb22184d --- /dev/null +++ b/js/src/main/scala/scorex/crypto/hash/Platform.scala @@ -0,0 +1,84 @@ +package scorex.crypto.hash + +import typings.nobleHashes.blake2Mod.BlakeOpts +import typings.nobleHashes.mod.blake2b +import typings.nobleHashes.sha256Mod.sha256 +import typings.nobleHashes.utilsMod + +import scala.scalajs.js.typedarray.Uint8Array + +/** JS platform specific implementation of methods. + * When shared code is compiled to JS, this implementation is used. + * + * The JS implementation is based on type wrappers generated by ScalablyTyped for the + * @noble/hashes library. (See configuration in build.sbt.) + * + * @see jvm/src/main/scala/scorex/crypto/hash/Platform.scala for JVM implementation + */ +object Platform { + + /** Represents abstract digest from @noble. + * See createBlake2bDigest, createSha256Digest methods. + */ + type Digest = utilsMod.Hash[_] + + private def bytesToShorts(xs: Array[Byte]): Array[Short] = + xs.map(x => (x & 0xFF).toShort) + + private def uint8ArrayToBytes(jsShorts: Uint8Array): Array[Byte] = { + jsShorts.toArray[Short].map(x => x.toByte) + } + + /** Creates an implementation of the cryptographic hash function Blakbe2b. + * + * @param bitSize the bit size of the digest + * @return the digest implementation + */ + def createBlake2bDigest(bitSize: Int): Digest = { + val opts = BlakeOpts().setDkLen(bitSize / 8) + blake2b.create(opts) + } + + /** Creates an implementation of the cryptographic hash function SHA-256. + * + * @return the digest implementation + */ + def createSha256Digest(): Digest = { + sha256.create() + } + + /** Update the message digest with a single byte. + * + * @param digest the digest to be updated + * @param b the input byte to be entered. + */ + def updateDigest(digest: Digest, b: Byte): Unit = { + digest.update(Uint8Array.of((b & 0xFF).toShort)) + } + + /** Update the message digest with a block of bytes. + * + * @param digest the digest to be updated + * @param bytes the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param inLen the length of the data. + */ + def updateDigest(digest: Digest, + bytes: Array[Byte], + inOff: Int, + inLen: Int): Unit = { + val in = Uint8Array.of(bytesToShorts(bytes.slice(inOff, inOff + inLen)): _*) + digest.update(in) + } + + /** Close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * A new array is created to store the result. + * + * @param digest the digest to be finalized + */ + def doFinalDigest(digest: Digest): Array[Byte] = { + val res = digest.digest() + uint8ArrayToBytes(res) + } +} diff --git a/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/BlockchainSimulator.scala b/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/BlockchainSimulator.scala new file mode 100644 index 00000000..4cdcfc8d --- /dev/null +++ b/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/BlockchainSimulator.scala @@ -0,0 +1,73 @@ +package scorex.crypto.authds.merkle.sparse + +import scorex.crypto.authds.LeafData + +import scala.util.Try +import scorex.crypto.hash.{CryptographicHash, Digest32, Blake2b256Unsafe} + +import scala.collection.mutable +import scorex.utils.Longs + +object BlockchainSimulator extends App { + type PubKey = Array[Byte] + val PubKeyLength = 32 + implicit val hf: CryptographicHash[Digest32] = new Blake2b256Unsafe + + case class Transaction(amount: Long, + sender: PubKey, + recipient: PubKey, + coinBalance: Long, + coinProof: SparseMerkleProof[Digest32]) + + object Transaction { + def coinBytes(pubKey: PubKey, balance: Long) = Some(LeafData @@ (pubKey ++ Longs.toByteArray(balance))) + + def process(tx: Transaction, + state: SparseMerkleTree[Digest32]): + Try[(SparseMerkleTree[Digest32], Seq[SparseMerkleProof[Digest32]])] = Try { + require(tx.amount <= tx.coinBalance) + require(tx.coinProof.leafDataOpt.get sameElements coinBytes(tx.sender, tx.coinBalance).get) + require(tx.coinProof.valid(state.rootDigest, height)) + val (state1, _) = state.update(tx.coinProof, None).get + val (state2, proofs2) = state1.update(state1.lastProof, + coinBytes(tx.recipient, tx.amount), + Seq(state1.lastProof)).get + if (tx.amount == tx.coinBalance) state2 -> proofs2 + else state2.update(state2.lastProof, + coinBytes(tx.sender, tx.coinBalance - tx.amount), + proofs2 :+ state2.lastProof).get + } + } + + case class Block(transactions: Seq[Transaction]) + + val txsCache = new mutable.ArrayBuffer() + val maxTxsCacheSize = 5000 + val txsPerBlock = 1000 + val numOfBlocks = 1000000 + val height = 30: Byte + val godAccount = Array.fill(32)(0: Byte) + val godBalance = 100000000000L //100B + val emptyState = SparseMerkleTree.emptyTree(height) + val (initialState, godProofs) = emptyState.update(emptyState.lastProof, + Transaction.coinBytes(godAccount, godBalance), + Seq(emptyState.lastProof)).get + var godProof = godProofs.head + var currentGodBalance = godBalance + val txAmount = 10 + (1 to numOfBlocks).foldLeft(initialState) { case (beforeBlocktree, blockNum) => + val (afterTree, processingTime) = (1 to txsPerBlock).foldLeft(beforeBlocktree -> 0L) { case ((tree, totalTime), txNum) => + val recipient = hf(scala.util.Random.nextString(20)) + val tx = Transaction(txAmount, godAccount, recipient, currentGodBalance, godProof) + val t0 = System.currentTimeMillis() + val (updState, proofs) = Transaction.process(tx, tree).get //we generate always valid transaction + val t = System.currentTimeMillis() + currentGodBalance = currentGodBalance - txAmount + godProof = proofs.last + updState -> (totalTime + (t - t0)) + } + println(s"Block $blockNum, processing time: $processingTime ms") + println(godProof) + afterTree + } +} diff --git a/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/OpsBenchmark.scala b/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/OpsBenchmark.scala new file mode 100644 index 00000000..173dd708 --- /dev/null +++ b/jvm/src/main/scala/scorex/crypto/authds/merkle/sparse/OpsBenchmark.scala @@ -0,0 +1,43 @@ +package scorex.crypto.authds.merkle.sparse + +import scorex.crypto.authds.LeafData +import scorex.crypto.hash.{CryptographicHash, Digest32, Blake2b256Unsafe} +import scorex.utils.Longs + +object OpsBenchmark extends App { + implicit val hf: CryptographicHash[Digest32] = new Blake2b256Unsafe + val height: Byte = 50 + var tree = SparseMerkleTree.emptyTree(height) + //bootstrapping + val bSize = 10000 + var proof: SparseMerkleProof[Digest32] = null + val tb0 = System.currentTimeMillis() + (0 until bSize).foreach { i => + if (i == bSize / 2) proof = tree.lastProof + val proofsToUpdate = if (i >= bSize / 2) { + Seq(proof) + } else Seq.empty[SparseMerkleProof[Digest32]] + if (i % 1000 == 0) println(s"$i elements added") + val (t, p) = tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(i)), proofsToUpdate).get + tree = t + if (i >= bSize / 2) proof = p.head + } + val tb = System.currentTimeMillis() + println(s"bootstrapping time: ${tb - tb0}") + val tv0 = System.currentTimeMillis() + (1 to 1000).foreach(i => proof.valid(tree)) + val tv = System.currentTimeMillis() + println(s"1000 updates time: ${tv - tv0} ms") + val te0 = System.currentTimeMillis() + (1 to 1000).foreach(_ => + tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(2)), Seq()) + ) + val te = System.currentTimeMillis() + val tp0 = System.currentTimeMillis() + (1 to 1000).foreach(_ => + tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(2)), Seq(proof)) + ) + val tp = System.currentTimeMillis() + val tu = (tp - tp0) - (te - te0) + println("1000 proof updates: " + tu) +} diff --git a/src/main/scala/scorex/crypto/hash/Blake2b256Unsafe.scala b/jvm/src/main/scala/scorex/crypto/hash/Blake2b256Unsafe.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/Blake2b256Unsafe.scala rename to jvm/src/main/scala/scorex/crypto/hash/Blake2b256Unsafe.scala diff --git a/src/main/scala/scorex/crypto/hash/Blake2b512.scala b/jvm/src/main/scala/scorex/crypto/hash/Blake2b512.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/Blake2b512.scala rename to jvm/src/main/scala/scorex/crypto/hash/Blake2b512.scala index 3285511c..605968f1 100644 --- a/src/main/scala/scorex/crypto/hash/Blake2b512.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Blake2b512.scala @@ -5,5 +5,4 @@ object Blake2b512 extends Blake2b[Digest64] with CryptographicHash64 { override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest64 = Digest64 @@ internalPrefixedHash(prefix, inputs: _*) - } diff --git a/src/main/scala/scorex/crypto/hash/CryptographicHash64.scala b/jvm/src/main/scala/scorex/crypto/hash/CryptographicHash64.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/CryptographicHash64.scala rename to jvm/src/main/scala/scorex/crypto/hash/CryptographicHash64.scala index 052f6afe..0b48e2e9 100644 --- a/src/main/scala/scorex/crypto/hash/CryptographicHash64.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/CryptographicHash64.scala @@ -3,12 +3,10 @@ package scorex.crypto.hash import scala.util.Try trait CryptographicHash64 extends CryptographicHash[Digest64] { - override val DigestSize: Int = 64 override def byteArrayToDigest(bytes: Array[Byte]): Try[Digest64] = Try { require(bytes.lengthCompare(DigestSize) == 0, "Incorrect digest size") Digest64 @@ bytes } - } diff --git a/src/main/scala/scorex/crypto/hash/Keccak.scala b/jvm/src/main/scala/scorex/crypto/hash/Keccak.scala similarity index 98% rename from src/main/scala/scorex/crypto/hash/Keccak.scala rename to jvm/src/main/scala/scorex/crypto/hash/Keccak.scala index a05c3b3b..8c87d451 100644 --- a/src/main/scala/scorex/crypto/hash/Keccak.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Keccak.scala @@ -5,7 +5,3 @@ import org.bouncycastle.crypto.digests.KeccakDigest trait Keccak[D <: Digest] extends BouncyCastleHash[D] { override protected lazy val digestFn = new KeccakDigest(DigestSize * 8) } - - - - diff --git a/src/main/scala/scorex/crypto/hash/Keccak256.scala b/jvm/src/main/scala/scorex/crypto/hash/Keccak256.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/Keccak256.scala rename to jvm/src/main/scala/scorex/crypto/hash/Keccak256.scala index 1c7db546..ec602f2a 100644 --- a/src/main/scala/scorex/crypto/hash/Keccak256.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Keccak256.scala @@ -5,5 +5,4 @@ object Keccak256 extends Keccak[Digest32] with CryptographicHash32 { override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest32 = Digest32 @@ internalPrefixedHash(prefix, inputs: _*) - } diff --git a/src/main/scala/scorex/crypto/hash/Keccak512.scala b/jvm/src/main/scala/scorex/crypto/hash/Keccak512.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/Keccak512.scala rename to jvm/src/main/scala/scorex/crypto/hash/Keccak512.scala index 9a477ae4..c878cb8b 100644 --- a/src/main/scala/scorex/crypto/hash/Keccak512.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Keccak512.scala @@ -5,5 +5,4 @@ object Keccak512 extends Keccak[Digest64] with CryptographicHash64 { override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest64 = Digest64 @@ internalPrefixedHash(prefix, inputs: _*) - } diff --git a/jvm/src/main/scala/scorex/crypto/hash/Platform.scala b/jvm/src/main/scala/scorex/crypto/hash/Platform.scala new file mode 100644 index 00000000..a6a495d2 --- /dev/null +++ b/jvm/src/main/scala/scorex/crypto/hash/Platform.scala @@ -0,0 +1,64 @@ +package scorex.crypto.hash + +import org.bouncycastle.crypto.digests.{Blake2bDigest, SHA256Digest} + +/** JVM platform specific implementation of methods. + * When shared code is compiled to JVM, this implementation is used. + * + * The JVM implementation is based on bouncycastle library. + + * @see js/src/main/scala/scorex/crypto/hash/Platform.scala for JS implementation + */ +object Platform { + + /** Represents abstract digest from bouncycastle. + * See createBlake2bDigest, createSha256Digest methods. + */ + type Digest = org.bouncycastle.crypto.ExtendedDigest + + /** Creates an implementation of the cryptographic hash function Blakbe2b. + * + * @param bitSize the bit size of the digest + * @return the digest implementation + */ + def createBlake2bDigest(bitSize: Int): Digest = new Blake2bDigest(bitSize) + + /** Creates an implementation of the cryptographic hash function SHA-256. + * + * @return the digest implementation + */ + def createSha256Digest(): Digest = new SHA256Digest() + + /** Update the message digest with a single byte. + * + * @param digest the digest to be updated + * @param b the input byte to be entered. + */ + def updateDigest(digest: Digest, b: Byte): Unit = digest.update(b) + + /** Update the message digest with a block of bytes. + * + * @param digest the digest to be updated + * @param in the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param inLen the length of the data. + */ + def updateDigest(digest: Digest, + in: Array[Byte], + inOff: Int, + inLen: Int): Unit = { + digest.update(in, inOff, inLen) + } + + /** Close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * A new array is created to store the result. + * + * @param digest the digest to be finalized + */ + def doFinalDigest(digest: Digest): Array[Byte] = { + val res = new Array[Byte](digest.getDigestSize) + digest.doFinal(res, 0) + res + } +} diff --git a/src/main/scala/scorex/crypto/hash/Sha256Unsafe.scala b/jvm/src/main/scala/scorex/crypto/hash/Sha256Unsafe.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/Sha256Unsafe.scala rename to jvm/src/main/scala/scorex/crypto/hash/Sha256Unsafe.scala diff --git a/src/main/scala/scorex/crypto/hash/Skein256.scala b/jvm/src/main/scala/scorex/crypto/hash/Skein256.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/Skein256.scala rename to jvm/src/main/scala/scorex/crypto/hash/Skein256.scala index e594ab6c..8d8dbbcd 100644 --- a/src/main/scala/scorex/crypto/hash/Skein256.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Skein256.scala @@ -12,9 +12,4 @@ object Skein256 extends BouncyCastleHash[Digest32] with CryptographicHash32 { override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest32 = Digest32 @@ internalPrefixedHash(prefix, inputs: _*) - } - - - - diff --git a/src/main/scala/scorex/crypto/hash/Skein512.scala b/jvm/src/main/scala/scorex/crypto/hash/Skein512.scala similarity index 98% rename from src/main/scala/scorex/crypto/hash/Skein512.scala rename to jvm/src/main/scala/scorex/crypto/hash/Skein512.scala index 1a8fedeb..2a9d5ae8 100644 --- a/src/main/scala/scorex/crypto/hash/Skein512.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Skein512.scala @@ -2,18 +2,11 @@ package scorex.crypto.hash import org.bouncycastle.crypto.digests.SkeinDigest - object Skein512 extends BouncyCastleHash[Digest64] with CryptographicHash64 { - override protected lazy val digestFn = new SkeinDigest(SkeinDigest.SKEIN_512, SkeinDigest.SKEIN_512) override def hash(input: Message): Digest64 = Digest64 @@ internalHash(input) override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest64 = Digest64 @@ internalPrefixedHash(prefix, inputs: _*) - } - - - - diff --git a/src/main/scala/scorex/crypto/hash/Stribog256.scala b/jvm/src/main/scala/scorex/crypto/hash/Stribog256.scala similarity index 98% rename from src/main/scala/scorex/crypto/hash/Stribog256.scala rename to jvm/src/main/scala/scorex/crypto/hash/Stribog256.scala index e468adb9..3eb1fd1b 100644 --- a/src/main/scala/scorex/crypto/hash/Stribog256.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Stribog256.scala @@ -3,16 +3,10 @@ package scorex.crypto.hash import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest object Stribog256 extends BouncyCastleHash[Digest32] with CryptographicHash32 { - override protected lazy val digestFn = new GOST3411_2012_256Digest override def hash(input: Message): Digest32 = Digest32 @@ internalHash(input) override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest32 = Digest32 @@ internalPrefixedHash(prefix, inputs: _*) - } - - - - diff --git a/src/main/scala/scorex/crypto/hash/Stribog512.scala b/jvm/src/main/scala/scorex/crypto/hash/Stribog512.scala similarity index 98% rename from src/main/scala/scorex/crypto/hash/Stribog512.scala rename to jvm/src/main/scala/scorex/crypto/hash/Stribog512.scala index 532d6e41..d2101fcb 100644 --- a/src/main/scala/scorex/crypto/hash/Stribog512.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Stribog512.scala @@ -3,16 +3,10 @@ package scorex.crypto.hash import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest object Stribog512 extends BouncyCastleHash[Digest64] with CryptographicHash64 { - override protected lazy val digestFn = new GOST3411_2012_512Digest override def hash(input: Message): Digest64 = Digest64 @@ internalHash(input) override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest64 = Digest64 @@ internalPrefixedHash(prefix, inputs: _*) - } - - - - diff --git a/src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala b/jvm/src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala rename to jvm/src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala index 12369647..c6978167 100644 --- a/src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/ThreadUnsafeHash.scala @@ -1,6 +1,5 @@ package scorex.crypto.hash - /** * Thread-unsafe hash classes may be used for performance purposes */ diff --git a/src/main/scala/scorex/crypto/hash/Whirlpool.scala b/jvm/src/main/scala/scorex/crypto/hash/Whirlpool.scala similarity index 99% rename from src/main/scala/scorex/crypto/hash/Whirlpool.scala rename to jvm/src/main/scala/scorex/crypto/hash/Whirlpool.scala index d10d75cd..37173bca 100644 --- a/src/main/scala/scorex/crypto/hash/Whirlpool.scala +++ b/jvm/src/main/scala/scorex/crypto/hash/Whirlpool.scala @@ -2,9 +2,7 @@ package scorex.crypto.hash import org.bouncycastle.crypto.digests.WhirlpoolDigest - object Whirlpool extends BouncyCastleHash[Digest64] with CryptographicHash64 { - override protected lazy val digestFn = new WhirlpoolDigest override def hash(input: Message): Digest64 = Digest64 @@ internalHash(input) diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala b/jvm/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala similarity index 96% rename from src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala rename to jvm/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala index e4061b7b..8efb0b9b 100644 --- a/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala +++ b/jvm/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchStatefulSpecification.scala @@ -1,14 +1,13 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Longs import org.scalacheck.commands.Commands import org.scalacheck.{Gen, Prop} import org.scalatest.propspec.AnyPropSpec import scorex.crypto.authds._ import scorex.crypto.hash.{Blake2b256, Digest32} -import scorex.utils.{Random => RandomBytes} +import scorex.utils.{Longs, Random => RandomBytes} -import scala.util.{Failure, Random, Success, Try} +import scala.util.{Random, Try, Success, Failure} class AVLBatchStatefulSpecification extends AnyPropSpec { diff --git a/src/test/scala/scorex/crypto/hash/Blake2bSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/Blake2b512Specification.scala similarity index 73% rename from src/test/scala/scorex/crypto/hash/Blake2bSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/Blake2b512Specification.scala index 8a655c60..ed3d650d 100644 --- a/src/test/scala/scorex/crypto/hash/Blake2bSpecification.scala +++ b/jvm/src/test/scala/scorex/crypto/hash/Blake2b512Specification.scala @@ -1,6 +1,6 @@ package scorex.crypto.hash -class Blake2bSpecification extends HashTest { +class Blake2b512Specification extends HashTest { hashCheckString(Blake2b512, Map( @@ -10,11 +10,4 @@ class Blake2bSpecification extends HashTest { "abc" -> "BA80A53F981C4D0D6A2797B69F12F6E94C212F14685AC4B74B12BB6FDBFFA2D17D87C5392AAB792DC252D5DE4533CC9518D38AA8DBF1925AB92386EDD4009923") ) - hashCheckString(Blake2b256, - Map( - "" -> "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8", - "abc" -> "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319" - ) - ) - } diff --git a/src/test/scala/scorex/crypto/hash/Blake2bUnsafeSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/Blake2bUnsafeSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/Blake2bUnsafeSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/Blake2bUnsafeSpecification.scala diff --git a/src/test/scala/scorex/crypto/hash/KeccakSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/KeccakSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/KeccakSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/KeccakSpecification.scala diff --git a/src/test/scala/scorex/crypto/hash/SkeinSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/SkeinSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/SkeinSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/SkeinSpecification.scala diff --git a/src/test/scala/scorex/crypto/hash/StribogSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/StribogSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/StribogSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/StribogSpecification.scala diff --git a/src/test/scala/scorex/crypto/hash/WirlpoolSpecification.scala b/jvm/src/test/scala/scorex/crypto/hash/WirlpoolSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/WirlpoolSpecification.scala rename to jvm/src/test/scala/scorex/crypto/hash/WirlpoolSpecification.scala diff --git a/package.json b/package.json new file mode 100644 index 00000000..5dd17a63 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "scrypto-js", + "version": "0.0.1", + "description": "Scrypto.js library", + "license": "CC0", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/input-output-hk/scrypto.git" + }, + "homepage": "https://github.com/input-output-hk/scrypto/README.md", + "dependencies": { + "@noble/hashes": "^1.1.4" + }, + "devDependencies": { + "jest": "^29.0.3", + "shx": "^0.3.4", + "typescript": "^4.9.4" + } +} diff --git a/project/build.properties b/project/build.properties index e2820dd8..14095e16 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,2 +1,2 @@ -sbt.version=1.2.8 +sbt.version=1.5.4 diff --git a/project/plugins.sbt b/project/plugins.sbt index c6c825c8..c514d551 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,3 +14,8 @@ addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.8") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.0") addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.1.1") + +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") +addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.20.0") +addSbtPlugin("org.scalablytyped.converter" % "sbt-converter" % "1.0.0-beta37") \ No newline at end of file diff --git a/release-notes.md b/release-notes.md index c047ef38..83e46ab4 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,15 @@ + +**3.0.0** +--------- + +* CI configuration for Scala.js +* configuration of SBT with necessary JS dependencies (@noble/hashes) using ScalablyTyped +* removed all Java dependencies in `shared` code (jvm still uses BouncyCastle) +* necessary code changes for Scala.js cross-compilation +* the following classes moved to JVM-only module (with the corresponding tests): + Blake2b256Unsafe, Blake2b512, CryptographicHash64, Keccak, Keccak256, Keccak512, + Sha256Unsafe, Skein256, Skein512, Stribog256, Stribog512, ThreadUnsafeHash, Whirlpool + **2.2.1** --------- diff --git a/src/main/scala/scorex/crypto/authds/ProofIterator.scala b/shared/src/main/scala/scorex/crypto/authds/ProofIterator.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/ProofIterator.scala rename to shared/src/main/scala/scorex/crypto/authds/ProofIterator.scala diff --git a/src/main/scala/scorex/crypto/authds/TwoPartyDictionary.scala b/shared/src/main/scala/scorex/crypto/authds/TwoPartyDictionary.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/TwoPartyDictionary.scala rename to shared/src/main/scala/scorex/crypto/authds/TwoPartyDictionary.scala diff --git a/src/main/scala/scorex/crypto/authds/TwoPartyProof.scala b/shared/src/main/scala/scorex/crypto/authds/TwoPartyProof.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/TwoPartyProof.scala rename to shared/src/main/scala/scorex/crypto/authds/TwoPartyProof.scala diff --git a/src/main/scala/scorex/crypto/authds/TwoPartyProofElement.scala b/shared/src/main/scala/scorex/crypto/authds/TwoPartyProofElement.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/TwoPartyProofElement.scala rename to shared/src/main/scala/scorex/crypto/authds/TwoPartyProofElement.scala diff --git a/src/main/scala/scorex/crypto/authds/authds.scala b/shared/src/main/scala/scorex/crypto/authds/authds.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/authds.scala rename to shared/src/main/scala/scorex/crypto/authds/authds.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/AuthenticatedTreeOps.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/AuthenticatedTreeOps.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/AuthenticatedTreeOps.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/AuthenticatedTreeOps.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala similarity index 98% rename from src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala index a1505efb..8c2a0ae1 100644 --- a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala +++ b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLProver.scala @@ -1,14 +1,12 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Ints import scorex.crypto.authds._ import scorex.crypto.hash.{Blake2b256, CryptographicHash, Digest} -import scorex.util.ScorexLogging -import scorex.utils.ByteArray +import scorex.utils.{Ints, ByteArray, Logger} import scala.annotation.tailrec import scala.collection.mutable -import scala.util.{Failure, Random, Success, Try} +import scala.util.{Random, Try, Success, Failure} /** @@ -27,8 +25,8 @@ class BatchAVLProver[D <: Digest, HF <: CryptographicHash[D]](val keyLength: Int oldRootAndHeight: Option[(ProverNodes[D], Int)] = None, val collectChangedNodes: Boolean = true) (implicit val hf: HF = Blake2b256) - extends AuthenticatedTreeOps[D] with ToStringHelper with ScorexLogging { - + extends AuthenticatedTreeOps[D] with ToStringHelper { + val logger: Logger = Logger.Default protected val labelLength: Int = hf.DigestSize private[batch] var topNode: ProverNodes[D] = oldRootAndHeight.map(_._1).getOrElse({ @@ -446,7 +444,7 @@ class BatchAVLProver[D <: Digest, HF <: CryptographicHash[D]](val keyLength: Int if (!t) { var x = rNode.key(0).toInt if (x < 0) x = x + 256 - log.error("Tree failed at key = " + x + ": " + s) + logger.error("Tree failed at key = " + x + ": " + s) fail = true } } diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala similarity index 99% rename from src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala index 270dd0e6..e4ccb63f 100644 --- a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala +++ b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchAVLVerifier.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Ints +import scorex.utils.Ints import scorex.crypto.authds._ import scorex.crypto.hash._ import scorex.utils.ByteArray @@ -189,7 +189,7 @@ class BatchAVLVerifier[D <: Digest, HF <: CryptographicHash[D]](startingDigest: previousLeaf = None case LeafInPackagedProof => val key = if (previousLeaf.nonEmpty) { - ADKey @@ previousLeaf.get.nextLeafKey + ADKey @@@ previousLeaf.get.nextLeafKey } else { val start = i diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchNode.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchNode.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/BatchNode.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchNode.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/BatchProofConstants.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchProofConstants.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/BatchProofConstants.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/BatchProofConstants.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala similarity index 98% rename from src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala index 687d0aca..3da9c80b 100644 --- a/src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala +++ b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/Operation.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Longs +import scorex.utils.Longs import scorex.crypto.authds.{ADKey, ADValue} import scorex.util.ScorexEncoding diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/PersistentBatchAVLProver.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/PersistentBatchAVLProver.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/PersistentBatchAVLProver.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/PersistentBatchAVLProver.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/ToStringHelper.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/ToStringHelper.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/ToStringHelper.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/ToStringHelper.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorage.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorage.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorage.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorage.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverManifest.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverManifest.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverManifest.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverManifest.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala similarity index 94% rename from src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala index d3bc0176..fc2ff319 100644 --- a/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala +++ b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSerializer.scala @@ -1,15 +1,15 @@ package scorex.crypto.authds.avltree.batch.serialization -import com.google.common.primitives.{Bytes, Ints} -import scorex.crypto.authds.avltree.batch.{BatchAVLProver, InternalProverNode, ProverLeaf, ProverNodes} -import scorex.crypto.authds.{ADKey, ADValue, Balance} +import scorex.crypto.authds.avltree.batch.{BatchAVLProver, ProverLeaf, InternalProverNode, ProverNodes} +import scorex.crypto.authds.{ADValue, ADKey, Balance} import scorex.crypto.hash.{CryptographicHash, Digest} import scorex.util.encode.Base16 -import scorex.utils.ByteArray +import scorex.utils.{Bytes, Ints, ByteArray, Logger} import scala.util.Try -class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit val hf: HF) { +class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]] + (implicit val hf: HF, val logger: Logger) { serializer => private val labelLength = hf.DigestSize @@ -78,7 +78,9 @@ class BatchAVLProverSerializer[D <: Digest, HF <: CryptographicHash[D]](implicit case _: ProverLeaf[D] => } - new BatchAVLProver[D, HF](keyLength, valueLengthOpt, Some(manifest.root -> manifest.rootHeight)) + new BatchAVLProver[D, HF](keyLength, valueLengthOpt, Some(manifest.root -> manifest.rootHeight)) { + override val logger = serializer.logger + } } def manifestToBytes(manifest: BatchAVLProverManifest[D]): Array[Byte] = { diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSubtree.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSubtree.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSubtree.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/BatchAVLProverSubtree.scala diff --git a/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/ProxyInternalNode.scala b/shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/ProxyInternalNode.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/avltree/batch/serialization/ProxyInternalNode.scala rename to shared/src/main/scala/scorex/crypto/authds/avltree/batch/serialization/ProxyInternalNode.scala diff --git a/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala similarity index 98% rename from src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala index 1a30fa8e..441c07cc 100644 --- a/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala +++ b/shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLModifyProof.scala @@ -1,12 +1,11 @@ package scorex.crypto.authds.legacy.avltree -import com.google.common.primitives.Bytes import scorex.crypto.authds._ import scorex.crypto.authds.avltree.batch.Modification import scorex.crypto.hash.{CryptographicHash, _} -import scorex.utils.ByteArray +import scorex.utils.{ByteArray, Bytes} -import scala.util.{Failure, Success, Try} +import scala.util.{Try, Success, Failure} case class AVLModifyProof(key: ADKey, proofSeq: Seq[AVLProofElement]) (implicit hf: CryptographicHash[_ <: Digest]) extends TwoPartyProof { @@ -213,7 +212,7 @@ case class AVLModifyProof(key: ADKey, proofSeq: Seq[AVLProofElement]) initializeIterator() val (newTopNode, _, _, oldLabel) = verifyHelper(updateFn) - if (oldLabel sameElements digest) Some(ADDigest @@ newTopNode.label) else None + if (oldLabel sameElements digest) Some(ADDigest @@@ newTopNode.label) else None }.getOrElse(None) /** diff --git a/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala similarity index 99% rename from src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala index 74f028e1..cf7f483d 100644 --- a/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala +++ b/shared/src/main/scala/scorex/crypto/authds/legacy/avltree/AVLTree.scala @@ -22,7 +22,7 @@ class AVLTree[HF <: CryptographicHash[_ <: Digest]](keyLength: Int, private var topNode: ProverNodes = rootOpt.getOrElse(DefaultTopNode) - def rootHash(): ADDigest = ADDigest @@ topNode.label + def rootHash(): ADDigest = ADDigest @@@ topNode.label override def run[O <: Operation](operation: O): Try[AVLModifyProof] = Try { val key = operation.key diff --git a/src/main/scala/scorex/crypto/authds/legacy/avltree/Node.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/avltree/Node.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/legacy/avltree/Node.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/avltree/Node.scala diff --git a/src/main/scala/scorex/crypto/authds/legacy/treap/Constants.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Constants.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/legacy/treap/Constants.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/treap/Constants.scala diff --git a/src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala similarity index 96% rename from src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala index c2ebdfb7..0e935b66 100644 --- a/src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala +++ b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Level.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds.legacy.treap -import com.google.common.primitives.Ints +import scorex.utils.Ints import scorex.crypto.authds.ADKey import scorex.crypto.hash.Sha256 import scorex.utils.ByteArray diff --git a/src/main/scala/scorex/crypto/authds/legacy/treap/Node.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Node.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/legacy/treap/Node.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/treap/Node.scala diff --git a/src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala similarity index 99% rename from src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala index d9c68d75..f1e5d4e8 100644 --- a/src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala +++ b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/Treap.scala @@ -18,7 +18,7 @@ class Treap[HF <: CryptographicHash[_ <: Digest]](rootOpt: Option[Leaf] = None) var topNode: ProverNodes = rootOpt.getOrElse(Leaf(NegativeInfinity._1, NegativeInfinity._2, PositiveInfinity._1)) - def rootHash(): ADDigest = ADDigest @@ topNode.label + def rootHash(): ADDigest = ADDigest @@@ topNode.label override def run[O <: Operation](operation: O): Try[TreapModifyProof] = Try { val key = operation.key diff --git a/src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala similarity index 99% rename from src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala rename to shared/src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala index 616d1c4c..4baef73b 100644 --- a/src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala +++ b/shared/src/main/scala/scorex/crypto/authds/legacy/treap/TreapModifyProof.scala @@ -111,7 +111,7 @@ case class TreapModifyProof(key: ADKey, proofSeq: Seq[WTProofElement]) val (newTopNode, changeHappened, oldLabel) = verifyHelper() if (oldLabel sameElements digest) { - Some(ADDigest @@ newTopNode.label) + Some(ADDigest @@@ newTopNode.label) } else { None } diff --git a/src/main/scala/scorex/crypto/authds/merkle/BatchMerkleProof.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/BatchMerkleProof.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/merkle/BatchMerkleProof.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/BatchMerkleProof.scala diff --git a/src/main/scala/scorex/crypto/authds/merkle/MerkleProof.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/MerkleProof.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/merkle/MerkleProof.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/MerkleProof.scala diff --git a/src/main/scala/scorex/crypto/authds/merkle/MerkleTree.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/MerkleTree.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/merkle/MerkleTree.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/MerkleTree.scala diff --git a/src/main/scala/scorex/crypto/authds/merkle/Node.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/Node.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/merkle/Node.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/Node.scala diff --git a/src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala similarity index 98% rename from src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala index 7d156a23..4602289b 100644 --- a/src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala +++ b/shared/src/main/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializer.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds.merkle.serialization -import com.google.common.primitives.{Bytes, Ints} +import scorex.utils.{Bytes, Ints} import scorex.crypto.authds.merkle.BatchMerkleProof import scorex.crypto.authds.{EmptyByteArray, Side} import scorex.crypto.hash.{CryptographicHash, Digest, Digest32} diff --git a/src/main/scala/scorex/crypto/authds/merkle/sparse/Node.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/sparse/Node.scala similarity index 100% rename from src/main/scala/scorex/crypto/authds/merkle/sparse/Node.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/sparse/Node.scala diff --git a/src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala b/shared/src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala similarity index 59% rename from src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala rename to shared/src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala index 9562b707..4d827a31 100644 --- a/src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala +++ b/shared/src/main/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTree.scala @@ -1,6 +1,5 @@ package scorex.crypto.authds.merkle.sparse -import com.google.common.primitives.Longs import scorex.crypto.authds.LeafData import scorex.crypto.hash._ @@ -174,139 +173,5 @@ case class SparseMerkleProof[D <: Digest](idx: Node.ID, def valid(tree: SparseMerkleTree[D])(implicit hf: CryptographicHash[D]): Boolean = valid(tree.rootDigest, tree.height) } -object OpsBenchmark extends App { - implicit val hf: CryptographicHash[Digest32] = new Blake2b256Unsafe - val height: Byte = 50 - var tree = SparseMerkleTree.emptyTree(height) - - //bootstrapping - - val bSize = 10000 - - var proof: SparseMerkleProof[Digest32] = null - - val tb0 = System.currentTimeMillis() - (0 until bSize).foreach {i => - if(i == bSize/2) proof = tree.lastProof - - val proofsToUpdate = if(i >= bSize/2) { - Seq(proof) - } else Seq.empty[SparseMerkleProof[Digest32]] - - if(i%1000 == 0) println(s"$i elements added") - val (t, p) = tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(i)), proofsToUpdate).get - - tree = t - if(i >= bSize/2) proof = p.head - } - val tb = System.currentTimeMillis() - println(s"bootstrapping time: ${tb-tb0}") - - val tv0 = System.currentTimeMillis() - (1 to 1000).foreach(i => proof.valid(tree)) - val tv = System.currentTimeMillis() - println(s"1000 updates time: ${tv-tv0} ms") - - - - val te0 = System.currentTimeMillis() - (1 to 1000).foreach(_ => - tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(2)), Seq()) - ) - val te = System.currentTimeMillis() - - val tp0 = System.currentTimeMillis() - (1 to 1000).foreach(_ => - tree.update(tree.lastProof, Some(LeafData @@ Longs.toByteArray(2)), Seq(proof)) - ) - val tp = System.currentTimeMillis() - - val tu = (tp-tp0) - (te-te0) - - println("1000 proof updates: " + tu) -} - -object BlockchainSimulator extends App { - - type PubKey = Array[Byte] - - val PubKeyLength = 32 - - implicit val hf: CryptographicHash[Digest32] = new Blake2b256Unsafe - - case class Transaction(amount: Long, - sender: PubKey, - recipient: PubKey, - coinBalance: Long, - coinProof: SparseMerkleProof[Digest32]) - - object Transaction { - def coinBytes(pubKey: PubKey, balance: Long) = Some(LeafData @@ (pubKey ++ Longs.toByteArray(balance))) - - def process(tx: Transaction, - state: SparseMerkleTree[Digest32]): - Try[(SparseMerkleTree[Digest32], Seq[SparseMerkleProof[Digest32]])] = Try { - - require(tx.amount <= tx.coinBalance) - require(tx.coinProof.leafDataOpt.get sameElements coinBytes(tx.sender, tx.coinBalance).get) - require(tx.coinProof.valid(state.rootDigest, height)) - - val (state1, _) = state.update(tx.coinProof, None).get - - val (state2, proofs2) = state1.update(state1.lastProof, - coinBytes(tx.recipient, tx.amount), - Seq(state1.lastProof)).get - - if (tx.amount == tx.coinBalance) state2 -> proofs2 - else state2.update(state2.lastProof, - coinBytes(tx.sender, tx.coinBalance - tx.amount), - proofs2 :+ state2.lastProof).get - } - } - - case class Block(transactions: Seq[Transaction]) - - val txsCache = new mutable.ArrayBuffer() - val maxTxsCacheSize = 5000 - - val txsPerBlock = 1000 - val numOfBlocks = 1000000 - - val height = 30: Byte - - val godAccount = Array.fill(32)(0: Byte) - val godBalance = 100000000000L //100B - - val emptyState = SparseMerkleTree.emptyTree(height) - val (initialState, godProofs) = emptyState.update(emptyState.lastProof, - Transaction.coinBytes(godAccount, godBalance), - Seq(emptyState.lastProof)).get - - var godProof = godProofs.head - var currentGodBalance = godBalance - val txAmount = 10 - - (1 to numOfBlocks).foldLeft(initialState) { case (beforeBlocktree, blockNum) => - val (afterTree, processingTime) = (1 to txsPerBlock).foldLeft(beforeBlocktree -> 0L) { case ((tree, totalTime), txNum) => - val recipient = hf(scala.util.Random.nextString(20)) - - val tx = Transaction(txAmount, godAccount, recipient, currentGodBalance, godProof) - - val t0 = System.currentTimeMillis() - val (updState, proofs) = Transaction.process(tx, tree).get //we generate always valid transaction - val t = System.currentTimeMillis() - - currentGodBalance = currentGodBalance - txAmount - godProof = proofs.last - - updState -> (totalTime + (t - t0)) - } - - println(s"Block $blockNum, processing time: $processingTime ms") - println(godProof) - - afterTree - } -} \ No newline at end of file diff --git a/src/main/scala/scorex/crypto/encode/package.scala b/shared/src/main/scala/scorex/crypto/encode/package.scala similarity index 100% rename from src/main/scala/scorex/crypto/encode/package.scala rename to shared/src/main/scala/scorex/crypto/encode/package.scala diff --git a/shared/src/main/scala/scorex/crypto/hash/Blake2b.scala b/shared/src/main/scala/scorex/crypto/hash/Blake2b.scala new file mode 100644 index 00000000..6a66183f --- /dev/null +++ b/shared/src/main/scala/scorex/crypto/hash/Blake2b.scala @@ -0,0 +1,9 @@ +package scorex.crypto.hash + + +trait Blake2b[D <: Digest] extends BouncyCastleHash[D] { + override protected def digestFn = createBlake2bDigest(DigestSize * 8) +} + + + diff --git a/src/main/scala/scorex/crypto/hash/Blake2b256.scala b/shared/src/main/scala/scorex/crypto/hash/Blake2b256.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/Blake2b256.scala rename to shared/src/main/scala/scorex/crypto/hash/Blake2b256.scala diff --git a/shared/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala b/shared/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala new file mode 100644 index 00000000..04d65edc --- /dev/null +++ b/shared/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala @@ -0,0 +1,29 @@ +package scorex.crypto.hash + +/** Default implementation of hash generation using bouncycastle-style pair of methods + * update/doFinal. + * The only thing you need to do is to provide digestFn, which creates the correct digest + * for your hash function. + */ +trait BouncyCastleHash[D <: Digest] extends CryptographicHash[D] { + + /** Compute the hash by creating a digest, updating it with the messages and then + * finalizing. */ + protected def internalHash(inputs: Message*): Array[Byte] = synchronized { + val digest = digestFn + inputs.foreach(i => updateDigest(digest, i, 0, i.length)) + doFinalDigest(digest) + } + + /** Compute the hash by creating a digest, updating it with the prefix and the messages + * and then finalizing. */ + protected def internalPrefixedHash(prefix: Byte, inputs: Message*): Array[Byte] = synchronized { + val digest = digestFn + updateDigest(digest, prefix) + inputs.foreach(i => updateDigest(digest, i, 0, i.length)) + doFinalDigest(digest) + } + + /** Should be overriden to provide appropriate Digest instance. */ + protected def digestFn: ExtendedDigest +} diff --git a/src/main/scala/scorex/crypto/hash/CommutativeHash.scala b/shared/src/main/scala/scorex/crypto/hash/CommutativeHash.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/CommutativeHash.scala rename to shared/src/main/scala/scorex/crypto/hash/CommutativeHash.scala diff --git a/src/main/scala/scorex/crypto/hash/CryptographicHash.scala b/shared/src/main/scala/scorex/crypto/hash/CryptographicHash.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/CryptographicHash.scala rename to shared/src/main/scala/scorex/crypto/hash/CryptographicHash.scala diff --git a/src/main/scala/scorex/crypto/hash/CryptographicHash32.scala b/shared/src/main/scala/scorex/crypto/hash/CryptographicHash32.scala similarity index 100% rename from src/main/scala/scorex/crypto/hash/CryptographicHash32.scala rename to shared/src/main/scala/scorex/crypto/hash/CryptographicHash32.scala diff --git a/shared/src/main/scala/scorex/crypto/hash/Sha256.scala b/shared/src/main/scala/scorex/crypto/hash/Sha256.scala new file mode 100644 index 00000000..ce2ddf42 --- /dev/null +++ b/shared/src/main/scala/scorex/crypto/hash/Sha256.scala @@ -0,0 +1,13 @@ +package scorex.crypto.hash + +/** + * Hashing functions implementation with sha256 impl from Java SDK + */ +object Sha256 extends CryptographicHash32 with BouncyCastleHash[Digest32] { + override def hash(input: Array[Byte]): Digest32 = Digest32 @@ internalHash(input) + + override protected def digestFn = createSha256Digest() + + override def prefixedHash(prefix: Byte, inputs: Array[Byte]*): Digest32 = + Digest32 @@ internalPrefixedHash(prefix, inputs: _*) +} \ No newline at end of file diff --git a/shared/src/main/scala/scorex/crypto/hash/hash.scala b/shared/src/main/scala/scorex/crypto/hash/hash.scala new file mode 100644 index 00000000..1924d472 --- /dev/null +++ b/shared/src/main/scala/scorex/crypto/hash/hash.scala @@ -0,0 +1,34 @@ +package scorex.crypto + +import supertagged.TaggedType + +package object hash { + + trait BaseDigest extends TaggedType[Array[Byte]] + + type Digest = BaseDigest#Type + + object Digest32 extends BaseDigest + + type Digest32 = Digest32.Type + + object Digest64 extends BaseDigest + + type Digest64 = Digest64.Type + + object NonStandardDigest extends BaseDigest + + type NonStandardDigest = NonStandardDigest.Type + + type ExtendedDigest = Platform.Digest + + def createBlake2bDigest(bitSize: Int): ExtendedDigest = Platform.createBlake2bDigest(bitSize) + + def createSha256Digest(): ExtendedDigest = Platform.createSha256Digest() + + def updateDigest(digest: ExtendedDigest, b: Byte) = Platform.updateDigest(digest, b) + + def updateDigest(digest: ExtendedDigest, in: Array[Byte], inOff: Int, inLen: Int) = Platform.updateDigest(digest, in, inOff, inLen) + + def doFinalDigest(digest: ExtendedDigest): Array[Byte] = Platform.doFinalDigest(digest) +} diff --git a/src/main/scala/scorex/utils/Booleans.scala b/shared/src/main/scala/scorex/utils/Booleans.scala similarity index 100% rename from src/main/scala/scorex/utils/Booleans.scala rename to shared/src/main/scala/scorex/utils/Booleans.scala diff --git a/src/main/scala/scorex/utils/ByteArray.scala b/shared/src/main/scala/scorex/utils/ByteArray.scala similarity index 90% rename from src/main/scala/scorex/utils/ByteArray.scala rename to shared/src/main/scala/scorex/utils/ByteArray.scala index a5dd4147..175b7aba 100644 --- a/src/main/scala/scorex/utils/ByteArray.scala +++ b/shared/src/main/scala/scorex/utils/ByteArray.scala @@ -1,7 +1,5 @@ package scorex.utils -import com.google.common.primitives.UnsignedBytes - object ByteArray { def compare(buffer1: Array[Byte], buffer2: Array[Byte]): Int = diff --git a/shared/src/main/scala/scorex/utils/Bytes.scala b/shared/src/main/scala/scorex/utils/Bytes.scala new file mode 100644 index 00000000..a274400e --- /dev/null +++ b/shared/src/main/scala/scorex/utils/Bytes.scala @@ -0,0 +1,33 @@ +package scorex.utils + +object Bytes { + + /** + * Returns the values from each provided array combined into a single array. For example, + * {@code concat(new byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, + * c}}. + * + * @param arrays zero or more {@code byte} arrays + * @return a single array containing all the values from the source arrays, in order + */ + def concat(arrays: Array[Byte]*): Array[Byte] = { + var length = 0 + val nArrays = arrays.length + var i = 0 + while (i < nArrays) { + length += arrays(i).length + i += 1 + } + val result = new Array[Byte](length) + var pos = 0 + i = 0 + while (i < nArrays) { + val array = arrays(i) + System.arraycopy(array, 0, result, pos, array.length) + pos += array.length + i += 1 + } + result + } + +} diff --git a/shared/src/main/scala/scorex/utils/Ints.scala b/shared/src/main/scala/scorex/utils/Ints.scala new file mode 100644 index 00000000..927ffa40 --- /dev/null +++ b/shared/src/main/scala/scorex/utils/Ints.scala @@ -0,0 +1,43 @@ +package scorex.utils + +object Ints { + /** + * The number of bytes required to represent a primitive {@code int} value. + * + *

Java 8 users: use {@link Integer# BYTES} instead. + */ + val BYTES: Int = Integer.SIZE / java.lang.Byte.SIZE + + /** + * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to + * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value + * {@code 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}. + */ + def toByteArray(value: Int): Array[Byte] = { + Array[Byte]((value >> 24).toByte, (value >> 16).toByte, (value >> 8).toByte, value.toByte) + } + + /** + * Returns the {@code int} value whose big-endian representation is stored in the first 4 bytes of + * {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getInt()}. For example, the input + * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x33}} would yield the {@code int} value + * {@code 0x12131415}. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 4 elements + */ + def fromByteArray(bytes: Array[Byte]): Int = { + require(bytes.length >= BYTES, s"array too small: ${bytes.length} < $BYTES") + fromBytes(bytes(0), bytes(1), bytes(2), bytes(3)) + } + + /** + * Returns the {@code int} value whose byte representation is the given 4 bytes, in big-endian + * order; equivalent to {@code Ints.fromByteArray(new byte[] {b1, b2, b3, b4})}. + */ + def fromBytes(b1: Byte, + b2: Byte, + b3: Byte, + b4: Byte): Int = { + b1 << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF) + } +} diff --git a/shared/src/main/scala/scorex/utils/Logger.scala b/shared/src/main/scala/scorex/utils/Logger.scala new file mode 100644 index 00000000..4ccb8d8c --- /dev/null +++ b/shared/src/main/scala/scorex/utils/Logger.scala @@ -0,0 +1,13 @@ +package scorex.utils + +abstract class Logger { + def error(message: String): Unit +} + +object Logger { + val Default = new Logger { + override def error(message: String): Unit = { + println(message) + } + } +} diff --git a/shared/src/main/scala/scorex/utils/Longs.scala b/shared/src/main/scala/scorex/utils/Longs.scala new file mode 100644 index 00000000..0dd8b7b1 --- /dev/null +++ b/shared/src/main/scala/scorex/utils/Longs.scala @@ -0,0 +1,61 @@ +package scorex.utils + +/** Operations with long values. The implementation is based on com.google.common.primitives.Longs. */ +object Longs { + /** + * The number of bytes required to represent a primitive {@code long} value. + * + *

Java 8 users: use {@link Long# BYTES} instead. + */ + val BYTES: Int = java.lang.Long.SIZE / java.lang.Byte.SIZE + + /** + * Returns a big-endian representation of {@code value} in an 8-element byte array; equivalent to + * {@code ByteBuffer.allocate(8).putLong(value).array()}. For example, the input value + * {@code 0x1213141516171819L} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, + * 0x17, 0x18, 0x19}}. + */ + def toByteArray(value: Long): Array[Byte] = { + // Note that this code needs to stay compatible with GWT, which has known + // bugs when narrowing byte casts of long values occur. + var v = value + val result = new Array[Byte](8) + var i = 7 + while (i >= 0) { + result(i) = (v & 0xffL).toByte + v >>= 8 + i -= 1 + } + result + } + + /** + * Returns the {@code long} value whose big-endian representation is stored in the first 8 bytes + * of {@code bytes}; equivalent to {@code ByteBuffer.wrap(bytes).getLong()}. For example, the + * input byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}} would yield the + * {@code long} value {@code 0x1213141516171819L}. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than 8 elements + */ + def fromByteArray(bytes: Array[Byte]): Long = { + require(bytes.length >= BYTES, s"array too small: ${bytes.length} < $BYTES") + fromBytes(bytes(0), bytes(1), bytes(2), bytes(3), bytes(4), bytes(5), bytes(6), bytes(7)) + } + + /** + * Returns the {@code long} value whose byte representation is the given 8 bytes, in big-endian + * order; equivalent to {@code Longs.fromByteArray(new byte[] {b1, b2, b3, b4, b5, b6, b7, b8})}. + * + * @since 7.0 + */ + def fromBytes(b1: Byte, + b2: Byte, + b3: Byte, + b4: Byte, + b5: Byte, + b6: Byte, + b7: Byte, + b8: Byte): Long = { + (b1 & 0xFFL) << 56 | (b2 & 0xFFL) << 48 | (b3 & 0xFFL) << 40 | (b4 & 0xFFL) << 32 | (b5 & 0xFFL) << 24 | (b6 & 0xFFL) << 16 | (b7 & 0xFFL) << 8 | (b8 & 0xFFL) + } +} diff --git a/src/main/scala/scorex/utils/Random.scala b/shared/src/main/scala/scorex/utils/Random.scala similarity index 100% rename from src/main/scala/scorex/utils/Random.scala rename to shared/src/main/scala/scorex/utils/Random.scala diff --git a/shared/src/main/scala/scorex/utils/Shorts.scala b/shared/src/main/scala/scorex/utils/Shorts.scala new file mode 100644 index 00000000..534f662b --- /dev/null +++ b/shared/src/main/scala/scorex/utils/Shorts.scala @@ -0,0 +1,12 @@ +package scorex.utils + +object Shorts { + /** + * Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to + * {@code ByteBuffer.allocate(2).putShort(value).array()}. For example, the input value {@code + * (short) 0x1234} would yield the byte array {@code {0x12, 0x34}}. + */ + def toByteArray(value: Short): Array[Byte] = { + Array[Byte]((value >> 8).toByte, value.toByte) + } +} diff --git a/shared/src/main/scala/scorex/utils/UnsignedBytes.scala b/shared/src/main/scala/scorex/utils/UnsignedBytes.scala new file mode 100644 index 00000000..da060898 --- /dev/null +++ b/shared/src/main/scala/scorex/utils/UnsignedBytes.scala @@ -0,0 +1,63 @@ +package scorex.utils + +import java.util.Comparator + +/** + * Static utility methods pertaining to {@code byte} primitives that interpret values as + * unsigned (that is, any negative value {@code b} is treated as the positive value + * {@code 256 + b}). + */ +object UnsignedBytes { + + private val UNSIGNED_MASK = 0xFF + + /** + * Returns the value of the given byte as an integer, when treated as unsigned. That is, returns + * {@code value + 256} if {@code value} is negative; {@code value} itself otherwise. + */ + @inline def toInt(value: Byte): Int = value & UNSIGNED_MASK + + /** + * Compares the two specified {@code byte} values, treating them as unsigned values between 0 and + * 255 inclusive. For example, {@code (byte) -127} is considered greater than {@code (byte) 127} + * because it is seen as having the value of positive {@code 129}. + * + * @param a the first {@code byte} to compare + * @param b the second {@code byte} to compare + * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is + * greater than {@code b}; or zero if they are equal + */ + @inline def compare(a: Byte, b: Byte): Int = toInt(a) - toInt(b) + + /** + * Returns a comparator that compares two {@code byte} arrays lexicographically. That is, it + * compares, using {@link # compare ( byte, byte)}), the first pair of values that follow any common + * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For + * example, {@code [] < [0x01] < [0x01, 0x7F] < [0x01, 0x80] < [0x02]}. Values are treated as + * unsigned. + * + *

The returned comparator is inconsistent with {@link Object# equals ( Object )} (since arrays + * support only identity equality), but it is consistent with + * {@link java.util.Arrays# equals ( byte [ ], byte[])}. + */ + // TODO optimize: use Unsafe for more efficient implementation (as in original guava) + def lexicographicalComparator(): Comparator[Array[Byte]] = PureJavaComparator + + object PureJavaComparator extends Comparator[Array[Byte]] { + + override def compare(left: Array[Byte], right: Array[Byte]): Int = { + val minLength = Math.min(left.length, right.length) + var i = 0 + while (i < minLength) { + val result = UnsignedBytes.compare(left(i), right(i)) + if (result != 0) return result + i += 1 + } + left.length - right.length + } + + } + +} + diff --git a/src/main/scala/scorex/utils/package.scala b/shared/src/main/scala/scorex/utils/package.scala similarity index 57% rename from src/main/scala/scorex/utils/package.scala rename to shared/src/main/scala/scorex/utils/package.scala index 39f18064..d1cbdf3e 100644 --- a/src/main/scala/scorex/utils/package.scala +++ b/shared/src/main/scala/scorex/utils/package.scala @@ -2,9 +2,6 @@ package scorex package object utils { - @deprecated("Use scorex.util.ScorexLogging instead.", "scorex-util 0.1.0") - type ScryptoLogging = scorex.util.ScorexLogging - @deprecated("Use scorex.util.ScorexEncoding instead.", "scorex-util 0.1.1") type ScorexEncoding = scorex.util.ScorexEncoding } diff --git a/shared/src/test/scala/scorex/ScalaJsSpec.scala b/shared/src/test/scala/scorex/ScalaJsSpec.scala new file mode 100644 index 00000000..5d5122ba --- /dev/null +++ b/shared/src/test/scala/scorex/ScalaJsSpec.scala @@ -0,0 +1,154 @@ +package scorex + +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec + +import java.io.{StringWriter, PrintWriter} +import java.lang.annotation.{ElementType, Target} +import java.lang.reflect.{UndeclaredThrowableException, InvocationTargetException} +import java.nio.ByteBuffer +import java.util +import scala.collection.mutable +import scala.io.Source +import scala.reflect.ClassTag + +/** This suite is used to check different Scala features and base libraries against + * Scala.js, i.e. whether they are supported or not. + * The `// Not supported` code fails at compile time when compiled as part of scrypto/js. + * See commented code for what is not supported. + */ +class ScalaJsSpec extends AnyPropSpec with Matchers { + class A { + val f: Int = 10 + def m(): String = "m.toString" + } + class Z extends A + + val x = new A + val y = new A + val z = new Z + val cls = x.getClass + val clsY = y.getClass + val clsZ = z.getClass + + property("getSimpleName") { + cls shouldNot be(null) + cls.getSimpleName shouldBe "A" + // Not supported + // cls.getEnclosingClass shouldNot be (null) // Referring to non-existent method java.lang.Class.getEnclosingClass() + println(cls) + } + + property("getName") { + cls.getName shouldBe "scorex.ScalaJsSpec$A" + val t = mutable.HashMap.empty[Class[_], Int] + t.put(cls, 1) + t.get(clsY) shouldBe Some(1) + } + +// Not supported +// property("getField") { +//// cls.getField("f") shouldNot be(null) // Referring to non-existent method java.lang.Class.getField(java.lang.String) +// } +// +// property("getConstructors") { +//// cls.getConstructors shouldNot be(null) // Referring to non-existent method java.lang.Class.getConstructors() +// } + + property("isPrimitive") { + cls.isPrimitive shouldBe false + classOf[Int].isPrimitive shouldBe true + } + + property("getSuperclass") { + cls.getSuperclass.getName shouldBe "java.lang.Object" + } + +// Not supported +// property("getDeclaringClass") { +// cls.getDeclaringClass.getName shouldBe "ScalaJsSpec" // Referring to non-existent method java.lang.Class.getDeclaringClass() +// } + + property("isAssignableFrom") { + cls.isAssignableFrom(clsY) shouldBe true + cls.isAssignableFrom(classOf[java.lang.Object]) shouldBe false + cls.isAssignableFrom(clsZ) shouldBe true + } + + property("PrintWriter") { + val pr = new PrintWriter(new StringWriter(100)) + pr.println("test") + } + + property("ByteBuffer") { + val bytes = Array[Byte](1, 2, 3) + val buf = ByteBuffer.wrap(bytes) + buf.position() shouldBe 0 + } + +// Not supported +// property("InvocationTargetException") { +// // [error] Referring to non-existent class java.lang.reflect.InvocationTargetException +// an[InvocationTargetException] should be thrownBy(throw new InvocationTargetException(null)) +// } + +// property("UndeclaredThrowableException") { +// // [error] Referring to non-existent class java.lang.reflect.UndeclaredThrowableException +// an[UndeclaredThrowableException] should be thrownBy(throw new UndeclaredThrowableException(null)) +// } + + property("ExceptionInInitializerError") { + // Not supported + // [error] Referring to non-existent class java.lang.reflect.UndeclaredThrowableException + an[ExceptionInInitializerError] should be thrownBy(throw new ExceptionInInitializerError("error")) + } + + property("ClassTag") { + val t = ClassTag[String](classOf[String]) + t.toString() shouldBe "java.lang.String" + } + +// Not supported +// property("Class.forName") { +// val t = Class.forName("java.lang.String") // error: Referring to non-existent method static java.lang.Class.forName(java.lang.String) +// t.toString() shouldBe "java.lang.String" +// } + + property("ClassTag.runtimeClass") { + val t = ClassTag[String](classOf[String]) + t.runtimeClass shouldBe classOf[String] + } + + property("NoSuchMethodException") { + try { + throw new NoSuchMethodException("methodName") + assert(false) + } + catch { + case e: NoSuchMethodException => + e.getMessage shouldBe "methodName" + } + } + + property("NoSuchFieldException") { + try { + throw new NoSuchFieldException("fieldName") + assert(false) + } + catch { + case e: NoSuchFieldException => + e.getMessage shouldBe "fieldName" + } + } + + property("java.util.HashMap") { + val m = new java.util.HashMap[Int, Int]() + m.put(1, 10) + m.get(1) shouldBe 10 + } + + property("scala.io.Source") { + val lines = Source.fromString("abc").getLines.toSeq + lines.length shouldBe 1 + } +} diff --git a/src/test/scala/scorex/crypto/TestingCommons.scala b/shared/src/test/scala/scorex/crypto/TestingCommons.scala similarity index 89% rename from src/test/scala/scorex/crypto/TestingCommons.scala rename to shared/src/test/scala/scorex/crypto/TestingCommons.scala index 7f0aa1f2..7b30cc86 100644 --- a/src/test/scala/scorex/crypto/TestingCommons.scala +++ b/shared/src/test/scala/scorex/crypto/TestingCommons.scala @@ -8,9 +8,6 @@ import org.scalatest.matchers.should.Matchers import scala.util.Random trait TestingCommons extends Matchers { - val dirName = "/tmp/scorex-test/test/" - val treeDir = new File(dirName) - treeDir.mkdirs() def genElements(howMany: Int, seed: Long, size: Int = 32): Seq[Array[Byte]] = { val r = Random diff --git a/src/test/scala/scorex/crypto/authds/TwoPartyTests.scala b/shared/src/test/scala/scorex/crypto/authds/TwoPartyTests.scala similarity index 95% rename from src/test/scala/scorex/crypto/authds/TwoPartyTests.scala rename to shared/src/test/scala/scorex/crypto/authds/TwoPartyTests.scala index 62157a43..99eda937 100644 --- a/src/test/scala/scorex/crypto/authds/TwoPartyTests.scala +++ b/shared/src/test/scala/scorex/crypto/authds/TwoPartyTests.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds -import com.google.common.primitives.Longs +import scorex.utils.{Longs, Logger} import scorex.crypto.TestingCommons import scorex.crypto.authds.avltree.batch.{Modification, Update} import scorex.crypto.hash.Digest @@ -10,6 +10,8 @@ import scala.util.Success trait TwoPartyTests extends TestingCommons { + implicit val loggerInTests: Logger = Logger.Default + def genUpd(key: ADKey) = Update(key, ADValue @@ key.take(8)) def profileTree(tree: TwoPartyDictionary, elements: Seq[ADKey], inDigest: ADDigest): Seq[Float] = { diff --git a/src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala similarity index 91% rename from src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala index 466e2c76..8cf73a37 100644 --- a/src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/avltree/AVLDeleteSpecification.scala @@ -4,8 +4,9 @@ package scorex.crypto.authds.avltree import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.{ADKey, ADValue, TwoPartyTests} +import scorex.crypto.authds.{ADValue, TwoPartyTests, ADKey} import scorex.crypto.hash.{Blake2b256, Digest32, Sha256} +import scorex.utils.Logger class AVLDeleteSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChecks with TwoPartyTests { diff --git a/src/test/scala/scorex/crypto/authds/avltree/AVLTreeSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/AVLTreeSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/authds/avltree/AVLTreeSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/AVLTreeSpecification.scala diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala similarity index 98% rename from src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala index 9c16e758..efa95f9c 100644 --- a/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/AVLBatchSpecification.scala @@ -1,17 +1,16 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Longs -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.{Gen, Arbitrary} import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import scorex.crypto.authds.legacy.avltree.AVLTree import scorex.crypto.authds._ -import scorex.util.encode.{Base16, Base58} +import scorex.util.encode.{Base58, Base16} import scorex.crypto.hash._ -import scorex.utils.{ByteArray, Random} +import scorex.utils.{Random, ByteArray, Longs} import scala.util.Random.{nextInt => randomInt} -import scala.util.{Failure, Try} +import scala.util.{Try, Failure} class AVLBatchSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChecks with TwoPartyTests with BatchTestingHelpers { @@ -201,8 +200,8 @@ class AVLBatchSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChe val prover = new BatchAVLProver[D, HF](KL, None) val digest = prover.digest val keyValues = (0 until InitialTreeSize) map { i => - val aValue = Keccak256(i.toString.getBytes("UTF-8")) - (ADKey @@ aValue.take(KL), ADValue @@ aValue) + val aValue = Blake2b256(i.toString.getBytes("UTF-8")) + (ADKey @@ aValue.take(KL), ADValue @@@ aValue) } keyValues.foreach(kv => prover.performOneOperation(Insert(kv._1, kv._2))) @@ -219,8 +218,8 @@ class AVLBatchSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChe } (0 until InitialTreeSize) foreach { i => - val aValue = Keccak256(i.toString.getBytes("UTF-8")) - verifier.performOneOperation(Insert(ADKey @@ aValue.take(KL), ADValue @@ aValue)) + val aValue = Blake2b256(i.toString.getBytes("UTF-8")) + verifier.performOneOperation(Insert(ADKey @@ aValue.take(KL), ADValue @@@ aValue)) } //extract all leafs val allLeafs = verifier.extractNodes(nonInfiniteLeaf) @@ -236,8 +235,8 @@ class AVLBatchSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChe val prover = new BatchAVLProver[D, HF](KL, None) val digest = prover.digest val keyValues = (0 until InitialTreeSize) map { i => - val aValue = Keccak256(i.toString.getBytes("UTF-8")) - (ADKey @@ aValue.take(KL), ADValue @@ aValue) + val aValue = Blake2b256(i.toString.getBytes("UTF-8")) + (ADKey @@ aValue.take(KL), ADValue @@@ aValue) } keyValues.foreach(kv => prover.performOneOperation(Insert(kv._1, kv._2))) @@ -504,7 +503,7 @@ class AVLBatchSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChe digest = p.digest val key = randomKey(KL) - p.performOneOperation(Insert(ADKey @@ key, randomValue(8))) + p.performOneOperation(Insert(ADKey @@@ key, randomValue(8))) pf = p.generateProof() p.checkTree() diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTesting.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTesting.scala similarity index 100% rename from src/test/scala/scorex/crypto/authds/avltree/batch/BatchTesting.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTesting.scala diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTestingHelpers.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTestingHelpers.scala similarity index 100% rename from src/test/scala/scorex/crypto/authds/avltree/batch/BatchTestingHelpers.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchTestingHelpers.scala diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala similarity index 98% rename from src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala index 511010b3..945fd545 100644 --- a/src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala +++ b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/BatchingPlayground.scala @@ -1,6 +1,6 @@ package scorex.crypto.authds.avltree.batch -import com.google.common.primitives.Longs +import scorex.utils.Longs import org.scalatest.matchers.should.Matchers import scorex.crypto.authds.legacy.avltree.{AVLModifyProof, AVLTree} import scorex.crypto.authds.{ADDigest, ADKey, ADValue} @@ -54,7 +54,7 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { System.gc() val toRemove = elements.slice(i * toRemoveSize, (i + 1) * toRemoveSize).map(e => Remove(e._1)) val toInsert = (0 until toInsertSize).map(j => Sha256(s"$i-$j")) - .map(k => Insert(ADKey @@ k, ADValue @@ k.take(8))) + .map(k => Insert(ADKey @@@ k, ADValue @@ k.take(8))) val treeSize = startTreeSize + i * (toInsert.length - toRemove.length) val oldTop = prover.topNode val mods = toRemove ++ toInsert @@ -93,7 +93,7 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { val ElementsToInsert = 100000 val elements = (0 until ElementsToInsert) .map(i => Sha256(i.toString)) - .map(k => (ADKey @@ k, ADValue @@ k.take(8))) + .map(k => (ADKey @@@ k, ADValue @@ k.take(8))) elements.foreach(e => prover.performOneOperation(Insert(e._1, e._2))) prover.generateProof() @@ -690,7 +690,7 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { for (i <- 0 until testAtTheEnd) { val key = randomKey() keys += key - val m = Insert(ADKey @@ key, randomValue(8)) + val m = Insert(ADKey @@@ key, randomValue(8)) newProver.performOneOperation(m) len += newProver.generateProof().length } @@ -700,7 +700,7 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { len = 0 for (i <- 0 until testAtTheEnd) { val j = Random.randomBytes(3) - val key = ADKey @@ keys( + val key = ADKey @@@ keys( (j(0).toInt.abs + j(1).toInt.abs * 128 + j(2).toInt.abs * 128 * 128) % keys.size) keys -= key val m = Remove(key) @@ -786,7 +786,7 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { digest = p.digest val key = randomKey() - p.performOneOperation(Insert(ADKey @@ key, randomValue(8))) + p.performOneOperation(Insert(ADKey @@@ key, randomValue(8))) pf = p.generateProof() p.checkTree() @@ -1032,16 +1032,16 @@ object BatchingPlayground extends App with BatchTestingHelpers with Matchers { val key1 = ADKey @@ Array(1: Byte) val key2 = ADKey @@ Array(2: Byte) val key3 = ADKey @@ Array(3: Byte) - val op1 = Insert(key1, ADValue @@ Longs.toByteArray(10)) - val op2 = Insert(key2, ADValue @@ Longs.toByteArray(20)) - val op3 = Insert(key3, ADValue @@ Longs.toByteArray(30)) + val op1 = Insert(key1, ADValue @@ Longs.toByteArray(10L)) + val op2 = Insert(key2, ADValue @@ Longs.toByteArray(20L)) + val op3 = Insert(key3, ADValue @@ Longs.toByteArray(30L)) require(prover.performOneOperation(op1).get.isEmpty) // Should return None require(prover.performOneOperation(op2).get.isEmpty) // Should return None require(prover.performOneOperation(op3).get.isEmpty) // Should return None val proof1 = prover.generateProof() val digest1 = prover.digest - val op4 = Update(key1, ADValue @@ Longs.toByteArray(50)) + val op4 = Update(key1, ADValue @@ Longs.toByteArray(50L)) val op5 = UpdateLongBy(key2, -40) val op6 = Lookup(key3) val op7 = Remove(ADKey @@ Array(5: Byte)) diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorageMock.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorageMock.scala similarity index 100% rename from src/test/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorageMock.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/VersionedAVLStorageMock.scala diff --git a/src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala similarity index 97% rename from src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala index da0c9bbf..e94c3555 100644 --- a/src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/avltree/batch/serialization/AVLBatchSerializationSpecification.scala @@ -36,7 +36,7 @@ class AVLBatchSerializationSpecification extends AnyPropSpec with ScalaCheckDriv } property("slice to pieces and combine tree back") { - forAll(Gen.choose(10, 100000)) { treeSize: Int => + forAll(Gen.choose(10, 10000)) { treeSize: Int => whenever(treeSize >= 10) { val tree = generateProver(treeSize) val height = tree.rootNodeHeight @@ -57,7 +57,7 @@ class AVLBatchSerializationSpecification extends AnyPropSpec with ScalaCheckDriv } property("slice to Array[Byte] pieces and combine tree back") { - forAll(Gen.choose(0, 100000)) { treeSize: Int => + forAll(Gen.choose(0, 10000)) { treeSize: Int => val serializer = new BatchAVLProverSerializer[D, HF] val tree = generateProver(treeSize) val kl = tree.keyLength @@ -83,7 +83,7 @@ class AVLBatchSerializationSpecification extends AnyPropSpec with ScalaCheckDriv property("manifest serialization") { val serializer = new BatchAVLProverSerializer[D, HF] - forAll(Gen.choose(0, 100000)) { treeSize: Int => + forAll(Gen.choose(0, 10000)) { treeSize: Int => val tree = generateProver(treeSize) val kl = tree.keyLength val digest = tree.digest diff --git a/src/test/scala/scorex/crypto/authds/legacy/treap/TreapSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/legacy/treap/TreapSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/authds/legacy/treap/TreapSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/legacy/treap/TreapSpecification.scala diff --git a/src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala similarity index 96% rename from src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala index 5f025282..89d11a74 100644 --- a/src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/merkle/MerkleTreeSpecification.scala @@ -4,14 +4,13 @@ import org.scalatest.propspec.AnyPropSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import scorex.crypto.TestingCommons -import scorex.crypto.authds.{EmptyByteArray, LeafData} -import scorex.crypto.authds.merkle.MerkleTree.InternalNodePrefix -import scorex.crypto.hash.{Digest, Keccak256} +import scorex.crypto.authds.LeafData +import scorex.crypto.hash.Blake2b256 import scala.util.Random class MerkleTreeSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChecks with Matchers with TestingCommons { - implicit val hf = Keccak256 + implicit val hf = Blake2b256 private val LeafSize = 32 diff --git a/src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala similarity index 93% rename from src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala index 5fe4be7d..ef8e19cd 100644 --- a/src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/merkle/serialization/BatchMerkleProofSerializerSpecification.scala @@ -3,9 +3,9 @@ package scorex.crypto.authds.merkle.serialization import org.scalatest.TryValues import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks -import scorex.crypto.authds.merkle.{BatchMerkleProof, Leaf, MerkleTree} -import scorex.crypto.authds.{LeafData, Side, TwoPartyTests} -import scorex.crypto.hash.{Digest, Digest32, Keccak256} +import scorex.crypto.authds.merkle.{MerkleTree, Leaf} +import scorex.crypto.authds.{Side, TwoPartyTests, LeafData} +import scorex.crypto.hash.{Digest32, Digest} import scala.util.Random @@ -15,8 +15,8 @@ class BatchMerkleProofSerializerSpecification extends AnyPropSpec with TryValues { type D = Digest32 - type HF = Keccak256.type - implicit val hf: HF = Keccak256 + type HF = scorex.crypto.hash.Blake2b256.type + implicit val hf: HF = scorex.crypto.hash.Blake2b256 private val LeafSize = 32 property("Batch proof serialization + deserialization") { diff --git a/src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala b/shared/src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala similarity index 90% rename from src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala rename to shared/src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala index 7a3857f4..d55fe8e7 100644 --- a/src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala +++ b/shared/src/test/scala/scorex/crypto/authds/merkle/sparse/SparseMerkleTreeSpecification.scala @@ -1,16 +1,16 @@ package scorex.crypto.authds.merkle.sparse -import com.google.common.primitives.Longs +import scorex.utils.Longs import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import scorex.crypto.TestingCommons import scorex.crypto.authds.LeafData -import scorex.crypto.hash.{Blake2b256Unsafe, CryptographicHash, Digest32} +import scorex.crypto.hash.{CryptographicHash, Digest32, Blake2b256} class SparseMerkleTreeSpecification extends AnyPropSpec with ScalaCheckDrivenPropertyChecks with Matchers with TestingCommons { - implicit val hf: CryptographicHash[Digest32] = new Blake2b256Unsafe + implicit val hf: CryptographicHash[Digest32] = Blake2b256 property("Tree has valid last proof") { forAll { height: Byte => diff --git a/shared/src/test/scala/scorex/crypto/hash/Blake2b256Specification.scala b/shared/src/test/scala/scorex/crypto/hash/Blake2b256Specification.scala new file mode 100644 index 00000000..a04a8e39 --- /dev/null +++ b/shared/src/test/scala/scorex/crypto/hash/Blake2b256Specification.scala @@ -0,0 +1,12 @@ +package scorex.crypto.hash + +class Blake2b256Specification extends HashTest { + + hashCheckString(Blake2b256, + Map( + "" -> "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8", + "abc" -> "bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319" + ) + ) + +} diff --git a/src/test/scala/scorex/crypto/hash/CommutativeHashSpecification.scala b/shared/src/test/scala/scorex/crypto/hash/CommutativeHashSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/CommutativeHashSpecification.scala rename to shared/src/test/scala/scorex/crypto/hash/CommutativeHashSpecification.scala diff --git a/src/test/scala/scorex/crypto/hash/HashTest.scala b/shared/src/test/scala/scorex/crypto/hash/HashTest.scala similarity index 82% rename from src/test/scala/scorex/crypto/hash/HashTest.scala rename to shared/src/test/scala/scorex/crypto/hash/HashTest.scala index d38613d2..4fce1e88 100644 --- a/src/test/scala/scorex/crypto/hash/HashTest.scala +++ b/shared/src/test/scala/scorex/crypto/hash/HashTest.scala @@ -40,11 +40,12 @@ trait HashTest extends AnyPropSpec } } - property(s"${hash.getClass.getSimpleName} is thread safe") { - val singleThreadHashes = (0 until 100).map(i => hash.hash(i.toString)) - val multiThreadHashes = Future.sequence((0 until 100).map(i => Future(hash.hash(i.toString)))) - singleThreadHashes.map(Base16.encode(_)) shouldBe Await.result(multiThreadHashes, 1.minute).map(Base16.encode(_)) - } +// TODO refactor: reimplement hashes in a thread-safe way and remove this test +// property(s"${hash.getClass.getSimpleName} is thread safe") { +// val singleThreadHashes = (0 until 100).map(i => hash.hash(i.toString)) +// val multiThreadHashes = Future.sequence((0 until 100).map(i => Future(hash.hash(i.toString)))) +// singleThreadHashes.map(Base16.encode(_)) shouldBe Await.result(multiThreadHashes, 1.minute).map(Base16.encode(_)) +// } property(s"${hash.getClass.getSimpleName} apply method") { forAll { (string: String, bytes: Array[Byte]) => diff --git a/src/test/scala/scorex/crypto/hash/ShaSpecification.scala b/shared/src/test/scala/scorex/crypto/hash/ShaSpecification.scala similarity index 100% rename from src/test/scala/scorex/crypto/hash/ShaSpecification.scala rename to shared/src/test/scala/scorex/crypto/hash/ShaSpecification.scala diff --git a/src/main/scala/scorex/crypto/hash/Blake2b.scala b/src/main/scala/scorex/crypto/hash/Blake2b.scala deleted file mode 100644 index 7698fba8..00000000 --- a/src/main/scala/scorex/crypto/hash/Blake2b.scala +++ /dev/null @@ -1,11 +0,0 @@ -package scorex.crypto.hash - -import org.bouncycastle.crypto.digests.Blake2bDigest - - -trait Blake2b[D <: Digest] extends BouncyCastleHash[D] { - override protected lazy val digestFn = new Blake2bDigest(DigestSize * 8) -} - - - diff --git a/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala b/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala deleted file mode 100644 index a64d6ff1..00000000 --- a/src/main/scala/scorex/crypto/hash/BouncyCastleHash.scala +++ /dev/null @@ -1,23 +0,0 @@ -package scorex.crypto.hash - -import org.bouncycastle.crypto.ExtendedDigest - -trait BouncyCastleHash[D <: Digest] extends CryptographicHash[D] { - - protected def internalHash(inputs: Message*): Array[Byte] = synchronized { - inputs.foreach(i => digestFn.update(i, 0, i.length)) - val res = new Array[Byte](DigestSize) - digestFn.doFinal(res, 0) - res - } - - protected def internalPrefixedHash(prefix: Byte, inputs: Message*): Array[Byte] = synchronized { - digestFn.update(prefix) - inputs.foreach(i => digestFn.update(i, 0, i.length)) - val res = new Array[Byte](DigestSize) - digestFn.doFinal(res, 0) - res - } - - protected def digestFn: ExtendedDigest -} diff --git a/src/main/scala/scorex/crypto/hash/Sha256.scala b/src/main/scala/scorex/crypto/hash/Sha256.scala deleted file mode 100644 index ef204c77..00000000 --- a/src/main/scala/scorex/crypto/hash/Sha256.scala +++ /dev/null @@ -1,10 +0,0 @@ -package scorex.crypto.hash - -import java.security.MessageDigest - -/** - * Hashing functions implementation with sha256 impl from Java SDK - */ -object Sha256 extends CryptographicHash32 { - override def hash(input: Array[Byte]): Digest32 = Digest32 @@ MessageDigest.getInstance("SHA-256").digest(input) -} \ No newline at end of file diff --git a/src/main/scala/scorex/crypto/hash/hash.scala b/src/main/scala/scorex/crypto/hash/hash.scala deleted file mode 100644 index 8e386384..00000000 --- a/src/main/scala/scorex/crypto/hash/hash.scala +++ /dev/null @@ -1,23 +0,0 @@ -package scorex.crypto - -import supertagged.TaggedType - -package object hash { - - trait BaseDigest extends TaggedType[Array[Byte]] - - type Digest = BaseDigest#Type - - object Digest32 extends BaseDigest - - type Digest32 = Digest32.Type - - object Digest64 extends BaseDigest - - type Digest64 = Digest64.Type - - object NonStandardDigest extends BaseDigest - - type NonStandardDigest = NonStandardDigest.Type - -} diff --git a/src/main/scala/scorex/crypto/signatures/Curve25519.scala b/src/main/scala/scorex/crypto/signatures/Curve25519.scala deleted file mode 100644 index 75718fcc..00000000 --- a/src/main/scala/scorex/crypto/signatures/Curve25519.scala +++ /dev/null @@ -1,55 +0,0 @@ -package scorex.crypto.signatures - -import java.lang.reflect.Constructor - -import org.whispersystems.curve25519.OpportunisticCurve25519Provider -import scorex.crypto.hash.Sha256 -import scorex.util.ScorexLogging - -import scala.util.{Failure, Try} - -object Curve25519 extends EllipticCurveSignatureScheme with ScorexLogging { - - val SignatureLength25519 = 64 - val KeyLength25519 = 32 - - override val SignatureLength = SignatureLength25519 - override val KeyLength = KeyLength25519 - - /* todo: dirty hack, switch to logic as described in WhisperSystem's Curve25519 tutorial when - it would be possible to pass a random seed from outside, see - https://github.com/WhisperSystems/curve25519-java/pull/7 - */ - private val provider: OpportunisticCurve25519Provider = { - val constructor = classOf[OpportunisticCurve25519Provider] - .getDeclaredConstructors - .head - .asInstanceOf[Constructor[OpportunisticCurve25519Provider]] - constructor.setAccessible(true) - constructor.newInstance() - } - - override def createKeyPair(seed: Array[Byte]): (PrivateKey, PublicKey) = { - val hashedSeed = Sha256.hash(seed) - val privateKey = PrivateKey @@ provider.generatePrivateKey(hashedSeed) - privateKey -> PublicKey @@ provider.generatePublicKey(privateKey) - } - - override def sign(privateKey: PrivateKey, message: MessageToSign): Signature = { - require(privateKey.length == KeyLength) - Signature @@ provider.calculateSignature(provider.getRandom(SignatureLength), privateKey, message) - } - - override def verify(signature: Signature, message: MessageToSign, publicKey: PublicKey): Boolean = Try { - require(signature.length == SignatureLength) - require(publicKey.length == KeyLength) - provider.verifySignature(publicKey, message, signature) - }.recoverWith { case e => - log.debug("Error while message signature verification", e) - Failure(e) - }.getOrElse(false) - - override def createSharedSecret(privateKey: PrivateKey, publicKey: PublicKey): SharedSecret = { - SharedSecret @@ provider.calculateAgreement(privateKey, publicKey) - } -} \ No newline at end of file diff --git a/src/main/scala/scorex/crypto/signatures/EllipticCurveSignatureScheme.scala b/src/main/scala/scorex/crypto/signatures/EllipticCurveSignatureScheme.scala deleted file mode 100644 index 31c77c4b..00000000 --- a/src/main/scala/scorex/crypto/signatures/EllipticCurveSignatureScheme.scala +++ /dev/null @@ -1,3 +0,0 @@ -package scorex.crypto.signatures - -trait EllipticCurveSignatureScheme extends SigningFunctions diff --git a/src/main/scala/scorex/crypto/signatures/SigningFunctions.scala b/src/main/scala/scorex/crypto/signatures/SigningFunctions.scala deleted file mode 100644 index fbda0c80..00000000 --- a/src/main/scala/scorex/crypto/signatures/SigningFunctions.scala +++ /dev/null @@ -1,23 +0,0 @@ -package scorex.crypto.signatures - -import java.security.SecureRandom - -trait SigningFunctions { - - val SignatureLength: Int - val KeyLength: Int - - def createKeyPair(seed: Array[Byte]): (PrivateKey, PublicKey) - - def createKeyPair: (PrivateKey, PublicKey) = { - val seed = new Array[Byte](KeyLength) - new SecureRandom().nextBytes(seed) // modifies seed - createKeyPair(seed) - } - - def sign(privateKey: PrivateKey, message: MessageToSign): Signature - - def verify(signature: Signature, message: MessageToSign, publicKey: PublicKey): Boolean - - def createSharedSecret(privateKey: PrivateKey, publicKey: PublicKey): SharedSecret -} diff --git a/src/main/scala/scorex/crypto/signatures/signatures.scala b/src/main/scala/scorex/crypto/signatures/signatures.scala deleted file mode 100644 index c8c222d0..00000000 --- a/src/main/scala/scorex/crypto/signatures/signatures.scala +++ /dev/null @@ -1,25 +0,0 @@ -package scorex.crypto - -import supertagged.TaggedType - -package object signatures { - - object PrivateKey extends TaggedType[Array[Byte]] - - type PrivateKey = PrivateKey.Type - - object PublicKey extends TaggedType[Array[Byte]] - - type PublicKey = PublicKey.Type - - object SharedSecret extends TaggedType[Array[Byte]] - - type SharedSecret = SharedSecret.Type - - object Signature extends TaggedType[Array[Byte]] - - type Signature = Signature.Type - - type MessageToSign = Array[Byte] - -} diff --git a/src/test/scala/scorex/crypto/signing/SigningFunctionsSpecification.scala b/src/test/scala/scorex/crypto/signing/SigningFunctionsSpecification.scala deleted file mode 100644 index 2eb1821a..00000000 --- a/src/test/scala/scorex/crypto/signing/SigningFunctionsSpecification.scala +++ /dev/null @@ -1,121 +0,0 @@ -package scorex.crypto.signing - - -import org.scalatest.propspec.AnyPropSpec -import org.scalatest.matchers.should.Matchers -import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks -import scorex.util.encode.Base16 -import scorex.crypto.signatures.{Curve25519, PrivateKey} - - -class SigningFunctionsSpecification extends AnyPropSpec - with ScalaCheckDrivenPropertyChecks - with Matchers { - - property("signed message should be verifiable with appropriate public key") { - forAll { (seed1: Array[Byte], seed2: Array[Byte], - message1: Array[Byte], message2: Array[Byte]) => - whenever(!seed1.sameElements(seed2) && !message1.sameElements(message2)) { - val keyPair = Curve25519.createKeyPair(seed1) - val keyPair2 = Curve25519.createKeyPair(seed2) - - val sig = Curve25519.sign(keyPair._1, message1) - - Curve25519.verify(sig, message1, keyPair._2) shouldBe true - Curve25519.verify(sig, message1, keyPair2._2) should not be true - Curve25519.verify(sig, message2, keyPair._2) should not be true - - } - } - } - - property("shared secret should be same for both parties ") { - - forAll { (seed1: Array[Byte], seed2: Array[Byte]) => - whenever(!seed1.sameElements(seed2)) { - val keyPair1 = Curve25519.createKeyPair(seed1) - val keyPair2 = Curve25519.createKeyPair(seed2) - - val shared = Curve25519.createSharedSecret(keyPair1._1, keyPair2._2) - val sharedWithKeysReversed = Curve25519.createSharedSecret(keyPair2._1, keyPair1._2) - - val badSharedSecret1 = Curve25519.createSharedSecret(PrivateKey @@ keyPair2._2, keyPair1._2) - val badSharedSecret2 = Curve25519.createSharedSecret(PrivateKey @@ keyPair2._2, keyPair1._2) - - shared.sameElements(sharedWithKeysReversed) should be(true) - - badSharedSecret1.sameElements(shared) shouldNot be(true) - - badSharedSecret2.sameElements(shared) shouldNot be(true) - } - } - } - - property("test vectors from https://tools.ietf.org/html/rfc8032#page-24 - test 1") { - val privKey = PrivateKey @@ Base16.decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60").get - val message = Array[Byte]() - val sig = Curve25519.sign(privKey, message) - val specSig = Base16.decode("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e" + - "39701cf9b46bd25bf5f0595bbe24655141438e7a100b").get - sig.sameElements(specSig) - } - - property("test vectors from https://tools.ietf.org/html/rfc8032#page-24 - test 2") { - val privKey = PrivateKey @@ Base16.decode("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb").get - val message = Base16.decode("72").get - val sig = Curve25519.sign(privKey, message) - val specSig = Base16.decode("92a009a9f0d4cab8720e820b5f642540" + - "a2b27b5416503f8fb3762223ebdb69da" + - "085ac1e43e15996e458f3613d0f11d8c" + - "387b2eaeb4302aeeb00d291612bb0c00").get - sig.sameElements(specSig) - } - - property("test vectors from https://tools.ietf.org/html/rfc8032#page-24 - test 3") { - val privKey = PrivateKey @@ Base16.decode("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7").get - val message = Base16.decode("af82").get - val sig = Curve25519.sign(privKey, message) - val specSig = Base16.decode("6291d657deec24024827e69c3abe01a3" + - "0ce548a284743a445e3680d7db5ac3ac" + - "18ff9b538d16f290ae67f760984dc659" + - "4a7c15e9716ed28dc027beceea1ec40a").get - sig.sameElements(specSig) - } - - property("test vectors from https://tools.ietf.org/html/rfc8032#page-24 - test 1024") { - val privKey = PrivateKey @@ Base16.decode("f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5").get - val message = Base16.decode("08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50" + - "f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d65" + - "8675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199e" + - "e5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560cc" + - "b9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd" + - "7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5" + - "aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187e" + - "bb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd6" + - "2481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27a" + - "f87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c" + - "144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1" + - "ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57" + - "fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a" + - "7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd4" + - "19c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342" + - "721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246" + - "f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11" + - "faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4d" + - "c1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0").get - val sig = Curve25519.sign(privKey, message) - val specSig = Base16.decode("0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ec" + - "ea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03").get - sig.sameElements(specSig) - } - - property("test vectors from https://tools.ietf.org/html/rfc8032#page-24 - test SHA") { - val privKey = PrivateKey @@ Base16.decode("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42").get - val message = Base16.decode("ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836" + - "ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f").get - val sig = Curve25519.sign(privKey, message) - val specSig = Base16.decode("dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdf" + - "bc7c66431e0303dca179c138ac17ad9bef1177331a704").get - sig.sameElements(specSig) - } -} \ No newline at end of file