From 0db192bc706b7cee649c96818206d5feffd8684a Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Wed, 28 Sep 2022 17:42:45 +0300 Subject: [PATCH 1/3] [ATL-1923] feat(iris): Implement in-memory ledger --- iris/service/build.sbt | 23 ++-- .../iohk/atala/iris/core/model/Models.scala | 1 - .../atala/iris/core/model/ledger/Funds.scala | 3 + .../atala/iris/core/model/ledger/Ledger.scala | 15 +++ .../model/ledger/TransactionDetails.scala | 3 + .../core/model/ledger/TransactionId.scala | 18 +++ .../core/model/ledger/TransactionStatus.scala | 16 +++ .../repository/OperationsRepository.scala | 6 +- .../InmemoryUnderlyingLedgerService.scala | 104 ++++++++++++++++++ .../iris/core/service/PublishingService.scala | 6 +- .../service/UnderlyingLedgerService.scala | 20 ++++ .../core/worker/PublishingScheduler.scala | 7 +- .../InmemoryUnderlyingLedgerServiceSpec.scala | 5 + iris/service/project/Dependencies.scala | 19 +++- iris/service/project/build.sbt | 3 - iris/service/project/plugins.sbt | 2 + .../io/iohk/atala/iris/server}/Main.scala | 2 +- .../io/iohk/atala/iris/server}/Modules.scala | 6 +- .../atala/iris/server}/grpc/GrpcServer.scala | 2 +- .../iris/server}/grpc/GrpcServices.scala | 2 +- .../grpc/service/IrisServiceGrpcImpl.scala | 2 +- .../repository/JdbcOperationsRepository.scala | 8 +- shared/README.md | 3 + shared/build.sbt | 17 +++ shared/project/Dependencies.scala | 11 ++ shared/project/build.properties | 1 + shared/project/plugins.sbt | 2 + .../io/iohk/atala/shared/HashValue.scala | 51 +++++++++ .../io/iohk/atala/shared/utils/Base64.scala | 18 +++ .../io/iohk/atala/shared/utils/BytesOps.scala | 28 +++++ 30 files changed, 368 insertions(+), 36 deletions(-) create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Funds.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionDetails.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala create mode 100644 iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/UnderlyingLedgerService.scala create mode 100644 iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala delete mode 100644 iris/service/project/build.sbt create mode 100644 iris/service/project/plugins.sbt rename iris/service/{api-server/src/main/scala/io/iohk/atala/iris/apiserver => server/src/main/scala/io/iohk/atala/iris/server}/Main.scala (69%) rename iris/service/{api-server/src/main/scala/io/iohk/atala/iris/apiserver => server/src/main/scala/io/iohk/atala/iris/server}/Modules.scala (91%) rename iris/service/{api-server/src/main/scala/io/iohk/atala/iris/apiserver => server/src/main/scala/io/iohk/atala/iris/server}/grpc/GrpcServer.scala (96%) rename iris/service/{api-server/src/main/scala/io/iohk/atala/iris/apiserver => server/src/main/scala/io/iohk/atala/iris/server}/grpc/GrpcServices.scala (91%) rename iris/service/{api-server/src/main/scala/io/iohk/atala/iris/apiserver => server/src/main/scala/io/iohk/atala/iris/server}/grpc/service/IrisServiceGrpcImpl.scala (97%) create mode 100644 shared/README.md create mode 100644 shared/build.sbt create mode 100644 shared/project/Dependencies.scala create mode 100644 shared/project/build.properties create mode 100644 shared/project/plugins.sbt create mode 100644 shared/src/main/scala/io/iohk/atala/shared/HashValue.scala create mode 100644 shared/src/main/scala/io/iohk/atala/shared/utils/Base64.scala create mode 100644 shared/src/main/scala/io/iohk/atala/shared/utils/BytesOps.scala diff --git a/iris/service/build.sbt b/iris/service/build.sbt index b534e2fe4f..2723fc860a 100644 --- a/iris/service/build.sbt +++ b/iris/service/build.sbt @@ -1,4 +1,5 @@ import Dependencies._ +import sbtghpackages.GitHubPackagesPlugin.autoImport._ ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / scalaVersion := "3.1.3" @@ -11,13 +12,22 @@ ThisBuild / apiBaseDirectory := baseDirectory.value / "../api" // Project definitions lazy val root = project .in(file(".")) - .aggregate(core, sql, `api-server`) + .aggregate(core, sql, server) lazy val core = project .in(file("core")) .settings( name := "iris-core", - libraryDependencies ++= coreDependencies + githubTokenSource := TokenSource.Environment("ATALA_GITHUB_TOKEN"), + resolvers += Resolver + .githubPackages("input-output-hk", "atala-prism-sdk"), + // Needed for Kotlin coroutines that support new memory management mode + resolvers += + "JetBrains Space Maven Repository" at "https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven", + libraryDependencies ++= coreDependencies, + // gRPC settings + Compile / PB.targets := Seq(scalapb.gen() -> (Compile / sourceManaged).value / "scalapb"), + Compile / PB.protoSources := Seq(apiBaseDirectory.value / "grpc") ) lazy val sql = project @@ -28,13 +38,10 @@ lazy val sql = project ) .dependsOn(core) -lazy val `api-server` = project - .in(file("api-server")) +lazy val server = project + .in(file("server")) .settings( - name := "iris-api-server", + name := "iris-server", libraryDependencies ++= apiServerDependencies, - // gRPC settings - Compile / PB.targets := Seq(scalapb.gen() -> (Compile / sourceManaged).value / "scalapb"), - Compile / PB.protoSources := Seq(apiBaseDirectory.value / "grpc") ) .dependsOn(core, sql) diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/Models.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/Models.scala index 89d3a8b44b..7fc22339cd 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/Models.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/Models.scala @@ -5,4 +5,3 @@ final case class IrisNotification(foo: String) final case class IrisOperationId(id: String) final case class IrisOperation(foo: String) -final case class SignedIrisOperation(foo: String) diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Funds.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Funds.scala new file mode 100644 index 0000000000..197ba8fd6f --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Funds.scala @@ -0,0 +1,3 @@ +package io.iohk.atala.iris.core.model.ledger + +case class Funds(lovelaces: Int) diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala new file mode 100644 index 0000000000..5cf93c7d9e --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala @@ -0,0 +1,15 @@ +package io.iohk.atala.iris.core.model.ledger + +import enumeratum.{Enum, EnumEntry} + +import scala.collection.immutable.ArraySeq + +sealed trait Ledger extends EnumEntry + +object Ledger extends Enum[Ledger] { + val values = ArraySeq(InMemory, CardanoMainnet, CardanoTestnet) + + case object InMemory extends Ledger + case object CardanoMainnet extends Ledger + case object CardanoTestnet extends Ledger +} diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionDetails.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionDetails.scala new file mode 100644 index 0000000000..a979b38dc3 --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionDetails.scala @@ -0,0 +1,3 @@ +package io.iohk.atala.iris.core.model.ledger + +case class TransactionDetails(id: TransactionId, status: TransactionStatus) diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala new file mode 100644 index 0000000000..40191ea26d --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala @@ -0,0 +1,18 @@ +package io.iohk.atala.iris.core.model.ledger + +import com.typesafe.config.ConfigMemorySize +import io.iohk.atala.shared.{HashValueConfig, HashValueFrom} + +import scala.collection.immutable.ArraySeq + +case class TransactionId(hex: ArraySeq[Byte]) + +object TransactionId extends HashValueFrom[TransactionId] { + + override val config: HashValueConfig = HashValueConfig( + ConfigMemorySize.ofBytes(32) + ) + + override protected def constructor(value: ArraySeq[Byte]): TransactionId = + new TransactionId(value) +} diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala new file mode 100644 index 0000000000..ca09ec1c9f --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala @@ -0,0 +1,16 @@ +package io.iohk.atala.iris.core.model.ledger + +import enumeratum.{Enum, EnumEntry} +import enumeratum.EnumEntry.Snakecase +import scala.collection.immutable.ArraySeq + +sealed trait TransactionStatus extends EnumEntry with Snakecase + +object TransactionStatus extends Enum[TransactionStatus] { + val values = ArraySeq(InWalletMempool, Submitted, Expired, InLedger) + + case object InWalletMempool extends TransactionStatus + case object Submitted extends TransactionStatus + case object Expired extends TransactionStatus + case object InLedger extends TransactionStatus +} diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/repository/OperationsRepository.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/repository/OperationsRepository.scala index 7cfc55cbba..5775e6954e 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/repository/OperationsRepository.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/repository/OperationsRepository.scala @@ -1,10 +1,10 @@ package io.iohk.atala.iris.core.repository -import io.iohk.atala.iris.core.model.{IrisOperation, IrisOperationId, SignedIrisOperation} +import io.iohk.atala.iris.core.model as model import zio.* // TODO: replace with actual implementation trait OperationsRepository[F[_]] { - def getOperation(id: IrisOperationId): F[IrisOperation] - def saveOperations(ops: Seq[SignedIrisOperation]): F[Unit] + def getOperation(id: model.IrisOperationId): F[model.IrisOperation] + def saveOperations(ops: Seq[model.IrisOperation]): F[Unit] } diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala new file mode 100644 index 0000000000..881ed07d3b --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala @@ -0,0 +1,104 @@ +package io.iohk.atala.iris.core.service + +import io.iohk.atala.iris.core.model.ledger.TransactionStatus.{InWalletMempool, InLedger} +import io.iohk.atala.iris.core.model.ledger.{Funds, TransactionDetails, TransactionId} +import io.iohk.atala.iris.core.service.InmemoryUnderlyingLedgerService.{CardanoBlock, CardanoTransaction, Config} +import io.iohk.atala.iris.proto.dlt as proto +import zio.{Duration as ZDuration, *} +import zio.stm.* +import io.iohk.atala.iris.proto.dlt as proto +import io.iohk.atala.prism.crypto.Sha256 + +import scala.concurrent.duration.Duration + +object InmemoryUnderlyingLedgerService { + case class Config(blockEvery: Duration, initialFunds: Funds, txFee: Funds) + + case class CardanoTransaction(operations: Seq[proto.IrisOperation]) { + lazy val transactionId: TransactionId = { + val objectBytes = proto.AtalaObject(operations).toByteArray + val hash = Sha256.compute(objectBytes) + TransactionId + .from(hash.getValue) + .getOrElse(throw new RuntimeException("Unexpected invalid hash")) + } + } + + case class CardanoBlock(txs: List[CardanoTransaction]) + + def layer(config: Config): ULayer[UnderlyingLedgerService] = ZLayer.fromZIO { + for { + mempoolRef <- TRef.make(List[CardanoTransaction]()).commit + blocksRef <- TRef.make(List[CardanoBlock]()).commit + initialBalance <- TRef.make(config.initialFunds).commit + srv = InmemoryUnderlyingLedgerService(config, mempoolRef, blocksRef, initialBalance) + _ <- srv.startBackgroundProcess() + } yield srv + } +} + +class InmemoryUnderlyingLedgerService( + config: Config, + mempoolRef: TRef[List[CardanoTransaction]], + blocksRef: TRef[List[CardanoBlock]], + balanceRef: TRef[Funds] + ) extends UnderlyingLedgerService { + + def startBackgroundProcess(): UIO[Unit] = STM.atomically { + for { + // Craft a new block from mempool transactions + txs <- mempoolRef.modify(old => (old, List.empty)) + _ <- blocksRef.update(CardanoBlock(txs) :: _) + } yield () + } + .repeat(Schedule.spaced(ZDuration.fromScala(config.blockEvery))) + .fork.map(_ => ()) + + override def publish(operations: Seq[proto.IrisOperation]): IO[LedgerError, Unit] = + STM.atomically { + for { + curFunds <- balanceRef.get + newFunds <- STM.cond( + curFunds.lovelaces >= config.txFee.lovelaces, + Funds(curFunds.lovelaces - config.txFee.lovelaces), + LedgerError("Insufficient wallet balance") + ) + _ <- balanceRef.set(newFunds) + _ <- mempoolRef.update(CardanoTransaction(operations) :: _) + } yield () + } + + override def getTransactionDetails(transactionId: TransactionId): IO[LedgerError, TransactionDetails] = + STM.atomically { + for { + mempool <- mempoolRef.get + blockchain <- blocksRef.get + tdetails <- STM.fromOption { + mempool.find(_.transactionId == transactionId) + .map(_ => TransactionDetails(transactionId, InWalletMempool)) + }.orElse { + STM.fromOption { + blockchain.find(block => block.txs.exists(t => t.transactionId == transactionId)) + .map(_ => TransactionDetails(transactionId, InLedger)) + } + } + .orElseFail(LedgerError(s"Couldn't find tx $transactionId")) + } yield tdetails + } + + override def deleteTransaction(transactionId: TransactionId): IO[LedgerError, Unit] = STM.atomically { + for { + mempool <- mempoolRef.get + _ <- STM.cond(mempool.exists(_.transactionId == transactionId), + (), + LedgerError(s"Transaction $transactionId not found in the mempool") + ) + _ <- mempoolRef.update(m => m.filter { + _.transactionId != transactionId + }) + _ <- balanceRef.update(b => Funds(b.lovelaces + config.txFee.lovelaces)) + } yield () + } + + override def getWalletBalance: IO[LedgerError, Funds] = balanceRef.get.commit +} diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/PublishingService.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/PublishingService.scala index 37c2141fd3..4f63fd8782 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/PublishingService.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/PublishingService.scala @@ -1,17 +1,17 @@ package io.iohk.atala.iris.core.service -import io.iohk.atala.iris.core.model.SignedIrisOperation +import io.iohk.atala.iris.proto.dlt as proto import zio.* // TODO: replace with actual implementation trait PublishingService { - def publishOperations(op: SignedIrisOperation): UIO[Unit] + def publishOperation(op: proto.IrisOperation): UIO[Unit] } object MockPublishingService { val layer: ULayer[PublishingService] = ZLayer.succeed { new PublishingService { - override def publishOperations(op: SignedIrisOperation): UIO[Unit] = ZIO.unit + override def publishOperation(op: proto.IrisOperation): UIO[Unit] = ZIO.unit } } } diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/UnderlyingLedgerService.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/UnderlyingLedgerService.scala new file mode 100644 index 0000000000..ff23525b09 --- /dev/null +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/UnderlyingLedgerService.scala @@ -0,0 +1,20 @@ +package io.iohk.atala.iris.core.service + +import io.iohk.atala.iris.proto.dlt as proto +import io.iohk.atala.iris.core.model.IrisOperation +import io.iohk.atala.iris.core.model.ledger.{Funds, TransactionDetails, TransactionId} +import zio.{IO, UIO} + +case class LedgerError(msg: String) extends RuntimeException(msg) + +trait UnderlyingLedgerService { +// def getType: Ledger + + def publish(operations: Seq[proto.IrisOperation]): IO[LedgerError, Unit] + + def getTransactionDetails(transactionId: TransactionId): IO[LedgerError, TransactionDetails] + + def deleteTransaction(transactionId: TransactionId): IO[LedgerError, Unit] + + def getWalletBalance: IO[LedgerError, Funds] +} diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/worker/PublishingScheduler.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/worker/PublishingScheduler.scala index fdd1b6206d..9db35963ef 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/worker/PublishingScheduler.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/worker/PublishingScheduler.scala @@ -1,17 +1,16 @@ package io.iohk.atala.iris.core.worker -import io.iohk.atala.iris.core.model.SignedIrisOperation -import io.iohk.atala.iris.core.service.PublishingService +import io.iohk.atala.iris.proto.dlt as proto import zio.{UIO, ULayer, ZIO, ZLayer} trait PublishingScheduler { - def scheduleOperations(op: SignedIrisOperation): UIO[Unit] + def scheduleOperations(op: proto.IrisOperation): UIO[Unit] } object MockPublishingScheduler { val layer: ULayer[PublishingScheduler] = ZLayer.succeed { new PublishingScheduler { - def scheduleOperations(op: SignedIrisOperation): UIO[Unit] = ZIO.unit + def scheduleOperations(op: proto.IrisOperation): UIO[Unit] = ZIO.unit } } } diff --git a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala new file mode 100644 index 0000000000..464619a72e --- /dev/null +++ b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala @@ -0,0 +1,5 @@ +package io.iohk.atala.iris.core.service + +class InmemoryUnderlyingLedgerServiceSpec { + +} diff --git a/iris/service/project/Dependencies.scala b/iris/service/project/Dependencies.scala index 8a750ca5c4..ee0413cee4 100644 --- a/iris/service/project/Dependencies.scala +++ b/iris/service/project/Dependencies.scala @@ -7,6 +7,9 @@ object Dependencies { val akkaHttp = "10.2.9" val doobie = "1.0.0-RC2" val zioCatsInterop = "3.3.0" + val prismSdk = "v1.3.3-snapshot-1657194253-992dd96" + val shared = "0.1.0" + val enumeratum = "1.7.0" } private lazy val zio = "dev.zio" %% "zio" % Versions.zio @@ -25,15 +28,25 @@ object Dependencies { private lazy val doobiePostgres = "org.tpolecat" %% "doobie-postgres" % Versions.doobie private lazy val doobieHikari = "org.tpolecat" %% "doobie-hikari" % Versions.doobie + // We have to exclude bouncycastle since for some reason bitcoinj depends on bouncycastle jdk15to18 + // (i.e. JDK 1.5 to 1.8), but we are using JDK 11 + private lazy val prismCrypto = "io.iohk.atala" % "prism-crypto-jvm" % Versions.prismSdk excludeAll + ExclusionRule( + organization = "org.bouncycastle" + ) + + private lazy val shared = "io.iohk.atala" % "shared" % Versions.shared + + private lazy val enumeratum = ("com.beachape" %% "enumeratum" % Versions.enumeratum).cross(CrossVersion.for3Use2_13) // Dependency Modules - private lazy val baseDependencies: Seq[ModuleID] = Seq(zio) + private lazy val baseDependencies: Seq[ModuleID] = Seq(zio, prismCrypto, shared, enumeratum) private lazy val akkaHttpDependencies: Seq[ModuleID] = Seq(akkaTyped, akkaStream, akkaHttp, akkaSprayJson).map(_.cross(CrossVersion.for3Use2_13)) private lazy val grpcDependencies: Seq[ModuleID] = Seq(grpcNetty, grpcServices, scalaPbProto, scalaPbGrpc) private lazy val doobieDependencies: Seq[ModuleID] = Seq(doobiePostgres, doobieHikari) // Project Dependencies - lazy val coreDependencies: Seq[ModuleID] = baseDependencies + lazy val coreDependencies: Seq[ModuleID] = baseDependencies ++ grpcDependencies lazy val sqlDependencies: Seq[ModuleID] = baseDependencies ++ doobieDependencies ++ Seq(zioCatsInterop) - lazy val apiServerDependencies: Seq[ModuleID] = baseDependencies ++ akkaHttpDependencies ++ grpcDependencies + lazy val apiServerDependencies: Seq[ModuleID] = baseDependencies ++ akkaHttpDependencies } diff --git a/iris/service/project/build.sbt b/iris/service/project/build.sbt deleted file mode 100644 index 2625ab2264..0000000000 --- a/iris/service/project/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") - -libraryDependencies ++= Seq("org.openapitools" % "openapi-generator" % "6.0.0") diff --git a/iris/service/project/plugins.sbt b/iris/service/project/plugins.sbt new file mode 100644 index 0000000000..7c00d7b7cc --- /dev/null +++ b/iris/service/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.5.3") diff --git a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Main.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Main.scala similarity index 69% rename from iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Main.scala rename to iris/service/server/src/main/scala/io/iohk/atala/iris/server/Main.scala index 1bdac42323..fb6cb3f94f 100644 --- a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Main.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Main.scala @@ -1,4 +1,4 @@ -package io.iohk.atala.iris.apiserver +package io.iohk.atala.iris.server import zio.* diff --git a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Modules.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala similarity index 91% rename from iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Modules.scala rename to iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala index 4774d9cb5b..6532653db0 100644 --- a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/Modules.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala @@ -1,12 +1,12 @@ -package io.iohk.atala.iris.apiserver +package io.iohk.atala.iris.server import akka.actor.typed.ActorSystem import akka.actor.typed.scaladsl.Behaviors import akka.http.scaladsl.server.Route import cats.effect.std.Dispatcher import doobie.util.transactor.Transactor -import io.iohk.atala.iris.apiserver.grpc.service.IrisServiceGrpcImpl -import io.iohk.atala.iris.apiserver.grpc.{GrpcServer, GrpcServices} +import io.iohk.atala.iris.server.grpc.service.IrisServiceGrpcImpl +import io.iohk.atala.iris.server.grpc.{GrpcServer, GrpcServices} import io.iohk.atala.iris.core.repository.OperationsRepository import io.iohk.atala.iris.core.service.* import io.iohk.atala.iris.core.worker.{MockPublishingScheduler, PublishingScheduler} diff --git a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServer.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServer.scala similarity index 96% rename from iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServer.scala rename to iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServer.scala index c0423b8ab1..b3e9203464 100644 --- a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServer.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServer.scala @@ -1,4 +1,4 @@ -package io.iohk.atala.iris.apiserver.grpc +package io.iohk.atala.iris.server.grpc import io.grpc.{ServerBuilder, ServerServiceDefinition} import io.grpc.protobuf.services.ProtoReflectionService diff --git a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServices.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala similarity index 91% rename from iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServices.scala rename to iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala index f4ef958a81..65838def92 100644 --- a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/GrpcServices.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala @@ -1,4 +1,4 @@ -package io.iohk.atala.iris.apiserver.grpc +package io.iohk.atala.iris.server.grpc import akka.actor.typed.ActorSystem import io.grpc.ServerServiceDefinition diff --git a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/service/IrisServiceGrpcImpl.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala similarity index 97% rename from iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/service/IrisServiceGrpcImpl.scala rename to iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala index 19464c4bf7..8b488ad463 100644 --- a/iris/service/api-server/src/main/scala/io/iohk/atala/iris/apiserver/grpc/service/IrisServiceGrpcImpl.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala @@ -1,4 +1,4 @@ -package io.iohk.atala.iris.apiserver.grpc.service +package io.iohk.atala.iris.server.grpc.service import com.google.protobuf.ByteString import io.iohk.atala.iris.core.service.PublishingService diff --git a/iris/service/sql/src/main/scala/io/iohk/atala/iris/sql/repository/JdbcOperationsRepository.scala b/iris/service/sql/src/main/scala/io/iohk/atala/iris/sql/repository/JdbcOperationsRepository.scala index dd7fecb8c8..75ac5c5017 100644 --- a/iris/service/sql/src/main/scala/io/iohk/atala/iris/sql/repository/JdbcOperationsRepository.scala +++ b/iris/service/sql/src/main/scala/io/iohk/atala/iris/sql/repository/JdbcOperationsRepository.scala @@ -2,7 +2,7 @@ package io.iohk.atala.iris.sql.repository import doobie.* import doobie.implicits.* -import io.iohk.atala.iris.core.model.{IrisOperation, IrisOperationId, SignedIrisOperation} +import io.iohk.atala.iris.core.model as model import io.iohk.atala.iris.core.repository.OperationsRepository import io.iohk.atala.iris.sql.repository.JdbcOperationsRepository import zio.* @@ -11,17 +11,17 @@ import zio.interop.catz.* // TODO: replace with actual implementation class JdbcOperationsRepository(xa: Transactor[Task]) extends OperationsRepository[Task] { - override def getOperation(id: IrisOperationId): Task[IrisOperation] = { + override def getOperation(id: model.IrisOperationId): Task[model.IrisOperation] = { val cxnIO = sql""" |SELECT foo FROM public.iris_operations |""".stripMargin.query[String].unique cxnIO .transact(xa) - .map(IrisOperation.apply) + .map(model.IrisOperation.apply) } - override def saveOperations(ops: Seq[SignedIrisOperation]): Task[Unit] = ZIO.unit + override def saveOperations(ops: Seq[model.IrisOperation]): Task[Unit] = ZIO.unit } object JdbcOperationsRepository { diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 0000000000..3b85b855ca --- /dev/null +++ b/shared/README.md @@ -0,0 +1,3 @@ +## shared + +Contains a stateless utility code which might be reused across all the building blocks. diff --git a/shared/build.sbt b/shared/build.sbt new file mode 100644 index 0000000000..486f3da97b --- /dev/null +++ b/shared/build.sbt @@ -0,0 +1,17 @@ +import sbtbuildinfo.BuildInfoPlugin +import sbtbuildinfo.BuildInfoPlugin.autoImport._ +import Dependencies._ + +ThisBuild / version := "0.1.0" + +ThisBuild / scalaVersion := "3.1.3" + +lazy val root = (project in file(".")) + .settings( + organization := "io.iohk.atala", + organizationName := "Input Output HK", + buildInfoPackage := "io.iohk.atala.shared", + name := "shared", + crossPaths := false, + libraryDependencies ++= dependencies + ).enablePlugins(BuildInfoPlugin) diff --git a/shared/project/Dependencies.scala b/shared/project/Dependencies.scala new file mode 100644 index 0000000000..d70c3dd4c1 --- /dev/null +++ b/shared/project/Dependencies.scala @@ -0,0 +1,11 @@ +import sbt._ + +object Dependencies { + object Versions { + val typesafeConfig = "1.4.2" + } + + private lazy val typesafeConfig = "com.typesafe" % "config" % Versions.typesafeConfig + + lazy val dependencies: Seq[ModuleID] = Seq(typesafeConfig) +} diff --git a/shared/project/build.properties b/shared/project/build.properties new file mode 100644 index 0000000000..d738b858c8 --- /dev/null +++ b/shared/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.7.1 diff --git a/shared/project/plugins.sbt b/shared/project/plugins.sbt new file mode 100644 index 0000000000..78bb5eba38 --- /dev/null +++ b/shared/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.1") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") diff --git a/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala new file mode 100644 index 0000000000..26387349a8 --- /dev/null +++ b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala @@ -0,0 +1,51 @@ +package io.iohk.atala.shared + +import java.util.Locale +import com.typesafe.config.ConfigMemorySize +import io.iohk.atala.shared.utils.BytesOps + +import scala.collection.immutable.ArraySeq +import scala.util.matching.Regex + +trait HashValue extends Any { + + def value: ArraySeq[Byte] + + override def toString: String = { + BytesOps.bytesToHex(value) + } +} + +trait HashValueFrom[A] { + protected val config: HashValueConfig + + protected def constructor(value: ArraySeq[Byte]): A + + def from(string: String): Option[A] = { + val lowercaseString = string.toLowerCase(Locale.ROOT) + + lowercaseString match { + case config.HexPattern() => + val bytes = lowercaseString + .grouped(2) + .toList + .map { hex => + Integer.parseInt(hex, 16).asInstanceOf[Byte] + } + Some(constructor(ArraySeq.from(bytes))) + case _ => None + } + } + + def from(bytes: Iterable[Byte]): Option[A] = { + if (bytes.size == config.size.toBytes) { + Some(constructor(ArraySeq.from(bytes))) + } else { + None + } + } +} + +case class HashValueConfig(size: ConfigMemorySize) { + private[shared] val HexPattern: Regex = s"^[a-f0-9]{${2 * size.toBytes}}$$".r +} diff --git a/shared/src/main/scala/io/iohk/atala/shared/utils/Base64.scala b/shared/src/main/scala/io/iohk/atala/shared/utils/Base64.scala new file mode 100644 index 0000000000..de3c217d4d --- /dev/null +++ b/shared/src/main/scala/io/iohk/atala/shared/utils/Base64.scala @@ -0,0 +1,18 @@ +package io.iohk.atala.shared.utils + +import java.nio.charset.StandardCharsets +import java.util.Base64 + +object Base64Utils { + def encodeURL(bytes: Array[Byte]): String = { + Base64.getUrlEncoder.encodeToString(bytes) + } + + def decodeUrlToString(encodedStr: String): String = { + new String(Base64.getUrlDecoder.decode(encodedStr), StandardCharsets.UTF_8) + } + + def decodeURL(string: String): Array[Byte] = { + Base64.getUrlDecoder.decode(string) + } +} diff --git a/shared/src/main/scala/io/iohk/atala/shared/utils/BytesOps.scala b/shared/src/main/scala/io/iohk/atala/shared/utils/BytesOps.scala new file mode 100644 index 0000000000..afc5c4dabc --- /dev/null +++ b/shared/src/main/scala/io/iohk/atala/shared/utils/BytesOps.scala @@ -0,0 +1,28 @@ +package io.iohk.atala.shared.utils + +import java.nio.charset.StandardCharsets + +object BytesOps { + private val HexArray = "0123456789abcdef".getBytes(StandardCharsets.US_ASCII); + + // Converts hex string to bytes representation + def hexToBytes(hexEncoded: String): Array[Byte] = { + require(hexEncoded.length % 2 == 0, "Hex length needs to be even") + hexEncoded.grouped(2).toVector.map(hexToByte).toArray + } + + // Converts bytes to hex string representation + def bytesToHex(bytes: Iterable[Byte]): String = { + val hexChars = new Array[Byte](bytes.size * 2) + for ((byte, i) <- bytes.zipWithIndex) { + val v = byte & 0xff + hexChars(i * 2) = HexArray(v >>> 4) + hexChars(i * 2 + 1) = HexArray(v & 0x0f) + } + new String(hexChars, StandardCharsets.UTF_8) + } + + private def hexToByte(h: String): Byte = { + Integer.parseInt(h, 16).toByte + } +} From a169b713cdbc8f580d3bce71dbe63fa0e4789c73 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Thu, 29 Sep 2022 18:01:30 +0300 Subject: [PATCH 2/3] [ATL-1923] feat(iris): Implement in-memory tests --- iris/service/build.sbt | 2 + .../core/model/ledger/TransactionId.scala | 6 +- .../core/model/ledger/TransactionStatus.scala | 4 +- .../InmemoryUnderlyingLedgerService.scala | 55 +++-- .../InmemoryUnderlyingLedgerServiceSpec.scala | 205 +++++++++++++++++- .../atala/iris/core/service/RandomUtils.scala | 54 +++++ iris/service/project/Dependencies.scala | 25 ++- .../grpc/service/IrisServiceGrpcImpl.scala | 2 +- .../io/iohk/atala/shared/HashValue.scala | 10 + 9 files changed, 319 insertions(+), 44 deletions(-) create mode 100644 iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala diff --git a/iris/service/build.sbt b/iris/service/build.sbt index 2723fc860a..285108f4b7 100644 --- a/iris/service/build.sbt +++ b/iris/service/build.sbt @@ -1,4 +1,5 @@ import Dependencies._ +import sbt.Keys.testFrameworks import sbtghpackages.GitHubPackagesPlugin.autoImport._ ThisBuild / version := "0.1.0-SNAPSHOT" @@ -25,6 +26,7 @@ lazy val core = project resolvers += "JetBrains Space Maven Repository" at "https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven", libraryDependencies ++= coreDependencies, + testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"), // gRPC settings Compile / PB.targets := Seq(scalapb.gen() -> (Compile / sourceManaged).value / "scalapb"), Compile / PB.protoSources := Seq(apiBaseDirectory.value / "grpc") diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala index 40191ea26d..9c3429bc6a 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionId.scala @@ -1,11 +1,13 @@ package io.iohk.atala.iris.core.model.ledger import com.typesafe.config.ConfigMemorySize -import io.iohk.atala.shared.{HashValueConfig, HashValueFrom} +import io.iohk.atala.shared.{HashValue, HashValueConfig, HashValueFrom} import scala.collection.immutable.ArraySeq -case class TransactionId(hex: ArraySeq[Byte]) +class TransactionId(bytes: ArraySeq[Byte]) extends HashValue { + override def value: ArraySeq[Byte] = bytes +} object TransactionId extends HashValueFrom[TransactionId] { diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala index ca09ec1c9f..9cc38a9225 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/TransactionStatus.scala @@ -7,9 +7,9 @@ import scala.collection.immutable.ArraySeq sealed trait TransactionStatus extends EnumEntry with Snakecase object TransactionStatus extends Enum[TransactionStatus] { - val values = ArraySeq(InWalletMempool, Submitted, Expired, InLedger) + val values = ArraySeq(InMempool, Submitted, Expired, InLedger) - case object InWalletMempool extends TransactionStatus + case object InMempool extends TransactionStatus case object Submitted extends TransactionStatus case object Expired extends TransactionStatus case object InLedger extends TransactionStatus diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala index 881ed07d3b..1ba447b141 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala @@ -1,15 +1,12 @@ package io.iohk.atala.iris.core.service -import io.iohk.atala.iris.core.model.ledger.TransactionStatus.{InWalletMempool, InLedger} +import io.iohk.atala.iris.core.model.ledger.TransactionStatus.{InLedger, InMempool} import io.iohk.atala.iris.core.model.ledger.{Funds, TransactionDetails, TransactionId} import io.iohk.atala.iris.core.service.InmemoryUnderlyingLedgerService.{CardanoBlock, CardanoTransaction, Config} import io.iohk.atala.iris.proto.dlt as proto -import zio.{Duration as ZDuration, *} -import zio.stm.* -import io.iohk.atala.iris.proto.dlt as proto import io.iohk.atala.prism.crypto.Sha256 - -import scala.concurrent.duration.Duration +import zio.stm.* +import zio.* object InmemoryUnderlyingLedgerService { case class Config(blockEvery: Duration, initialFunds: Funds, txFee: Funds) @@ -23,13 +20,13 @@ object InmemoryUnderlyingLedgerService { .getOrElse(throw new RuntimeException("Unexpected invalid hash")) } } + + case class CardanoBlock(txs: Seq[CardanoTransaction]) - case class CardanoBlock(txs: List[CardanoTransaction]) - - def layer(config: Config): ULayer[UnderlyingLedgerService] = ZLayer.fromZIO { + def layer(config: Config): ULayer[InmemoryUnderlyingLedgerService] = ZLayer.fromZIO { for { - mempoolRef <- TRef.make(List[CardanoTransaction]()).commit - blocksRef <- TRef.make(List[CardanoBlock]()).commit + mempoolRef <- TRef.make(Vector[CardanoTransaction]()).commit + blocksRef <- TRef.make(Vector[CardanoBlock]()).commit initialBalance <- TRef.make(config.initialFunds).commit srv = InmemoryUnderlyingLedgerService(config, mempoolRef, blocksRef, initialBalance) _ <- srv.startBackgroundProcess() @@ -39,21 +36,11 @@ object InmemoryUnderlyingLedgerService { class InmemoryUnderlyingLedgerService( config: Config, - mempoolRef: TRef[List[CardanoTransaction]], - blocksRef: TRef[List[CardanoBlock]], + mempoolRef: TRef[Vector[CardanoTransaction]], + blocksRef: TRef[Vector[CardanoBlock]], balanceRef: TRef[Funds] ) extends UnderlyingLedgerService { - def startBackgroundProcess(): UIO[Unit] = STM.atomically { - for { - // Craft a new block from mempool transactions - txs <- mempoolRef.modify(old => (old, List.empty)) - _ <- blocksRef.update(CardanoBlock(txs) :: _) - } yield () - } - .repeat(Schedule.spaced(ZDuration.fromScala(config.blockEvery))) - .fork.map(_ => ()) - override def publish(operations: Seq[proto.IrisOperation]): IO[LedgerError, Unit] = STM.atomically { for { @@ -64,7 +51,7 @@ class InmemoryUnderlyingLedgerService( LedgerError("Insufficient wallet balance") ) _ <- balanceRef.set(newFunds) - _ <- mempoolRef.update(CardanoTransaction(operations) :: _) + _ <- mempoolRef.update(_.appended(CardanoTransaction(operations))) } yield () } @@ -75,7 +62,7 @@ class InmemoryUnderlyingLedgerService( blockchain <- blocksRef.get tdetails <- STM.fromOption { mempool.find(_.transactionId == transactionId) - .map(_ => TransactionDetails(transactionId, InWalletMempool)) + .map(_ => TransactionDetails(transactionId, InMempool)) }.orElse { STM.fromOption { blockchain.find(block => block.txs.exists(t => t.transactionId == transactionId)) @@ -93,12 +80,24 @@ class InmemoryUnderlyingLedgerService( (), LedgerError(s"Transaction $transactionId not found in the mempool") ) - _ <- mempoolRef.update(m => m.filter { - _.transactionId != transactionId - }) + _ <- mempoolRef.update(m => m.filter(_.transactionId != transactionId)) _ <- balanceRef.update(b => Funds(b.lovelaces + config.txFee.lovelaces)) } yield () } override def getWalletBalance: IO[LedgerError, Funds] = balanceRef.get.commit + + def getMempool: UIO[List[CardanoTransaction]] = mempoolRef.get.commit.map(_.toList) + + def getBlocks: UIO[List[CardanoBlock]] = blocksRef.get.commit.map(_.toList) + + private[service] def startBackgroundProcess(): UIO[Unit] = STM.atomically { + for { + // Craft a new block from mempool transactions + txs <- mempoolRef.modify(old => (old, Vector.empty)) + _ <- blocksRef.update(_.appended(CardanoBlock(txs))) + } yield () + } + .repeat(Schedule.spaced(config.blockEvery)) + .fork.map(_ => ()) } diff --git a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala index 464619a72e..81a02b7410 100644 --- a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala +++ b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala @@ -1,5 +1,208 @@ package io.iohk.atala.iris.core.service -class InmemoryUnderlyingLedgerServiceSpec { +import com.google.protobuf.ByteString +import io.iohk.atala.iris.core.model.ledger.TransactionStatus.{InLedger, InMempool} +import io.iohk.atala.iris.core.model.ledger.{Funds, TransactionDetails} +import io.iohk.atala.iris.core.service.InmemoryUnderlyingLedgerService.{CardanoBlock, CardanoTransaction} +import io.iohk.atala.iris.core.service.RandomUtils.* +import io.iohk.atala.iris.proto.did_operations.{CreateDid, DocumentDefinition, UpdateDid} +import io.iohk.atala.iris.proto.dlt as proto +import zio.* +import zio.test.* +import zio.test.Assertion.* +object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { + val defaultConfig = InmemoryUnderlyingLedgerService.Config(10.seconds, Funds(1000), Funds(1)) + val inmemoryLedger = InmemoryUnderlyingLedgerService.layer(defaultConfig) + + case class PublishThenAdjust(operations: Seq[proto.IrisOperation], adjust: Duration) + + object PublishThenAdjust { + implicit class Then(operations: Seq[proto.IrisOperation]) { + def >>(adj: Duration): PublishThenAdjust = PublishThenAdjust(operations, adj) + } + + def foreachZIO[R](srv: InmemoryUnderlyingLedgerService)(xs: Iterable[PublishThenAdjust]): ZIO[R, Any, Unit] = + ZIO.foreachDiscard[R, Any, PublishThenAdjust](xs) { + case PublishThenAdjust(ops, adj) => + srv.publish(ops).flatMap(_ => TestClock.adjust(adj)) + } + } + + import PublishThenAdjust.Then + + def spec = suite("InmemoryUnderlyingLedgerServiceSpec")( + suite("Background worker")( + test("All the operations in the one block within 4 different transactions") { + val testCase = + for { + op <- ZIO.replicateZIO(4)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 1.seconds, + Seq(op(1)) >> 1.seconds, + Seq(op(2)) >> 0.seconds, + Seq(op(3)) >> 20.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + mempool <- srvc.getMempool + blocks <- srvc.getBlocks + } yield + assertTrue( + mempool == List.empty && + blocks == List( + CardanoBlock(List()), + CardanoBlock(List( + CardanoTransaction(Seq(op(0))), + CardanoTransaction(Seq(op(1))), + CardanoTransaction(Seq(op(2))), + CardanoTransaction(Seq(op(3))) + )), + CardanoBlock(List()) + ) + ) + testCase.provideLayer(inmemoryLedger) + }, + + test("Operations distributed between 2 blocks") { + val testCase = + for { + op <- ZIO.replicateZIO(4)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 1.seconds, + Seq(op(1)) >> 10.seconds, + + Seq(op(2)) >> 0.seconds, + Seq(op(3)) >> 10.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + mempool <- srvc.getMempool + blocks <- srvc.getBlocks + } yield + assertTrue( + mempool == List.empty && + blocks == List( + CardanoBlock(List()), + CardanoBlock(List( + CardanoTransaction(Seq(op(0))), + CardanoTransaction(Seq(op(1))), + )), + CardanoBlock(List( + CardanoTransaction(Seq(op(2))), + CardanoTransaction(Seq(op(3))), + )), + ) + ) + testCase.provideLayer(inmemoryLedger) + } + ), + + suite("getTransactionDetails")( + test("Find unconfirmed transaction") { + val testCase = + for { + op <- ZIO.replicateZIO(5)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 1.seconds, + Seq(op(1)) >> 10.seconds, + + Seq(op(2), op(3)) >> 0.seconds, + Seq(op(4)) >> 2.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + targetTx = CardanoTransaction(Seq(op(2), op(3))) + txDetails <- srvc.getTransactionDetails(targetTx.transactionId) + } yield + assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InMempool)) + testCase.provideLayer(inmemoryLedger) + }, + + test("Find confirmed transaction") { + val testCase = + for { + op <- ZIO.replicateZIO(5)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 11.seconds, + Seq(op(1)) >> 11.seconds, + Seq(op(2), op(3)) >> 0.seconds, + Seq(op(4)) >> 12.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + targetTx = CardanoTransaction(Seq(op(2), op(3))) + txDetails <- srvc.getTransactionDetails(targetTx.transactionId) + } yield + assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InLedger)) + testCase.provideLayer(inmemoryLedger) + }, + + test("Find unknown transaction") { + val testCase = + for { + op <- ZIO.replicateZIO(5)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 11.seconds, + Seq(op(1)) >> 11.seconds, + Seq(op(2), op(3)) >> 0.seconds, + Seq(op(4)) >> 12.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + targetTx = CardanoTransaction(Seq(op(1), op(2))) + testResult <- assertZIO(srvc.getTransactionDetails(targetTx.transactionId).exit) { + fails(equalTo(LedgerError(s"Couldn't find tx ${targetTx.transactionId}"))) + } + } yield testResult + testCase.provideLayer(inmemoryLedger) + } + ), + + suite("deleteTransaction")( + test("Delete transaction from mempool") { + val testCase = + for { + op <- ZIO.replicateZIO(5)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 1.seconds, + Seq(op(1)) >> 10.seconds, + + Seq(op(2), op(3)) >> 0.seconds, + Seq(op(4)) >> 2.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + targetTx = CardanoTransaction(Seq(op(2), op(3))) + _ <- srvc.deleteTransaction(targetTx.transactionId) + mempool <- srvc.getMempool + } yield + assertTrue(mempool == List(CardanoTransaction(Seq(op(4))))) + testCase.provideLayer(inmemoryLedger) + }, + + test("Delete confirmed transaction") { + val testCase = + for { + op <- ZIO.replicateZIO(5)(genOperation()).map(_.toList) + srvc <- ZIO.service[InmemoryUnderlyingLedgerService] + scenario = List( + Seq(op(0)) >> 1.seconds, + Seq(op(1)) >> 10.seconds, + + Seq(op(2), op(3)) >> 0.seconds, + Seq(op(4)) >> 2.seconds + ) + _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) + targetTx = CardanoTransaction(Seq(op(1))) + testResult <- + assertZIO(srvc.deleteTransaction(targetTx.transactionId).exit) { + fails( + equalTo(LedgerError(s"Transaction ${targetTx.transactionId} not found in the mempool"))) + } + } yield testResult + testCase.provideLayer(inmemoryLedger) + } + ) + ) } diff --git a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala new file mode 100644 index 0000000000..47e30044b5 --- /dev/null +++ b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala @@ -0,0 +1,54 @@ +package io.iohk.atala.iris.core.service + +import com.google.protobuf.ByteString +import io.iohk.atala.iris.proto.did_operations.{CreateDid, DocumentDefinition, UpdateDid} +import io.iohk.atala.iris.proto.dlt as proto +import zio.{Random, UIO} + +object RandomUtils { + + private def nextBytes(length: Int): UIO[ByteString] = + Random.nextBytes(length).map(x => ByteString.copyFrom(x.toArray)) + + def genCreateOperation(): UIO[proto.IrisOperation] = + for { + updComm <- nextBytes(20) + recComm <- nextBytes(20) + } yield + proto.IrisOperation(proto.IrisOperation.Operation.CreateDid( + CreateDid( + initialUpdateCommitment = updComm, + initialRecoveryCommitment = recComm, + storage = "mainnet", + document = Some(DocumentDefinition(publicKeys = Seq(), services = Seq())) + ) + ) + ) + + def genUpdateOperation(): UIO[proto.IrisOperation] = + for { + didSuff <- Random.nextString(10) + updKey <- nextBytes(20) + prevVers <- nextBytes(20) + forwUpdComm <- nextBytes(20) + sig <- nextBytes(20) + } yield + proto.IrisOperation(proto.IrisOperation.Operation.UpdateDid( + UpdateDid( + did = "did:prism:" + didSuff, + revealedUpdateKey = updKey, + previousVersion = prevVers, + forwardUpdateCommitment = forwUpdComm, + patches = Seq(), + signature = sig + ) + ) + ) + + def genOperation(): UIO[proto.IrisOperation] = + for { + op <- Random.nextBoolean + res <- if (op) genCreateOperation() + else genUpdateOperation() + } yield res +} diff --git a/iris/service/project/Dependencies.scala b/iris/service/project/Dependencies.scala index ee0413cee4..530b6959c3 100644 --- a/iris/service/project/Dependencies.scala +++ b/iris/service/project/Dependencies.scala @@ -10,11 +10,20 @@ object Dependencies { val prismSdk = "v1.3.3-snapshot-1657194253-992dd96" val shared = "0.1.0" val enumeratum = "1.7.0" + val zioTest = "2.0.2" } private lazy val zio = "dev.zio" %% "zio" % Versions.zio private lazy val zioStream = "dev.zio" %% "zio-streams" % Versions.zio private lazy val zioCatsInterop = "dev.zio" %% "zio-interop-cats" % Versions.zioCatsInterop + // We have to exclude bouncycastle since for some reason bitcoinj depends on bouncycastle jdk15to18 + // (i.e. JDK 1.5 to 1.8), but we are using JDK 11 + private lazy val prismCrypto = "io.iohk.atala" % "prism-crypto-jvm" % Versions.prismSdk excludeAll + ExclusionRule( + organization = "org.bouncycastle" + ) + private lazy val shared = "io.iohk.atala" % "shared" % Versions.shared + private lazy val enumeratum = ("com.beachape" %% "enumeratum" % Versions.enumeratum).cross(CrossVersion.for3Use2_13) private lazy val akkaTyped = "com.typesafe.akka" %% "akka-actor-typed" % Versions.akka private lazy val akkaStream = "com.typesafe.akka" %% "akka-stream" % Versions.akka @@ -28,25 +37,21 @@ object Dependencies { private lazy val doobiePostgres = "org.tpolecat" %% "doobie-postgres" % Versions.doobie private lazy val doobieHikari = "org.tpolecat" %% "doobie-hikari" % Versions.doobie - // We have to exclude bouncycastle since for some reason bitcoinj depends on bouncycastle jdk15to18 - // (i.e. JDK 1.5 to 1.8), but we are using JDK 11 - private lazy val prismCrypto = "io.iohk.atala" % "prism-crypto-jvm" % Versions.prismSdk excludeAll - ExclusionRule( - organization = "org.bouncycastle" - ) - - private lazy val shared = "io.iohk.atala" % "shared" % Versions.shared - private lazy val enumeratum = ("com.beachape" %% "enumeratum" % Versions.enumeratum).cross(CrossVersion.for3Use2_13) + // Tests + private lazy val zioTest = "dev.zio" %% "zio-test" % "2.0.2" % Test + private lazy val zioTestSbt = "dev.zio" %% "zio-test-sbt" % "2.0.2" % Test + private lazy val zioTestMagnolia = "dev.zio" %% "zio-test-magnolia" % "2.0.2" % Test // Dependency Modules private lazy val baseDependencies: Seq[ModuleID] = Seq(zio, prismCrypto, shared, enumeratum) private lazy val akkaHttpDependencies: Seq[ModuleID] = Seq(akkaTyped, akkaStream, akkaHttp, akkaSprayJson).map(_.cross(CrossVersion.for3Use2_13)) private lazy val grpcDependencies: Seq[ModuleID] = Seq(grpcNetty, grpcServices, scalaPbProto, scalaPbGrpc) private lazy val doobieDependencies: Seq[ModuleID] = Seq(doobiePostgres, doobieHikari) + private lazy val zioTestDependencies: Seq[ModuleID] = Seq(zioTest, zioTestSbt, zioTestMagnolia) // Project Dependencies - lazy val coreDependencies: Seq[ModuleID] = baseDependencies ++ grpcDependencies + lazy val coreDependencies: Seq[ModuleID] = baseDependencies ++ grpcDependencies ++ zioTestDependencies lazy val sqlDependencies: Seq[ModuleID] = baseDependencies ++ doobieDependencies ++ Seq(zioCatsInterop) lazy val apiServerDependencies: Seq[ModuleID] = baseDependencies ++ akkaHttpDependencies } diff --git a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala index 8b488ad463..26a62afb49 100644 --- a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/service/IrisServiceGrpcImpl.scala @@ -19,7 +19,7 @@ class IrisServiceGrpcImpl(service: PublishingScheduler)(using runtime: Runtime[A initialRecoveryCommitment = ByteString.copyFrom("b".getBytes()), storage = "https://atalaprism.io", document = Some(DocumentDefinition(publicKeys = Seq(), services = Seq())) - )); + )) override def scheduleOperation(request: IrisOperation): Future[IrisOperationOutcome] = Unsafe.unsafe { implicit unsafe => runtime.unsafe.runToFuture(ZIO.succeed(IrisOperationOutcome(mockOperationId))) diff --git a/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala index 26387349a8..e1256fc0c2 100644 --- a/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala +++ b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala @@ -14,6 +14,16 @@ trait HashValue extends Any { override def toString: String = { BytesOps.bytesToHex(value) } + + override def equals(obj: Any): Boolean = { + if (this === obj) + return true + + if (obj == null || obj.getClass != this.getClass) + return false + + value.equals(obj.asInstanceOf[HashValue].value) + } } trait HashValueFrom[A] { From 5d946eeee0e2c3d8dbd9c9263eeb810346eac599 Mon Sep 17 00:00:00 2001 From: Ilya Peresadin Date: Sat, 1 Oct 2022 00:02:06 +0400 Subject: [PATCH 3/3] Fix review --- iris/api/grpc/protocol/did_operations.proto | 2 +- iris/api/grpc/protocol/dlt.proto | 2 +- iris/api/grpc/service.proto | 2 + iris/service/.scalafmt.conf | 5 ++ iris/service/build.sbt | 2 +- .../atala/iris/core/model/ledger/Ledger.scala | 11 ++-- .../InmemoryUnderlyingLedgerService.scala | 55 +++++++++------- .../InmemoryUnderlyingLedgerServiceSpec.scala | 65 ++++++++----------- .../atala/iris/core/service/RandomUtils.scala | 19 +++--- iris/service/project/Dependencies.scala | 17 ++--- .../io/iohk/atala/iris/server/Modules.scala | 3 - .../atala/iris/server/grpc/GrpcServices.scala | 1 - shared/.scalafmt.conf | 5 ++ .../io/iohk/atala/shared/HashValue.scala | 3 - 14 files changed, 93 insertions(+), 99 deletions(-) create mode 100644 iris/service/.scalafmt.conf create mode 100644 shared/.scalafmt.conf diff --git a/iris/api/grpc/protocol/did_operations.proto b/iris/api/grpc/protocol/did_operations.proto index 562e491944..ab3261c8a7 100644 --- a/iris/api/grpc/protocol/did_operations.proto +++ b/iris/api/grpc/protocol/did_operations.proto @@ -55,7 +55,7 @@ message DocumentDefinition { message CreateDid { bytes initial_update_commitment = 1; bytes initial_recovery_commitment = 2; - string storage = 3; + string ledger = 3; DocumentDefinition document = 4; } diff --git a/iris/api/grpc/protocol/dlt.proto b/iris/api/grpc/protocol/dlt.proto index 821f73e6aa..925cc269a9 100644 --- a/iris/api/grpc/protocol/dlt.proto +++ b/iris/api/grpc/protocol/dlt.proto @@ -32,6 +32,6 @@ message IrisOperation { } // List of operations which will be stored in the blockchain transaction metadata -message AtalaObject { +message IrisObject { repeated IrisOperation operations = 1; } diff --git a/iris/api/grpc/service.proto b/iris/api/grpc/service.proto index c66d9a6b9f..c18920a1d0 100644 --- a/iris/api/grpc/service.proto +++ b/iris/api/grpc/service.proto @@ -32,6 +32,8 @@ message IrisOperationInfo { UpdateDid update_did = 3; RecoverDid recovery_did = 4; DeactivateDid deactivate_did = 5; + IssueCredentialsBatch issue_credentials_batch = 6; + RevokeCredentials revoke_credentials = 7; } } diff --git a/iris/service/.scalafmt.conf b/iris/service/.scalafmt.conf new file mode 100644 index 0000000000..6f39b7a2f7 --- /dev/null +++ b/iris/service/.scalafmt.conf @@ -0,0 +1,5 @@ +version = 3.5.8 +runner.dialect = scala3 + +maxColumn = 120 +trailingCommas = preserve diff --git a/iris/service/build.sbt b/iris/service/build.sbt index 285108f4b7..a76b3588d5 100644 --- a/iris/service/build.sbt +++ b/iris/service/build.sbt @@ -44,6 +44,6 @@ lazy val server = project .in(file("server")) .settings( name := "iris-server", - libraryDependencies ++= apiServerDependencies, + libraryDependencies ++= serverDependencies, ) .dependsOn(core, sql) diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala index 5cf93c7d9e..891e4424c1 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/model/ledger/Ledger.scala @@ -4,12 +4,9 @@ import enumeratum.{Enum, EnumEntry} import scala.collection.immutable.ArraySeq -sealed trait Ledger extends EnumEntry +case class Ledger(name: String) -object Ledger extends Enum[Ledger] { - val values = ArraySeq(InMemory, CardanoMainnet, CardanoTestnet) - - case object InMemory extends Ledger - case object CardanoMainnet extends Ledger - case object CardanoTestnet extends Ledger +object Ledger{ + val InMemory: Ledger = Ledger("in-memory") + val Mainnet: Ledger = Ledger("mainnet") } diff --git a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala index 1ba447b141..940a003a0f 100644 --- a/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala +++ b/iris/service/core/src/main/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerService.scala @@ -13,14 +13,14 @@ object InmemoryUnderlyingLedgerService { case class CardanoTransaction(operations: Seq[proto.IrisOperation]) { lazy val transactionId: TransactionId = { - val objectBytes = proto.AtalaObject(operations).toByteArray + val objectBytes = proto.IrisObject(operations).toByteArray val hash = Sha256.compute(objectBytes) TransactionId .from(hash.getValue) .getOrElse(throw new RuntimeException("Unexpected invalid hash")) } } - + case class CardanoBlock(txs: Seq[CardanoTransaction]) def layer(config: Config): ULayer[InmemoryUnderlyingLedgerService] = ZLayer.fromZIO { @@ -35,11 +35,11 @@ object InmemoryUnderlyingLedgerService { } class InmemoryUnderlyingLedgerService( - config: Config, - mempoolRef: TRef[Vector[CardanoTransaction]], - blocksRef: TRef[Vector[CardanoBlock]], - balanceRef: TRef[Funds] - ) extends UnderlyingLedgerService { + config: Config, + mempoolRef: TRef[Vector[CardanoTransaction]], + blocksRef: TRef[Vector[CardanoBlock]], + balanceRef: TRef[Funds] +) extends UnderlyingLedgerService { override def publish(operations: Seq[proto.IrisOperation]): IO[LedgerError, Unit] = STM.atomically { @@ -60,15 +60,19 @@ class InmemoryUnderlyingLedgerService( for { mempool <- mempoolRef.get blockchain <- blocksRef.get - tdetails <- STM.fromOption { - mempool.find(_.transactionId == transactionId) - .map(_ => TransactionDetails(transactionId, InMempool)) - }.orElse { - STM.fromOption { - blockchain.find(block => block.txs.exists(t => t.transactionId == transactionId)) - .map(_ => TransactionDetails(transactionId, InLedger)) + tdetails <- STM + .fromOption { + mempool + .find(_.transactionId == transactionId) + .map(_ => TransactionDetails(transactionId, InMempool)) + } + .orElse { + STM.fromOption { + blockchain + .find(block => block.txs.exists(t => t.transactionId == transactionId)) + .map(_ => TransactionDetails(transactionId, InLedger)) + } } - } .orElseFail(LedgerError(s"Couldn't find tx $transactionId")) } yield tdetails } @@ -76,7 +80,8 @@ class InmemoryUnderlyingLedgerService( override def deleteTransaction(transactionId: TransactionId): IO[LedgerError, Unit] = STM.atomically { for { mempool <- mempoolRef.get - _ <- STM.cond(mempool.exists(_.transactionId == transactionId), + _ <- STM.cond( + mempool.exists(_.transactionId == transactionId), (), LedgerError(s"Transaction $transactionId not found in the mempool") ) @@ -91,13 +96,15 @@ class InmemoryUnderlyingLedgerService( def getBlocks: UIO[List[CardanoBlock]] = blocksRef.get.commit.map(_.toList) - private[service] def startBackgroundProcess(): UIO[Unit] = STM.atomically { - for { - // Craft a new block from mempool transactions - txs <- mempoolRef.modify(old => (old, Vector.empty)) - _ <- blocksRef.update(_.appended(CardanoBlock(txs))) - } yield () - } + private[service] def startBackgroundProcess(): UIO[Unit] = STM + .atomically { + for { + // Craft a new block from mempool transactions + txs <- mempoolRef.modify(old => (old, Vector.empty)) + _ <- blocksRef.update(_.appended(CardanoBlock(txs))) + } yield () + } .repeat(Schedule.spaced(config.blockEvery)) - .fork.map(_ => ()) + .fork + .map(_ => ()) } diff --git a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala index 81a02b7410..d51ddf9440 100644 --- a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala +++ b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/InmemoryUnderlyingLedgerServiceSpec.scala @@ -23,9 +23,8 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { } def foreachZIO[R](srv: InmemoryUnderlyingLedgerService)(xs: Iterable[PublishThenAdjust]): ZIO[R, Any, Unit] = - ZIO.foreachDiscard[R, Any, PublishThenAdjust](xs) { - case PublishThenAdjust(ops, adj) => - srv.publish(ops).flatMap(_ => TestClock.adjust(adj)) + ZIO.foreachDiscard[R, Any, PublishThenAdjust](xs) { case PublishThenAdjust(ops, adj) => + srv.publish(ops).flatMap(_ => TestClock.adjust(adj)) } } @@ -47,23 +46,23 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) mempool <- srvc.getMempool blocks <- srvc.getBlocks - } yield + } yield assertTrue(mempool == List.empty) && assertTrue( - mempool == List.empty && - blocks == List( - CardanoBlock(List()), - CardanoBlock(List( + blocks == List( + CardanoBlock(List()), + CardanoBlock( + List( CardanoTransaction(Seq(op(0))), CardanoTransaction(Seq(op(1))), CardanoTransaction(Seq(op(2))), CardanoTransaction(Seq(op(3))) - )), - CardanoBlock(List()) - ) + ) + ), + CardanoBlock(List()) + ) ) testCase.provideLayer(inmemoryLedger) }, - test("Operations distributed between 2 blocks") { val testCase = for { @@ -72,32 +71,33 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { scenario = List( Seq(op(0)) >> 1.seconds, Seq(op(1)) >> 10.seconds, - Seq(op(2)) >> 0.seconds, Seq(op(3)) >> 10.seconds ) _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) mempool <- srvc.getMempool blocks <- srvc.getBlocks - } yield + } yield assertTrue(mempool == List.empty) && assertTrue( - mempool == List.empty && - blocks == List( - CardanoBlock(List()), - CardanoBlock(List( + blocks == List( + CardanoBlock(List()), + CardanoBlock( + List( CardanoTransaction(Seq(op(0))), CardanoTransaction(Seq(op(1))), - )), - CardanoBlock(List( + ) + ), + CardanoBlock( + List( CardanoTransaction(Seq(op(2))), CardanoTransaction(Seq(op(3))), - )), - ) + ) + ), + ) ) testCase.provideLayer(inmemoryLedger) } ), - suite("getTransactionDetails")( test("Find unconfirmed transaction") { val testCase = @@ -107,18 +107,15 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { scenario = List( Seq(op(0)) >> 1.seconds, Seq(op(1)) >> 10.seconds, - Seq(op(2), op(3)) >> 0.seconds, Seq(op(4)) >> 2.seconds ) _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) targetTx = CardanoTransaction(Seq(op(2), op(3))) txDetails <- srvc.getTransactionDetails(targetTx.transactionId) - } yield - assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InMempool)) + } yield assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InMempool)) testCase.provideLayer(inmemoryLedger) }, - test("Find confirmed transaction") { val testCase = for { @@ -133,11 +130,9 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { _ <- PublishThenAdjust.foreachZIO(srvc)(scenario) targetTx = CardanoTransaction(Seq(op(2), op(3))) txDetails <- srvc.getTransactionDetails(targetTx.transactionId) - } yield - assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InLedger)) + } yield assertTrue(txDetails == TransactionDetails(targetTx.transactionId, InLedger)) testCase.provideLayer(inmemoryLedger) }, - test("Find unknown transaction") { val testCase = for { @@ -158,7 +153,6 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { testCase.provideLayer(inmemoryLedger) } ), - suite("deleteTransaction")( test("Delete transaction from mempool") { val testCase = @@ -168,7 +162,6 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { scenario = List( Seq(op(0)) >> 1.seconds, Seq(op(1)) >> 10.seconds, - Seq(op(2), op(3)) >> 0.seconds, Seq(op(4)) >> 2.seconds ) @@ -176,11 +169,9 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { targetTx = CardanoTransaction(Seq(op(2), op(3))) _ <- srvc.deleteTransaction(targetTx.transactionId) mempool <- srvc.getMempool - } yield - assertTrue(mempool == List(CardanoTransaction(Seq(op(4))))) + } yield assertTrue(mempool == List(CardanoTransaction(Seq(op(4))))) testCase.provideLayer(inmemoryLedger) }, - test("Delete confirmed transaction") { val testCase = for { @@ -189,7 +180,6 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { scenario = List( Seq(op(0)) >> 1.seconds, Seq(op(1)) >> 10.seconds, - Seq(op(2), op(3)) >> 0.seconds, Seq(op(4)) >> 2.seconds ) @@ -197,8 +187,7 @@ object InmemoryUnderlyingLedgerServiceSpec extends ZIOSpecDefault { targetTx = CardanoTransaction(Seq(op(1))) testResult <- assertZIO(srvc.deleteTransaction(targetTx.transactionId).exit) { - fails( - equalTo(LedgerError(s"Transaction ${targetTx.transactionId} not found in the mempool"))) + fails(equalTo(LedgerError(s"Transaction ${targetTx.transactionId} not found in the mempool"))) } } yield testResult testCase.provideLayer(inmemoryLedger) diff --git a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala index 47e30044b5..3c2ef4f18e 100644 --- a/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala +++ b/iris/service/core/src/test/scala/io/iohk/atala/iris/core/service/RandomUtils.scala @@ -14,16 +14,16 @@ object RandomUtils { for { updComm <- nextBytes(20) recComm <- nextBytes(20) - } yield - proto.IrisOperation(proto.IrisOperation.Operation.CreateDid( + } yield proto.IrisOperation( + proto.IrisOperation.Operation.CreateDid( CreateDid( initialUpdateCommitment = updComm, initialRecoveryCommitment = recComm, - storage = "mainnet", + ledger = "mainnet", document = Some(DocumentDefinition(publicKeys = Seq(), services = Seq())) ) ) - ) + ) def genUpdateOperation(): UIO[proto.IrisOperation] = for { @@ -32,8 +32,8 @@ object RandomUtils { prevVers <- nextBytes(20) forwUpdComm <- nextBytes(20) sig <- nextBytes(20) - } yield - proto.IrisOperation(proto.IrisOperation.Operation.UpdateDid( + } yield proto.IrisOperation( + proto.IrisOperation.Operation.UpdateDid( UpdateDid( did = "did:prism:" + didSuff, revealedUpdateKey = updKey, @@ -43,12 +43,13 @@ object RandomUtils { signature = sig ) ) - ) + ) def genOperation(): UIO[proto.IrisOperation] = for { op <- Random.nextBoolean - res <- if (op) genCreateOperation() - else genUpdateOperation() + res <- + if (op) genCreateOperation() + else genUpdateOperation() } yield res } diff --git a/iris/service/project/Dependencies.scala b/iris/service/project/Dependencies.scala index 530b6959c3..b83e140166 100644 --- a/iris/service/project/Dependencies.scala +++ b/iris/service/project/Dependencies.scala @@ -4,7 +4,6 @@ object Dependencies { object Versions { val zio = "2.0.2" val akka = "2.6.19" - val akkaHttp = "10.2.9" val doobie = "1.0.0-RC2" val zioCatsInterop = "3.3.0" val prismSdk = "v1.3.3-snapshot-1657194253-992dd96" @@ -25,27 +24,23 @@ object Dependencies { private lazy val shared = "io.iohk.atala" % "shared" % Versions.shared private lazy val enumeratum = ("com.beachape" %% "enumeratum" % Versions.enumeratum).cross(CrossVersion.for3Use2_13) - private lazy val akkaTyped = "com.typesafe.akka" %% "akka-actor-typed" % Versions.akka - private lazy val akkaStream = "com.typesafe.akka" %% "akka-stream" % Versions.akka - private lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % Versions.akkaHttp - private lazy val akkaSprayJson = "com.typesafe.akka" %% "akka-http-spray-json" % Versions.akkaHttp - private lazy val grpcNetty = "io.grpc" % "grpc-netty" % scalapb.compiler.Version.grpcJavaVersion private lazy val grpcServices = "io.grpc" % "grpc-services" % scalapb.compiler.Version.grpcJavaVersion - private lazy val scalaPbProto = "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion % "protobuf" - private lazy val scalaPbGrpc = "com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion + private lazy val scalaPbProto = + "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion % "protobuf" + private lazy val scalaPbGrpc = + "com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion private lazy val doobiePostgres = "org.tpolecat" %% "doobie-postgres" % Versions.doobie private lazy val doobieHikari = "org.tpolecat" %% "doobie-hikari" % Versions.doobie // Tests private lazy val zioTest = "dev.zio" %% "zio-test" % "2.0.2" % Test - private lazy val zioTestSbt = "dev.zio" %% "zio-test-sbt" % "2.0.2" % Test + private lazy val zioTestSbt = "dev.zio" %% "zio-test-sbt" % "2.0.2" % Test private lazy val zioTestMagnolia = "dev.zio" %% "zio-test-magnolia" % "2.0.2" % Test // Dependency Modules private lazy val baseDependencies: Seq[ModuleID] = Seq(zio, prismCrypto, shared, enumeratum) - private lazy val akkaHttpDependencies: Seq[ModuleID] = Seq(akkaTyped, akkaStream, akkaHttp, akkaSprayJson).map(_.cross(CrossVersion.for3Use2_13)) private lazy val grpcDependencies: Seq[ModuleID] = Seq(grpcNetty, grpcServices, scalaPbProto, scalaPbGrpc) private lazy val doobieDependencies: Seq[ModuleID] = Seq(doobiePostgres, doobieHikari) private lazy val zioTestDependencies: Seq[ModuleID] = Seq(zioTest, zioTestSbt, zioTestMagnolia) @@ -53,5 +48,5 @@ object Dependencies { // Project Dependencies lazy val coreDependencies: Seq[ModuleID] = baseDependencies ++ grpcDependencies ++ zioTestDependencies lazy val sqlDependencies: Seq[ModuleID] = baseDependencies ++ doobieDependencies ++ Seq(zioCatsInterop) - lazy val apiServerDependencies: Seq[ModuleID] = baseDependencies ++ akkaHttpDependencies + lazy val serverDependencies: Seq[ModuleID] = baseDependencies } diff --git a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala index 6532653db0..82b944e4e7 100644 --- a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/Modules.scala @@ -1,8 +1,5 @@ package io.iohk.atala.iris.server -import akka.actor.typed.ActorSystem -import akka.actor.typed.scaladsl.Behaviors -import akka.http.scaladsl.server.Route import cats.effect.std.Dispatcher import doobie.util.transactor.Transactor import io.iohk.atala.iris.server.grpc.service.IrisServiceGrpcImpl diff --git a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala index 65838def92..f6275acfd8 100644 --- a/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala +++ b/iris/service/server/src/main/scala/io/iohk/atala/iris/server/grpc/GrpcServices.scala @@ -1,6 +1,5 @@ package io.iohk.atala.iris.server.grpc -import akka.actor.typed.ActorSystem import io.grpc.ServerServiceDefinition import io.iohk.atala.iris.proto.service.IrisServiceGrpc import zio.* diff --git a/shared/.scalafmt.conf b/shared/.scalafmt.conf new file mode 100644 index 0000000000..6f39b7a2f7 --- /dev/null +++ b/shared/.scalafmt.conf @@ -0,0 +1,5 @@ +version = 3.5.8 +runner.dialect = scala3 + +maxColumn = 120 +trailingCommas = preserve diff --git a/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala index e1256fc0c2..18db7f310b 100644 --- a/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala +++ b/shared/src/main/scala/io/iohk/atala/shared/HashValue.scala @@ -16,9 +16,6 @@ trait HashValue extends Any { } override def equals(obj: Any): Boolean = { - if (this === obj) - return true - if (obj == null || obj.getClass != this.getClass) return false