From 4e600ecf78e5b4a3fa9be8ac4ebc2f885b4c389c Mon Sep 17 00:00:00 2001 From: Bassam Riman Date: Tue, 5 Mar 2024 13:01:40 -0500 Subject: [PATCH] ATL-6665: Fix Update Schema and CredentialDef on Receive Credential Signed-off-by: Bassam Riman --- .../repository/CredentialRepository.scala | 2 + .../CredentialRepositoryInMemory.scala | 6 +- .../core/service/CredentialServiceImpl.scala | 129 ++++++++++-------- .../service/MockPresentationService.scala | 2 - .../core/service/PresentationService.scala | 2 - .../service/PresentationServiceImpl.scala | 58 ++++---- .../service/PresentationServiceNotifier.scala | 6 +- .../CredentialRepositorySpecSuite.scala | 8 ++ .../service/PresentationServiceSpec.scala | 10 +- .../repository/JdbcCredentialRepository.scala | 9 +- .../server/jobs/PresentBackgroundJobs.scala | 5 - 11 files changed, 141 insertions(+), 96 deletions(-) diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala index 2f2b9615e5..f919f33e46 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepository.scala @@ -67,6 +67,8 @@ trait CredentialRepository { recordId: DidCommID, issue: IssueCredential, issuedRawCredential: String, + schemaUri: Option[String], + credentialDefinitionUri: Option[String], protocolState: ProtocolState ): RIO[WalletAccessContext, Int] diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala index 2bc9bce4c5..a952b8b223 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/CredentialRepositoryInMemory.scala @@ -98,6 +98,8 @@ class CredentialRepositoryInMemory( recordId: DidCommID, issue: IssueCredential, issuedRawCredential: String, + schemaUri: Option[String], + credentialDefinitionUri: Option[String], protocolState: ProtocolState ): RIO[WalletAccessContext, Int] = { for { @@ -111,6 +113,8 @@ class CredentialRepositoryInMemory( recordId, record.copy( updatedAt = Some(Instant.now), + schemaUri = schemaUri, + credentialDefinitionUri = credentialDefinitionUri, issueCredentialData = Some(issue), issuedCredentialRaw = Some(issuedRawCredential), protocolState = protocolState, @@ -149,7 +153,7 @@ class CredentialRepositoryInMemory( rec.id ) && rec.issueCredentialData.isDefined && rec.schemaUri.isDefined - && rec.credentialDefinitionId.isDefined + && rec.credentialDefinitionUri.isDefined && rec.credentialFormat == CredentialFormat.AnonCreds ) .map(rec => diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala index 8b273ebdb5..0d4250c9a2 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceImpl.scala @@ -607,67 +607,86 @@ private class CredentialServiceImpl( override def receiveCredentialIssue( issueCredential: IssueCredential - ): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] = { - for { - // TODO Move this type of generic/reusable code to a helper trait - record <- getRecordFromThreadIdWithState( - issueCredential.thid.map(DidCommID(_)), - ignoreWithZeroRetries = true, - ProtocolState.RequestPending, - ProtocolState.RequestSent + ): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] = for { + // TODO Move this type of generic/reusable code to a helper trait + record <- getRecordFromThreadIdWithState( + issueCredential.thid.map(DidCommID(_)), + ignoreWithZeroRetries = true, + ProtocolState.RequestPending, + ProtocolState.RequestSent + ) + attachment <- ZIO + .fromOption(issueCredential.attachments.headOption) + .mapError(_ => CredentialServiceError.UnexpectedError("Missing attachment in credential issued credential")) + + _ <- { + val result = attachment match { + case AttachmentDescriptor( + id, + media_type, + Base64(v), + Some(IssueCredentialIssuedFormat.Anoncred.name), + _, + _, + _, + _ + ) => + for { + processedCredential <- processAnonCredsCredential(record, java.util.Base64.getUrlDecoder.decode(v)) + attachment = AttachmentDescriptor.buildBase64Attachment( + id = id, + mediaType = media_type, + format = Some(IssueCredentialIssuedFormat.Anoncred.name), + payload = processedCredential.data.getBytes + ) + processedIssuedCredential = issueCredential.copy(attachments = Seq(attachment)) + result <- + updateWithCredential( + processedIssuedCredential, + record, + attachment, + Some(processedCredential.getSchemaId), + Some(processedCredential.getCredDefId) + ) + } yield result + case attachment => + updateWithCredential(issueCredential, record, attachment, None, None) + } + result + } + record <- credentialRepository + .getIssueCredentialRecord(record.id) + .mapError(RepositoryError.apply) + .someOrFail(RecordIdNotFound(record.id)) + } yield record + + private def updateWithCredential( + issueCredential: IssueCredential, + record: IssueCredentialRecord, + attachment: AttachmentDescriptor, + schemaId: Option[String], + credDefId: Option[String] + ) = { + credentialRepository + .updateWithIssuedRawCredential( + record.id, + issueCredential, + attachment.data.asJson.noSpaces, + schemaId, + credDefId, + ProtocolState.CredentialReceived ) - processedAttachments <- { - import IssueCredentialIssuedFormat.Anoncred - ZIO.collectAll( - issueCredential.attachments - .map { - case AttachmentDescriptor( - id, - media_type, - Base64(v), - Some(Anoncred.name), - _, - _, - _, - _ - ) => - processAnonCredsCredential(record, java.util.Base64.getUrlDecoder.decode(v)) - .map(processedCredential => - AttachmentDescriptor.buildBase64Attachment( - id = id, - mediaType = media_type, - format = Some(IssueCredentialIssuedFormat.Anoncred.name), - payload = processedCredential - ) - ) - case attachment => ZIO.succeed(attachment) - } - ) + .flatMap { + case 1 => ZIO.succeed(()) + case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) } - processedIssuedCredential = issueCredential.copy(attachments = processedAttachments) - _ <- credentialRepository - .updateWithIssuedRawCredential( - record.id, - processedIssuedCredential, - processedIssuedCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???"), - ProtocolState.CredentialReceived - ) - .flatMap { - case 1 => ZIO.succeed(()) - case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n")) - } - .mapError(RepositoryError.apply) - record <- credentialRepository - .getIssueCredentialRecord(record.id) - .mapError(RepositoryError.apply) - .someOrFail(RecordIdNotFound(record.id)) - } yield record + .mapError(RepositoryError.apply) } private[this] def processAnonCredsCredential( record: IssueCredentialRecord, credentialBytes: Array[Byte] - ): ZIO[WalletAccessContext, CredentialServiceError, Array[Byte]] = { + ): ZIO[WalletAccessContext, CredentialServiceError, anoncreds.AnoncredCredential] = { for { credential <- ZIO.succeed(anoncreds.AnoncredCredential(new String(credentialBytes))) credDefContent <- uriDereferencer @@ -690,7 +709,7 @@ private class CredentialServiceImpl( ) ) .mapError(error => UnexpectedError(s"AnonCreds credential processing error: ${error.getMessage}")) - } yield credential.data.getBytes() + } yield credential } override def markOfferSent( diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala index 6049790a99..3faba1a4c9 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockPresentationService.scala @@ -173,7 +173,6 @@ object MockPresentationService extends Mock[PresentationService] { override def createAnoncredPresentationPayloadFromRecord( record: DidCommID, - issuer: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): IO[PresentationError, AnoncredPresentation] = ??? @@ -181,7 +180,6 @@ object MockPresentationService extends Mock[PresentationService] { override def createAnoncredPresentation( requestPresentation: RequestPresentation, recordId: DidCommID, - prover: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, Presentation] = ??? diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala index 9727983870..b54df6784c 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationService.scala @@ -47,7 +47,6 @@ trait PresentationService { def createAnoncredPresentationPayloadFromRecord( record: DidCommID, - issuer: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] @@ -55,7 +54,6 @@ trait PresentationService { def createAnoncredPresentation( requestPresentation: RequestPresentation, recordId: DidCommID, - prover: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, Presentation] diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala index d75e785f19..a355f9d847 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceImpl.scala @@ -108,7 +108,6 @@ private class PresentationServiceImpl( override def createAnoncredPresentationPayloadFromRecord( recordId: DidCommID, - prover: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = { @@ -151,7 +150,6 @@ private class PresentationServiceImpl( def createAnoncredPresentation( requestPresentation: RequestPresentation, recordId: DidCommID, - prover: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, Presentation] = { @@ -159,7 +157,6 @@ private class PresentationServiceImpl( presentationPayload <- createAnoncredPresentationPayloadFromRecord( recordId, - prover, anoncredCredentialProof, issuanceDate ) @@ -605,10 +602,10 @@ private class PresentationServiceImpl( issuedCredentials <- credentialRepository .getValidIssuedCredentials(credentialsToUse.map(DidCommID(_))) .mapError(RepositoryError.apply) + validatedCredentialsFormat <- validateCredentialsFormat(record, issuedCredentials) _ <- validateCredentials( s"No matching issued credentials found in prover db from the given: $credentialsToUse", - record, - issuedCredentials + validatedCredentialsFormat ) count <- presentationRepository .updatePresentationWithCredentialsToUse(recordId, Option(credentialsToUse), ProtocolState.PresentationPending) @@ -641,13 +638,13 @@ private class PresentationServiceImpl( for { record <- getRecordWithState(recordId, ProtocolState.RequestReceived) - issuedCredentials <- credentialRepository - .getValidIssuedCredentials( - credentialsToUse.credentialProofs.map(credentialProof => DidCommID(credentialProof.credential)) - ) - .mapError(RepositoryError.apply) - _ <- validateCredentials( - s"No matching issued credentials found in prover db from the given: $credentialsToUse", + issuedCredentials <- + credentialRepository + .getValidAnoncredIssuedCredentials( + credentialsToUse.credentialProofs.map(credentialProof => DidCommID(credentialProof.credential)) + ) + .mapError(RepositoryError.apply) + _ <- validateFullCredentialsFormat( record, issuedCredentials ) @@ -676,6 +673,23 @@ private class PresentationServiceImpl( private def validateCredentials( errorMessage: String, + issuedCredentials: Seq[ValidIssuedCredentialRecord] + ) = { + val issuedCredentialRaw = issuedCredentials.flatMap(_.issuedCredentialRaw) + for { + _ <- ZIO.fromEither( + Either.cond( + issuedCredentialRaw.nonEmpty, + issuedCredentialRaw, + PresentationError.IssuedCredentialNotFoundError( + new Throwable(errorMessage) + ) + ) + ) + } yield () + } + + private def validateCredentialsFormat( record: PresentationRecord, issuedCredentials: Seq[ValidIssuedCredentialRecord] ) = { @@ -701,19 +715,17 @@ private class PresentationServiceImpl( ) ) ) - signedCredentials = validatedCredentials.flatMap(_.issuedCredentialRaw) - _ <- ZIO.fromEither( - Either.cond( - signedCredentials.nonEmpty, - signedCredentials, - PresentationError.IssuedCredentialNotFoundError( - new Throwable(errorMessage) - ) - ) - ) - } yield () + } yield validatedCredentials } + private def validateFullCredentialsFormat( + record: PresentationRecord, + issuedCredentials: Seq[ValidFullIssuedCredentialRecord] + ) = validateCredentialsFormat( + record, + issuedCredentials.map(cred => ValidIssuedCredentialRecord(cred.id, None, cred.credentialFormat, cred.subjectId)) + ) + override def acceptPresentation( recordId: DidCommID ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = { diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala index 59afcefd9c..0739b63067 100644 --- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala +++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifier.scala @@ -169,20 +169,18 @@ class PresentationServiceNotifier( override def createAnoncredPresentationPayloadFromRecord( record: DidCommID, - issuer: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = - svc.createAnoncredPresentationPayloadFromRecord(record, issuer, anoncredCredentialProof, issuanceDate) + svc.createAnoncredPresentationPayloadFromRecord(record, anoncredCredentialProof, issuanceDate) override def createAnoncredPresentation( requestPresentation: RequestPresentation, recordId: DidCommID, - prover: Issuer, anoncredCredentialProof: AnoncredCredentialProofsV1, issuanceDate: Instant ): ZIO[WalletAccessContext, PresentationError, Presentation] = - svc.createAnoncredPresentation(requestPresentation, recordId, prover, anoncredCredentialProof, issuanceDate) + svc.createAnoncredPresentation(requestPresentation, recordId, anoncredCredentialProof, issuanceDate) override def getPresentationRecordsByStates( ignoreWithZeroRetries: Boolean, diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/CredentialRepositorySpecSuite.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/CredentialRepositorySpecSuite.scala index 0d0a1eb1c0..39e3f65995 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/CredentialRepositorySpecSuite.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/CredentialRepositorySpecSuite.scala @@ -269,12 +269,16 @@ object CredentialRepositorySpecSuite { aRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), "RAW_CREDENTIAL_DATA", + None, + None, ProtocolState.CredentialReceived ) _ <- repo.updateWithIssuedRawCredential( dRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), "RAW_CREDENTIAL_DATA", + None, + None, ProtocolState.CredentialReceived ) records <- repo.getValidIssuedCredentials(Seq(aRecord.id, bRecord.id, dRecord.id)) @@ -387,6 +391,8 @@ object CredentialRepositorySpecSuite { aRecord.id, issueCredential, "RAW_CREDENTIAL_DATA", + Some("schemaUri"), + Some("credentialDefinitionUri"), ProtocolState.CredentialReceived ) updatedRecord <- repo.getIssueCredentialRecord(aRecord.id) @@ -395,6 +401,8 @@ object CredentialRepositorySpecSuite { assertTrue(record.get.issueCredentialData.isEmpty) && assertTrue(updatedRecord.get.issueCredentialData.contains(issueCredential)) && assertTrue(updatedRecord.get.issuedCredentialRaw.contains("RAW_CREDENTIAL_DATA")) + assertTrue(updatedRecord.get.credentialDefinitionUri.contains("credentialDefinitionUri")) + assertTrue(updatedRecord.get.schemaUri.contains("schemaUri")) } }, test("updateFail (fail one retry) updates record") { diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala index 1032557c38..abe9b32a1a 100644 --- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala +++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpec.scala @@ -232,6 +232,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp aIssueCredentialRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), rawCredentialData, + None, + None, IssueCredentialRecord.ProtocolState.CredentialReceived ) svc <- ZIO.service[PresentationService] @@ -471,6 +473,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp aIssueCredentialRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), rawCredentialData, + None, + None, IssueCredentialRecord.ProtocolState.CredentialReceived ) svc <- ZIO.service[PresentationService] @@ -500,6 +504,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp aIssueCredentialRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), rawCredentialData, + Some("SchemaId"), + Some("CredDefId"), IssueCredentialRecord.ProtocolState.CredentialReceived ) svc <- ZIO.service[PresentationService] @@ -557,6 +563,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp aIssueCredentialRecord.id, IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage), rawCredentialData, + None, + None, IssueCredentialRecord.ProtocolState.CredentialReceived ) svc <- ZIO.service[PresentationService] @@ -888,11 +896,9 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp Some(credentialsToUseJson), PresentationRecord.ProtocolState.RequestPending ) - issuer = createIssuer(DID("did:prism:issuer")) presentation <- svc.createAnoncredPresentation( aRecord.requestPresentationData.get, aRecord.id, - issuer, credentialsToUse, Instant.now() ) diff --git a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala index 8d86a432df..a6b36aead7 100644 --- a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala +++ b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcCredentialRepository.scala @@ -2,6 +2,7 @@ package io.iohk.atala.pollux.sql.repository import cats.data.NonEmptyList import doobie.* +import doobie.free.connection import doobie.implicits.* import doobie.postgres.implicits.* import io.circe.* @@ -19,11 +20,11 @@ import io.iohk.atala.shared.db.Implicits.* import io.iohk.atala.shared.models.WalletAccessContext import org.postgresql.util.PSQLException import zio.* +import zio.interop.catz.* import zio.json.* -import doobie.free.connection import java.time.Instant -import zio.interop.catz.* +import java.util.UUID class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[Task], maxRetries: Int) extends CredentialRepository { @@ -500,11 +501,15 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[ recordId: DidCommID, issue: IssueCredential, issuedRawCredential: String, + schemaUri: Option[String], + credentialDefinitionUri: Option[String], protocolState: ProtocolState ): RIO[WalletAccessContext, Int] = { val cxnIO = sql""" | UPDATE public.issue_credential_records | SET + | schema_uri = $schemaUri, + | credential_definition_uri = $credentialDefinitionUri, | issue_credential_data = $issue, | issued_credential_raw = $issuedRawCredential, | protocol_state = $protocolState, diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala index 836be0556d..87c39794a0 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/PresentBackgroundJobs.scala @@ -373,16 +373,11 @@ object PresentBackgroundJobs extends BackgroundJobsHelper { AnoncredCredentialProofsV1.schemaSerDes .deserialize(credentialsToUseJson) .mapError(error => PresentationError.UnexpectedError(error.error)) - prover <- createPrismDIDIssuerFromPresentationCredentials( - id, - anoncredCredentialProofs.credentialProofs.map(_.credential) - ).provideSomeLayer(ZLayer.succeed(walletAccessContext)) presentation <- presentationService .createAnoncredPresentation( requestPresentation, id, - prover, anoncredCredentialProofs, Instant.now() )