diff --git a/build.sbt b/build.sbt index 6243d2d..4a90ce1 100644 --- a/build.sbt +++ b/build.sbt @@ -1,12 +1,15 @@ name := "prism" -version := "0.2.7" +version := "0.3.0" scalaVersion := "2.12.6" organization := "org.encry" -resolvers += "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/" +resolvers ++= Seq("Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/", + "SonaType" at "https://oss.sonatype.org/content/groups/public", + "Typesafe maven releases" at "http://repo.typesafe.com/typesafe/maven-releases/", + "Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/") libraryDependencies ++= Seq( "com.lihaoyi" %% "fastparse" % "1.0.0", diff --git a/src/main/scala/org/encryfoundation/prismlang/compiler/CompiledContract.scala b/src/main/scala/org/encryfoundation/prismlang/compiler/CompiledContract.scala index e8e6a6d..68ee8ed 100644 --- a/src/main/scala/org/encryfoundation/prismlang/compiler/CompiledContract.scala +++ b/src/main/scala/org/encryfoundation/prismlang/compiler/CompiledContract.scala @@ -1,12 +1,10 @@ package org.encryfoundation.prismlang.compiler import java.nio.charset.Charset - import org.encryfoundation.prismlang.codec.PCodec import org.encryfoundation.prismlang.core.{Ast, Types} import scodec.bits.BitVector import scorex.crypto.hash.Blake2b256 - import scala.util.Try case class CompiledContract(args: List[(String, Types.PType)], script: Ast.Expr) { diff --git a/src/main/scala/org/encryfoundation/prismlang/compiler/CostEstimator.scala b/src/main/scala/org/encryfoundation/prismlang/compiler/CostEstimator.scala index 27fca76..9615812 100644 --- a/src/main/scala/org/encryfoundation/prismlang/compiler/CostEstimator.scala +++ b/src/main/scala/org/encryfoundation/prismlang/compiler/CostEstimator.scala @@ -7,6 +7,7 @@ import org.encryfoundation.prismlang.core.CostTable case class CostEstimator(initialEnv: Map[String, Int]) { import CostTable._ + type Cost = PartialFunction[Expr, Int] private var env: Map[String, Int] = initialEnv diff --git a/src/main/scala/org/encryfoundation/prismlang/compiler/StaticAnalyser.scala b/src/main/scala/org/encryfoundation/prismlang/compiler/StaticAnalyser.scala index 0b559a7..6c544a6 100644 --- a/src/main/scala/org/encryfoundation/prismlang/compiler/StaticAnalyser.scala +++ b/src/main/scala/org/encryfoundation/prismlang/compiler/StaticAnalyser.scala @@ -3,7 +3,7 @@ package org.encryfoundation.prismlang.compiler import org.encryfoundation.prismlang.compiler.scope.{PredefinedScope, ScopedSymbolTable, Symbol} import org.encryfoundation.prismlang.core.{Constants, TypeSystem, Types} import org.encryfoundation.prismlang.core.Ast._ -import scorex.crypto.encode.{Base16, Base58} +import org.encryfoundation.utils.encoding.{Base16, Base58} case class StaticAnalyser(initialScope: ScopedSymbolTable, types: TypeSystem) extends TypeMatching { diff --git a/src/main/scala/org/encryfoundation/prismlang/compiler/scope/PredefinedScope.scala b/src/main/scala/org/encryfoundation/prismlang/compiler/scope/PredefinedScope.scala index 4f4acd1..481e3d9 100644 --- a/src/main/scala/org/encryfoundation/prismlang/compiler/scope/PredefinedScope.scala +++ b/src/main/scala/org/encryfoundation/prismlang/compiler/scope/PredefinedScope.scala @@ -2,7 +2,6 @@ package org.encryfoundation.prismlang.compiler.scope import org.encryfoundation.prismlang.core.Types - object PredefinedScope { import org.encryfoundation.prismlang.lib.predefined._ diff --git a/src/main/scala/org/encryfoundation/prismlang/core/Types.scala b/src/main/scala/org/encryfoundation/prismlang/core/Types.scala index 0d9ee30..dad1c5a 100644 --- a/src/main/scala/org/encryfoundation/prismlang/core/Types.scala +++ b/src/main/scala/org/encryfoundation/prismlang/core/Types.scala @@ -2,7 +2,7 @@ package org.encryfoundation.prismlang.core import org.encryfoundation.prismlang.codec.PCodec import org.encryfoundation.prismlang.core.wrapped.{PFunction, PObject} -import scorex.crypto.encode.Base58 +import org.encryfoundation.utils.encoding.Base58 import scorex.crypto.hash.Blake2b256 object Types { diff --git a/src/main/scala/org/encryfoundation/prismlang/evaluator/Evaluator.scala b/src/main/scala/org/encryfoundation/prismlang/evaluator/Evaluator.scala index b30cb5a..39ce5ee 100644 --- a/src/main/scala/org/encryfoundation/prismlang/evaluator/Evaluator.scala +++ b/src/main/scala/org/encryfoundation/prismlang/evaluator/Evaluator.scala @@ -3,7 +3,7 @@ package org.encryfoundation.prismlang.evaluator import org.encryfoundation.prismlang.core.Ast._ import org.encryfoundation.prismlang.core.wrapped._ import org.encryfoundation.prismlang.core.{TypeSystem, Types} -import scorex.crypto.encode.{Base16, Base58} +import org.encryfoundation.utils.encoding.{Base16, Base58} case class Evaluator(initialEnv: ScopedRuntimeEnvironment, types: TypeSystem) { diff --git a/src/main/scala/org/encryfoundation/prismlang/lib/predefined/decode/Base58decode.scala b/src/main/scala/org/encryfoundation/prismlang/lib/predefined/decode/Base58decode.scala index e19b36a..597a8fb 100644 --- a/src/main/scala/org/encryfoundation/prismlang/lib/predefined/decode/Base58decode.scala +++ b/src/main/scala/org/encryfoundation/prismlang/lib/predefined/decode/Base58decode.scala @@ -4,7 +4,7 @@ import org.encryfoundation.prismlang.core.Types import org.encryfoundation.prismlang.core.wrapped.PFunctionPredef.PredefFunctionExecFailure import org.encryfoundation.prismlang.core.wrapped.{PFunctionPredef, PValue} import org.encryfoundation.prismlang.lib.predefined.BuiltInFunctionHolder -import scorex.crypto.encode.Base58 +import org.encryfoundation.utils.encoding.Base58 object Base58decode extends BuiltInFunctionHolder { @@ -22,8 +22,6 @@ object Base58decode extends BuiltInFunctionHolder { if (validNumberOfArgs && validArgTypes) { val fnArg = args.map(_._2.value.asInstanceOf[String]).head Right(Base58.decode(fnArg).map(_.toList).toOption) - } else { - Left(PredefFunctionExecFailure) - } + } else Left(PredefFunctionExecFailure) } } diff --git a/src/main/scala/org/encryfoundation/utils/encoding/Base16.scala b/src/main/scala/org/encryfoundation/utils/encoding/Base16.scala new file mode 100644 index 0000000..e492698 --- /dev/null +++ b/src/main/scala/org/encryfoundation/utils/encoding/Base16.scala @@ -0,0 +1,13 @@ +package org.encryfoundation.utils.encoding + +import org.bouncycastle.util.encoders.Hex +import scala.util.Try + +object Base16 { + + val Alphabet: String = "0123456789abcdefABCDEF" + + def encode(input: Array[Byte]): String = Hex.toHexString(input) + + def decode(input: String): Try[Array[Byte]] = Try(Hex.decode(input)) +} diff --git a/src/main/scala/org/encryfoundation/utils/encoding/Base58.scala b/src/main/scala/org/encryfoundation/utils/encoding/Base58.scala new file mode 100644 index 0000000..63cd2a6 --- /dev/null +++ b/src/main/scala/org/encryfoundation/utils/encoding/Base58.scala @@ -0,0 +1,65 @@ +package org.encryfoundation.utils.encoding + +import scala.annotation.tailrec +import scala.util.{Success, Try} + +object Base58 { + + val Alphabet: String = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + private val Base58Size: Int = Alphabet.length + + private def index(char: Char): Option[Int] = char match { + case c if c <= '9' && c >= '1' => Some(c - '1') + case c if c <= 'k' && c >= 'a' => Some(c - 'a' + 33) + case c if c <= 'z' && c >= 'm' => Some(c - 'm' + 44) + case c if c >= 'A' && c <= 'H' => Some(c - 'A' + 9) + case c if c >= 'J' && c <= 'N' => Some(c - 'J' + 17) + case c if c >= 'P' && c <= 'Z' => Some(c - 'P' + 22) + case _ => None + } + + def encode(input: Array[Byte]): String = + if (input.isEmpty) "" + else { + val bi: BigInt = BigInt(1, input) + val s: StringBuilder = new StringBuilder + + @tailrec + def append(rest: BigInt): Unit = { + val div: BigInt = rest / Base58Size + val mod: BigInt = rest % Base58Size + s.insert(0, Alphabet(mod.intValue)) + if (div > 0) append(div) + } + + append(bi) + + val zeros: Int = input.indexWhere(_ != 0) + 0 until zeros foreach { _ => s.insert(0, Alphabet(0)) } + + s.toString + } + + def decode(input: String): Try[Array[Byte]] = + if (input.isEmpty) Success(Array.empty[Byte]) + else { + def toBytes: String => Try[Array[Byte]] = (in: String) => Try { + val size: Int = in.length + in.zipWithIndex.foldRight(BigInt(0)) { (c, bi) => + index(c._1).map { i => + bi + (BigInt(i) * BigInt(Base58Size).pow(size - 1 - c._2)) + }.getOrElse(throw InvalidCharacterException(c._1, c._2)) + }.toByteArray.dropWhile(_ == 0) + } + + val (z: String, in) = input.span(_ == Alphabet.head) + val zeros: Array[Byte] = z.map(_ => 0: Byte).toArray + + if (in.isEmpty) Success(zeros) + else toBytes(in).map { bytes => zeros ++ bytes } + } + + case class InvalidCharacterException(char: Char, index: Int) extends IllegalArgumentException( + s"An invalid character ($char)) at index $index") +} diff --git a/src/main/scala/org/encryfoundation/utils/encoding/Base58Check.scala b/src/main/scala/org/encryfoundation/utils/encoding/Base58Check.scala new file mode 100644 index 0000000..6de616e --- /dev/null +++ b/src/main/scala/org/encryfoundation/utils/encoding/Base58Check.scala @@ -0,0 +1,22 @@ +package org.encryfoundation.utils.encoding + +import scorex.crypto.hash.Blake2b256 +import scala.util.{Failure, Success, Try} + +object Base58Check { + + val Version: Byte = 1 + val ChecksumLength: Int = 4 + + private def getChecksum(bytes: Array[Byte]): Array[Byte] = Blake2b256.hash(bytes).take(ChecksumLength) + + def encode(input: Array[Byte]): String = Base58.encode((Version +: input) ++ getChecksum(input)) + + def decode(input: String): Try[Array[Byte]] = Base58.decode(input).flatMap { bytes => + val checksum: Array[Byte] = bytes.takeRight(ChecksumLength) + val checksumActual: Array[Byte] = getChecksum(bytes.dropRight(ChecksumLength).tail) + + if (checksum.sameElements(checksumActual)) Success(bytes.dropRight(ChecksumLength).tail) + else Failure(new Exception("Wrong checksum")) + } +} diff --git a/src/test/scala/org/encryfoundation/utils/encoding/Base16Spec.scala b/src/test/scala/org/encryfoundation/utils/encoding/Base16Spec.scala new file mode 100644 index 0000000..e55f6fb --- /dev/null +++ b/src/test/scala/org/encryfoundation/utils/encoding/Base16Spec.scala @@ -0,0 +1,20 @@ +package org.encryfoundation.utils.encoding + +import org.scalatest.{Matchers, PropSpec} +import scorex.utils.Random + +class Base16Spec extends PropSpec with Matchers { + + private val iData = Random.randomBytes() + + property("encode/decode") { + + val dEncoded = Base16.encode(iData) + + val dDecoded = Base16.decode(dEncoded) + + dDecoded.isSuccess shouldBe true + + iData sameElements dDecoded.get shouldBe true + } +} diff --git a/src/test/scala/org/encryfoundation/utils/encoding/Base58CheckSpec.scala b/src/test/scala/org/encryfoundation/utils/encoding/Base58CheckSpec.scala new file mode 100644 index 0000000..a839bdf --- /dev/null +++ b/src/test/scala/org/encryfoundation/utils/encoding/Base58CheckSpec.scala @@ -0,0 +1,20 @@ +package org.encryfoundation.utils.encoding + +import org.scalatest.{Matchers, PropSpec} +import scorex.utils.Random + +class Base58CheckSpec extends PropSpec with Matchers { + + private val iData = Random.randomBytes() + + property("encode/decode") { + + val dEncoded = Base58Check.encode(iData) + + val dDecoded = Base58Check.decode(dEncoded) + + dDecoded.isSuccess shouldBe true + + iData sameElements dDecoded.get shouldBe true + } +} diff --git a/src/test/scala/org/encryfoundation/utils/encoding/Base58Spec.scala b/src/test/scala/org/encryfoundation/utils/encoding/Base58Spec.scala new file mode 100644 index 0000000..fb23f7e --- /dev/null +++ b/src/test/scala/org/encryfoundation/utils/encoding/Base58Spec.scala @@ -0,0 +1,20 @@ +package org.encryfoundation.utils.encoding + +import org.scalatest.{Matchers, PropSpec} +import scorex.utils.Random + +class Base58Spec extends PropSpec with Matchers { + + private val iData = Random.randomBytes() + + property("encode/decode") { + + val dEncoded = Base58.encode(iData) + + val dDecoded = Base58.decode(dEncoded) + + dDecoded.isSuccess shouldBe true + + iData sameElements dDecoded.get shouldBe true + } +}