diff --git a/build.sbt b/build.sbt
index 1e369601c1..e95c354731 100644
--- a/build.sbt
+++ b/build.sbt
@@ -142,6 +142,7 @@ lazy val D = new {
"com.github.dasniko" % "testcontainers-keycloak" % V.testContainersJavaKeycloak % Test
val doobiePostgres: ModuleID = "org.tpolecat" %% "doobie-postgres" % V.doobie
+ val doobiePostgresCirce: ModuleID = "org.tpolecat" %% "doobie-postgres-circe" % V.doobie
val doobieHikari: ModuleID = "org.tpolecat" %% "doobie-hikari" % V.doobie
val flyway: ModuleID = "org.flywaydb" % "flyway-core" % V.flyway
@@ -160,7 +161,7 @@ lazy val D = new {
// LIST of Dependencies
val doobieDependencies: Seq[ModuleID] =
- Seq(doobiePostgres, doobieHikari, flyway)
+ Seq(doobiePostgres, doobiePostgresCirce, doobieHikari, flyway)
}
lazy val D_Shared = new {
diff --git a/docs/docusaurus/credentials/present-proof.md b/docs/docusaurus/credentials/present-proof.md
index 4509eac59b..9469c57af1 100644
--- a/docs/docusaurus/credentials/present-proof.md
+++ b/docs/docusaurus/credentials/present-proof.md
@@ -1,3 +1,6 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
# Present proof
The [Present Proof Protocol](/docs/concepts/glossary#present-proof-protocol) allows:
@@ -57,6 +60,9 @@ To do this, he makes a `POST` request to the [`/present-proof/presentations`](/a
1. `connectionId`: This field represents the unique identifier of an existing connection between the verifier and the Holder/prover. It is for exchanging messages related to the protocol flow execution.
2. `challenge` and `domain`: The Verifier provides the random seed challenge and operational domain, and the Holder/Prover must sign the generated proof to protect from replay attacks.
+
+
+
```bash
curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
-H 'accept: application/json' \
@@ -72,6 +78,56 @@ curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
}'
```
+
+
+
+```bash
+curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
+ -H 'accept: application/json' \
+ -H 'Content-Type: application/json' \
+ -H "apikey: $API_KEY" \
+ -d '{
+ "connectionId": "872ddfa9-4115-46c2-8a1b-22c24c7431d7",
+ "anoncredPresentationRequest": {
+ "requested_attributes": {
+ "attribute1": {
+ "name": "Attribute 1",
+ "restrictions": [
+ {
+ "cred_def_id": "credential_definition_id_of_attribute1"
+ }
+ ],
+ "non_revoked": {
+ "from": 1635734400,
+ "to": 1735734400
+ }
+ }
+ },
+ "requested_predicates": {
+ "predicate1": {
+ "name": "age",
+ "p_type": ">=",
+ "p_value": 18,
+ "restrictions": [
+ {
+ "schema_id": "schema_id_of_predicate1"
+ }
+ ],
+ "non_revoked": {
+ "from": 1635734400
+ }
+ }
+ },
+ "name": "Example Presentation Request",
+ "nonce": "1234567890",
+ "version": "1.0"
+ },
+ "credentialFormat": "AnonCreds"
+ }'
+```
+
+
+
Upon execution, a new presentation request record gets created with an initial state of `RequestPending`. The Verifier PRISM Agent will send the presentation request message to the PRISM Agent of the Holder/Prover through the specified DIDComm connection. The record state then is updated to `RequestSent`.
The Verifier can retrieve the list of presentation records by making a `GET` request to the [`/present-proof/presentations`](/agent-api/#tag/Present-Proof/operation/getAllPresentation) endpoint:
@@ -121,6 +177,9 @@ curl -X 'GET' 'http://localhost:8090/prism-agent/present-proof/presentations' \
The Holder/Prover can then accept a specific request, generate the proof, and send it to the Verifier PRISM Agent by making a `PATCH` request to the [`/present-proof/presentations/{id}`](/agent-api/#tag/Present-Proof/operation/updatePresentation) endpoint:
+
+
+
```bash
curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{PRESENTATION_ID}' \
-H 'Content-Type: application/json' \
@@ -133,8 +192,39 @@ curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{
The Holder/Prover will have to provide the following information:
1. `presentationId`: The unique identifier of the presentation record to accept.
-2. `proofId`: The unique identifier of the verifiable credential record to use as proof.
+2. `proofId`: The unique identifier of the verifiable credential record to use as proof.
+
+
+
+
+```bash
+curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{PRESENTATION_ID}' \
+ -H 'Content-Type: application/json' \
+ -H "apikey: $API_KEY" \
+ -d '{
+ "action": "request-accept",
+ "anoncredPresentationRequest":{
+ "credentialProofs":[
+ {
+ "credential":"3e849b98-f0fd-4cb4-ae96-9ea527a76267",
+ "requestedAttribute":[
+ "age"
+ ],
+ "requestedPredicate":[
+ "age"
+ ]
+ }
+ ]
+ }
+ }'
+```
+
+
+The Holder/Prover will have to provide the following information:
+1. `presentationId`: The unique identifier of the presentation record to accept.
+2. `anoncredPresentationRequest`: A list of credential unique identifier with the attribute and predicate the credential is answering for.
+
The record state is updated to `PresentationPending` and processed by the Holder/Prover PRISM Agent. The agent will automatically generate the proof presentation, change the state to `PresentationGenerated`, and will eventually send it to the Verifier Agent, and change the state to `PresentationSent`.
```mermaid
@@ -152,4 +242,20 @@ stateDiagram-v2
The following diagram shows the end-to-end flow for a verifier to request and verify a proof presentation from a Holder/prover.
-![](present-proof-flow.png)
\ No newline at end of file
+### JWT Present Proof Flow Diagram
+![](present-proof-flow.png)
+### Anoncreds Present Proof Flow Diagram
+![](anoncreds-present-proof-flow.png)
+
+
+
+
+![](present-proof-flow.png)
+
+
+
+
+![](anoncreds-present-proof-flow.png)
+
+
+
\ No newline at end of file
diff --git a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala
index 9bd681714c..5d6b3c7cf4 100644
--- a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala
+++ b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/AnoncredLib.scala
@@ -18,16 +18,16 @@ object AnoncredLib {
version: String, // SCHEMA_Version
attr_names: AttributeNames,
issuer_id: IssuerId, // ISSUER_DID
- ): SchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id)
+ ): AnoncredSchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id)
// issuer
def createCredDefinition(
issuer_id: String,
- schema: SchemaDef,
+ schema: AnoncredSchemaDef,
tag: String,
supportRevocation: Boolean,
signature_type: uniffi.anoncreds_wrapper.SignatureType.CL.type = uniffi.anoncreds_wrapper.SignatureType.CL
- ) = {
+ ): AnoncredCreateCredentialDefinition = {
val credentialDefinition: uniffi.anoncreds_wrapper.IssuerCreateCredentialDefinitionReturn =
uniffi.anoncreds_wrapper
.Issuer()
@@ -40,7 +40,7 @@ object AnoncredLib {
uniffi.anoncreds_wrapper.CredentialDefinitionConfig(supportRevocation)
)
- CreateCredentialDefinition(
+ AnoncredCreateCredentialDefinition(
credentialDefinition.getCredentialDefinition(),
credentialDefinition.getCredentialDefinitionPrivate(),
credentialDefinition.getCredentialKeyCorrectnessProof()
@@ -49,9 +49,9 @@ object AnoncredLib {
// issuer
def createOffer(
- credentialDefinition: CreateCredentialDefinition,
+ credentialDefinition: AnoncredCreateCredentialDefinition,
credentialDefinitionId: String
- ): CredentialOffer =
+ ): AnoncredCredentialOffer =
uniffi.anoncreds_wrapper
.Issuer()
.createCredentialOffer(
@@ -62,15 +62,15 @@ object AnoncredLib {
// holder
def createCredentialRequest(
- linkSecret: LinkSecretWithId,
- credentialDefinition: CredentialDefinition,
- credentialOffer: CredentialOffer,
+ linkSecret: AnoncredLinkSecretWithId,
+ credentialDefinition: AnoncredCredentialDefinition,
+ credentialOffer: AnoncredCredentialOffer,
entropy: String = {
val tmp = scala.util.Random()
tmp.setSeed(java.security.SecureRandom.getInstanceStrong().nextLong())
tmp.nextString(80)
}
- ): CreateCrendentialRequest = {
+ ): AnoncredCreateCrendentialRequest = {
val credentialRequest =
uniffi.anoncreds_wrapper
.Prover()
@@ -83,16 +83,16 @@ object AnoncredLib {
credentialOffer, // CredentialOffer credential_offer
)
- CreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata())
+ AnoncredCreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata())
}
// holder
def processCredential(
- credential: Credential,
- metadata: CredentialRequestMetadata,
- linkSecret: LinkSecretWithId,
- credentialDefinition: CredentialDefinition,
- ): Credential = {
+ credential: AnoncredCredential,
+ metadata: AnoncredCredentialRequestMetadata,
+ linkSecret: AnoncredLinkSecretWithId,
+ credentialDefinition: AnoncredCredentialDefinition,
+ ): AnoncredCredential = {
uniffi.anoncreds_wrapper
.Prover()
.processCredential(
@@ -106,16 +106,16 @@ object AnoncredLib {
// issuer
def createCredential(
- credentialDefinition: CredentialDefinition,
- credentialDefinitionPrivate: CredentialDefinitionPrivate,
- credentialOffer: CredentialOffer,
- credentialRequest: CredentialRequest,
+ credentialDefinition: AnoncredCredentialDefinition,
+ credentialDefinitionPrivate: AnoncredCredentialDefinitionPrivate,
+ credentialOffer: AnoncredCredentialOffer,
+ credentialRequest: AnoncredCredentialRequest,
attributeValues: Seq[(String, String)]
// java.util.List[AttributeValues] : java.util.List[AttributeValues]
// revocationRegistryId : String
// revocationStatusList : RevocationStatusList
// credentialRevocationConfig : CredentialRevocationConfig
- ): Credential = {
+ ): AnoncredCredential = {
uniffi.anoncreds_wrapper
.Issuer()
.createCredential(
@@ -140,13 +140,13 @@ object AnoncredLib {
// [info] Caused by: Predicate is not satisfied
def createPresentation(
- presentationRequest: PresentationRequest,
- credentialRequests: Seq[CredentialAndRequestedAttributesPredicates],
+ presentationRequest: AnoncredPresentationRequest,
+ credentialRequests: Seq[AnoncredCredentialRequests],
selfAttested: Map[String, String],
- linkSecret: LinkSecret,
- schemas: Map[SchemaId, SchemaDef],
- credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition],
- ): Either[uniffi.anoncreds_wrapper.AnoncredsException.CreatePresentationException, Presentation] = {
+ linkSecret: AnoncredLinkSecret,
+ schemas: Map[SchemaId, AnoncredSchemaDef],
+ credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition],
+ ): Either[uniffi.anoncreds_wrapper.AnoncredsException.CreatePresentationException, AnoncredPresentation] = {
try {
Right(
uniffi.anoncreds_wrapper
@@ -181,10 +181,10 @@ object AnoncredLib {
// FIXME its always return false ....
def verifyPresentation(
- presentation: Presentation,
- presentationRequest: PresentationRequest,
- schemas: Map[SchemaId, SchemaDef],
- credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition],
+ presentation: AnoncredPresentation,
+ presentationRequest: AnoncredPresentationRequest,
+ schemas: Map[SchemaId, AnoncredSchemaDef],
+ credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition],
): Boolean = {
uniffi.anoncreds_wrapper
.Verifier()
diff --git a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala
index 0324c89601..38f9d4014e 100644
--- a/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala
+++ b/pollux/lib/anoncreds/src/main/scala/io/iohk/atala/pollux/anoncreds/Models.scala
@@ -3,17 +3,17 @@ package io.iohk.atala.pollux.anoncreds
import uniffi.anoncreds_wrapper.{
Nonce,
Credential as UniffiCredential,
- CredentialRequests as UniffiCredentialRequests,
CredentialDefinition as UniffiCredentialDefinition,
CredentialDefinitionPrivate as UniffiCredentialDefinitionPrivate,
CredentialKeyCorrectnessProof as UniffiCredentialKeyCorrectnessProof,
CredentialOffer as UniffiCredentialOffer,
CredentialRequest as UniffiCredentialRequest,
CredentialRequestMetadata as UniffiCredentialRequestMetadata,
+ CredentialRequests as UniffiCredentialRequests,
LinkSecret as UniffiLinkSecret,
- Schema as UniffiSchema,
Presentation as UniffiPresentation,
PresentationRequest as UniffiPresentationRequest,
+ Schema as UniffiSchema
}
import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder}
@@ -21,41 +21,42 @@ import scala.jdk.CollectionConverters.*
type AttributeNames = Set[String]
type IssuerId = String
-case class LinkSecretWithId(id: String, secret: LinkSecret) { def data = secret.data }
-object LinkSecretWithId {
- def apply(id: String): LinkSecretWithId = LinkSecretWithId(id, LinkSecret())
+case class AnoncredLinkSecretWithId(id: String, secret: AnoncredLinkSecret) { def data = secret.data }
+object AnoncredLinkSecretWithId {
+ def apply(id: String): AnoncredLinkSecretWithId = AnoncredLinkSecretWithId(id, AnoncredLinkSecret())
}
-case class LinkSecret(data: String)
-object LinkSecret {
+case class AnoncredLinkSecret(data: String)
+object AnoncredLinkSecret {
- def apply(): LinkSecret = LinkSecret.given_Conversion_UniffiLinkSecret_LinkSecret(UniffiLinkSecret())
+ def apply(): AnoncredLinkSecret =
+ AnoncredLinkSecret.given_Conversion_UniffiLinkSecret_AnoncredLinkSecret(UniffiLinkSecret())
- given Conversion[LinkSecret, UniffiLinkSecret] with {
- def apply(linkSecret: LinkSecret): UniffiLinkSecret =
+ given Conversion[AnoncredLinkSecret, UniffiLinkSecret] with {
+ def apply(linkSecret: AnoncredLinkSecret): UniffiLinkSecret =
UniffiLinkSecret.Companion.newFromValue(linkSecret.data)
}
- given Conversion[UniffiLinkSecret, LinkSecret] with {
- def apply(uniffiLinkSecret: UniffiLinkSecret): LinkSecret =
- LinkSecret.apply(uniffiLinkSecret.getValue())
+ given Conversion[UniffiLinkSecret, AnoncredLinkSecret] with {
+ def apply(uniffiLinkSecret: UniffiLinkSecret): AnoncredLinkSecret =
+ AnoncredLinkSecret.apply(uniffiLinkSecret.getValue())
}
}
//FIXME use same names as in https://hyperledger.github.io/anoncreds-spec/#term:schemas
-case class SchemaDef(
+case class AnoncredSchemaDef(
name: String, // SCHEMA_ID
version: String, // SCHEMA_Version
attributes: AttributeNames,
issuer_id: IssuerId, // ISSUER_DID
)
-object SchemaDef {
+object AnoncredSchemaDef {
- given Conversion[SchemaDef, UniffiSchema] with {
- def apply(schemaDef: SchemaDef): UniffiSchema =
+ given Conversion[AnoncredSchemaDef, UniffiSchema] with {
+ def apply(schemaDef: AnoncredSchemaDef): UniffiSchema =
UniffiSchema.apply(
schemaDef.name,
schemaDef.version,
@@ -65,9 +66,9 @@ object SchemaDef {
}
- given Conversion[UniffiSchema, SchemaDef] with {
- def apply(schema: UniffiSchema): SchemaDef =
- SchemaDef.apply(
+ given Conversion[UniffiSchema, AnoncredSchemaDef] with {
+ def apply(schema: UniffiSchema): AnoncredSchemaDef =
+ AnoncredSchemaDef.apply(
name = schema.getName(),
version = schema.getVersion(),
attributes = schema.getAttrNames().asScala.toSet,
@@ -106,20 +107,20 @@ object SchemaDef {
// value: String,
// issuerId: String,
// )
-case class CredentialDefinition(data: String) { // TODO
- def schemaId = CredentialDefinition
- .given_Conversion_CredentialDefinition_UniffiCredentialDefinition(this)
+case class AnoncredCredentialDefinition(data: String) { // TODO
+ def schemaId = AnoncredCredentialDefinition
+ .given_Conversion_AnoncredCredentialDefinition_UniffiCredentialDefinition(this)
.getSchemaId()
}
-object CredentialDefinition {
- given Conversion[CredentialDefinition, UniffiCredentialDefinition] with {
- def apply(credentialDefinition: CredentialDefinition): UniffiCredentialDefinition =
+object AnoncredCredentialDefinition {
+ given Conversion[AnoncredCredentialDefinition, UniffiCredentialDefinition] with {
+ def apply(credentialDefinition: AnoncredCredentialDefinition): UniffiCredentialDefinition =
UniffiCredentialDefinition(credentialDefinition.data)
}
- given Conversion[UniffiCredentialDefinition, CredentialDefinition] with {
- def apply(credentialDefinition: UniffiCredentialDefinition): CredentialDefinition =
- CredentialDefinition(credentialDefinition.getJson())
+ given Conversion[UniffiCredentialDefinition, AnoncredCredentialDefinition] with {
+ def apply(credentialDefinition: UniffiCredentialDefinition): AnoncredCredentialDefinition =
+ AnoncredCredentialDefinition(credentialDefinition.getJson())
}
}
@@ -134,90 +135,94 @@ object CredentialDefinition {
// "r_key": null
// }
// }
-case class CredentialDefinitionPrivate(data: String)
-object CredentialDefinitionPrivate {
- given Conversion[CredentialDefinitionPrivate, UniffiCredentialDefinitionPrivate] with {
- def apply(credentialDefinitionPrivate: CredentialDefinitionPrivate): UniffiCredentialDefinitionPrivate =
+case class AnoncredCredentialDefinitionPrivate(data: String)
+object AnoncredCredentialDefinitionPrivate {
+ given Conversion[AnoncredCredentialDefinitionPrivate, UniffiCredentialDefinitionPrivate] with {
+ def apply(credentialDefinitionPrivate: AnoncredCredentialDefinitionPrivate): UniffiCredentialDefinitionPrivate =
UniffiCredentialDefinitionPrivate(credentialDefinitionPrivate.data)
}
- given Conversion[UniffiCredentialDefinitionPrivate, CredentialDefinitionPrivate] with {
- def apply(credentialDefinitionPrivate: UniffiCredentialDefinitionPrivate): CredentialDefinitionPrivate =
- CredentialDefinitionPrivate(credentialDefinitionPrivate.getJson())
+ given Conversion[UniffiCredentialDefinitionPrivate, AnoncredCredentialDefinitionPrivate] with {
+ def apply(credentialDefinitionPrivate: UniffiCredentialDefinitionPrivate): AnoncredCredentialDefinitionPrivate =
+ AnoncredCredentialDefinitionPrivate(credentialDefinitionPrivate.getJson())
}
}
// ****************************************************************************
-case class CredentialKeyCorrectnessProof(data: String)
-object CredentialKeyCorrectnessProof {
- given Conversion[CredentialKeyCorrectnessProof, UniffiCredentialKeyCorrectnessProof] with {
- def apply(credentialKeyCorrectnessProof: CredentialKeyCorrectnessProof): UniffiCredentialKeyCorrectnessProof =
+case class AnoncredCredentialKeyCorrectnessProof(data: String)
+object AnoncredCredentialKeyCorrectnessProof {
+ given Conversion[AnoncredCredentialKeyCorrectnessProof, UniffiCredentialKeyCorrectnessProof] with {
+ def apply(
+ credentialKeyCorrectnessProof: AnoncredCredentialKeyCorrectnessProof
+ ): UniffiCredentialKeyCorrectnessProof =
UniffiCredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.data)
}
- given Conversion[UniffiCredentialKeyCorrectnessProof, CredentialKeyCorrectnessProof] with {
- def apply(credentialKeyCorrectnessProof: UniffiCredentialKeyCorrectnessProof): CredentialKeyCorrectnessProof =
- CredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.getJson())
+ given Conversion[UniffiCredentialKeyCorrectnessProof, AnoncredCredentialKeyCorrectnessProof] with {
+ def apply(
+ credentialKeyCorrectnessProof: UniffiCredentialKeyCorrectnessProof
+ ): AnoncredCredentialKeyCorrectnessProof =
+ AnoncredCredentialKeyCorrectnessProof(credentialKeyCorrectnessProof.getJson())
}
}
-case class CreateCredentialDefinition(
- cd: CredentialDefinition,
- cdPrivate: CredentialDefinitionPrivate,
- proofKey: CredentialKeyCorrectnessProof,
+case class AnoncredCreateCredentialDefinition(
+ cd: AnoncredCredentialDefinition,
+ cdPrivate: AnoncredCredentialDefinitionPrivate,
+ proofKey: AnoncredCredentialKeyCorrectnessProof,
)
// ****************************************************************************
-case class CredentialOffer(data: String) {
- lazy val schemaId = CredentialOffer
- .given_Conversion_CredentialOffer_UniffiCredentialOffer(this)
+case class AnoncredCredentialOffer(data: String) {
+ lazy val schemaId = AnoncredCredentialOffer
+ .given_Conversion_AnoncredCredentialOffer_UniffiCredentialOffer(this)
.getSchemaId()
- lazy val credDefId = CredentialOffer
- .given_Conversion_CredentialOffer_UniffiCredentialOffer(this)
+ lazy val credDefId = AnoncredCredentialOffer
+ .given_Conversion_AnoncredCredentialOffer_UniffiCredentialOffer(this)
.getCredDefId()
}
-object CredentialOffer {
- given Conversion[CredentialOffer, UniffiCredentialOffer] with {
- def apply(credentialOffer: CredentialOffer): UniffiCredentialOffer =
+object AnoncredCredentialOffer {
+ given Conversion[AnoncredCredentialOffer, UniffiCredentialOffer] with {
+ def apply(credentialOffer: AnoncredCredentialOffer): UniffiCredentialOffer =
UniffiCredentialOffer(credentialOffer.data)
}
- given Conversion[UniffiCredentialOffer, CredentialOffer] with {
- def apply(credentialOffer: UniffiCredentialOffer): CredentialOffer =
- CredentialOffer(credentialOffer.getJson())
+ given Conversion[UniffiCredentialOffer, AnoncredCredentialOffer] with {
+ def apply(credentialOffer: UniffiCredentialOffer): AnoncredCredentialOffer =
+ AnoncredCredentialOffer(credentialOffer.getJson())
}
}
// ****************************************************************************
-case class CreateCrendentialRequest(
- request: CredentialRequest,
- metadata: CredentialRequestMetadata
+case class AnoncredCreateCrendentialRequest(
+ request: AnoncredCredentialRequest,
+ metadata: AnoncredCredentialRequestMetadata
)
-case class CredentialRequest(data: String)
-object CredentialRequest {
+case class AnoncredCredentialRequest(data: String)
+object AnoncredCredentialRequest {
- given Conversion[CredentialRequest, UniffiCredentialRequest] with {
- def apply(credentialRequest: CredentialRequest): UniffiCredentialRequest =
+ given Conversion[AnoncredCredentialRequest, UniffiCredentialRequest] with {
+ def apply(credentialRequest: AnoncredCredentialRequest): UniffiCredentialRequest =
UniffiCredentialRequest(credentialRequest.data)
}
- given Conversion[UniffiCredentialRequest, CredentialRequest] with {
- def apply(credentialRequest: UniffiCredentialRequest): CredentialRequest =
- CredentialRequest(credentialRequest.getJson())
+ given Conversion[UniffiCredentialRequest, AnoncredCredentialRequest] with {
+ def apply(credentialRequest: UniffiCredentialRequest): AnoncredCredentialRequest =
+ AnoncredCredentialRequest(credentialRequest.getJson())
}
}
-case class CredentialRequestMetadata(
+case class AnoncredCredentialRequestMetadata(
linkSecretBlinding: String,
nonce: String,
linkSecretName: String,
)
-object CredentialRequestMetadata {
- given Conversion[CredentialRequestMetadata, UniffiCredentialRequestMetadata] with {
- def apply(credentialRequestMetadata: CredentialRequestMetadata): UniffiCredentialRequestMetadata =
+object AnoncredCredentialRequestMetadata {
+ given Conversion[AnoncredCredentialRequestMetadata, UniffiCredentialRequestMetadata] with {
+ def apply(credentialRequestMetadata: AnoncredCredentialRequestMetadata): UniffiCredentialRequestMetadata =
UniffiCredentialRequestMetadata(
/*link_secret_blinding_data*/ credentialRequestMetadata.linkSecretBlinding,
/*nonce*/ Nonce.Companion.newFromValue(credentialRequestMetadata.nonce),
@@ -225,62 +230,63 @@ object CredentialRequestMetadata {
)
}
- given Conversion[UniffiCredentialRequestMetadata, CredentialRequestMetadata] with {
- def apply(credentialRequestMetadata: UniffiCredentialRequestMetadata): CredentialRequestMetadata =
- CredentialRequestMetadata(
+ given Conversion[UniffiCredentialRequestMetadata, AnoncredCredentialRequestMetadata] with {
+ def apply(credentialRequestMetadata: UniffiCredentialRequestMetadata): AnoncredCredentialRequestMetadata =
+ AnoncredCredentialRequestMetadata(
linkSecretBlinding = credentialRequestMetadata.getLinkSecretBlindingData(),
nonce = credentialRequestMetadata.getNonce().getValue(),
linkSecretName = credentialRequestMetadata.getLinkSecretName(),
)
}
- given JsonDecoder[CredentialRequestMetadata] = DeriveJsonDecoder.gen[CredentialRequestMetadata]
- given JsonEncoder[CredentialRequestMetadata] = DeriveJsonEncoder.gen[CredentialRequestMetadata]
+ given JsonDecoder[AnoncredCredentialRequestMetadata] = DeriveJsonDecoder.gen[AnoncredCredentialRequestMetadata]
+ given JsonEncoder[AnoncredCredentialRequestMetadata] = DeriveJsonEncoder.gen[AnoncredCredentialRequestMetadata]
}
// ****************************************************************************
//Credential
-case class Credential(data: String) {
- lazy val credDefId: String = Credential
- .given_Conversion_Credential_UniffiCredential(this)
+case class AnoncredCredential(data: String) {
+ lazy val credDefId: String = AnoncredCredential
+ .given_Conversion_AnoncredCredential_UniffiCredential(this)
.getCredDefId
}
-object Credential {
- given Conversion[Credential, UniffiCredential] with {
- def apply(credential: Credential): UniffiCredential =
+object AnoncredCredential {
+ given Conversion[AnoncredCredential, UniffiCredential] with {
+ def apply(credential: AnoncredCredential): UniffiCredential =
UniffiCredential(credential.data)
}
- given Conversion[UniffiCredential, Credential] with {
- def apply(credential: UniffiCredential): Credential =
- Credential(credential.getJson())
+ given Conversion[UniffiCredential, AnoncredCredential] with {
+ def apply(credential: UniffiCredential): AnoncredCredential =
+ AnoncredCredential(credential.getJson())
}
}
// ****************************************************************************
-case class CredentialAndRequestedAttributesPredicates(
- credential: Credential,
+case class AnoncredCredentialRequests(
+ credential: AnoncredCredential,
requestedAttribute: Seq[String],
requestedPredicate: Seq[String],
)
-object CredentialAndRequestedAttributesPredicates {
- given Conversion[CredentialAndRequestedAttributesPredicates, UniffiCredentialRequests] with {
+object AnoncredCredentialRequests {
+ given Conversion[AnoncredCredentialRequests, UniffiCredentialRequests] with {
import uniffi.anoncreds_wrapper.RequestedAttribute
import uniffi.anoncreds_wrapper.RequestedPredicate
- def apply(credentialRequests: CredentialAndRequestedAttributesPredicates): UniffiCredentialRequests = {
- val credential = Credential.given_Conversion_Credential_UniffiCredential(credentialRequests.credential)
+ def apply(credentialRequests: AnoncredCredentialRequests): UniffiCredentialRequests = {
+ val credential =
+ AnoncredCredential.given_Conversion_AnoncredCredential_UniffiCredential(credentialRequests.credential)
val requestedAttributes = credentialRequests.requestedAttribute.map(a => RequestedAttribute(a, true))
val requestedPredicates = credentialRequests.requestedPredicate.map(p => RequestedPredicate(p))
UniffiCredentialRequests(credential, requestedAttributes.asJava, requestedPredicates.asJava)
}
}
- given Conversion[UniffiCredentialRequests, CredentialAndRequestedAttributesPredicates] with {
- def apply(credentialRequests: UniffiCredentialRequests): CredentialAndRequestedAttributesPredicates = {
- CredentialAndRequestedAttributesPredicates(
- Credential.given_Conversion_UniffiCredential_Credential(credentialRequests.getCredential()),
+ given Conversion[UniffiCredentialRequests, AnoncredCredentialRequests] with {
+ def apply(credentialRequests: UniffiCredentialRequests): AnoncredCredentialRequests = {
+ AnoncredCredentialRequests(
+ AnoncredCredential.given_Conversion_UniffiCredential_AnoncredCredential(credentialRequests.getCredential()),
credentialRequests
.getRequestedAttribute()
.asScala
@@ -301,32 +307,32 @@ object CredentialAndRequestedAttributesPredicates {
// ****************************************************************************
-case class PresentationRequest(data: String)
-object PresentationRequest {
- given Conversion[PresentationRequest, UniffiPresentationRequest] with {
- def apply(presentationRequest: PresentationRequest): UniffiPresentationRequest =
+case class AnoncredPresentationRequest(data: String)
+object AnoncredPresentationRequest {
+ given Conversion[AnoncredPresentationRequest, UniffiPresentationRequest] with {
+ def apply(presentationRequest: AnoncredPresentationRequest): UniffiPresentationRequest =
UniffiPresentationRequest(presentationRequest.data)
}
- given Conversion[UniffiPresentationRequest, PresentationRequest] with {
- def apply(presentationRequest: UniffiPresentationRequest): PresentationRequest =
- PresentationRequest(presentationRequest.getJson())
+ given Conversion[UniffiPresentationRequest, AnoncredPresentationRequest] with {
+ def apply(presentationRequest: UniffiPresentationRequest): AnoncredPresentationRequest =
+ AnoncredPresentationRequest(presentationRequest.getJson())
}
}
// ****************************************************************************
-case class Presentation(data: String)
-object Presentation {
- given Conversion[Presentation, UniffiPresentation] with {
- def apply(presentation: Presentation): UniffiPresentation = {
+case class AnoncredPresentation(data: String)
+object AnoncredPresentation {
+ given Conversion[AnoncredPresentation, UniffiPresentation] with {
+ def apply(presentation: AnoncredPresentation): UniffiPresentation = {
UniffiPresentation(presentation.data)
}
}
- given Conversion[UniffiPresentation, Presentation] with {
- def apply(presentation: UniffiPresentation): Presentation = {
- Presentation(presentation.getJson())
+ given Conversion[UniffiPresentation, AnoncredPresentation] with {
+ def apply(presentation: UniffiPresentation): AnoncredPresentation = {
+ AnoncredPresentation(presentation.getJson())
}
}
}
diff --git a/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala b/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala
index 7172e28ea7..fd75b61a4b 100644
--- a/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala
+++ b/pollux/lib/anoncredsTest/src/test/scala/io/iohk/atala/pollux/anoncreds/PoCNewLib.scala
@@ -15,13 +15,13 @@ class PoCNewLib extends AnyFlatSpec {
"LinkSecret" should "be able to parse back to the anoncreds lib" in {
import scala.language.implicitConversions
- val ls1 = LinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890")
+ val ls1 = AnoncredLinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890")
val ls1p = ls1: uniffi.anoncreds_wrapper.LinkSecret
assert(ls1p.getValue() == "65965334953670062552662719679603258895632947953618378932199361160021795698890")
- val ls0 = LinkSecret()
+ val ls0 = AnoncredLinkSecret()
val ls0p = ls0: uniffi.anoncreds_wrapper.LinkSecret
- val ls0_ = ls0p: LinkSecret
+ val ls0_ = ls0p: AnoncredLinkSecret
assert(ls0.data == ls0_.data)
}
@@ -58,8 +58,8 @@ class PoCNewLib extends AnyFlatSpec {
// ##############
println("*** holder " + ("*" * 100))
- val ls1 = LinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890")
- val linkSecret = LinkSecretWithId("ID_of_some_secret_1", ls1)
+ val ls1 = AnoncredLinkSecret("65965334953670062552662719679603258895632947953618378932199361160021795698890")
+ val linkSecret = AnoncredLinkSecretWithId("ID_of_some_secret_1", ls1)
val credentialRequest = AnoncredLib.createCredentialRequest(linkSecret, credentialDefinition.cd, credentialOffer)
println("*" * 100)
@@ -89,7 +89,7 @@ class PoCNewLib extends AnyFlatSpec {
// ##############
// TODO READ about PresentationRequest https://hyperledger.github.io/anoncreds-spec/#create-presentation-request
- val presentationRequest = PresentationRequest(
+ val presentationRequest = AnoncredPresentationRequest(
s"""{
"nonce": "1103253414365527824079144",
"name":"proof_req_1",
@@ -109,7 +109,7 @@ class PoCNewLib extends AnyFlatSpec {
val presentation = AnoncredLib.createPresentation(
presentationRequest, // : PresentationRequest,
Seq(
- CredentialAndRequestedAttributesPredicates(processedCredential, Seq("sex"), Seq("age"))
+ AnoncredCredentialRequests(processedCredential, Seq("sex"), Seq("age"))
), // credentials: Seq[Credential],
Map(), // selfAttested: Map[String, String],
linkSecret.secret, // linkSecret: LinkSecret,
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala
index a232e39848..c803f3421f 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/IssueCredentialRecord.scala
@@ -9,7 +9,7 @@ import io.iohk.atala.mercury.protocol.issuecredential.{
OfferCredential,
RequestCredential
}
-import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata
+import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.*
import java.time.Instant
@@ -21,8 +21,9 @@ final case class IssueCredentialRecord(
createdAt: Instant,
updatedAt: Option[Instant],
thid: DidCommID,
- schemaId: Option[String],
+ schemaUri: Option[String],
credentialDefinitionId: Option[UUID],
+ credentialDefinitionUri: Option[String],
credentialFormat: CredentialFormat,
role: Role,
subjectId: Option[String],
@@ -31,7 +32,7 @@ final case class IssueCredentialRecord(
protocolState: ProtocolState,
offerCredentialData: Option[OfferCredential],
requestCredentialData: Option[RequestCredential],
- anonCredsRequestMetadata: Option[CredentialRequestMetadata],
+ anonCredsRequestMetadata: Option[AnoncredCredentialRequestMetadata],
issueCredentialData: Option[IssueCredential],
issuedCredentialRaw: Option[String],
issuingDID: Option[CanonicalPrismDID],
@@ -69,6 +70,16 @@ final case class IssueCredentialRecord(
final case class ValidIssuedCredentialRecord(
id: DidCommID,
issuedCredentialRaw: Option[String],
+ credentialFormat: CredentialFormat,
+ subjectId: Option[String]
+)
+
+final case class ValidFullIssuedCredentialRecord(
+ id: DidCommID,
+ issuedCredential: Option[IssueCredential],
+ credentialFormat: CredentialFormat,
+ schemaUri: Option[String],
+ credentialDefinitionUri: Option[String],
subjectId: Option[String]
)
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/PresentationRecord.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/PresentationRecord.scala
index 60f35f11ae..7d0c5f2ef9 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/PresentationRecord.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/PresentationRecord.scala
@@ -1,13 +1,13 @@
package io.iohk.atala.pollux.core.model
-import io.iohk.atala.mercury.protocol.presentproof.ProposePresentation
-import io.iohk.atala.mercury.protocol.presentproof.RequestPresentation
-import io.iohk.atala.mercury.protocol.presentproof.Presentation
import io.iohk.atala.mercury.model.DidId
+import io.iohk.atala.mercury.protocol.presentproof.{Presentation, ProposePresentation, RequestPresentation}
import java.time.Instant
import java.time.temporal.ChronoUnit
+type AnoncredCredentialProofs = zio.json.ast.Json
+
final case class PresentationRecord(
id: DidCommID,
createdAt: Instant,
@@ -23,6 +23,8 @@ final case class PresentationRecord(
proposePresentationData: Option[ProposePresentation],
presentationData: Option[Presentation],
credentialsToUse: Option[List[String]],
+ anoncredCredentialsToUseJsonSchemaId: Option[String],
+ anoncredCredentialsToUse: Option[AnoncredCredentialProofs],
metaRetries: Int,
metaNextRetry: Option[Instant],
metaLastFailure: Option[String],
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala
index 064d6fa9d3..12176583fd 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/error/PresentationError.scala
@@ -11,6 +11,7 @@ object PresentationError {
final case class InvalidFlowStateError(msg: String) extends PresentationError
final case class UnexpectedError(msg: String) extends PresentationError
final case class IssuedCredentialNotFoundError(cause: Throwable) extends PresentationError
+ final case class NotMatchingPresentationCredentialFormat(cause: Throwable) extends PresentationError
final case class PresentationDecodingError(cause: Throwable) extends PresentationError
final case class PresentationNotFoundError(cause: Throwable) extends PresentationError
final case class HolderBindingError(msg: String) extends PresentationError
@@ -18,4 +19,9 @@ object PresentationError {
object MissingCredentialFormat extends PresentationError
final case class UnsupportedCredentialFormat(vcFormat: String) extends PresentationError
+ final case class InvalidAnoncredPresentationRequest(error: String) extends PresentationError
+ final case class InvalidAnoncredPresentation(error: String) extends PresentationError
+ final case class MissingAnoncredPresentationRequest(error: String) extends PresentationError
+ final case class AnoncredPresentationCreationError(cause: Throwable) extends PresentationError
+ final case class AnoncredPresentationVerificationError(cause: Throwable) extends PresentationError
}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala
index 554d36aab0..98823ed3f5 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialDefinition.scala
@@ -8,6 +8,7 @@ import zio.json.*
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.util.UUID
+import scala.util.Try
type Definition = zio.json.ast.Json
type CorrectnessProof = zio.json.ast.Json
@@ -58,7 +59,7 @@ case class CredentialDefinition(
signatureType: String,
supportRevocation: Boolean
) {
- def longId = CredentialDefinition.makeLongId(author, id, version)
+ def longId = CredentialDefinition.makeLongId(author, guid, version)
}
object CredentialDefinition {
@@ -69,6 +70,17 @@ object CredentialDefinition {
def makeGUID(author: String, id: UUID, version: String) =
UUID.nameUUIDFromBytes(makeLongId(author, id, version).getBytes)
+ def extractGUID(longId: String): Option[UUID] = {
+ longId.split("/") match {
+ case Array(_, idWithVersion) =>
+ idWithVersion.split("\\?") match {
+ case Array(id, _) => Try(UUID.fromString(id)).toOption
+ case _ => None
+ }
+ case _ => None
+ }
+ }
+
def make(
in: Input,
definitionSchemaId: String,
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialSchema.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialSchema.scala
index ea77c80792..6c05b941cb 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialSchema.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/CredentialSchema.scala
@@ -149,10 +149,11 @@ object CredentialSchema {
for {
uri <- ZIO.attempt(new URI(schemaId)).mapError(t => URISyntaxError(t.getMessage))
content <- uriDereferencer.dereference(uri).mapError(err => UnexpectedError(err.toString))
- validAttrNames <- ZIO
- .fromEither(content.fromJson[AnoncredSchemaSerDesV1])
- .mapError(error => CredentialSchemaParsingError(s"AnonCreds Schema parsing error: $error"))
- .map(_.attrNames)
+ validAttrNames <-
+ AnoncredSchemaSerDesV1.schemaSerDes
+ .deserialize(content)
+ .mapError(error => CredentialSchemaParsingError(s"AnonCreds Schema parsing error: $error"))
+ .map(_.attrNames)
jsonClaims <- ZIO.fromEither(claims.fromJson[Json]).mapError(err => UnexpectedError(err))
_ <- jsonClaims match
case Json.Obj(fields) =>
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala
index bec8fc2e06..365f8a0251 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/model/schema/validator/SchemaSerDes.scala
@@ -2,18 +2,24 @@ package io.iohk.atala.pollux.core.model.schema.validator
import com.networknt.schema.JsonSchema
import io.iohk.atala.pollux.core.model.schema.validator.JsonSchemaError.*
-import zio.IO
-import zio.ZIO
import zio.json.*
-import zio.json.JsonDecoder
import zio.json.ast.Json
import zio.json.ast.Json.*
+import zio.{IO, ZIO}
class SchemaSerDes[S](jsonSchemaSchemaStr: String) {
def initialiseJsonSchema: IO[JsonSchemaError, JsonSchema] =
JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
+ def serializeToJsonString(instance: S)(using encoder: JsonEncoder[S]): String = {
+ instance.toJson
+ }
+
+ def serialize(instance: S)(using encoder: JsonEncoder[S]): Either[String, Json] = {
+ instance.toJsonAST
+ }
+
def deserialize(
schema: zio.json.ast.Json
)(using decoder: JsonDecoder[S]): IO[JsonSchemaError, S] = {
@@ -42,12 +48,12 @@ class SchemaSerDes[S](jsonSchemaSchemaStr: String) {
} yield json
}
- def validate(jsonString: String): IO[JsonSchemaError, Boolean] = {
+ def validate(jsonString: String): IO[JsonSchemaError, Unit] = {
for {
jsonSchemaSchema <- JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
schemaValidator = JsonSchemaValidatorImpl(jsonSchemaSchema)
- _ <- schemaValidator.validate(jsonString)
- } yield true
+ result <- schemaValidator.validate(jsonString)
+ } yield result
}
}
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 593f2e7725..2f2b9615e5 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
@@ -1,7 +1,7 @@
package io.iohk.atala.pollux.core.repository
import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, RequestCredential}
-import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata
+import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState
import io.iohk.atala.shared.models.WalletAccessContext
@@ -53,7 +53,7 @@ trait CredentialRepository {
def updateWithAnonCredsRequestCredential(
recordId: DidCommID,
request: RequestCredential,
- metadata: CredentialRequestMetadata,
+ metadata: AnoncredCredentialRequestMetadata,
protocolState: ProtocolState
): RIO[WalletAccessContext, Int]
@@ -74,6 +74,10 @@ trait CredentialRepository {
def getValidIssuedCredentials(recordId: Seq[DidCommID]): RIO[WalletAccessContext, Seq[ValidIssuedCredentialRecord]]
+ def getValidAnoncredIssuedCredentials(
+ recordIds: Seq[DidCommID]
+ ): RIO[WalletAccessContext, Seq[ValidFullIssuedCredentialRecord]]
+
def updateAfterFail(
recordId: DidCommID,
failReason: Option[String]
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 f69e3b94e4..2bc9bce4c5 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
@@ -1,7 +1,7 @@
package io.iohk.atala.pollux.core.repository
import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, RequestCredential}
-import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata
+import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState
import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError.*
@@ -133,7 +133,35 @@ class CredentialRepositoryInMemory(
store <- storeRef.get
} yield store.values
.filter(rec => recordId.contains(rec.id) && rec.issuedCredentialRaw.isDefined)
- .map(rec => ValidIssuedCredentialRecord(rec.id, rec.issuedCredentialRaw, rec.subjectId))
+ .map(rec => ValidIssuedCredentialRecord(rec.id, rec.issuedCredentialRaw, rec.credentialFormat, rec.subjectId))
+ .toSeq
+ }
+
+ override def getValidAnoncredIssuedCredentials(
+ recordId: Seq[DidCommID]
+ ): RIO[WalletAccessContext, Seq[ValidFullIssuedCredentialRecord]] = {
+ for {
+ storeRef <- walletStoreRef
+ store <- storeRef.get
+ } yield store.values
+ .filter(rec =>
+ recordId.contains(
+ rec.id
+ ) && rec.issueCredentialData.isDefined
+ && rec.schemaUri.isDefined
+ && rec.credentialDefinitionId.isDefined
+ && rec.credentialFormat == CredentialFormat.AnonCreds
+ )
+ .map(rec =>
+ ValidFullIssuedCredentialRecord(
+ rec.id,
+ rec.issueCredentialData,
+ rec.credentialFormat,
+ rec.schemaUri,
+ rec.credentialDefinitionUri,
+ rec.subjectId
+ )
+ )
.toSeq
}
@@ -287,7 +315,7 @@ class CredentialRepositoryInMemory(
override def updateWithAnonCredsRequestCredential(
recordId: DidCommID,
request: RequestCredential,
- metadata: CredentialRequestMetadata,
+ metadata: AnoncredCredentialRequestMetadata,
protocolState: ProtocolState
): RIO[WalletAccessContext, RuntimeFlags] = {
for {
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepository.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepository.scala
index 506a5b6e66..5f361ac2fa 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepository.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepository.scala
@@ -51,6 +51,13 @@ trait PresentationRepository {
protocolState: ProtocolState
): RIO[WalletAccessContext, Int]
+ def updateAnoncredPresentationWithCredentialsToUse(
+ recordId: DidCommID,
+ anoncredCredentialsToUseJsonSchemaId: Option[String],
+ anoncredCredentialsToUse: Option[AnoncredCredentialProofs],
+ protocolState: ProtocolState
+ ): RIO[WalletAccessContext, Int]
+
def updateAfterFail(
recordId: DidCommID,
failReason: Option[String]
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepositoryInMemory.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepositoryInMemory.scala
index d4a663d218..76cb0f0154 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepositoryInMemory.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/repository/PresentationRepositoryInMemory.scala
@@ -1,11 +1,10 @@
package io.iohk.atala.pollux.core.repository
-import io.iohk.atala.mercury.protocol.presentproof._
+import io.iohk.atala.mercury.protocol.presentproof.*
+import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.PresentationRecord.ProtocolState
-import io.iohk.atala.pollux.core.model._
-import io.iohk.atala.pollux.core.model.error.PresentationError._
-import io.iohk.atala.shared.models.WalletAccessContext
-import io.iohk.atala.shared.models.WalletId
+import io.iohk.atala.pollux.core.model.error.PresentationError.*
+import io.iohk.atala.shared.models.{WalletAccessContext, WalletId}
import zio.*
import java.time.Instant
@@ -114,6 +113,37 @@ class PresentationRepositoryInMemory(
} yield count
}
+ def updateAnoncredPresentationWithCredentialsToUse(
+ recordId: DidCommID,
+ anoncredCredentialsToUseJsonSchemaId: Option[String],
+ anoncredCredentialsToUse: Option[AnoncredCredentialProofs],
+ protocolState: ProtocolState
+ ): RIO[WalletAccessContext, Int] = {
+ for {
+ storeRef <- walletStoreRef
+ maybeRecord <- getPresentationRecord(recordId)
+ count <- maybeRecord
+ .map(record =>
+ for {
+ _ <- storeRef.update(r =>
+ r.updated(
+ recordId,
+ record.copy(
+ updatedAt = Some(Instant.now),
+ anoncredCredentialsToUseJsonSchemaId = anoncredCredentialsToUseJsonSchemaId,
+ anoncredCredentialsToUse = anoncredCredentialsToUse,
+ protocolState = protocolState,
+ metaRetries = maxRetries,
+ metaLastFailure = None,
+ )
+ )
+ )
+ } yield 1
+ )
+ .getOrElse(ZIO.succeed(0))
+ } yield count
+ }
+
override def updateWithPresentation(
recordId: DidCommID,
presentation: Presentation,
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala
index a7ec38bb81..01ea444476 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialDefinitionServiceImpl.scala
@@ -2,9 +2,9 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.agent.walletapi.storage
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
-import io.iohk.atala.pollux.anoncreds.{AnoncredLib, SchemaDef}
+import io.iohk.atala.pollux.anoncreds.{AnoncredLib, AnoncredSchemaDef}
import io.iohk.atala.pollux.core.model.error.CredentialSchemaError
-import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.URISyntaxError
+import io.iohk.atala.pollux.core.model.error.CredentialSchemaError.{SchemaError, URISyntaxError}
import io.iohk.atala.pollux.core.model.schema.CredentialDefinition
import io.iohk.atala.pollux.core.model.schema.CredentialDefinition.{Filter, FilteredEntries}
import io.iohk.atala.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
@@ -37,7 +37,7 @@ class CredentialDefinitionServiceImpl(
content <- uriDereferencer.dereference(uri)
anoncredSchema <- AnoncredSchemaSerDesV1.schemaSerDes.deserialize(content)
anoncredLibSchema =
- SchemaDef(
+ AnoncredSchemaDef(
in.schemaId,
anoncredSchema.version,
anoncredSchema.attrNames,
@@ -84,12 +84,10 @@ class CredentialDefinitionServiceImpl(
)
} yield createdCredentialDefinition
}.mapError {
- case e: CredentialDefinitionCreationError => e
- case j: JsonSchemaError => UnexpectedError(j.error)
- case s: URISyntaxError => UnexpectedError(s.message)
- case u: URIDereferencerError => UnexpectedError(u.error)
- case e: CredentialSchemaError => CredentialDefinitionValidationError(e)
+ case u: URIDereferencerError => CredentialDefinitionValidationError(URISyntaxError(u.error))
+ case j: JsonSchemaError => CredentialDefinitionValidationError(SchemaError(j))
case t: Throwable => RepositoryError(t)
+ case e: CredentialDefinitionCreationError => e
}
override def delete(guid: UUID): Result[CredentialDefinition] =
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialService.scala
index 0af8d97b01..85e453ce08 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialService.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialService.scala
@@ -32,10 +32,10 @@ trait CredentialService {
pairwiseHolderDID: DidId,
thid: DidCommID,
credentialDefinitionGUID: UUID,
+ credentialDefinitionId: String,
claims: io.circe.Json,
validityPeriod: Option[Double] = None,
- automaticIssuance: Option[Boolean],
- credentialDefinitionId: String
+ automaticIssuance: Option[Boolean]
): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord]
/** Return a list of records as well as a count of all filtered items */
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 a580e0bdfb..9b2034ce81 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
@@ -10,7 +10,12 @@ import io.iohk.atala.castor.core.service.DIDService
import io.iohk.atala.mercury.model.*
import io.iohk.atala.mercury.protocol.issuecredential.*
import io.iohk.atala.pollux.*
-import io.iohk.atala.pollux.anoncreds.{AnoncredLib, CreateCredentialDefinition, CredentialOffer}
+import io.iohk.atala.pollux.anoncreds.{
+ AnoncredCreateCredentialDefinition,
+ AnoncredCredential,
+ AnoncredCredentialOffer,
+ AnoncredLib
+}
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.CredentialFormat.AnonCreds
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.ProtocolState.OfferReceived
@@ -153,8 +158,9 @@ private class CredentialServiceImpl(
createdAt = Instant.now,
updatedAt = None,
thid = thid,
- schemaId = maybeSchemaId,
+ schemaUri = maybeSchemaId,
credentialDefinitionId = None,
+ credentialDefinitionUri = None,
credentialFormat = CredentialFormat.JWT,
role = IssueCredentialRecord.Role.Issuer,
subjectId = None,
@@ -188,10 +194,10 @@ private class CredentialServiceImpl(
pairwiseHolderDID: DidId,
thid: DidCommID,
credentialDefinitionGUID: UUID,
+ credentialDefinitionId: String,
claims: Json,
validityPeriod: Option[Double],
- automaticIssuance: Option[Boolean],
- credentialDefinitionId: String
+ automaticIssuance: Option[Boolean]
): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] = {
for {
credentialDefinition <- credentialDefinitionService
@@ -204,11 +210,11 @@ private class CredentialServiceImpl(
offer <- createAnonCredsDidCommOfferCredential(
pairwiseIssuerDID = pairwiseIssuerDID,
pairwiseHolderDID = pairwiseHolderDID,
- schemaId = credentialDefinition.schemaId,
+ schemaUri = credentialDefinition.schemaId,
credentialDefinitionGUID = credentialDefinitionGUID,
+ credentialDefinitionId = credentialDefinitionId,
claims = attributes,
thid = thid,
- credentialDefinitionId
)
record <- ZIO.succeed(
IssueCredentialRecord(
@@ -216,8 +222,9 @@ private class CredentialServiceImpl(
createdAt = Instant.now,
updatedAt = None,
thid = thid,
- schemaId = Some(credentialDefinition.schemaId),
+ schemaUri = Some(credentialDefinition.schemaId),
credentialDefinitionId = Some(credentialDefinitionGUID),
+ credentialDefinitionUri = Some(credentialDefinitionId),
credentialFormat = CredentialFormat.AnonCreds,
role = IssueCredentialRecord.Role.Issuer,
subjectId = None,
@@ -292,8 +299,9 @@ private class CredentialServiceImpl(
createdAt = Instant.now,
updatedAt = None,
thid = DidCommID(offer.thid.getOrElse(offer.id)),
- schemaId = None,
+ schemaUri = None,
credentialDefinitionId = None,
+ credentialDefinitionUri = None,
credentialFormat = credentialFormat,
role = Role.Holder,
subjectId = None,
@@ -345,7 +353,7 @@ private class CredentialServiceImpl(
case Base64(value) =>
for {
_ <- ZIO
- .attempt(CredentialOffer(value))
+ .attempt(AnoncredCredentialOffer(value))
.mapError(e =>
CredentialServiceError.UnexpectedError(
s"Unexpected error parsing credential offer attachment: ${e.toString}"
@@ -563,12 +571,12 @@ private class CredentialServiceImpl(
}
)
.mapError(_ => InvalidFlowStateError(s"No AnonCreds offer attachment found"))
- credentialOffer = anoncreds.CredentialOffer(attachmentData)
+ credentialOffer = anoncreds.AnoncredCredentialOffer(attachmentData)
_ <- ZIO.logInfo(s"Cred def ID => ${credentialOffer.getCredDefId}")
credDefContent <- uriDereferencer
.dereference(new URI(credentialOffer.getCredDefId))
.mapError(err => UnexpectedError(err.toString))
- credentialDefinition = anoncreds.CredentialDefinition(credDefContent)
+ credentialDefinition = anoncreds.AnoncredCredentialDefinition(credDefContent)
linkSecret <- linkSecretService
.fetchOrCreate()
.mapError(e => CredentialServiceError.LinkSecretError.apply(e.cause))
@@ -627,34 +635,48 @@ private class CredentialServiceImpl(
override def receiveCredentialIssue(
issueCredential: IssueCredential
): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] = {
- // TODO We can get rid of this 'raw' representation stored in DB, because it is not used.
- val rawIssuedCredential = issueCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???")
for {
// TODO Move this type of generic/reusable code to a helper trait
- attachmentFormatAndData <- ZIO.succeed {
- import IssueCredentialIssuedFormat.{Anoncred, JWT}
- issueCredential.attachments
- .collectFirst {
- case AttachmentDescriptor(_, _, Base64(v), Some(JWT.name), _, _, _, _) => (JWT, v)
- case AttachmentDescriptor(_, _, Base64(v), Some(Anoncred.name), _, _, _, _) => (Anoncred, v)
- }
- .map { case (f, v) => (f, java.util.Base64.getUrlDecoder.decode(v)) }
- }
record <- getRecordFromThreadIdWithState(
issueCredential.thid.map(DidCommID(_)),
ignoreWithZeroRetries = true,
ProtocolState.RequestPending,
ProtocolState.RequestSent
)
- _ <- attachmentFormatAndData match
- case Some(IssueCredentialIssuedFormat.JWT, _) => ZIO.succeed(())
- case Some(IssueCredentialIssuedFormat.Anoncred, ba) => processAnonCredsCredential(record, ba)
- case _ => ZIO.fail(UnexpectedError("No AnonCreds or JWT credential attachment found"))
+ 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)
+ }
+ )
+ }
+ processedIssuedCredential = issueCredential.copy(attachments = processedAttachments)
_ <- credentialRepository
.updateWithIssuedRawCredential(
record.id,
- issueCredential,
- rawIssuedCredential,
+ processedIssuedCredential,
+ processedIssuedCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???"),
ProtocolState.CredentialReceived
)
.flatMap {
@@ -669,30 +691,33 @@ private class CredentialServiceImpl(
} yield record
}
- private[this] def processAnonCredsCredential(record: IssueCredentialRecord, credentialBytes: Array[Byte]) = {
+ private[this] def processAnonCredsCredential(
+ record: IssueCredentialRecord,
+ credentialBytes: Array[Byte]
+ ): ZIO[WalletAccessContext, CredentialServiceError, Array[Byte]] = {
for {
- credential <- ZIO.succeed(anoncreds.Credential(new String(credentialBytes)))
+ credential <- ZIO.succeed(anoncreds.AnoncredCredential(new String(credentialBytes)))
credDefContent <- uriDereferencer
.dereference(new URI(credential.getCredDefId))
.mapError(err => UnexpectedError(err.toString))
- credentialDefinition = anoncreds.CredentialDefinition(credDefContent)
+ credentialDefinition = anoncreds.AnoncredCredentialDefinition(credDefContent)
metadata <- ZIO
.fromOption(record.anonCredsRequestMetadata)
.mapError(_ => CredentialServiceError.UnexpectedError(s"No request metadata Id found un record: ${record.id}"))
linkSecret <- linkSecretService
.fetchOrCreate()
.mapError(e => CredentialServiceError.LinkSecretError.apply(e.cause))
- _ <- ZIO
+ credential <- ZIO
.attempt(
AnoncredLib.processCredential(
- anoncreds.Credential(new String(credentialBytes)),
+ anoncreds.AnoncredCredential(new String(credentialBytes)),
metadata,
linkSecret,
credentialDefinition
)
)
.mapError(error => UnexpectedError(s"AnonCreds credential processing error: ${error.getMessage}"))
- } yield ()
+ } yield credential.data.getBytes()
}
override def markOfferSent(
@@ -848,14 +873,14 @@ private class CredentialServiceImpl(
private[this] def createAnonCredsDidCommOfferCredential(
pairwiseIssuerDID: DidId,
pairwiseHolderDID: DidId,
- schemaId: String,
+ schemaUri: String,
credentialDefinitionGUID: UUID,
+ credentialDefinitionId: String,
claims: Seq[Attribute],
- thid: DidCommID,
- credentialDefinitionId: String
+ thid: DidCommID
) = {
for {
- credentialPreview <- ZIO.succeed(CredentialPreview(schema_id = Some(schemaId), attributes = claims))
+ credentialPreview <- ZIO.succeed(CredentialPreview(schema_id = Some(schemaUri), attributes = claims))
body = OfferCredential.Body(
goal_code = Some("Offer Credential"),
credential_preview = credentialPreview,
@@ -883,16 +908,16 @@ private class CredentialServiceImpl(
credentialDefinition <- credentialDefinitionService
.getByGUID(credentialDefinitionGUID)
.mapError(e => CredentialServiceError.UnexpectedError(e.toString))
- cd = anoncreds.CredentialDefinition(credentialDefinition.definition.toString)
- kcp = anoncreds.CredentialKeyCorrectnessProof(credentialDefinition.keyCorrectnessProof.toString)
+ cd = anoncreds.AnoncredCredentialDefinition(credentialDefinition.definition.toString)
+ kcp = anoncreds.AnoncredCredentialKeyCorrectnessProof(credentialDefinition.keyCorrectnessProof.toString)
maybeCredentialDefinitionSecret <- genericSecretStorage
.get[UUID, CredentialDefinitionSecret](credentialDefinition.guid)
.orDie
credentialDefinitionSecret <- ZIO
.fromOption(maybeCredentialDefinitionSecret)
.mapError(_ => CredentialServiceError.CredentialDefinitionPrivatePartNotFound(credentialDefinition.guid))
- cdp = anoncreds.CredentialDefinitionPrivate(credentialDefinitionSecret.json.toString)
- createCredentialDefinition = CreateCredentialDefinition(cd, cdp, kcp)
+ cdp = anoncreds.AnoncredCredentialDefinitionPrivate(credentialDefinitionSecret.json.toString)
+ createCredentialDefinition = AnoncredCreateCredentialDefinition(cd, cdp, kcp)
offer = AnoncredLib.createOffer(createCredentialDefinition, credentialDefinitionId)
} yield offer
@@ -1139,7 +1164,7 @@ private class CredentialServiceImpl(
credentialDefinition <- credentialDefinitionService
.getByGUID(credentialDefinitionId)
.mapError(e => CredentialServiceError.UnexpectedError(e.toString))
- cd = anoncreds.CredentialDefinition(credentialDefinition.definition.toString)
+ cd = anoncreds.AnoncredCredentialDefinition(credentialDefinition.definition.toString)
offerCredential <- ZIO
.fromOption(record.offerCredentialData)
.mapError(_ => InvalidFlowStateError(s"No offer found for this record: ${record.id}"))
@@ -1154,7 +1179,7 @@ private class CredentialServiceImpl(
}
)
.mapError(_ => InvalidFlowStateError(s"No AnonCreds offer attachment found"))
- credentialOffer = anoncreds.CredentialOffer(offerCredentialAttachmentData)
+ credentialOffer = anoncreds.AnoncredCredentialOffer(offerCredentialAttachmentData)
requestCredential <- ZIO
.fromOption(record.requestCredentialData)
.mapError(_ => InvalidFlowStateError(s"No request found for this record: ${record.id}"))
@@ -1169,7 +1194,7 @@ private class CredentialServiceImpl(
}
)
.mapError(_ => InvalidFlowStateError(s"No AnonCreds request attachment found"))
- credentialRequest = anoncreds.CredentialRequest(requestCredentialAttachmentData)
+ credentialRequest = anoncreds.AnoncredCredentialRequest(requestCredentialAttachmentData)
attrValues = offerCredential.body.credential_preview.body.attributes.map { attr =>
(attr.name, attr.value)
}
@@ -1179,14 +1204,15 @@ private class CredentialServiceImpl(
credentialDefinitionSecret <- ZIO
.fromOption(maybeCredentialDefinitionSecret)
.mapError(_ => CredentialServiceError.CredentialDefinitionPrivatePartNotFound(credentialDefinition.guid))
- cdp = anoncreds.CredentialDefinitionPrivate(credentialDefinitionSecret.json.toString)
- credential = AnoncredLib.createCredential(
- cd,
- cdp,
- credentialOffer,
- credentialRequest,
- attrValues
- )
+ cdp = anoncreds.AnoncredCredentialDefinitionPrivate(credentialDefinitionSecret.json.toString)
+ credential =
+ AnoncredLib.createCredential(
+ cd,
+ cdp,
+ credentialOffer,
+ credentialRequest,
+ attrValues
+ )
} yield credential
}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifier.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifier.scala
index bdd7e64947..504b82b5d1 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifier.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifier.scala
@@ -47,10 +47,10 @@ class CredentialServiceNotifier(
pairwiseHolderDID: DidId,
thid: DidCommID,
credentialDefinitionGUID: UUID,
+ credentialDefinitionId: _root_.java.lang.String,
claims: Json,
validityPeriod: Option[Double],
- automaticIssuance: Option[Boolean],
- credentialDefinitionId: _root_.java.lang.String
+ automaticIssuance: Option[Boolean]
): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] =
notifyOnSuccess(
svc.createAnonCredsIssueCredentialRecord(
@@ -58,10 +58,10 @@ class CredentialServiceNotifier(
pairwiseHolderDID,
thid,
credentialDefinitionGUID,
+ credentialDefinitionId,
claims,
validityPeriod,
- automaticIssuance,
- credentialDefinitionId
+ automaticIssuance
)
)
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala
index c87c63e6ed..f0d6aa2448 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretService.scala
@@ -1,6 +1,6 @@
package io.iohk.atala.pollux.core.service
-import io.iohk.atala.pollux.anoncreds.LinkSecretWithId
+import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecretWithId
import io.iohk.atala.pollux.core.model.error.LinkSecretError
import io.iohk.atala.shared.models.WalletAccessContext
import zio.ZIO
@@ -8,5 +8,5 @@ import zio.ZIO
trait LinkSecretService {
type Result[T] = ZIO[WalletAccessContext, LinkSecretError, T]
- def fetchOrCreate(): Result[LinkSecretWithId]
+ def fetchOrCreate(): Result[AnoncredLinkSecretWithId]
}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala
index 4219d2bbd0..88e51199be 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImpl.scala
@@ -1,7 +1,7 @@
package io.iohk.atala.pollux.core.service
import io.iohk.atala.agent.walletapi.storage.{GenericSecret, GenericSecretStorage}
-import io.iohk.atala.pollux.anoncreds.{LinkSecret, LinkSecretWithId}
+import io.iohk.atala.pollux.anoncreds.{AnoncredLinkSecret, AnoncredLinkSecretWithId}
import io.iohk.atala.pollux.core.model.error.LinkSecretError
import io.iohk.atala.shared.models.WalletAccessContext
import zio.*
@@ -15,18 +15,18 @@ class LinkSecretServiceImpl(genericSecretStorage: GenericSecretStorage) extends
type Result[T] = ZIO[WalletAccessContext, LinkSecretError, T]
- override def fetchOrCreate(): Result[LinkSecretWithId] = {
+ override def fetchOrCreate(): Result[AnoncredLinkSecretWithId] = {
genericSecretStorage
- .get[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId)
+ .get[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId)
.flatMap {
case Some(secret) => ZIO.succeed(secret)
case None =>
- val linkSecret = LinkSecret()
+ val linkSecret = AnoncredLinkSecret()
genericSecretStorage
- .set[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId, linkSecret)
+ .set[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId, linkSecret)
.as(linkSecret)
}
- .map(linkSecret => LinkSecretWithId(LinkSecretServiceImpl.defaultLinkSecretId, linkSecret))
+ .map(linkSecret => AnoncredLinkSecretWithId(LinkSecretServiceImpl.defaultLinkSecretId, linkSecret))
.mapError(LinkSecretError.apply)
}
}
@@ -40,13 +40,13 @@ object LinkSecretServiceImpl {
] =
ZLayer.fromFunction(LinkSecretServiceImpl(_))
- given GenericSecret[String, LinkSecret] = new {
+ given GenericSecret[String, AnoncredLinkSecret] = new {
override def keyPath(id: String): String = s"link-secret/${id.toString}"
- override def encodeValue(secret: LinkSecret): Json = Json.Str(secret.data)
+ override def encodeValue(secret: AnoncredLinkSecret): Json = Json.Str(secret.data)
- override def decodeValue(json: Json): Try[LinkSecret] = json match {
- case Json.Str(data) => Try(LinkSecret(data))
+ override def decodeValue(json: Json): Try[AnoncredLinkSecret] = json match {
+ case Json.Str(data) => Try(AnoncredLinkSecret(data))
case _ => scala.util.Failure(new Exception("Invalid JSON format for LinkSecret"))
}
}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockCredentialService.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockCredentialService.scala
index e73c337845..21b7d82e73 100644
--- a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockCredentialService.scala
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/MockCredentialService.scala
@@ -100,10 +100,10 @@ object MockCredentialService extends Mock[CredentialService] {
pairwiseHolderDID: DidId,
thid: DidCommID,
credentialDefinitionGUID: UUID,
+ credentialDefinitionId: String,
claims: Json,
validityPeriod: Option[Double],
- automaticIssuance: Option[Boolean],
- credentialDefinitionId: String
+ automaticIssuance: Option[Boolean]
): ZIO[WalletAccessContext, CredentialServiceError, IssueCredentialRecord] =
proxy(
CreateAnonCredsIssueCredentialRecord,
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 8376f80d90..6049790a99 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
@@ -2,26 +2,35 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.mercury.protocol.presentproof.{Presentation, ProofType, ProposePresentation, RequestPresentation}
+import io.iohk.atala.pollux.anoncreds.AnoncredPresentation
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.presentation.Options
import io.iohk.atala.pollux.core.model.{DidCommID, PresentationRecord}
+import io.iohk.atala.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import io.iohk.atala.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
+import io.iohk.atala.shared.models.WalletAccessContext
import zio.mock.{Mock, Proxy}
import zio.{IO, URLayer, ZIO, ZLayer, mock}
import java.time.Instant
import java.util.UUID
-import io.iohk.atala.pollux.core.model.CredentialFormat
object MockPresentationService extends Mock[PresentationService] {
- object CreatePresentationRecord
+ object CreateJwtPresentationRecord
extends Effect[
(DidId, DidId, DidCommID, Option[String], Seq[ProofType], Option[Options]),
PresentationError,
PresentationRecord
]
+ object CreateAnoncredPresentationRecord
+ extends Effect[
+ (DidId, DidId, DidCommID, Option[String], AnoncredPresentationRequestV1),
+ PresentationError,
+ PresentationRecord
+ ]
+
object MarkRequestPresentationSent extends Effect[DidCommID, PresentationError, PresentationRecord]
object ReceivePresentation extends Effect[Presentation, PresentationError, PresentationRecord]
@@ -34,8 +43,17 @@ object MockPresentationService extends Mock[PresentationService] {
object MarkPresentationVerificationFailed extends Effect[DidCommID, PresentationError, PresentationRecord]
+ object VerifyAnoncredPresentation extends Effect[DidCommID, PresentationError, PresentationRecord]
+
object AcceptRequestPresentation extends Effect[(DidCommID, Seq[String]), PresentationError, PresentationRecord]
+ object AcceptAnoncredRequestPresentation
+ extends Effect[
+ (DidCommID, AnoncredCredentialProofsV1),
+ PresentationError,
+ PresentationRecord
+ ]
+
object RejectRequestPresentation extends Effect[DidCommID, PresentationError, PresentationRecord]
object MarkPresentationGenerated extends Effect[(DidCommID, Presentation), PresentationError, PresentationRecord]
@@ -54,26 +72,44 @@ object MockPresentationService extends Mock[PresentationService] {
proxy <- ZIO.service[Proxy]
} yield new PresentationService {
- override def createPresentationRecord(
+ override def createJwtPresentationRecord(
pairwiseVerifierDID: DidId,
pairwiseProverDID: DidId,
thid: DidCommID,
connectionId: Option[String],
proofTypes: Seq[ProofType],
- options: Option[Options],
- format: CredentialFormat,
+ options: Option[Options]
): IO[PresentationError, PresentationRecord] =
proxy(
- CreatePresentationRecord,
+ CreateJwtPresentationRecord,
(pairwiseVerifierDID, pairwiseProverDID, thid, connectionId, proofTypes, options)
)
+ override def createAnoncredPresentationRecord(
+ pairwiseVerifierDID: DidId,
+ pairwiseProverDID: DidId,
+ thid: DidCommID,
+ connectionId: Option[String],
+ presentationRequest: AnoncredPresentationRequestV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+ proxy(
+ CreateAnoncredPresentationRecord,
+ (pairwiseVerifierDID, pairwiseProverDID, thid, connectionId, presentationRequest)
+ )
+ }
+
override def acceptRequestPresentation(
recordId: DidCommID,
credentialsToUse: Seq[String]
): IO[PresentationError, PresentationRecord] =
proxy(AcceptRequestPresentation, (recordId, credentialsToUse))
+ override def acceptAnoncredRequestPresentation(
+ recordId: DidCommID,
+ credentialsToUse: AnoncredCredentialProofsV1
+ ): IO[PresentationError, PresentationRecord] =
+ proxy(AcceptAnoncredRequestPresentation, (recordId, credentialsToUse))
+
override def rejectRequestPresentation(recordId: DidCommID): IO[PresentationError, PresentationRecord] =
proxy(RejectRequestPresentation, recordId)
@@ -104,6 +140,13 @@ object MockPresentationService extends Mock[PresentationService] {
override def markPresentationVerificationFailed(recordId: DidCommID): IO[PresentationError, PresentationRecord] =
proxy(MarkPresentationVerificationFailed, recordId)
+ override def verifyAnoncredPresentation(
+ presentation: Presentation,
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
+ proxy(VerifyAnoncredPresentation, recordId)
+
override def receiveRequestPresentation(
connectionId: Option[String],
request: RequestPresentation
@@ -122,12 +165,27 @@ object MockPresentationService extends Mock[PresentationService] {
ignoreWithZeroRetries: Boolean
): IO[PresentationError, Seq[PresentationRecord]] = ???
- override def createPresentationPayloadFromRecord(
+ override def createJwtPresentationPayloadFromRecord(
record: DidCommID,
issuer: Issuer,
issuanceDate: Instant
): IO[PresentationError, PresentationPayload] = ???
+ override def createAnoncredPresentationPayloadFromRecord(
+ record: DidCommID,
+ issuer: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): IO[PresentationError, AnoncredPresentation] = ???
+
+ override def createAnoncredPresentation(
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID,
+ prover: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, Presentation] = ???
+
override def getPresentationRecordsByStates(
ignoreWithZeroRetries: Boolean,
limit: Int,
@@ -158,6 +216,7 @@ object MockPresentationService extends Mock[PresentationService] {
recordId: DidCommID,
failReason: Option[String]
): IO[PresentationError, Unit] = ???
+
}
}
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 5b314426bd..9727983870 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
@@ -2,9 +2,11 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.mercury.model.*
import io.iohk.atala.mercury.protocol.presentproof.*
+import io.iohk.atala.pollux.anoncreds.AnoncredPresentation
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.presentation.*
+import io.iohk.atala.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import io.iohk.atala.pollux.vc.jwt.*
import io.iohk.atala.shared.models.WalletAccessContext
import zio.*
@@ -14,29 +16,50 @@ import java.util as ju
import java.util.UUID
trait PresentationService {
-
def extractIdFromCredential(credential: W3cCredentialPayload): Option[UUID]
- def createPresentationRecord(
+ def createJwtPresentationRecord(
pairwiseVerifierDID: DidId,
pairwiseProverDID: DidId,
thid: DidCommID,
connectionId: Option[String],
proofTypes: Seq[ProofType],
options: Option[io.iohk.atala.pollux.core.model.presentation.Options],
- format: CredentialFormat,
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
+
+ def createAnoncredPresentationRecord(
+ pairwiseVerifierDID: DidId,
+ pairwiseProverDID: DidId,
+ thid: DidCommID,
+ connectionId: Option[String],
+ presentationRequest: AnoncredPresentationRequestV1
): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
def getPresentationRecords(
ignoreWithZeroRetries: Boolean
): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]]
- def createPresentationPayloadFromRecord(
+ def createJwtPresentationPayloadFromRecord(
record: DidCommID,
issuer: Issuer,
issuanceDate: Instant
): ZIO[WalletAccessContext, PresentationError, PresentationPayload]
+ def createAnoncredPresentationPayloadFromRecord(
+ record: DidCommID,
+ issuer: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation]
+
+ def createAnoncredPresentation(
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID,
+ prover: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, Presentation]
+
def getPresentationRecordsByStates(
ignoreWithZeroRetries: Boolean,
limit: Int,
@@ -67,6 +90,11 @@ trait PresentationService {
credentialsToUse: Seq[String]
): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
+ def acceptAnoncredRequestPresentation(
+ recordId: DidCommID,
+ credentialsToUse: AnoncredCredentialProofsV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
+
def rejectRequestPresentation(recordId: DidCommID): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
def receiveProposePresentation(
@@ -106,6 +134,12 @@ trait PresentationService {
recordId: DidCommID
): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
+ def verifyAnoncredPresentation(
+ presentation: Presentation,
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord]
+
def reportProcessingFailure(
recordId: DidCommID,
failReason: Option[String]
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 8022b26ea9..d75e785f19 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
@@ -6,23 +6,31 @@ import io.circe.*
import io.circe.parser.*
import io.circe.syntax.*
import io.iohk.atala.mercury.model.*
+import io.iohk.atala.mercury.protocol.issuecredential.IssueCredentialIssuedFormat
import io.iohk.atala.mercury.protocol.presentproof.*
+import io.iohk.atala.pollux.anoncreds.*
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.error.PresentationError.*
import io.iohk.atala.pollux.core.model.presentation.*
+import io.iohk.atala.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
import io.iohk.atala.pollux.core.repository.{CredentialRepository, PresentationRepository}
+import io.iohk.atala.pollux.core.service.serdes.*
import io.iohk.atala.pollux.vc.jwt.*
import io.iohk.atala.shared.models.WalletAccessContext
import io.iohk.atala.shared.utils.aspects.CustomMetricsAspect
import zio.*
+import java.net.URI
import java.rmi.UnexpectedException
import java.time.Instant
import java.util as ju
-import java.util.UUID
+import java.util.{UUID, Base64 as JBase64}
+import scala.util.Try
private class PresentationServiceImpl(
+ uriDereferencer: URIDereferencer,
+ linkSecretService: LinkSecretService,
presentationRepository: PresentationRepository,
credentialRepository: CredentialRepository,
maxRetries: Int = 5, // TODO move to config
@@ -46,7 +54,7 @@ private class PresentationServiceImpl(
)
_ <- count match
case 1 => ZIO.succeed(())
- case n => ZIO.fail(RecordIdNotFound(recordId))
+ case _ => ZIO.fail(RecordIdNotFound(recordId))
record <- presentationRepository
.getPresentationRecord(recordId)
.mapError(RepositoryError.apply)
@@ -57,7 +65,7 @@ private class PresentationServiceImpl(
} yield record
}
- override def createPresentationPayloadFromRecord(
+ override def createJwtPresentationPayloadFromRecord(
recordId: DidCommID,
prover: Issuer,
issuanceDate: Instant
@@ -90,15 +98,93 @@ private class PresentationServiceImpl(
)
)
- presentationPayload <- createPresentationPayloadFromCredential(
+ presentationPayload <- createJwtPresentationPayloadFromCredential(
issuedCredentials,
- record.credentialFormat,
requestPresentation,
prover
)
} yield presentationPayload
}
+ override def createAnoncredPresentationPayloadFromRecord(
+ recordId: DidCommID,
+ prover: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = {
+
+ for {
+ maybeRecord <- presentationRepository
+ .getPresentationRecord(recordId)
+ .mapError(RepositoryError.apply)
+ record <- ZIO
+ .fromOption(maybeRecord)
+ .mapError(_ => RecordIdNotFound(recordId))
+ requestPresentation <- ZIO
+ .fromOption(record.requestPresentationData)
+ .mapError(_ => InvalidFlowStateError(s"RequestPresentation not found: $recordId"))
+ issuedValidCredentials <-
+ credentialRepository
+ .getValidAnoncredIssuedCredentials(
+ anoncredCredentialProof.credentialProofs.map(credentialProof => DidCommID(credentialProof.credential))
+ )
+ .mapError(RepositoryError.apply)
+ issuedCredentials <- ZIO.fromEither(
+ Either.cond(
+ issuedValidCredentials.nonEmpty,
+ issuedValidCredentials,
+ PresentationError.IssuedCredentialNotFoundError(
+ new Throwable("No matching issued credentials found in prover db")
+ )
+ )
+ )
+ presentationPayload <- createAnoncredPresentationPayloadFromCredential(
+ issuedCredentials,
+ issuedValidCredentials.flatMap(_.schemaUri),
+ issuedValidCredentials.flatMap(_.credentialDefinitionUri),
+ requestPresentation,
+ anoncredCredentialProof.credentialProofs
+ )
+ } yield presentationPayload
+ }
+
+ def createAnoncredPresentation(
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID,
+ prover: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, Presentation] = {
+ for {
+ presentationPayload <-
+ createAnoncredPresentationPayloadFromRecord(
+ recordId,
+ prover,
+ anoncredCredentialProof,
+ issuanceDate
+ )
+ presentation <- ZIO.succeed(
+ Presentation(
+ body = Presentation.Body(
+ goal_code = requestPresentation.body.goal_code,
+ comment = requestPresentation.body.comment
+ ),
+ attachments = Seq(
+ AttachmentDescriptor
+ .buildBase64Attachment(
+ payload = presentationPayload.data.getBytes(),
+ mediaType = Some(PresentCredentialFormat.Anoncred.name),
+ format = Some(PresentCredentialFormat.Anoncred.name),
+ )
+ ),
+ thid = requestPresentation.thid.orElse(Some(requestPresentation.id)),
+ from = requestPresentation.to,
+ to = requestPresentation.from
+ )
+ )
+ } yield presentation
+ }
+
override def extractIdFromCredential(credential: W3cCredentialPayload): Option[UUID] =
credential.maybeId.map(_.split("/").last).map(UUID.fromString)
@@ -141,15 +227,52 @@ private class PresentationServiceImpl(
markPresentationRejected(recordId)
}
- override def createPresentationRecord(
+ override def createJwtPresentationRecord(
pairwiseVerifierDID: DidId,
pairwiseProverDID: DidId,
thid: DidCommID,
connectionId: Option[String],
proofTypes: Seq[ProofType],
- maybeOptions: Option[io.iohk.atala.pollux.core.model.presentation.Options],
- format: CredentialFormat,
+ maybeOptions: Option[io.iohk.atala.pollux.core.model.presentation.Options]
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+ createPresentationRecord(
+ pairwiseVerifierDID,
+ pairwiseProverDID,
+ thid,
+ connectionId,
+ CredentialFormat.JWT,
+ proofTypes,
+ maybeOptions.map(options => Seq(toJWTAttachment(options))).getOrElse(Seq.empty)
+ )
+ }
+
+ override def createAnoncredPresentationRecord(
+ pairwiseVerifierDID: DidId,
+ pairwiseProverDID: DidId,
+ thid: DidCommID,
+ connectionId: Option[String],
+ presentationRequest: AnoncredPresentationRequestV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+ createPresentationRecord(
+ pairwiseVerifierDID,
+ pairwiseProverDID,
+ thid,
+ connectionId,
+ CredentialFormat.AnonCreds,
+ Seq.empty,
+ Seq(toAnoncredAttachment(presentationRequest))
+ )
+ }
+
+ private def createPresentationRecord(
+ pairwiseVerifierDID: DidId,
+ pairwiseProverDID: DidId,
+ thid: DidCommID,
+ connectionId: Option[String],
+ format: CredentialFormat,
+ proofTypes: Seq[ProofType],
+ attachments: Seq[AttachmentDescriptor]
+ ) = {
for {
request <- ZIO.succeed(
createDidCommRequestPresentation(
@@ -157,13 +280,7 @@ private class PresentationServiceImpl(
thid,
pairwiseVerifierDID,
pairwiseProverDID,
- format match {
- case CredentialFormat.JWT => maybeOptions.map(options => Seq(toJWTAttachment(options))).getOrElse(Seq.empty)
- case CredentialFormat.AnonCreds =>
- maybeOptions
- .map(options => Seq(toAnoncredAttachment(options)))
- .getOrElse(Seq.empty) // TODO ATL-5945 Create Actual Anoncred Request
- }
+ attachments
)
)
record <- ZIO.succeed(
@@ -182,12 +299,14 @@ private class PresentationServiceImpl(
proposePresentationData = None,
presentationData = None,
credentialsToUse = None,
+ anoncredCredentialsToUseJsonSchemaId = None,
+ anoncredCredentialsToUse = None,
metaRetries = maxRetries,
metaNextRetry = Some(Instant.now()),
metaLastFailure = None,
)
)
- count <- presentationRepository
+ _ <- presentationRepository
.createPresentationRecord(record)
.flatMap {
case 1 => ZIO.succeed(())
@@ -234,9 +353,17 @@ private class PresentationServiceImpl(
val jsonF = PresentCredentialRequestFormat.JWT.name // stable identifier
val anoncredF = PresentCredentialRequestFormat.Anoncred.name // stable identifier
head.format match
- case None => ZIO.fail(PresentationError.MissingCredentialFormat)
- case Some(`jsonF`) => ZIO.succeed(CredentialFormat.JWT)
- case Some(`anoncredF`) => ZIO.succeed(CredentialFormat.AnonCreds)
+ case None => ZIO.fail(PresentationError.MissingCredentialFormat)
+ case Some(`jsonF`) => ZIO.succeed(CredentialFormat.JWT)
+ case Some(`anoncredF`) =>
+ head.data match
+ case Base64(data) =>
+ val decodedData = new String(JBase64.getUrlDecoder.decode(data))
+ AnoncredPresentationRequestV1.schemaSerDes
+ .validate(decodedData)
+ .map(_ => CredentialFormat.AnonCreds)
+ .mapError(error => InvalidAnoncredPresentationRequest(error.error))
+ case _ => ZIO.fail(InvalidAnoncredPresentationRequest("Expecting Base64-encoded data"))
case Some(unsupportedFormat) => ZIO.fail(PresentationError.UnsupportedCredentialFormat(unsupportedFormat))
case _ => ZIO.fail(PresentationError.UnexpectedError("Presentation with multi attachments"))
}
@@ -256,12 +383,14 @@ private class PresentationServiceImpl(
proposePresentationData = None,
presentationData = None,
credentialsToUse = None,
+ anoncredCredentialsToUseJsonSchemaId = None,
+ anoncredCredentialsToUse = None,
metaRetries = maxRetries,
metaNextRetry = Some(Instant.now()),
metaLastFailure = None,
)
)
- count <- presentationRepository
+ _ <- presentationRepository
.createPresentationRecord(record)
.flatMap {
case 1 => ZIO.succeed(())
@@ -272,32 +401,22 @@ private class PresentationServiceImpl(
}
/** All credentials MUST be of the same format */
- private def createPresentationPayloadFromCredential(
+ private def createJwtPresentationPayloadFromCredential(
issuedCredentials: Seq[String],
- format: CredentialFormat,
requestPresentation: RequestPresentation,
prover: Issuer
): IO[PresentationError, PresentationPayload] = {
val verifiableCredentials: Either[
PresentationError.PresentationDecodingError,
- Seq[JwtVerifiableCredentialPayload | AnoncredVerifiableCredentialPayload]
+ Seq[JwtVerifiableCredentialPayload]
] =
issuedCredentials.map { signedCredential =>
- format match {
- case CredentialFormat.JWT =>
- decode[io.iohk.atala.mercury.model.Base64](signedCredential)
- .flatMap(x => Right(new String(java.util.Base64.getDecoder().decode(x.base64))))
- .flatMap(x => Right(JwtVerifiableCredentialPayload(JWT(x))))
- .left
- .map(err => PresentationDecodingError(new Throwable(s"JsonData decoding error: $err")))
- case CredentialFormat.AnonCreds =>
- decode[io.iohk.atala.mercury.model.Base64](signedCredential)
- .flatMap(x => Right(new String(java.util.Base64.getDecoder().decode(x.base64))))
- .flatMap(x => Right(AnoncredVerifiableCredentialPayload(x)))
- .left
- .map(err => PresentationDecodingError(new Throwable(s"JsonData decoding error: $err")))
- }
+ decode[io.iohk.atala.mercury.model.Base64](signedCredential)
+ .flatMap(x => Right(new String(java.util.Base64.getDecoder.decode(x.base64))))
+ .flatMap(x => Right(JwtVerifiableCredentialPayload(JWT(x))))
+ .left
+ .map(err => PresentationDecodingError(new Throwable(s"JsonData decoding error: $err")))
}.sequence
val maybePresentationOptions
@@ -349,7 +468,131 @@ private class PresentationServiceImpl(
}
)
} yield presentationPayload
+ }
+
+ private case class AnoncredCredentialProof(
+ credential: String,
+ requestedAttribute: Seq[String],
+ requestedPredicate: Seq[String]
+ )
+
+ private def createAnoncredPresentationPayloadFromCredential(
+ issuedCredentialRecords: Seq[ValidFullIssuedCredentialRecord],
+ schemaIds: Seq[String],
+ credentialDefinitionIds: Seq[String],
+ requestPresentation: RequestPresentation,
+ credentialProofs: List[AnoncredCredentialProofV1],
+ ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] = {
+ for {
+ schemaMap <-
+ ZIO
+ .collectAll(schemaIds.map { schemaUri =>
+ resolveSchema(schemaUri)
+ })
+ .map(_.toMap)
+ credentialDefinitionMap <-
+ ZIO
+ .collectAll(credentialDefinitionIds.map { credentialDefinitionUri =>
+ resolveCredentialDefinition(credentialDefinitionUri)
+ })
+ .map(_.toMap)
+ credentialProofsMap = credentialProofs.map(credentialProof => (credentialProof.credential, credentialProof)).toMap
+ verifiableCredentials <-
+ ZIO.collectAll(
+ issuedCredentialRecords
+ .flatMap(issuedCredentialRecord => {
+ issuedCredentialRecord.issuedCredential
+ .map(issuedCredential =>
+ issuedCredential.attachments
+ .filter(attachment => attachment.format.contains(IssueCredentialIssuedFormat.Anoncred.name))
+ .map(_.data)
+ .map {
+ case Base64(data) =>
+ Right(
+ AnoncredCredentialProof(
+ new String(JBase64.getUrlDecoder.decode(data)),
+ credentialProofsMap(issuedCredentialRecord.id.value).requestedAttribute,
+ credentialProofsMap(issuedCredentialRecord.id.value).requestedPredicate
+ )
+ )
+ case _ => Left(InvalidAnoncredPresentationRequest("Expecting Base64-encoded data"))
+ }
+ .map(ZIO.fromEither(_))
+ )
+ .toSeq
+ .flatten
+ })
+ )
+ presentationRequestAttachment <- ZIO.fromEither(
+ requestPresentation.attachments.headOption.toRight(InvalidAnoncredPresentationRequest("Missing Presentation"))
+ )
+ presentationRequestData <-
+ presentationRequestAttachment.data match
+ case Base64(data) => ZIO.succeed(new String(JBase64.getUrlDecoder.decode(data)))
+ case _ => ZIO.fail(InvalidAnoncredPresentationRequest("Expecting Base64-encoded data"))
+ _ <-
+ AnoncredPresentationRequestV1.schemaSerDes
+ .deserialize(presentationRequestData)
+ .mapError(error => InvalidAnoncredPresentationRequest(error.error))
+ linkSecret <-
+ linkSecretService
+ .fetchOrCreate()
+ .map(_.secret)
+ .mapError(t => AnoncredPresentationCreationError(t.cause))
+ credentialRequest =
+ verifiableCredentials.map(verifiableCredential =>
+ AnoncredCredentialRequests(
+ AnoncredCredential(verifiableCredential.credential),
+ verifiableCredential.requestedAttribute,
+ verifiableCredential.requestedPredicate
+ )
+ )
+ presentation <-
+ ZIO
+ .fromEither(
+ AnoncredLib.createPresentation(
+ AnoncredPresentationRequest(presentationRequestData),
+ credentialRequest,
+ Map.empty, // TO FIX
+ linkSecret,
+ schemaMap,
+ credentialDefinitionMap
+ )
+ )
+ .mapError((t: Throwable) => AnoncredPresentationCreationError(t))
+ } yield presentation
+ }
+
+ private def resolveSchema(schemaUri: String): IO[UnexpectedError, (String, AnoncredSchemaDef)] = {
+ for {
+ uri <- ZIO.attempt(new URI(schemaUri)).mapError(e => UnexpectedError(e.getMessage))
+ content <- uriDereferencer.dereference(uri).mapError(e => UnexpectedError(e.error))
+ anoncredSchema <-
+ AnoncredSchemaSerDesV1.schemaSerDes
+ .deserialize(content)
+ .mapError(error => UnexpectedError(s"AnonCreds Schema parsing error: $error"))
+ anoncredLibSchema =
+ AnoncredSchemaDef(
+ schemaUri,
+ anoncredSchema.version,
+ anoncredSchema.attrNames,
+ anoncredSchema.issuerId
+ )
+ } yield (schemaUri, anoncredLibSchema)
+ }
+ private def resolveCredentialDefinition(
+ credentialDefinitionUri: String
+ ): IO[UnexpectedError, (String, AnoncredCredentialDefinition)] = {
+ for {
+ uri <- ZIO.attempt(new URI(credentialDefinitionUri)).mapError(e => UnexpectedError(e.getMessage))
+ content <- uriDereferencer.dereference(uri).mapError(e => UnexpectedError(e.error))
+ _ <-
+ PublicCredentialDefinitionSerDesV1.schemaSerDes
+ .validate(content)
+ .mapError(error => UnexpectedError(s"AnonCreds Schema parsing error: $error"))
+ anoncredCredentialDefinition = AnoncredCredentialDefinition(content)
+ } yield (credentialDefinitionUri, anoncredCredentialDefinition)
}
def acceptRequestPresentation(
@@ -359,49 +602,118 @@ private class PresentationServiceImpl(
for {
record <- getRecordWithState(recordId, ProtocolState.RequestReceived)
- issuedValidCredentials <- credentialRepository
+ issuedCredentials <- credentialRepository
.getValidIssuedCredentials(credentialsToUse.map(DidCommID(_)))
.mapError(RepositoryError.apply)
- _ <- ZIO.cond(
- (issuedValidCredentials.map(_.subjectId).toSet.size == 1),
- (),
- PresentationError.HolderBindingError(
- s"Creating a Verifiable Presentation for credential with different subject DID is not supported, found : ${issuedValidCredentials
- .map(_.subjectId)}"
- )
- )
- signedCredentials = issuedValidCredentials.flatMap(_.issuedCredentialRaw)
- // record.credentialFormat match {
- // case PresentationRecord.CredentialFormat.JWT => issuedRawCredentials
- // case CredentialFormat.AnonCreds => issuedRawCredentials
- // }
- issuedCredentials <- ZIO.fromEither(
- Either.cond(
- signedCredentials.nonEmpty,
- signedCredentials,
- PresentationError.IssuedCredentialNotFoundError(
- new Throwable(s"No matching issued credentials found in prover db from the given: $credentialsToUse")
- )
- )
+ _ <- validateCredentials(
+ s"No matching issued credentials found in prover db from the given: $credentialsToUse",
+ record,
+ issuedCredentials
)
count <- presentationRepository
.updatePresentationWithCredentialsToUse(recordId, Option(credentialsToUse), ProtocolState.PresentationPending)
.mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime(
s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge"
)
+ record <- fetchPresentationRecord(recordId, count)
+ } yield record
+ }
+
+ private def fetchPresentationRecord(recordId: DidCommID, count: RuntimeFlags) = {
+ for {
_ <- count match
case 1 => ZIO.succeed(())
- case n => ZIO.fail(RecordIdNotFound(recordId))
+ case _ => ZIO.fail(RecordIdNotFound(recordId))
record <- presentationRepository
.getPresentationRecord(recordId)
.mapError(RepositoryError.apply)
.flatMap {
- case None => ZIO.fail(RecordIdNotFound(record.id))
+ case None => ZIO.fail(RecordIdNotFound(recordId))
case Some(value) => ZIO.succeed(value)
}
} yield record
}
+ override def acceptAnoncredRequestPresentation(
+ recordId: DidCommID,
+ credentialsToUse: AnoncredCredentialProofsV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+
+ 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",
+ record,
+ issuedCredentials
+ )
+ anoncredCredentialProofsV1AsJson <- ZIO
+ .fromEither(
+ AnoncredCredentialProofsV1.schemaSerDes.serialize(credentialsToUse)
+ )
+ .mapError(error =>
+ PresentationError.UnexpectedError(
+ s"Unable to serialize credentialsToUse. credentialsToUse:$credentialsToUse, error:$error"
+ )
+ )
+ count <- presentationRepository
+ .updateAnoncredPresentationWithCredentialsToUse(
+ recordId,
+ Option(AnoncredCredentialProofsV1.version),
+ Option(anoncredCredentialProofsV1AsJson),
+ ProtocolState.PresentationPending
+ )
+ .mapError(RepositoryError.apply) @@ CustomMetricsAspect.startRecordingTime(
+ s"${record.id}_present_proof_flow_prover_presentation_pending_to_generated_ms_gauge"
+ )
+ record <- fetchPresentationRecord(recordId, count)
+ } yield record
+ }
+
+ private def validateCredentials(
+ errorMessage: String,
+ record: PresentationRecord,
+ issuedCredentials: Seq[ValidIssuedCredentialRecord]
+ ) = {
+ for {
+ _ <- ZIO.cond(
+ issuedCredentials.map(_.subjectId).toSet.size == 1,
+ (),
+ PresentationError.HolderBindingError(
+ s"Creating a Verifiable Presentation for credential with different subject DID is not supported, found : ${issuedCredentials
+ .map(_.subjectId)}"
+ )
+ )
+ validatedCredentials <- ZIO.fromEither(
+ Either.cond(
+ issuedCredentials.forall(issuedValidCredential =>
+ issuedValidCredential.credentialFormat == record.credentialFormat
+ ),
+ issuedCredentials,
+ PresentationError.NotMatchingPresentationCredentialFormat(
+ new IllegalArgumentException(
+ s"No matching issued credentials format: expectedFormat=${record.credentialFormat}"
+ )
+ )
+ )
+ )
+ signedCredentials = validatedCredentials.flatMap(_.issuedCredentialRaw)
+ _ <- ZIO.fromEither(
+ Either.cond(
+ signedCredentials.nonEmpty,
+ signedCredentials,
+ PresentationError.IssuedCredentialNotFoundError(
+ new Throwable(errorMessage)
+ )
+ )
+ )
+ } yield ()
+ }
+
override def acceptPresentation(
recordId: DidCommID
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
@@ -463,7 +775,7 @@ private class PresentationServiceImpl(
.mapError(RepositoryError.apply)
_ <- count match
case 1 => ZIO.succeed(())
- case n => ZIO.fail(RecordIdNotFound(recordId))
+ case _ => ZIO.fail(RecordIdNotFound(recordId))
record <- presentationRepository
.getPresentationRecord(record.id)
.mapError(RepositoryError.apply)
@@ -585,6 +897,63 @@ private class PresentationServiceImpl(
PresentationRecord.ProtocolState.PresentationVerificationFailed
)
+ override def verifyAnoncredPresentation(
+ presentation: Presentation,
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+ for {
+ serializedPresentation <- presentation.attachments.head.data match {
+ case Base64(data) => ZIO.succeed(AnoncredPresentation(new String(JBase64.getUrlDecoder.decode(data))))
+ case _ => ZIO.fail(InvalidAnoncredPresentation("Expecting Base64-encoded data"))
+ }
+ deserializedPresentation <-
+ AnoncredPresentationV1.schemaSerDes
+ .deserialize(serializedPresentation.data)
+ .mapError(error => PresentationError.UnexpectedError(error.error))
+ schemaIds = deserializedPresentation.identifiers.map(_.schema_id)
+ schemaMap <-
+ ZIO
+ .collectAll(schemaIds.map { schemaId =>
+ resolveSchema(schemaId)
+ })
+ .map(_.toMap)
+ credentialDefinitionIds = deserializedPresentation.identifiers.map(_.cred_def_id)
+ credentialDefinitionMap <-
+ ZIO
+ .collectAll(credentialDefinitionIds.map { credentialDefinitionId =>
+ resolveCredentialDefinition(credentialDefinitionId)
+ })
+ .map(_.toMap)
+ serializedPresentationRequest <- requestPresentation.attachments.head.data match {
+ case Base64(data) => ZIO.succeed(AnoncredPresentationRequest(new String(JBase64.getUrlDecoder.decode(data))))
+ case _ => ZIO.fail(InvalidAnoncredPresentationRequest("Expecting Base64-encoded data"))
+ }
+ isValid <-
+ ZIO
+ .fromTry(
+ Try(
+ AnoncredLib.verifyPresentation(
+ serializedPresentation,
+ serializedPresentationRequest,
+ schemaMap,
+ credentialDefinitionMap
+ )
+ )
+ )
+ .mapError((t: Throwable) => AnoncredPresentationVerificationError(t))
+ .flatMapError(e =>
+ for {
+ _ <- markPresentationVerificationFailed(recordId)
+ } yield ()
+ ZIO.succeed(e)
+ )
+ result <-
+ if isValid then markPresentationVerified(recordId)
+ else markPresentationVerificationFailed(recordId)
+ } yield result
+ }
+
def reportProcessingFailure(
recordId: DidCommID,
failReason: Option[String]
@@ -621,11 +990,13 @@ private class PresentationServiceImpl(
)
}
- // TODO ATL-5945 Create Actual Anoncred Request
- private[this] def toAnoncredAttachment(options: Options): AttachmentDescriptor = {
- AttachmentDescriptor.buildJsonAttachment(
- payload = PresentationAttachment.build(Some(options)),
- format = Some(PresentCredentialRequestFormat.Anoncred.name)
+ private[this] def toAnoncredAttachment(
+ presentationRequest: AnoncredPresentationRequestV1
+ ): AttachmentDescriptor = {
+ AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(PresentCredentialRequestFormat.Anoncred.name),
+ payload = AnoncredPresentationRequestV1.schemaSerDes.serializeToJsonString(presentationRequest).getBytes()
)
}
@@ -676,19 +1047,16 @@ private class PresentationServiceImpl(
case n => ZIO.fail(UnexpectedException(s"Invalid row count result: $n"))
}
.mapError(RepositoryError.apply)
- record <- presentationRepository
- .getPresentationRecord(id)
- .mapError(RepositoryError.apply)
- .flatMap {
- case None => ZIO.fail(RecordIdNotFound(id))
- case Some(value) => ZIO.succeed(value)
- }
+ record <- fetchPresentationRecord(id, 1)
} yield record
}
}
object PresentationServiceImpl {
- val layer: URLayer[PresentationRepository & CredentialRepository, PresentationService] =
- ZLayer.fromFunction(PresentationServiceImpl(_, _))
+ val layer: URLayer[
+ URIDereferencer & LinkSecretService & PresentationRepository & CredentialRepository,
+ PresentationService
+ ] =
+ ZLayer.fromFunction(PresentationServiceImpl(_, _, _, _))
}
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 287b1b2ae5..59afcefd9c 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
@@ -3,16 +3,17 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.event.notification.{Event, EventNotificationService}
import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.mercury.protocol.presentproof.{Presentation, ProofType, ProposePresentation, RequestPresentation}
+import io.iohk.atala.pollux.anoncreds.AnoncredPresentation
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.presentation.Options
import io.iohk.atala.pollux.core.model.{DidCommID, PresentationRecord}
+import io.iohk.atala.pollux.core.service.serdes.{AnoncredCredentialProofsV1, AnoncredPresentationRequestV1}
import io.iohk.atala.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
import io.iohk.atala.shared.models.WalletAccessContext
-import zio.{URLayer, ZIO, ZLayer, IO}
+import zio.{IO, URLayer, ZIO, ZLayer}
import java.time.Instant
import java.util.UUID
-import io.iohk.atala.pollux.core.model.CredentialFormat
class PresentationServiceNotifier(
svc: PresentationService,
@@ -21,24 +22,39 @@ class PresentationServiceNotifier(
private val presentationUpdatedEvent = "PresentationUpdated"
- override def createPresentationRecord(
+ override def createJwtPresentationRecord(
pairwiseVerifierDID: DidId,
pairwiseProverDID: DidId,
thid: DidCommID,
connectionId: Option[String],
proofTypes: Seq[ProofType],
options: Option[Options],
- format: CredentialFormat,
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
notifyOnSuccess(
- svc.createPresentationRecord(
+ svc.createJwtPresentationRecord(
pairwiseVerifierDID,
pairwiseProverDID,
thid,
connectionId,
proofTypes,
- options,
- format: CredentialFormat
+ options
+ )
+ )
+
+ def createAnoncredPresentationRecord(
+ pairwiseVerifierDID: DidId,
+ pairwiseProverDID: DidId,
+ thid: DidCommID,
+ connectionId: Option[String],
+ presentationRequest: AnoncredPresentationRequestV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
+ notifyOnSuccess(
+ svc.createAnoncredPresentationRecord(
+ pairwiseVerifierDID,
+ pairwiseProverDID,
+ thid,
+ connectionId,
+ presentationRequest
)
)
@@ -64,6 +80,13 @@ class PresentationServiceNotifier(
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
notifyOnSuccess(svc.acceptRequestPresentation(recordId, credentialsToUse))
+ override def acceptAnoncredRequestPresentation(
+ recordId: DidCommID,
+ credentialsToUse: AnoncredCredentialProofsV1
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = notifyOnSuccess(
+ svc.acceptAnoncredRequestPresentation(recordId, credentialsToUse)
+ )
+
override def rejectRequestPresentation(
recordId: DidCommID
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
@@ -95,6 +118,15 @@ class PresentationServiceNotifier(
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
notifyOnSuccess(svc.markPresentationVerificationFailed(recordId))
+ override def verifyAnoncredPresentation(
+ presentation: Presentation,
+ requestPresentation: RequestPresentation,
+ recordId: DidCommID
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
+ notifyOnSuccess(
+ svc.verifyAnoncredPresentation(presentation, requestPresentation, recordId)
+ )
+
override def acceptPresentation(
recordId: DidCommID
): ZIO[WalletAccessContext, PresentationError, PresentationRecord] =
@@ -128,12 +160,29 @@ class PresentationServiceNotifier(
): ZIO[WalletAccessContext, PresentationError, Seq[PresentationRecord]] =
svc.getPresentationRecords(ignoreWithZeroRetries)
- override def createPresentationPayloadFromRecord(
+ override def createJwtPresentationPayloadFromRecord(
record: DidCommID,
issuer: Issuer,
issuanceDate: Instant
): ZIO[WalletAccessContext, PresentationError, PresentationPayload] =
- svc.createPresentationPayloadFromRecord(record, issuer, issuanceDate)
+ svc.createJwtPresentationPayloadFromRecord(record, issuer, issuanceDate)
+
+ override def createAnoncredPresentationPayloadFromRecord(
+ record: DidCommID,
+ issuer: Issuer,
+ anoncredCredentialProof: AnoncredCredentialProofsV1,
+ issuanceDate: Instant
+ ): ZIO[WalletAccessContext, PresentationError, AnoncredPresentation] =
+ svc.createAnoncredPresentationPayloadFromRecord(record, issuer, 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)
override def getPresentationRecordsByStates(
ignoreWithZeroRetries: Boolean,
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala
new file mode 100644
index 0000000000..fc8dfdd3ef
--- /dev/null
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala
@@ -0,0 +1,65 @@
+package io.iohk.atala.pollux.core.service.serdes
+
+import io.iohk.atala.pollux.core.model.schema.validator.SchemaSerDes
+import zio.*
+import zio.json.*
+
+case class AnoncredCredentialProofV1(
+ credential: String,
+ requestedAttribute: Seq[String],
+ requestedPredicate: Seq[String]
+)
+
+case class AnoncredCredentialProofsV1(credentialProofs: List[AnoncredCredentialProofV1])
+
+object AnoncredCredentialProofsV1 {
+ val version: String = "AnoncredCredentialProofsV1"
+ private val schema: String =
+ """
+ |{
+ | "$schema": "http://json-schema.org/draft-07/schema#",
+ | "type": "object",
+ | "properties": {
+ | "credentialProofs": {
+ | "type": "array",
+ | "items": {
+ | "type": "object",
+ | "properties": {
+ | "credential": {
+ | "type": "string"
+ | },
+ | "requestedAttribute": {
+ | "type": "array",
+ | "items": {
+ | "type": "string"
+ | }
+ | },
+ | "requestedPredicate": {
+ | "type": "array",
+ | "items": {
+ | "type": "string"
+ | }
+ | }
+ | },
+ | "required": ["credential", "requestedAttribute", "requestedPredicate"]
+ | }
+ | }
+ | },
+ | "required": ["credentialProofs"]
+ |}
+ |""".stripMargin
+
+ val schemaSerDes: SchemaSerDes[AnoncredCredentialProofsV1] = SchemaSerDes(schema)
+
+ given JsonDecoder[AnoncredCredentialProofV1] =
+ DeriveJsonDecoder.gen[AnoncredCredentialProofV1]
+
+ given JsonEncoder[AnoncredCredentialProofV1] =
+ DeriveJsonEncoder.gen[AnoncredCredentialProofV1]
+
+ given JsonDecoder[AnoncredCredentialProofsV1] =
+ DeriveJsonDecoder.gen[AnoncredCredentialProofsV1]
+
+ given JsonEncoder[AnoncredCredentialProofsV1] =
+ DeriveJsonEncoder.gen[AnoncredCredentialProofsV1]
+}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala
new file mode 100644
index 0000000000..e16fe79fff
--- /dev/null
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala
@@ -0,0 +1,131 @@
+package io.iohk.atala.pollux.core.service.serdes
+
+import io.iohk.atala.pollux.core.model.schema.validator.SchemaSerDes
+import zio.*
+import zio.json.*
+
+case class AnoncredPresentationRequestV1(
+ requested_attributes: Map[String, AnoncredRequestedAttributeV1],
+ requested_predicates: Map[String, AnoncredRequestedPredicateV1],
+ name: String,
+ nonce: String,
+ version: String,
+ non_revoked: Option[AnoncredNonRevokedIntervalV1]
+)
+
+case class AnoncredRequestedAttributeV1(
+ name: String,
+ restrictions: List[Map[String, String]],
+ non_revoked: Option[AnoncredNonRevokedIntervalV1]
+)
+
+case class AnoncredRequestedPredicateV1(
+ name: String,
+ p_type: String,
+ p_value: Int,
+ restrictions: List[Map[String, String]],
+ non_revoked: Option[AnoncredNonRevokedIntervalV1]
+)
+
+case class AnoncredNonRevokedIntervalV1(from: Option[Int], to: Option[Int])
+
+object AnoncredPresentationRequestV1 {
+ val version: String = "AnoncredPresentationRequestV1"
+
+ private val schema: String =
+ """
+ |{
+ | "$schema": "http://json-schema.org/draft-07/schema#",
+ | "type": "object",
+ | "properties": {
+ | "requested_attributes": {
+ | "type": "object",
+ | "additionalProperties": {
+ | "type": "object",
+ | "properties": {
+ | "name": { "type": "string" },
+ | "restrictions": {
+ | "type": "array",
+ | "items": {
+ | "type": "object"
+ | }
+ | },
+ | "non_revoked": {
+ | "type": "object",
+ | "properties": {
+ | "from": { "type": "integer" },
+ | "to": { "type": "integer" }
+ | }
+ | }
+ | },
+ | "required": ["name", "restrictions"]
+ | }
+ | },
+ | "requested_predicates": {
+ | "type": "object",
+ | "additionalProperties": {
+ | "type": "object",
+ | "properties": {
+ | "name": { "type": "string" },
+ | "p_type": { "type": "string" },
+ | "p_value": { "type": "integer" },
+ | "restrictions": {
+ | "type": "array",
+ | "items": {
+ | "type": "object"
+ | }
+ | },
+ | "non_revoked": {
+ | "type": "object",
+ | "properties": {
+ | "from": { "type": "integer" },
+ | "to": { "type": "integer" }
+ | }
+ | }
+ | },
+ | "required": ["name", "p_type", "p_value", "restrictions"]
+ | }
+ | },
+ | "name": { "type": "string" },
+ | "nonce": { "type": "string" },
+ | "version": { "type": "string" },
+ | "non_revoked": {
+ | "type": "object",
+ | "properties": {
+ | "from": { "type": "integer" },
+ | "to": { "type": "integer" }
+ | }
+ | }
+ | },
+ | "required": ["requested_attributes", "requested_predicates", "name", "nonce", "version" ]
+ |}
+ |
+ |""".stripMargin
+
+ val schemaSerDes: SchemaSerDes[AnoncredPresentationRequestV1] = SchemaSerDes(schema)
+
+ given JsonDecoder[AnoncredRequestedAttributeV1] =
+ DeriveJsonDecoder.gen[AnoncredRequestedAttributeV1]
+
+ given JsonEncoder[AnoncredRequestedAttributeV1] =
+ DeriveJsonEncoder.gen[AnoncredRequestedAttributeV1]
+
+ given JsonDecoder[AnoncredRequestedPredicateV1] =
+ DeriveJsonDecoder.gen[AnoncredRequestedPredicateV1]
+
+ given JsonEncoder[AnoncredRequestedPredicateV1] =
+ DeriveJsonEncoder.gen[AnoncredRequestedPredicateV1]
+
+ given JsonEncoder[AnoncredNonRevokedIntervalV1] =
+ DeriveJsonEncoder.gen[AnoncredNonRevokedIntervalV1]
+
+ given JsonDecoder[AnoncredNonRevokedIntervalV1] =
+ DeriveJsonDecoder.gen[AnoncredNonRevokedIntervalV1]
+
+ given JsonDecoder[AnoncredPresentationRequestV1] =
+ DeriveJsonDecoder.gen[AnoncredPresentationRequestV1]
+
+ given JsonEncoder[AnoncredPresentationRequestV1] =
+ DeriveJsonEncoder.gen[AnoncredPresentationRequestV1]
+
+}
diff --git a/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationV1.scala b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationV1.scala
new file mode 100644
index 0000000000..f773f8311a
--- /dev/null
+++ b/pollux/lib/core/src/main/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationV1.scala
@@ -0,0 +1,512 @@
+package io.iohk.atala.pollux.core.service.serdes
+
+import io.iohk.atala.pollux.core.model.schema.validator.SchemaSerDes
+import zio.*
+import zio.json.*
+
+case class AnoncredAdditionalPropertiesTypeStringV1(value: String)
+
+case class AnoncredRevealedAttrV1(sub_proof_index: Int, raw: String, encoded: String)
+
+case class AnoncredRequestedProofV1(
+ revealed_attrs: Map[String, AnoncredRevealedAttrV1],
+ self_attested_attrs: Map[String, String],
+ unrevealed_attrs: Map[String, String],
+ predicates: Map[String, AnoncredSubProofIndexV1]
+)
+
+case class AnoncredSubProofIndexV1(sub_proof_index: Int)
+
+case class AnoncredIdentifierV1(
+ schema_id: String,
+ cred_def_id: String,
+ rev_reg_id: Option[String],
+ timestamp: Option[Int]
+)
+
+// Adjusted classes for proofs
+case class AnoncredEqProofV1(
+ revealed_attrs: Map[String, String],
+ a_prime: String,
+ e: String,
+ v: String,
+ m: Map[String, String],
+ m2: String
+)
+
+case class AnoncredPredicateV1(attr_name: String, p_type: String, value: Int)
+
+case class AnoncredGeProofV1(
+ u: Map[String, String],
+ r: Map[String, String],
+ mj: String,
+ alpha: String,
+ t: Map[String, String],
+ predicate: AnoncredPredicateV1
+)
+
+case class AnoncredPrimaryProofV1(eq_proof: AnoncredEqProofV1, ge_proofs: List[AnoncredGeProofV1])
+
+case class AnoncredSubProofV1(primary_proof: AnoncredPrimaryProofV1, non_revoc_proof: Option[AnoncredNonRevocProofV1])
+
+case class AnoncredProofV1(proofs: List[AnoncredSubProofV1], aggregated_proof: AnoncredAggregatedProofV1)
+
+case class AnoncredAggregatedProofV1(c_hash: String, c_list: List[List[Int]])
+
+case class AnoncredNonRevocProofXListV1(
+ rho: String,
+ r: String,
+ r_prime: String,
+ r_prime_prime: String,
+ r_prime_prime_prime: String,
+ o: String,
+ o_prime: String,
+ m: String,
+ m_prime: String,
+ t: String,
+ t_prime: String,
+ m2: String,
+ s: String,
+ c: String
+)
+
+case class AnoncredNonRevocProofCListV1(e: String, d: String, a: String, g: String, w: String, s: String, u: String)
+
+case class AnoncredNonRevocProofV1(x_list: AnoncredNonRevocProofXListV1, c_list: AnoncredNonRevocProofCListV1)
+
+case class AnoncredPresentationV1(
+ proof: AnoncredProofV1,
+ requested_proof: AnoncredRequestedProofV1,
+ identifiers: List[AnoncredIdentifierV1]
+)
+
+object AnoncredPresentationV1 {
+ val version: String = "AnoncredPresentationV1"
+
+ private val schema: String =
+ """
+ |{
+ | "$schema":"http://json-schema.org/draft-07/schema#",
+ | "type":"object",
+ | "properties":{
+ | "proof":{
+ | "type":"object",
+ | "properties":{
+ | "proofs":{
+ | "type":"array",
+ | "items":{
+ | "type":"object",
+ | "properties":{
+ | "primary_proof":{
+ | "type":"object",
+ | "properties":{
+ | "eq_proof":{
+ | "type":"object",
+ | "properties":{
+ | "revealed_attrs":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"string"
+ | }
+ | },
+ | "a_prime":{
+ | "type":"string"
+ | },
+ | "e":{
+ | "type":"string"
+ | },
+ | "v":{
+ | "type":"string"
+ | },
+ | "m":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"string"
+ | }
+ | },
+ | "m2":{
+ | "type":"string"
+ | }
+ | },
+ | "required":[
+ | "revealed_attrs",
+ | "a_prime",
+ | "e",
+ | "v",
+ | "m",
+ | "m2"
+ | ]
+ | },
+ | "ge_proofs":{
+ | "type":"array",
+ | "items":{
+ | "type":"object",
+ | "properties":{
+ | "u":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"string"
+ | }
+ | },
+ | "r":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"string"
+ | }
+ | },
+ | "mj":{
+ | "type":"string"
+ | },
+ | "alpha":{
+ | "type":"string"
+ | },
+ | "t":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"string"
+ | }
+ | },
+ | "predicate":{
+ | "type":"object",
+ | "properties":{
+ | "attr_name":{
+ | "type":"string"
+ | },
+ | "p_type":{
+ | "type":"string"
+ | },
+ | "value":{
+ | "type":"integer"
+ | }
+ | },
+ | "required":[
+ | "attr_name",
+ | "p_type",
+ | "value"
+ | ]
+ | }
+ | },
+ | "required":[
+ | "u",
+ | "r",
+ | "mj",
+ | "alpha",
+ | "t",
+ | "predicate"
+ | ]
+ | }
+ | }
+ | },
+ | "required":[
+ | "eq_proof",
+ | "ge_proofs"
+ | ]
+ | },
+ | "non_revoc_proof":{
+ | "oneOf":[
+ | {
+ | "type":"object",
+ | "properties":{
+ | "x_list":{
+ | "type":"object",
+ | "properties":{
+ | "rho":{
+ | "type":"string"
+ | },
+ | "r":{
+ | "type":"string"
+ | },
+ | "r_prime":{
+ | "type":"string"
+ | },
+ | "r_prime_prime":{
+ | "type":"string"
+ | },
+ | "r_prime_prime_prime":{
+ | "type":"string"
+ | },
+ | "o":{
+ | "type":"string"
+ | },
+ | "o_prime":{
+ | "type":"string"
+ | },
+ | "m":{
+ | "type":"string"
+ | },
+ | "m_prime":{
+ | "type":"string"
+ | },
+ | "t":{
+ | "type":"string"
+ | },
+ | "t_prime":{
+ | "type":"string"
+ | },
+ | "m2":{
+ | "type":"string"
+ | },
+ | "s":{
+ | "type":"string"
+ | },
+ | "c":{
+ | "type":"string"
+ | }
+ | },
+ | "required":[
+ | "rho",
+ | "r",
+ | "r_prime",
+ | "r_prime_prime",
+ | "r_prime_prime_prime",
+ | "o",
+ | "o_prime",
+ | "m",
+ | "m_prime",
+ | "t",
+ | "t_prime",
+ | "m2",
+ | "s",
+ | "c"
+ | ]
+ | },
+ | "c_list":{
+ | "type":"object",
+ | "properties":{
+ | "e":{
+ | "type":"string"
+ | },
+ | "d":{
+ | "type":"string"
+ | },
+ | "a":{
+ | "type":"string"
+ | },
+ | "g":{
+ | "type":"string"
+ | },
+ | "w":{
+ | "type":"string"
+ | },
+ | "s":{
+ | "type":"string"
+ | },
+ | "u":{
+ | "type":"string"
+ | }
+ | },
+ | "required":[
+ | "e",
+ | "d",
+ | "a",
+ | "g",
+ | "w",
+ | "s",
+ | "u"
+ | ]
+ | }
+ | },
+ | "required":[
+ | "x_list",
+ | "c_list"
+ | ]
+ | },
+ | {
+ | "type":"null"
+ | }
+ | ]
+ | }
+ | },
+ | "required":[
+ | "primary_proof"
+ | ]
+ | }
+ | },
+ | "aggregated_proof":{
+ | "type":"object",
+ | "properties":{
+ | "c_hash":{
+ | "type":"string"
+ | },
+ | "c_list":{
+ | "type":"array",
+ | "items":{
+ | "type":"array",
+ | "items":{
+ | "type":"integer"
+ | }
+ | }
+ | }
+ | },
+ | "required":[
+ | "c_hash",
+ | "c_list"
+ | ]
+ | }
+ | },
+ | "required":[
+ | "proofs",
+ | "aggregated_proof"
+ | ]
+ | },
+ | "requested_proof":{
+ | "type":"object",
+ | "properties":{
+ | "revealed_attrs":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"object",
+ | "properties":{
+ | "sub_proof_index":{
+ | "type":"integer"
+ | },
+ | "raw":{
+ | "type":"string"
+ | },
+ | "encoded":{
+ | "type":"string"
+ | }
+ | },
+ | "required":[
+ | "sub_proof_index",
+ | "raw",
+ | "encoded"
+ | ]
+ | }
+ | },
+ | "self_attested_attrs":{
+ | "type":"object"
+ | },
+ | "unrevealed_attrs":{
+ | "type":"object"
+ | },
+ | "predicates":{
+ | "type":"object",
+ | "additionalProperties":{
+ | "type":"object",
+ | "properties":{
+ | "sub_proof_index":{
+ | "type":"integer"
+ | }
+ | },
+ | "required":[
+ | "sub_proof_index"
+ | ]
+ | }
+ | }
+ | },
+ | "required":[
+ | "revealed_attrs",
+ | "self_attested_attrs",
+ | "unrevealed_attrs",
+ | "predicates"
+ | ]
+ | },
+ | "identifiers":{
+ | "type":"array",
+ | "items":{
+ | "type":"object",
+ | "properties":{
+ | "schema_id":{
+ | "type":"string"
+ | },
+ | "cred_def_id":{
+ | "type":"string"
+ | },
+ | "rev_reg_id":{
+ | "type":[
+ | "string",
+ | "null"
+ | ]
+ | },
+ | "timestamp":{
+ | "type":[
+ | "integer",
+ | "null"
+ | ]
+ | }
+ | },
+ | "required":[
+ | "schema_id",
+ | "cred_def_id"
+ | ]
+ | }
+ | }
+ | },
+ | "required":[
+ | "proof",
+ | "requested_proof",
+ | "identifiers"
+ | ]
+ |}
+ |""".stripMargin
+
+ val schemaSerDes: SchemaSerDes[AnoncredPresentationV1] = SchemaSerDes(schema)
+
+ given JsonDecoder[AnoncredAdditionalPropertiesTypeStringV1] =
+ DeriveJsonDecoder.gen[AnoncredAdditionalPropertiesTypeStringV1]
+
+ given JsonEncoder[AnoncredAdditionalPropertiesTypeStringV1] =
+ DeriveJsonEncoder.gen[AnoncredAdditionalPropertiesTypeStringV1]
+
+ given JsonDecoder[AnoncredEqProofV1] = DeriveJsonDecoder.gen[AnoncredEqProofV1]
+
+ given JsonEncoder[AnoncredEqProofV1] = DeriveJsonEncoder.gen[AnoncredEqProofV1]
+
+ given JsonDecoder[AnoncredPredicateV1] = DeriveJsonDecoder.gen[AnoncredPredicateV1]
+
+ given JsonEncoder[AnoncredPredicateV1] = DeriveJsonEncoder.gen[AnoncredPredicateV1]
+
+ given JsonDecoder[AnoncredGeProofV1] = DeriveJsonDecoder.gen[AnoncredGeProofV1]
+
+ given JsonEncoder[AnoncredGeProofV1] = DeriveJsonEncoder.gen[AnoncredGeProofV1]
+
+ given JsonDecoder[AnoncredNonRevocProofXListV1] = DeriveJsonDecoder.gen[AnoncredNonRevocProofXListV1]
+
+ given JsonEncoder[AnoncredNonRevocProofXListV1] = DeriveJsonEncoder.gen[AnoncredNonRevocProofXListV1]
+
+ given JsonDecoder[AnoncredNonRevocProofCListV1] = DeriveJsonDecoder.gen[AnoncredNonRevocProofCListV1]
+
+ given JsonEncoder[AnoncredNonRevocProofCListV1] = DeriveJsonEncoder.gen[AnoncredNonRevocProofCListV1]
+
+ given JsonDecoder[AnoncredNonRevocProofV1] = DeriveJsonDecoder.gen[AnoncredNonRevocProofV1]
+
+ given JsonEncoder[AnoncredNonRevocProofV1] = DeriveJsonEncoder.gen[AnoncredNonRevocProofV1]
+
+ given JsonDecoder[AnoncredPrimaryProofV1] = DeriveJsonDecoder.gen[AnoncredPrimaryProofV1]
+
+ given JsonEncoder[AnoncredPrimaryProofV1] = DeriveJsonEncoder.gen[AnoncredPrimaryProofV1]
+
+ given JsonDecoder[AnoncredAggregatedProofV1] = DeriveJsonDecoder.gen[AnoncredAggregatedProofV1]
+
+ given JsonEncoder[AnoncredAggregatedProofV1] = DeriveJsonEncoder.gen[AnoncredAggregatedProofV1]
+
+ given JsonDecoder[AnoncredSubProofV1] = DeriveJsonDecoder.gen[AnoncredSubProofV1]
+
+ given JsonEncoder[AnoncredSubProofV1] = DeriveJsonEncoder.gen[AnoncredSubProofV1]
+
+ given JsonDecoder[AnoncredProofV1] = DeriveJsonDecoder.gen[AnoncredProofV1]
+
+ given JsonEncoder[AnoncredProofV1] = DeriveJsonEncoder.gen[AnoncredProofV1]
+
+ given JsonDecoder[AnoncredRevealedAttrV1] = DeriveJsonDecoder.gen[AnoncredRevealedAttrV1]
+
+ given JsonEncoder[AnoncredRevealedAttrV1] = DeriveJsonEncoder.gen[AnoncredRevealedAttrV1]
+
+ given JsonDecoder[AnoncredSubProofIndexV1] = DeriveJsonDecoder.gen[AnoncredSubProofIndexV1]
+
+ given JsonEncoder[AnoncredSubProofIndexV1] = DeriveJsonEncoder.gen[AnoncredSubProofIndexV1]
+
+ given JsonDecoder[AnoncredRequestedProofV1] = DeriveJsonDecoder.gen[AnoncredRequestedProofV1]
+
+ given JsonEncoder[AnoncredRequestedProofV1] = DeriveJsonEncoder.gen[AnoncredRequestedProofV1]
+
+ given JsonDecoder[AnoncredIdentifierV1] = DeriveJsonDecoder.gen[AnoncredIdentifierV1]
+
+ given JsonEncoder[AnoncredIdentifierV1] = DeriveJsonEncoder.gen[AnoncredIdentifierV1]
+
+ given JsonDecoder[AnoncredPresentationV1] = DeriveJsonDecoder.gen[AnoncredPresentationV1]
+
+ given JsonEncoder[AnoncredPresentationV1] = DeriveJsonEncoder.gen[AnoncredPresentationV1]
+
+}
diff --git a/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json b/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json
new file mode 100644
index 0000000000..febd78d65d
--- /dev/null
+++ b/pollux/lib/core/src/test/resources/anoncred-presentation-schema-example.json
@@ -0,0 +1,10 @@
+{
+ "name": "resource:///anoncred-presentation-schema-example.json",
+ "version": "1.0",
+ "attrNames": [
+ "name",
+ "sex",
+ "age"
+ ],
+ "issuerId": "did:prism:issuer"
+}
\ No newline at end of file
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 b363446d36..0d0a1eb1c0 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
@@ -17,14 +17,15 @@ import java.util.UUID
object CredentialRepositorySpecSuite {
val maxRetries = 5 // TODO Move to config
- private def issueCredentialRecord = IssueCredentialRecord(
+ private def issueCredentialRecord(credentialFormat: CredentialFormat) = IssueCredentialRecord(
id = DidCommID(),
createdAt = Instant.now,
updatedAt = None,
thid = DidCommID(),
- schemaId = None,
+ schemaUri = None,
credentialDefinitionId = None,
- credentialFormat = CredentialFormat.JWT,
+ credentialDefinitionUri = None,
+ credentialFormat = credentialFormat,
role = IssueCredentialRecord.Role.Issuer,
subjectId = None,
validityPeriod = None,
@@ -53,7 +54,7 @@ object CredentialRepositorySpecSuite {
test("createIssueCredentialRecord creates a new record in DB") {
for {
repo <- ZIO.service[CredentialRepository]
- record = issueCredentialRecord
+ record = issueCredentialRecord(CredentialFormat.JWT)
count <- repo.createIssueCredentialRecord(record)
} yield assertTrue(count == 1)
},
@@ -61,8 +62,8 @@ object CredentialRepositorySpecSuite {
for {
repo <- ZIO.service[CredentialRepository]
thid = DidCommID()
- aRecord = issueCredentialRecord.copy(thid = thid)
- bRecord = issueCredentialRecord.copy(thid = thid)
+ aRecord = issueCredentialRecord(CredentialFormat.JWT).copy(thid = thid)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT).copy(thid = thid)
aCount <- repo.createIssueCredentialRecord(aRecord)
bCount <- repo.createIssueCredentialRecord(bRecord).exit
} yield assertTrue(aCount == 1) && assert(bCount)(fails(isSubtype[UniqueConstraintViolation](anything)))
@@ -71,7 +72,7 @@ object CredentialRepositorySpecSuite {
for {
repo <- ZIO.service[CredentialRepository]
issuingDID <- ZIO.fromEither(PrismDID.buildCanonicalFromSuffix("0" * 64))
- record = issueCredentialRecord.copy(issuingDID = Some(issuingDID))
+ record = issueCredentialRecord(CredentialFormat.JWT).copy(issuingDID = Some(issuingDID))
count <- repo.createIssueCredentialRecord(record)
readRecord <- repo.getIssueCredentialRecord(record.id)
} yield assertTrue(count == 1) && assert(readRecord)(isSome(equalTo(record)))
@@ -79,8 +80,8 @@ object CredentialRepositorySpecSuite {
test("getIssueCredentialRecord correctly returns an existing record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
record <- repo.getIssueCredentialRecord(bRecord.id)
@@ -89,8 +90,8 @@ object CredentialRepositorySpecSuite {
test("getIssuanceCredentialRecord returns None for an unknown record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
record <- repo.getIssueCredentialRecord(DidCommID())
@@ -99,8 +100,8 @@ object CredentialRepositorySpecSuite {
test("getIssuanceCredentialRecord returns all records") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
records <- repo.getIssueCredentialRecords(false).map(_._1)
@@ -113,8 +114,8 @@ object CredentialRepositorySpecSuite {
test("getIssuanceCredentialRecord returns records with offset") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
records <- repo.getIssueCredentialRecords(false, offset = Some(1)).map(_._1)
@@ -126,8 +127,8 @@ object CredentialRepositorySpecSuite {
test("getIssuanceCredentialRecord returns records with limit") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
records <- repo.getIssueCredentialRecords(false, limit = Some(1)).map(_._1)
@@ -139,9 +140,9 @@ object CredentialRepositorySpecSuite {
test("getIssuanceCredentialRecord returns records with offset and limit") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
- cRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
+ cRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
_ <- repo.createIssueCredentialRecord(cRecord)
@@ -156,8 +157,8 @@ object CredentialRepositorySpecSuite {
test("deleteIssueCredentialRecord deletes an exsiting record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
count <- repo.deleteIssueCredentialRecord(aRecord.id)
@@ -171,8 +172,8 @@ object CredentialRepositorySpecSuite {
test("deleteIssueCredentialRecord does nothing for an unknown record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
count <- repo.deleteIssueCredentialRecord(DidCommID())
@@ -188,8 +189,8 @@ object CredentialRepositorySpecSuite {
for {
repo <- ZIO.service[CredentialRepository]
thid = DidCommID()
- aRecord = issueCredentialRecord.copy(thid = thid)
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT).copy(thid = thid)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
record <- repo.getIssueCredentialRecordByThreadId(thid, false)
@@ -198,8 +199,8 @@ object CredentialRepositorySpecSuite {
test("getIssueCredentialRecordByThreadId returns nothing for an unknown thid") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
record <- repo.getIssueCredentialRecordByThreadId(DidCommID(), false)
@@ -208,9 +209,9 @@ object CredentialRepositorySpecSuite {
test("getIssueCredentialRecordsByStates returns valid records") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
- cRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
+ cRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
_ <- repo.createIssueCredentialRecord(cRecord)
@@ -242,9 +243,9 @@ object CredentialRepositorySpecSuite {
test("getIssueCredentialRecordsByStates returns an empty list if 'states' parameter is empty") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
- cRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
+ cRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
_ <- repo.createIssueCredentialRecord(cRecord)
@@ -256,30 +257,55 @@ object CredentialRepositorySpecSuite {
test("getValidIssuedCredentials returns valid records") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
- bRecord = issueCredentialRecord
- cRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
+ bRecord = issueCredentialRecord(CredentialFormat.JWT)
+ cRecord = issueCredentialRecord(CredentialFormat.JWT)
+ dRecord = issueCredentialRecord(CredentialFormat.AnonCreds)
_ <- repo.createIssueCredentialRecord(aRecord)
_ <- repo.createIssueCredentialRecord(bRecord)
_ <- repo.createIssueCredentialRecord(cRecord)
+ _ <- repo.createIssueCredentialRecord(dRecord)
_ <- repo.updateWithIssuedRawCredential(
aRecord.id,
IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage),
"RAW_CREDENTIAL_DATA",
ProtocolState.CredentialReceived
)
- records <- repo.getValidIssuedCredentials(Seq(aRecord.id, bRecord.id))
+ _ <- repo.updateWithIssuedRawCredential(
+ dRecord.id,
+ IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage),
+ "RAW_CREDENTIAL_DATA",
+ ProtocolState.CredentialReceived
+ )
+ records <- repo.getValidIssuedCredentials(Seq(aRecord.id, bRecord.id, dRecord.id))
} yield {
- assertTrue(records.size == 1) &&
+ assertTrue(records.size == 2) &&
assertTrue(
- records.contains(ValidIssuedCredentialRecord(aRecord.id, Some("RAW_CREDENTIAL_DATA"), aRecord.subjectId))
+ records.contains(
+ ValidIssuedCredentialRecord(
+ dRecord.id,
+ Some("RAW_CREDENTIAL_DATA"),
+ CredentialFormat.JWT,
+ aRecord.subjectId
+ )
+ )
+ )
+ assertTrue(
+ records.contains(
+ ValidIssuedCredentialRecord(
+ dRecord.id,
+ Some("RAW_CREDENTIAL_DATA"),
+ CredentialFormat.AnonCreds,
+ aRecord.subjectId
+ )
+ )
)
}
},
test("updateCredentialRecordProtocolState updates the record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
record <- repo.getIssueCredentialRecord(aRecord.id)
count <- repo.updateCredentialRecordProtocolState(
@@ -297,7 +323,7 @@ object CredentialRepositorySpecSuite {
test("updateCredentialRecordProtocolState doesn't update the record for invalid from state") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
record <- repo.getIssueCredentialRecord(aRecord.id)
count <- repo.updateCredentialRecordProtocolState(
@@ -315,7 +341,7 @@ object CredentialRepositorySpecSuite {
test("updateWithRequestCredential updates record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
record <- repo.getIssueCredentialRecord(aRecord.id)
request = requestCredential
@@ -334,7 +360,7 @@ object CredentialRepositorySpecSuite {
test("updateWithIssueCredential updates record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
record <- repo.getIssueCredentialRecord(aRecord.id)
issueCredential = IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage)
@@ -353,7 +379,7 @@ object CredentialRepositorySpecSuite {
test("updateWithIssuedRawCredential updates record") {
for {
repo <- ZIO.service[CredentialRepository]
- aRecord = issueCredentialRecord
+ aRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aRecord)
record <- repo.getIssueCredentialRecord(aRecord.id)
issueCredential = IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage)
@@ -372,7 +398,7 @@ object CredentialRepositorySpecSuite {
}
},
test("updateFail (fail one retry) updates record") {
- val aRecord = issueCredentialRecord
+ val aRecord = issueCredentialRecord(CredentialFormat.JWT)
val failReason = Some("Just to test")
for {
@@ -402,7 +428,7 @@ object CredentialRepositorySpecSuite {
}
},
test("updateFail (fail all retry) updates record") {
- val aRecord = issueCredentialRecord
+ val aRecord = issueCredentialRecord(CredentialFormat.JWT)
for {
repo <- ZIO.service[CredentialRepository]
@@ -443,8 +469,8 @@ object CredentialRepositorySpecSuite {
val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2))
for {
repo <- ZIO.service[CredentialRepository]
- record1 = issueCredentialRecord
- record2 = issueCredentialRecord
+ record1 = issueCredentialRecord(CredentialFormat.JWT)
+ record2 = issueCredentialRecord(CredentialFormat.JWT)
count1 <- repo.createIssueCredentialRecord(record1).provide(wallet1)
count2 <- repo.createIssueCredentialRecord(record2).provide(wallet2)
ownWalletRecords1 <- repo.getIssueCredentialRecords(false).provide(wallet1)
@@ -466,8 +492,8 @@ object CredentialRepositorySpecSuite {
val newState = IssueCredentialRecord.ProtocolState.OfferReceived
for {
repo <- ZIO.service[CredentialRepository]
- record1 = issueCredentialRecord
- record2 = issueCredentialRecord
+ record1 = issueCredentialRecord(CredentialFormat.JWT)
+ record2 = issueCredentialRecord(CredentialFormat.JWT)
count1 <- repo.createIssueCredentialRecord(record1).provide(wallet1)
update1 <- repo.updateWithSubjectId(record2.id, "my-id", newState).provide(wallet2)
update2 <- repo.updateAfterFail(record2.id, Some("fail reason")).provide(wallet2)
@@ -486,7 +512,7 @@ object CredentialRepositorySpecSuite {
val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2))
for {
repo <- ZIO.service[CredentialRepository]
- record1 = issueCredentialRecord
+ record1 = issueCredentialRecord(CredentialFormat.JWT)
count1 <- repo.createIssueCredentialRecord(record1).provide(wallet1)
delete1 <- repo.deleteIssueCredentialRecord(record1.id).provide(wallet2)
} yield assert(count1)(equalTo(1)) && assert(delete1)(isZero)
@@ -498,8 +524,8 @@ object CredentialRepositorySpecSuite {
val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2))
for {
repo <- ZIO.service[CredentialRepository]
- record1 = issueCredentialRecord
- record2 = issueCredentialRecord
+ record1 = issueCredentialRecord(CredentialFormat.JWT)
+ record2 = issueCredentialRecord(CredentialFormat.JWT)
count1 <- repo.createIssueCredentialRecord(record1).provide(wallet1)
count2 <- repo.createIssueCredentialRecord(record2).provide(wallet2)
_ <- repo
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/PresentationRepositorySpecSuite.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/PresentationRepositorySpecSuite.scala
index f1e0ed5b7d..b396ee05f5 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/PresentationRepositorySpecSuite.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/repository/PresentationRepositorySpecSuite.scala
@@ -4,8 +4,8 @@ import io.iohk.atala.mercury.model.DidId
import io.iohk.atala.mercury.protocol.presentproof.{Presentation, ProposePresentation, RequestPresentation}
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.PresentationRecord.*
-import io.iohk.atala.shared.models.WalletAccessContext
-import io.iohk.atala.shared.models.WalletId
+import io.iohk.atala.pollux.core.service.serdes.{AnoncredCredentialProofV1, AnoncredCredentialProofsV1}
+import io.iohk.atala.shared.models.{WalletAccessContext, WalletId}
import zio.test.*
import zio.test.Assertion.*
import zio.{ZIO, ZLayer}
@@ -31,6 +31,8 @@ object PresentationRepositorySpecSuite {
proposePresentationData = None,
presentationData = None,
credentialsToUse = None,
+ anoncredCredentialsToUseJsonSchemaId = None,
+ anoncredCredentialsToUse = None,
metaRetries = maxRetries,
metaNextRetry = Some(Instant.now()),
metaLastFailure = None,
@@ -195,6 +197,34 @@ object PresentationRepositorySpecSuite {
assertTrue(records.exists(_.credentialsToUse.contains(Seq("credential1", "credential2"))))
}
},
+ test("updatePresentationWithCredentialsToUse updates the record") {
+ for {
+ repo <- ZIO.service[PresentationRepository]
+ aRecord = presentationRecord
+ bRecord = presentationRecord
+ cRecord = presentationRecord
+ _ <- repo.createPresentationRecord(aRecord)
+ _ <- repo.createPresentationRecord(bRecord)
+ _ <- repo.createPresentationRecord(cRecord)
+ anoncredCredentialProofs = AnoncredCredentialProofsV1(
+ List(AnoncredCredentialProofV1("credential1", Seq("requestedAttribute"), Seq("requestedPredicate")))
+ )
+ anoncredCredentialProofsJson <- ZIO.fromEither(
+ AnoncredCredentialProofsV1.schemaSerDes.serialize(anoncredCredentialProofs)
+ )
+ _ <- repo.updateAnoncredPresentationWithCredentialsToUse(
+ aRecord.id,
+ Some(AnoncredCredentialProofsV1.version),
+ Some(anoncredCredentialProofsJson),
+ ProtocolState.PresentationPending
+ )
+ records <- repo.getPresentationRecord(aRecord.id)
+ } yield {
+ assertTrue(records.size == 1) &&
+ assertTrue(records.exists(_.anoncredCredentialsToUse.contains(anoncredCredentialProofsJson))) &&
+ assertTrue(records.exists(_.anoncredCredentialsToUseJsonSchemaId.contains(AnoncredCredentialProofsV1.version)))
+ }
+ },
test("updateCredentialRecordProtocolState updates the record") {
for {
repo <- ZIO.service[PresentationRepository]
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala
index 7707cb107f..94840909fb 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceImplSpec.scala
@@ -9,7 +9,7 @@ import io.iohk.atala.castor.core.service.MockDIDService
import io.iohk.atala.mercury.model
import io.iohk.atala.mercury.model.*
import io.iohk.atala.mercury.protocol.issuecredential.*
-import io.iohk.atala.pollux.anoncreds.Credential
+import io.iohk.atala.pollux.anoncreds.AnoncredCredential
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.{ProtocolState, Role}
import io.iohk.atala.pollux.core.model.error.CredentialServiceError
@@ -82,7 +82,7 @@ object CredentialServiceImplSpec extends MockSpecDefault with CredentialServiceS
} yield {
assertTrue(record.thid == thid) &&
assertTrue(record.updatedAt.isEmpty) &&
- assertTrue(record.schemaId.isEmpty) &&
+ assertTrue(record.schemaUri.isEmpty) &&
assertTrue(record.validityPeriod == validityPeriod) &&
assertTrue(record.automaticIssuance == automaticIssuance) &&
assertTrue(record.role == Role.Issuer) &&
@@ -158,7 +158,7 @@ object CredentialServiceImplSpec extends MockSpecDefault with CredentialServiceS
assertTrue(record.thid == thid) &&
assertTrue(record.updatedAt.isEmpty) &&
assertTrue(
- record.schemaId.contains("resource:///vc-schema-example.json")
+ record.schemaUri.contains("resource:///vc-schema-example.json")
) &&
assertTrue(record.validityPeriod == validityPeriod) &&
assertTrue(record.automaticIssuance == automaticIssuance) &&
@@ -290,7 +290,7 @@ object CredentialServiceImplSpec extends MockSpecDefault with CredentialServiceS
} yield {
assertTrue(holderRecord.thid.toString == offer.thid.get) &&
assertTrue(holderRecord.updatedAt.isEmpty) &&
- assertTrue(holderRecord.schemaId.isEmpty) &&
+ assertTrue(holderRecord.schemaUri.isEmpty) &&
assertTrue(holderRecord.validityPeriod.isEmpty) &&
assertTrue(holderRecord.automaticIssuance.isEmpty) &&
assertTrue(holderRecord.role == Role.Holder) &&
@@ -605,7 +605,7 @@ object CredentialServiceImplSpec extends MockSpecDefault with CredentialServiceS
assertTrue(record.issueCredentialData.get.attachments.head.data match
case model.Base64(value) =>
val ba = new String(Base64.getUrlDecoder.decode(value))
- Credential(ba).credDefId == credDefId
+ AnoncredCredential(ba).credDefId == credDefId
case _ => false
)
}
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifierSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifierSpec.scala
index b2babb80ca..4bbba5c2b0 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifierSpec.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/CredentialServiceNotifierSpec.scala
@@ -21,6 +21,7 @@ object CredentialServiceNotifierSpec extends MockSpecDefault with CredentialServ
DidCommID(),
None,
None,
+ None,
CredentialFormat.JWT,
IssueCredentialRecord.Role.Issuer,
None,
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala
index c6ea4e6b49..c65219bc37 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/LinkSecretServiceImplSpec.scala
@@ -2,7 +2,7 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
-import io.iohk.atala.pollux.anoncreds.LinkSecret
+import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecret
import io.iohk.atala.shared.models.WalletId.*
import io.iohk.atala.shared.models.{WalletAccessContext, WalletId}
import zio.*
@@ -30,7 +30,7 @@ object LinkSecretServiceImplSpec extends ZIOSpecDefault {
record1 <- svc.fetchOrCreate()
storage <- ZIO.service[GenericSecretStorage]
maybeDidSecret <- storage
- .get[String, LinkSecret](LinkSecretServiceImpl.defaultLinkSecretId)
+ .get[String, AnoncredLinkSecret](LinkSecretServiceImpl.defaultLinkSecretId)
} yield {
assertTrue(record.id == LinkSecretServiceImpl.defaultLinkSecretId)
assertTrue(record == record1)
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifierSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifierSpec.scala
index 06f5751e11..f08aa46f16 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifierSpec.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceNotifierSpec.scala
@@ -2,7 +2,7 @@ package io.iohk.atala.pollux.core.service
import io.iohk.atala.event.notification.{EventNotificationService, EventNotificationServiceImpl}
import io.iohk.atala.mercury.model.DidId
-import io.iohk.atala.mercury.protocol.presentproof.{Presentation, RequestPresentation}
+import io.iohk.atala.mercury.protocol.presentproof.{PresentCredentialRequestFormat, Presentation, RequestPresentation}
import io.iohk.atala.pollux.core.model.PresentationRecord.ProtocolState
import io.iohk.atala.pollux.core.model.{CredentialFormat, DidCommID, PresentationRecord}
import zio.mock.Expectation
@@ -30,13 +30,15 @@ object PresentationServiceNotifierSpec extends ZIOSpecDefault with PresentationS
None,
None,
None,
+ None,
+ None,
5,
None,
None
)
private val verifierHappyFlowExpectations =
- MockPresentationService.CreatePresentationRecord(
+ MockPresentationService.CreateJwtPresentationRecord(
assertion = Assertion.anything,
result = Expectation.value(record)
) ++
@@ -100,14 +102,13 @@ object PresentationServiceNotifierSpec extends ZIOSpecDefault with PresentationS
svc <- ZIO.service[PresentationService]
ens <- ZIO.service[EventNotificationService]
- record <- svc.createPresentationRecord(
+ record <- svc.createJwtPresentationRecord(
DidId(""),
DidId(""),
DidCommID(""),
None,
Seq.empty,
- None,
- format = CredentialFormat.JWT,
+ None
)
_ <- svc.markRequestPresentationSent(record.id)
_ <- svc.receivePresentation(presentation(record.thid.value))
@@ -170,7 +171,7 @@ object PresentationServiceNotifierSpec extends ZIOSpecDefault with PresentationS
svc <- ZIO.service[PresentationService]
ens <- ZIO.service[EventNotificationService]
- _ <- svc.receiveRequestPresentation(None, requestPresentationJWT)
+ _ <- svc.receiveRequestPresentation(None, requestPresentation(PresentCredentialRequestFormat.JWT))
_ <- svc.acceptRequestPresentation(record.id, Seq.empty)
_ <- svc.markPresentationGenerated(record.id, presentation(record.thid.value))
_ <- svc.markPresentationSent(record.id)
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 94135bc62d..1032557c38 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
@@ -2,33 +2,47 @@ package io.iohk.atala.pollux.core.service
import io.circe.parser.decode
import io.circe.syntax.*
-import io.iohk.atala.mercury.model.{AttachmentDescriptor, DidId}
-import io.iohk.atala.mercury.protocol.issuecredential.IssueCredential
+import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
+import io.iohk.atala.mercury.model.{AttachmentDescriptor, Base64, DidId}
+import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, IssueCredentialIssuedFormat}
import io.iohk.atala.mercury.protocol.presentproof.*
+import io.iohk.atala.pollux.anoncreds.*
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.IssueCredentialRecord.*
import io.iohk.atala.pollux.core.model.PresentationRecord.*
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.error.PresentationError.*
import io.iohk.atala.pollux.core.model.presentation.Options
+import io.iohk.atala.pollux.core.model.schema.CredentialDefinition.Input
+import io.iohk.atala.pollux.core.model.secret.CredentialDefinitionSecret
import io.iohk.atala.pollux.core.repository.{CredentialRepository, PresentationRepository}
+import io.iohk.atala.pollux.core.service.serdes.{
+ AnoncredCredentialProofV1,
+ AnoncredCredentialProofsV1,
+ AnoncredPresentationRequestV1,
+ AnoncredPresentationV1
+}
import io.iohk.atala.pollux.vc.jwt.*
+import io.iohk.atala.shared.models.{WalletAccessContext, WalletId}
import zio.*
import zio.test.*
import zio.test.Assertion.*
-import java.time.Instant
-import io.iohk.atala.shared.models.WalletId
-import io.iohk.atala.shared.models.WalletAccessContext
+import java.nio.charset.StandardCharsets
+import java.nio.file.{Files, Path, Paths}
+import java.time.{Instant, OffsetDateTime}
+import java.util.{UUID, Base64 as JBase64}
object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSpecHelper {
- override def spec =
- suite("PresentationService")(singleWalletSpec, multiWalletSpec).provide(presentationServiceLayer)
+ override def spec: Spec[Any, Any] =
+ suite("PresentationService")(singleWalletSpec, multiWalletSpec).provide(
+ presentationServiceLayer ++ genericSecretStorageLayer
+ )
private val singleWalletSpec =
suite("singleWalletSpec")(
- test("createPresentationRecord creates a valid PresentationRecord") {
+ test("createPresentationRecord creates a valid JWT PresentationRecord") {
val didGen = for {
suffix <- Gen.stringN(10)(Gen.alphaNumericChar)
} yield DidId("did:peer:" + suffix)
@@ -45,7 +59,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
} yield Options(challenge, domain)
check(
- Gen.uuid.map(e => DidCommID(e.toString())),
+ Gen.uuid.map(e => DidCommID(e.toString)),
Gen.option(Gen.string),
Gen.listOfBounded(1, 5)(proofTypeGen),
Gen.option(optionsGen)
@@ -54,14 +68,13 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
svc <- ZIO.service[PresentationService]
pairwiseVerifierDid = DidId("did:peer:Verifier")
pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createPresentationRecord(
+ record <- svc.createJwtPresentationRecord(
pairwiseVerifierDid,
pairwiseProverDid,
thid,
connectionId,
proofTypes,
- options,
- format = CredentialFormat.JWT,
+ options
)
} yield {
assertTrue(record.thid == thid) &&
@@ -75,7 +88,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
assertTrue(record.requestPresentationData.get.body.goal_code.contains("Request Proof Presentation")) &&
assertTrue(record.requestPresentationData.get.body.proof_types == proofTypes) &&
assertTrue(
- if (record.requestPresentationData.get.attachments.length != 0) {
+ if (record.requestPresentationData.get.attachments.nonEmpty) {
val maybePresentationOptions =
record.requestPresentationData.get.attachments.headOption
.map(attachment =>
@@ -100,12 +113,75 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
}
}
},
+ test("createPresentationRecord creates a valid Anoncred PresentationRecord") {
+ check(
+ Gen.uuid.map(e => DidCommID(e.toString)),
+ Gen.option(Gen.string),
+ Gen.string,
+ Gen.string,
+ Gen.string
+ ) { (thid, connectionId, name, nonce, version) =>
+ for {
+ svc <- ZIO.service[PresentationService]
+ pairwiseVerifierDid = DidId("did:peer:Verifier")
+ pairwiseProverDid = DidId("did:peer:Prover")
+ anoncredPresentationRequestV1 = AnoncredPresentationRequestV1(
+ Map.empty,
+ Map.empty,
+ name,
+ nonce,
+ version,
+ None
+ )
+ record <-
+ svc.createAnoncredPresentationRecord(
+ pairwiseVerifierDid,
+ pairwiseProverDid,
+ thid,
+ connectionId,
+ anoncredPresentationRequestV1
+ )
+ } yield {
+ assertTrue(record.thid == thid) &&
+ assertTrue(record.updatedAt.isEmpty) &&
+ assertTrue(record.connectionId == connectionId) &&
+ assertTrue(record.role == PresentationRecord.Role.Verifier) &&
+ assertTrue(record.protocolState == PresentationRecord.ProtocolState.RequestPending) &&
+ assertTrue(record.requestPresentationData.isDefined) &&
+ assertTrue(record.requestPresentationData.get.to == pairwiseProverDid) &&
+ assertTrue(record.requestPresentationData.get.thid.contains(thid.toString)) &&
+ assertTrue(record.requestPresentationData.get.body.goal_code.contains("Request Proof Presentation")) &&
+ assertTrue(
+ record.requestPresentationData.get.attachments.map(_.media_type) == Seq(Some("application/json"))
+ ) &&
+ assertTrue(
+ record.requestPresentationData.get.attachments.map(_.format) == Seq(
+ Some(PresentCredentialRequestFormat.Anoncred.name)
+ )
+ ) &&
+ assertTrue(
+ record.requestPresentationData.get.attachments.map(_.data) ==
+ Seq(
+ Base64(
+ JBase64.getUrlEncoder.encodeToString(
+ AnoncredPresentationRequestV1.schemaSerDes
+ .serializeToJsonString(anoncredPresentationRequestV1)
+ .getBytes()
+ )
+ )
+ )
+ ) &&
+ assertTrue(record.proposePresentationData.isEmpty) &&
+ assertTrue(record.presentationData.isEmpty) &&
+ assertTrue(record.credentialsToUse.isEmpty)
+ }
+ }
+ },
test("getPresentationRecords returns created PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record1 <- svc.createRecord()
- record2 <- svc.createRecord()
+ _ <- svc.createJwtRecord()
+ _ <- svc.createJwtRecord()
records <- svc.getPresentationRecords(false)
} yield {
assertTrue(records.size == 2)
@@ -114,7 +190,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
test("getPresentationRecordsByStates returns the correct records") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
records <- svc.getPresentationRecordsByStates(
ignoreWithZeroRetries = true,
limit = 10,
@@ -132,23 +208,23 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
test("getPresentationRecord returns the correct record") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
- bRecord <- svc.createRecord()
+ _ <- svc.createJwtRecord()
+ bRecord <- svc.createJwtRecord()
record <- svc.getPresentationRecord(bRecord.id)
} yield assertTrue(record.contains(bRecord))
},
test("getPresentationRecord returns nothing for an unknown 'recordId'") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
- bRecord <- svc.createRecord()
+ _ <- svc.createJwtRecord()
+ _ <- svc.createJwtRecord()
record <- svc.getPresentationRecord(DidCommID())
} yield assertTrue(record.isEmpty)
},
- test("createPresentationPayloadFromRecord returns jwt prsentation payload") {
+ test("createJwtPresentationPayloadFromRecord returns jwt presentation payload") {
for {
repo <- ZIO.service[CredentialRepository]
- aIssueCredentialRecord = issueCredentialRecord
+ aIssueCredentialRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aIssueCredentialRecord)
rawCredentialData =
"""{"base64":"ZXlKaGJHY2lPaUpGVXpJMU5rc2lMQ0owZVhBaU9pSktWMVFpZlEuZXlKcFlYUWlPakUyTnprek1qYzROaklzSW1GMVpDSTZJbVJ2YldGcGJpSXNJbTV2Ym1ObElqb2lZMlk1T1RJMk56Z3RPREV3TmkwME1EZzVMV0UxWXprdE5tTmhObU0wWkRBMU1HVTBJaXdpZG5BaU9uc2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZMeTkzZDNjdWR6TXViM0puTHpJd01UZ3ZjSEpsYzJWdWRHRjBhVzl1Y3k5Mk1TSmRMQ0owZVhCbElqcGJJbFpsY21sbWFXRmliR1ZRY21WelpXNTBZWFJwYjI0aVhYMHNJbWx6Y3lJNkltUnBaRHB3Y21semJUcGhaR0psT1RJNE9XUXdZelZtWWpVMlptWmhOVEF6T0Rka01UZ3dOR0ZpTkdFeE5UYzJOVEkzWXprME5tRTFNalV5T0RFM1ptRTRaVGhoTW1OalpXUXdPa056YzBKRGMyZENSVzFKUzBSWE1XaGpNMUpzWTJsb2NHSnRVbXhsUTJ0UlFWVktVRU5uYkZSYVYwNTNUV3BWTW1GNlJWTkpSUzFNYVVkTU0xRklaRlZ1VG10d1dXSkthSE5VYTIxWVVGaEpVM0ZXZWpjMll6RlZPWGhvVURseWNFZHBSSEZXTlRselJYcEtWbEpEYWxJMGEwMHdaMGg0YkhWUU5tVk5Ta2wwZHpJMk4yWllWbEpoTUhoRE5XaEthVU5uTVhSWldFNHdXbGhKYjJGWE5XdGFXR2R3UlVGU1ExUjNiMHBWTWxacVkwUkpNVTV0YzNoRmFVSlFhVFJvYVRrd1FqTldTbnBhUzFkSGVWbGlSVFZLYkhveGVVVnhiR010TFc1T1ZsQmpXVlJmWVRaU2IyYzJiR1ZtWWtKTmVWWlZVVzh3WlVwRVRrbENPRnBpYWkxdWFrTlRUR05PZFhVek1URlZWM1JOVVhWWkluMC5CcmFpbEVXa2VlSXhWbjY3dnpkVHZGTXpBMV9oNzFoaDZsODBHRFBpbkRaVVB4ajAxSC0tUC1QZDIxTk9wRDd3am51SDkxdUNBOFZMUW9fS2FnVjlnQQo="}"""
@@ -159,7 +235,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
IssueCredentialRecord.ProtocolState.CredentialReceived
)
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationWithCredentialsToUse(
aRecord.id,
@@ -167,16 +243,48 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
PresentationRecord.ProtocolState.RequestPending
)
issuer = createIssuer(DID("did:prism:issuer"))
- aPresentationPayload <- svc.createPresentationPayloadFromRecord(aRecord.id, issuer, Instant.now())
+ aPresentationPayload <- svc.createJwtPresentationPayloadFromRecord(aRecord.id, issuer, Instant.now())
} yield {
assertTrue(aPresentationPayload.toJwtPresentationPayload.iss == "did:prism:issuer")
}
},
+ test("createAnoncredPresentationPayloadFromRecord returns Anoncred presentation payload") {
+ for {
+ presentationWithRecord <- createAnoncredPresentation
+ (presentation, _) = presentationWithRecord
+ serializedPresentation <- presentation.attachments.head.data match {
+ case Base64(data) => ZIO.succeed(AnoncredPresentation(new String(JBase64.getUrlDecoder.decode(data))))
+ case _ => ZIO.fail(InvalidAnoncredPresentation("Expecting Base64-encoded data"))
+ }
+ validation <- AnoncredPresentationV1.schemaSerDes.validate(serializedPresentation.data)
+ presentation <- AnoncredPresentationV1.schemaSerDes.deserialize(serializedPresentation.data)
+ } yield {
+ assert(validation)(isUnit)
+ assert(
+ presentation.proof.proofs.headOption.flatMap(_.primary_proof.eq_proof.revealed_attrs.headOption.map(_._1))
+ )(isSome(equalTo("sex")))
+ }
+ },
+ test("verify anoncred presentation") {
+ for {
+ presentationWithRecord <- createAnoncredPresentation
+ (presentation, aRecord) = presentationWithRecord
+ svc <- ZIO.service[PresentationService]
+ _ <- svc.receivePresentation(presentation)
+ validateRecord <-
+ svc.verifyAnoncredPresentation(
+ presentation,
+ aRecord.requestPresentationData.get,
+ aRecord.id
+ )
+ } yield {
+ assert(validateRecord.protocolState)(equalTo(PresentationRecord.ProtocolState.PresentationVerified))
+ }
+ },
test("markRequestPresentationSent returns updated PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createRecord()
+ record <- svc.createJwtRecord()
record <- svc.markRequestPresentationSent(record.id)
} yield {
@@ -186,8 +294,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
test("markRequestPresentationRejected returns updated PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createRecord()
+ record <- svc.createJwtRecord()
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationRecordProtocolState(
record.id,
@@ -257,7 +364,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
fails(equalTo(UnsupportedCredentialFormat(vcFormat = "Some/UnsupportedCredentialFormat")))
)
},
- test("receiveRequestPresentation updates the RequestPresentation in PresentatinRecord") {
+ test("receiveRequestPresentation JWT updates the RequestPresentation in PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
connectionId = Some("connectionId")
@@ -285,13 +392,78 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
} yield {
assertTrue(aRecord.connectionId == connectionId) &&
assertTrue(aRecord.protocolState == PresentationRecord.ProtocolState.RequestReceived) &&
- assertTrue(aRecord.requestPresentationData == Some(requestPresentation))
+ assertTrue(aRecord.requestPresentationData.contains(requestPresentation))
+ }
+ },
+ test("receiveRequestPresentation Anoncred updates the RequestPresentation in PresentationRecord") {
+ val anoncredPresentationRequestV1 = AnoncredPresentationRequestV1(
+ Map.empty,
+ Map.empty,
+ "name",
+ "nonce",
+ "version",
+ None
+ )
+ val attachmentDescriptor = AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(PresentCredentialRequestFormat.Anoncred.name),
+ payload =
+ AnoncredPresentationRequestV1.schemaSerDes.serializeToJsonString(anoncredPresentationRequestV1).getBytes()
+ )
+ val connectionId = Some("connectionId")
+ for {
+ svc <- ZIO.service[PresentationService]
+ requestPresentationWithRecord <- receiveRequestPresentationTest(attachmentDescriptor)
+ (_, requestPresentation) = requestPresentationWithRecord
+ aRecord <- svc.receiveRequestPresentation(connectionId, requestPresentation)
+
+ } yield {
+ assertTrue(aRecord.connectionId == connectionId) &&
+ assertTrue(aRecord.protocolState == PresentationRecord.ProtocolState.RequestReceived) &&
+ assertTrue(aRecord.requestPresentationData.contains(requestPresentation))
}
},
- test("acceptRequestPresentation updates the PresentatinRecord JWT") {
+ test("receiveRequestPresentation Anoncred should fail given invalid attachment") {
+
+ val presentationAttachmentAsJson =
+ """{
+ "challenge": "1f44d55f-f161-4938-a659-f8026467f126",
+ "domain": "us.gov/DriverLicense",
+ "credential_manifest": {}
+ }"""
+
+ val attachmentDescriptor = AttachmentDescriptor.buildJsonAttachment(
+ payload = presentationAttachmentAsJson,
+ format = Some(PresentCredentialProposeFormat.Anoncred.name)
+ )
+ for {
+ requestPresentation <- receiveRequestPresentationTest(attachmentDescriptor).exit
+ } yield assert(requestPresentation)(
+ fails(isSubtype[InvalidAnoncredPresentationRequest](anything))
+ )
+ },
+ test("receiveRequestPresentation Anoncred should fail given invalid anoncred format") {
+ val presentationAttachmentAsJson =
+ """{
+ "challenge": "1f44d55f-f161-4938-a659-f8026467f126",
+ "domain": "us.gov/DriverLicense",
+ "credential_manifest": {}
+ }"""
+ val attachmentDescriptor = AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(PresentCredentialRequestFormat.Anoncred.name),
+ payload = presentationAttachmentAsJson.getBytes()
+ )
+ for {
+ requestPresentation <- receiveRequestPresentationTest(attachmentDescriptor).exit
+ } yield assert(requestPresentation)(
+ fails(isSubtype[InvalidAnoncredPresentationRequest](anything))
+ )
+ },
+ test("acceptRequestPresentation updates the PresentationRecord JWT") {
for {
repo <- ZIO.service[CredentialRepository]
- aIssueCredentialRecord = issueCredentialRecord
+ aIssueCredentialRecord = issueCredentialRecord(CredentialFormat.JWT)
_ <- repo.createIssueCredentialRecord(aIssueCredentialRecord)
rawCredentialData =
"""{"base64":"ZXlKaGJHY2lPaUpGVXpJMU5rc2lMQ0owZVhBaU9pSktWMVFpZlEuZXlKcFlYUWlPakUyTnprek1qYzROaklzSW1GMVpDSTZJbVJ2YldGcGJpSXNJbTV2Ym1ObElqb2lZMlk1T1RJMk56Z3RPREV3TmkwME1EZzVMV0UxWXprdE5tTmhObU0wWkRBMU1HVTBJaXdpZG5BaU9uc2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZMeTkzZDNjdWR6TXViM0puTHpJd01UZ3ZjSEpsYzJWdWRHRjBhVzl1Y3k5Mk1TSmRMQ0owZVhCbElqcGJJbFpsY21sbWFXRmliR1ZRY21WelpXNTBZWFJwYjI0aVhYMHNJbWx6Y3lJNkltUnBaRHB3Y21semJUcGhaR0psT1RJNE9XUXdZelZtWWpVMlptWmhOVEF6T0Rka01UZ3dOR0ZpTkdFeE5UYzJOVEkzWXprME5tRTFNalV5T0RFM1ptRTRaVGhoTW1OalpXUXdPa056YzBKRGMyZENSVzFKUzBSWE1XaGpNMUpzWTJsb2NHSnRVbXhsUTJ0UlFWVktVRU5uYkZSYVYwNTNUV3BWTW1GNlJWTkpSUzFNYVVkTU0xRklaRlZ1VG10d1dXSkthSE5VYTIxWVVGaEpVM0ZXZWpjMll6RlZPWGhvVURseWNFZHBSSEZXTlRselJYcEtWbEpEYWxJMGEwMHdaMGg0YkhWUU5tVk5Ta2wwZHpJMk4yWllWbEpoTUhoRE5XaEthVU5uTVhSWldFNHdXbGhKYjJGWE5XdGFXR2R3UlVGU1ExUjNiMHBWTWxacVkwUkpNVTV0YzNoRmFVSlFhVFJvYVRrd1FqTldTbnBhUzFkSGVWbGlSVFZLYkhveGVVVnhiR010TFc1T1ZsQmpXVlJmWVRaU2IyYzJiR1ZtWWtKTmVWWlZVVzh3WlVwRVRrbENPRnBpYWkxdWFrTlRUR05PZFhVek1URlZWM1JOVVhWWkluMC5CcmFpbEVXa2VlSXhWbjY3dnpkVHZGTXpBMV9oNzFoaDZsODBHRFBpbkRaVVB4ajAxSC0tUC1QZDIxTk9wRDd3am51SDkxdUNBOFZMUW9fS2FnVjlnQQo="}"""
@@ -304,7 +476,10 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
svc <- ZIO.service[PresentationService]
connectionId = Some("connectionId")
- aRecord <- svc.receiveRequestPresentation(connectionId, requestPresentationJWT)
+ aRecord <- svc.receiveRequestPresentation(
+ connectionId,
+ requestPresentation(PresentCredentialRequestFormat.JWT)
+ )
credentialsToUse = Seq(aIssueCredentialRecord.id.value)
updateRecord <- svc.acceptRequestPresentation(aRecord.id, credentialsToUse)
@@ -314,11 +489,114 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
assertTrue(updateRecord.credentialsToUse.contains(credentialsToUse))
}
},
- test("rejectRequestPresentation updates the PresentatinRecord") {
+ test("acceptRequestPresentation updates the PresentationRecord AnonCreds") {
for {
+ repo <- ZIO.service[CredentialRepository]
+ aIssueCredentialRecord = issueCredentialRecord(CredentialFormat.AnonCreds)
+ _ <- repo.createIssueCredentialRecord(aIssueCredentialRecord)
+ rawCredentialData =
+ """{"base64":"ZXlKaGJHY2lPaUpGVXpJMU5rc2lMQ0owZVhBaU9pSktWMVFpZlEuZXlKcFlYUWlPakUyTnprek1qYzROaklzSW1GMVpDSTZJbVJ2YldGcGJpSXNJbTV2Ym1ObElqb2lZMlk1T1RJMk56Z3RPREV3TmkwME1EZzVMV0UxWXprdE5tTmhObU0wWkRBMU1HVTBJaXdpZG5BaU9uc2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZMeTkzZDNjdWR6TXViM0puTHpJd01UZ3ZjSEpsYzJWdWRHRjBhVzl1Y3k5Mk1TSmRMQ0owZVhCbElqcGJJbFpsY21sbWFXRmliR1ZRY21WelpXNTBZWFJwYjI0aVhYMHNJbWx6Y3lJNkltUnBaRHB3Y21semJUcGhaR0psT1RJNE9XUXdZelZtWWpVMlptWmhOVEF6T0Rka01UZ3dOR0ZpTkdFeE5UYzJOVEkzWXprME5tRTFNalV5T0RFM1ptRTRaVGhoTW1OalpXUXdPa056YzBKRGMyZENSVzFKUzBSWE1XaGpNMUpzWTJsb2NHSnRVbXhsUTJ0UlFWVktVRU5uYkZSYVYwNTNUV3BWTW1GNlJWTkpSUzFNYVVkTU0xRklaRlZ1VG10d1dXSkthSE5VYTIxWVVGaEpVM0ZXZWpjMll6RlZPWGhvVURseWNFZHBSSEZXTlRselJYcEtWbEpEYWxJMGEwMHdaMGg0YkhWUU5tVk5Ta2wwZHpJMk4yWllWbEpoTUhoRE5XaEthVU5uTVhSWldFNHdXbGhKYjJGWE5XdGFXR2R3UlVGU1ExUjNiMHBWTWxacVkwUkpNVTV0YzNoRmFVSlFhVFJvYVRrd1FqTldTbnBhUzFkSGVWbGlSVFZLYkhveGVVVnhiR010TFc1T1ZsQmpXVlJmWVRaU2IyYzJiR1ZtWWtKTmVWWlZVVzh3WlVwRVRrbENPRnBpYWkxdWFrTlRUR05PZFhVek1URlZWM1JOVVhWWkluMC5CcmFpbEVXa2VlSXhWbjY3dnpkVHZGTXpBMV9oNzFoaDZsODBHRFBpbkRaVVB4ajAxSC0tUC1QZDIxTk9wRDd3am51SDkxdUNBOFZMUW9fS2FnVjlnQQo="}"""
+ _ <- repo.updateWithIssuedRawCredential(
+ aIssueCredentialRecord.id,
+ IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage),
+ rawCredentialData,
+ IssueCredentialRecord.ProtocolState.CredentialReceived
+ )
+ svc <- ZIO.service[PresentationService]
+ connectionId = Some("connectionId")
+ anoncredPresentationRequestV1 = AnoncredPresentationRequestV1(
+ Map.empty,
+ Map.empty,
+ "name",
+ "nonce",
+ "version",
+ None
+ )
+ attachmentDescriptor = AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(PresentCredentialRequestFormat.Anoncred.name),
+ payload =
+ AnoncredPresentationRequestV1.schemaSerDes.serializeToJsonString(anoncredPresentationRequestV1).getBytes()
+ )
+ requestPresentation = RequestPresentation(
+ body = RequestPresentation.Body(goal_code = Some("Presentation Request")),
+ attachments = Seq(attachmentDescriptor),
+ to = DidId("did:peer:Prover"),
+ from = DidId("did:peer:Verifier"),
+ )
+ aRecord <- svc.receiveRequestPresentation(connectionId, requestPresentation)
+ credentialsToUse =
+ AnoncredCredentialProofsV1(
+ List(
+ AnoncredCredentialProofV1(
+ aIssueCredentialRecord.id.value,
+ Seq("requestedAttribute"),
+ Seq("requestedPredicate")
+ )
+ )
+ )
+ anoncredCredentialProofsJson <- ZIO.fromEither(
+ AnoncredCredentialProofsV1.schemaSerDes.serialize(credentialsToUse)
+ )
+ updateRecord <- svc.acceptAnoncredRequestPresentation(aRecord.id, credentialsToUse)
+
+ } yield {
+ assertTrue(updateRecord.connectionId == connectionId) &&
+ assertTrue(updateRecord.anoncredCredentialsToUse.contains(anoncredCredentialProofsJson)) &&
+ assertTrue(updateRecord.anoncredCredentialsToUseJsonSchemaId.contains(AnoncredCredentialProofsV1.version))
+ }
+ },
+ test("acceptRequestPresentation should fail given unmatching format") {
+ for {
+ repo <- ZIO.service[CredentialRepository]
+ aIssueCredentialRecord = issueCredentialRecord(CredentialFormat.JWT)
+ _ <- repo.createIssueCredentialRecord(aIssueCredentialRecord)
+ rawCredentialData =
+ """{"base64":"ZXlKaGJHY2lPaUpGVXpJMU5rc2lMQ0owZVhBaU9pSktWMVFpZlEuZXlKcFlYUWlPakUyTnprek1qYzROaklzSW1GMVpDSTZJbVJ2YldGcGJpSXNJbTV2Ym1ObElqb2lZMlk1T1RJMk56Z3RPREV3TmkwME1EZzVMV0UxWXprdE5tTmhObU0wWkRBMU1HVTBJaXdpZG5BaU9uc2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZMeTkzZDNjdWR6TXViM0puTHpJd01UZ3ZjSEpsYzJWdWRHRjBhVzl1Y3k5Mk1TSmRMQ0owZVhCbElqcGJJbFpsY21sbWFXRmliR1ZRY21WelpXNTBZWFJwYjI0aVhYMHNJbWx6Y3lJNkltUnBaRHB3Y21semJUcGhaR0psT1RJNE9XUXdZelZtWWpVMlptWmhOVEF6T0Rka01UZ3dOR0ZpTkdFeE5UYzJOVEkzWXprME5tRTFNalV5T0RFM1ptRTRaVGhoTW1OalpXUXdPa056YzBKRGMyZENSVzFKUzBSWE1XaGpNMUpzWTJsb2NHSnRVbXhsUTJ0UlFWVktVRU5uYkZSYVYwNTNUV3BWTW1GNlJWTkpSUzFNYVVkTU0xRklaRlZ1VG10d1dXSkthSE5VYTIxWVVGaEpVM0ZXZWpjMll6RlZPWGhvVURseWNFZHBSSEZXTlRselJYcEtWbEpEYWxJMGEwMHdaMGg0YkhWUU5tVk5Ta2wwZHpJMk4yWllWbEpoTUhoRE5XaEthVU5uTVhSWldFNHdXbGhKYjJGWE5XdGFXR2R3UlVGU1ExUjNiMHBWTWxacVkwUkpNVTV0YzNoRmFVSlFhVFJvYVRrd1FqTldTbnBhUzFkSGVWbGlSVFZLYkhveGVVVnhiR010TFc1T1ZsQmpXVlJmWVRaU2IyYzJiR1ZtWWtKTmVWWlZVVzh3WlVwRVRrbENPRnBpYWkxdWFrTlRUR05PZFhVek1URlZWM1JOVVhWWkluMC5CcmFpbEVXa2VlSXhWbjY3dnpkVHZGTXpBMV9oNzFoaDZsODBHRFBpbkRaVVB4ajAxSC0tUC1QZDIxTk9wRDd3am51SDkxdUNBOFZMUW9fS2FnVjlnQQo="}"""
+ _ <- repo.updateWithIssuedRawCredential(
+ aIssueCredentialRecord.id,
+ IssueCredential.makeIssueCredentialFromRequestCredential(requestCredential.makeMessage),
+ rawCredentialData,
+ IssueCredentialRecord.ProtocolState.CredentialReceived
+ )
svc <- ZIO.service[PresentationService]
connectionId = Some("connectionId")
- aRecord <- svc.receiveRequestPresentation(connectionId, requestPresentationJWT)
+ anoncredPresentationRequestV1 = AnoncredPresentationRequestV1(
+ Map.empty,
+ Map.empty,
+ "name",
+ "nonce",
+ "version",
+ None
+ )
+ attachmentDescriptor = AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(PresentCredentialRequestFormat.Anoncred.name),
+ payload =
+ AnoncredPresentationRequestV1.schemaSerDes.serializeToJsonString(anoncredPresentationRequestV1).getBytes()
+ )
+ requestPresentation = RequestPresentation(
+ body = RequestPresentation.Body(goal_code = Some("Presentation Request")),
+ attachments = Seq(attachmentDescriptor),
+ to = DidId("did:peer:Prover"),
+ from = DidId("did:peer:Verifier"),
+ )
+ aRecord <- svc.receiveRequestPresentation(connectionId, requestPresentation)
+ credentialsToUse = Seq(aIssueCredentialRecord.id.value)
+ result <- svc.acceptRequestPresentation(aRecord.id, credentialsToUse).exit
+
+ } yield assert(result)(
+ fails(isSubtype[NotMatchingPresentationCredentialFormat](anything))
+ )
+ },
+ test("rejectRequestPresentation updates the PresentationRecord") {
+ for {
+ svc <- ZIO.service[PresentationService]
+ connectionId = Some("connectionId")
+ aRecord <- svc.receiveRequestPresentation(
+ connectionId,
+ requestPresentation(PresentCredentialRequestFormat.JWT)
+ )
updateRecord <- svc.rejectRequestPresentation(aRecord.id)
} yield {
@@ -330,8 +608,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
test("markPresentationSent returns updated PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createRecord()
+ _ = DidId("did:peer:Prover")
+ record <- svc.createJwtRecord()
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationRecordProtocolState(
record.id,
@@ -344,22 +622,22 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
assertTrue(record.protocolState == PresentationRecord.ProtocolState.PresentationSent)
}
},
- test("receivePresentation updates the PresentatinRecord") {
+ test("receivePresentation updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = presentation(aRecord.thid.value)
aRecordReceived <- svc.receivePresentation(p)
} yield {
assertTrue(aRecordReceived.id == aRecord.id) &&
- assertTrue(aRecordReceived.presentationData == Some(p))
+ assertTrue(aRecordReceived.presentationData.contains(p))
}
},
- test("acceptPresentation updates the PresentatinRecord") {
+ test("acceptPresentation updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = presentation(aRecord.thid.value)
aRecordReceived <- svc.receivePresentation(p)
repo <- ZIO.service[PresentationRepository]
@@ -368,16 +646,16 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
PresentationRecord.ProtocolState.PresentationReceived,
PresentationRecord.ProtocolState.PresentationVerified
)
- aRecordAccept <- svc.acceptPresentation(aRecord.id)
+ _ <- svc.acceptPresentation(aRecord.id)
} yield {
assertTrue(aRecordReceived.id == aRecord.id) &&
- assertTrue(aRecordReceived.presentationData == Some(p))
+ assertTrue(aRecordReceived.presentationData.contains(p))
}
},
- test("markPresentationRejected updates the PresentatinRecord") {
+ test("markPresentationRejected updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = presentation(aRecord.thid.value)
_ <- svc.receivePresentation(p)
repo <- ZIO.service[PresentationRepository]
@@ -389,16 +667,16 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
aRecordReject <- svc.markPresentationRejected(aRecord.id)
} yield {
assertTrue(aRecordReject.id == aRecord.id) &&
- assertTrue(aRecordReject.presentationData == Some(p)) &&
+ assertTrue(aRecordReject.presentationData.contains(p)) &&
assertTrue(aRecordReject.protocolState == PresentationRecord.ProtocolState.PresentationRejected)
}
},
- test("rejectPresentation updates the PresentatinRecord") {
+ test("rejectPresentation updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = presentation(aRecord.thid.value)
- aRecordReceived <- svc.receivePresentation(p)
+ _ <- svc.receivePresentation(p)
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationRecordProtocolState(
aRecord.id,
@@ -408,15 +686,14 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
aRecordReject <- svc.rejectPresentation(aRecord.id)
} yield {
assertTrue(aRecordReject.id == aRecord.id) &&
- assertTrue(aRecordReject.presentationData == Some(p)) &&
+ assertTrue(aRecordReject.presentationData.contains(p)) &&
assertTrue(aRecordReject.protocolState == PresentationRecord.ProtocolState.PresentationRejected)
}
},
test("markPresentationGenerated returns updated PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createRecord()
+ record <- svc.createJwtRecord()
p = presentation(record.thid.value)
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationRecordProtocolState(
@@ -432,8 +709,7 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
test("markProposePresentationSent returns updated PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- pairwiseProverDid = DidId("did:peer:Prover")
- record <- svc.createRecord()
+ record <- svc.createJwtRecord()
repo <- ZIO.service[PresentationRepository]
_ <- repo.updatePresentationRecordProtocolState(
record.id,
@@ -445,21 +721,21 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
assertTrue(record.protocolState == PresentationRecord.ProtocolState.ProposalSent)
}
},
- test("receiveProposePresentation updates the PresentatinRecord") {
+ test("receiveProposePresentation updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = proposePresentation(aRecord.thid.value)
aRecordReceived <- svc.receiveProposePresentation(p)
} yield {
assertTrue(aRecordReceived.id == aRecord.id) &&
- assertTrue(aRecordReceived.proposePresentationData == Some(p))
+ assertTrue(aRecordReceived.proposePresentationData.contains(p))
}
},
- test("acceptProposePresentation updates the PresentatinRecord") {
+ test("acceptProposePresentation updates the PresentationRecord") {
for {
svc <- ZIO.service[PresentationService]
- aRecord <- svc.createRecord()
+ aRecord <- svc.createJwtRecord()
p = proposePresentation(aRecord.thid.value)
aRecordReceived <- svc.receiveProposePresentation(p)
repo <- ZIO.service[PresentationRepository]
@@ -468,14 +744,161 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
PresentationRecord.ProtocolState.ProposalPending,
PresentationRecord.ProtocolState.ProposalReceived
)
- aRecordAccept <- svc.acceptProposePresentation(aRecord.id)
+ _ <- svc.acceptProposePresentation(aRecord.id)
} yield {
assertTrue(aRecordReceived.id == aRecord.id) &&
- assertTrue(aRecordReceived.proposePresentationData == Some(p))
+ assertTrue(aRecordReceived.proposePresentationData.contains(p))
}
},
).provideSomeLayer(ZLayer.succeed(WalletAccessContext(WalletId.random)))
+ private def receiveRequestPresentationTest(attachment: AttachmentDescriptor) = {
+ for {
+ svc <- ZIO.service[PresentationService]
+ connectionId = Some("connectionId")
+ body = RequestPresentation.Body(goal_code = Some("Presentation Request"))
+ prover = DidId("did:peer:Prover")
+ verifier = DidId("did:peer:Verifier")
+ attachmentDescriptor = attachment
+ requestPresentation = RequestPresentation(
+ body = body,
+ attachments = Seq(attachmentDescriptor),
+ to = prover,
+ from = verifier,
+ )
+ requestPresentationRecord <- svc.receiveRequestPresentation(connectionId, requestPresentation)
+ } yield (requestPresentationRecord, requestPresentation)
+ }
+
+ private def createAnoncredPresentation = {
+ for {
+ credentialDefinitionService <- ZIO.service[CredentialDefinitionService]
+ issuerId = "did:prism:issuer"
+ holderID = "did:prism:holder"
+ schemaId = "resource:///anoncred-presentation-schema-example.json"
+ credentialDefinitionDb <- credentialDefinitionService.create(
+ Input(
+ name = "Credential Definition Name",
+ description = "Credential Definition Description",
+ version = "1.2",
+ signatureType = "CL",
+ tag = "tag",
+ author = issuerId,
+ authored = Some(OffsetDateTime.parse("2022-03-10T12:00:00Z")),
+ schemaId = schemaId,
+ supportRevocation = false
+ )
+ )
+ repo <- ZIO.service[CredentialRepository]
+ linkSecretService <- ZIO.service[LinkSecretService]
+ linkSecret <- linkSecretService.fetchOrCreate()
+ genericSecretStorage <- ZIO.service[GenericSecretStorage]
+ maybeCredentialDefintionPrivate <-
+ genericSecretStorage
+ .get[UUID, CredentialDefinitionSecret](credentialDefinitionDb.guid)
+ credentialDefinition = AnoncredCreateCredentialDefinition(
+ AnoncredCredentialDefinition(credentialDefinitionDb.definition.toString()),
+ AnoncredCredentialDefinitionPrivate(maybeCredentialDefintionPrivate.get.json.toString()),
+ AnoncredCredentialKeyCorrectnessProof(credentialDefinitionDb.keyCorrectnessProof.toString())
+ )
+ file = createTempJsonFile(credentialDefinition.cd.data, "anoncred-presentation-credential-definition-example")
+ credentialDefinitionId = "resource:///" + file.getFileName
+ credentialOffer = AnoncredLib.createOffer(credentialDefinition, credentialDefinitionId)
+ credentialRequest = AnoncredLib.createCredentialRequest(linkSecret, credentialDefinition.cd, credentialOffer)
+ processedCredential =
+ AnoncredLib.processCredential(
+ AnoncredLib
+ .createCredential(
+ credentialDefinition.cd,
+ credentialDefinition.cdPrivate,
+ credentialOffer,
+ credentialRequest.request,
+ Seq(
+ ("name", "Miguel"),
+ ("sex", "M"),
+ ("age", "31"),
+ )
+ ),
+ credentialRequest.metadata,
+ linkSecret,
+ credentialDefinition.cd
+ )
+ issueCredential =
+ IssueCredential(
+ from = DidId(issuerId),
+ to = DidId(holderID),
+ body = IssueCredential.Body(),
+ attachments = Seq(
+ AttachmentDescriptor.buildBase64Attachment(
+ mediaType = Some("application/json"),
+ format = Some(IssueCredentialIssuedFormat.Anoncred.name),
+ payload = processedCredential.data.getBytes()
+ )
+ )
+ )
+ aIssueCredentialRecord =
+ IssueCredentialRecord(
+ id = DidCommID(),
+ createdAt = Instant.now,
+ updatedAt = None,
+ thid = DidCommID(),
+ schemaUri = Some(schemaId),
+ credentialDefinitionId = Some(credentialDefinitionDb.guid),
+ credentialDefinitionUri = Some(credentialDefinitionId),
+ credentialFormat = CredentialFormat.AnonCreds,
+ role = IssueCredentialRecord.Role.Issuer,
+ subjectId = None,
+ validityPeriod = None,
+ automaticIssuance = None,
+ protocolState = IssueCredentialRecord.ProtocolState.CredentialReceived,
+ offerCredentialData = None,
+ requestCredentialData = None,
+ anonCredsRequestMetadata = None,
+ issueCredentialData = Some(issueCredential),
+ issuedCredentialRaw =
+ Some(issueCredential.attachments.map(_.data.asJson.noSpaces).headOption.getOrElse("???")),
+ issuingDID = None,
+ metaRetries = 5,
+ metaNextRetry = Some(Instant.now()),
+ metaLastFailure = None,
+ )
+ _ <- repo.createIssueCredentialRecord(aIssueCredentialRecord)
+ svc <- ZIO.service[PresentationService]
+ aRecord <- svc.createAnoncredRecord(
+ credentialDefinitionId = credentialDefinitionId
+ )
+ repo <- ZIO.service[PresentationRepository]
+ credentialsToUse =
+ AnoncredCredentialProofsV1(
+ List(
+ AnoncredCredentialProofV1(
+ aIssueCredentialRecord.id.value,
+ Seq("sex"),
+ Seq("age")
+ )
+ )
+ )
+ credentialsToUseJson <- ZIO.fromEither(
+ AnoncredCredentialProofsV1.schemaSerDes.serialize(credentialsToUse)
+ )
+ _ <-
+ repo.updateAnoncredPresentationWithCredentialsToUse(
+ aRecord.id,
+ Some(AnoncredPresentationV1.version),
+ Some(credentialsToUseJson),
+ PresentationRecord.ProtocolState.RequestPending
+ )
+ issuer = createIssuer(DID("did:prism:issuer"))
+ presentation <- svc.createAnoncredPresentation(
+ aRecord.requestPresentationData.get,
+ aRecord.id,
+ issuer,
+ credentialsToUse,
+ Instant.now()
+ )
+ } yield (presentation, aRecord)
+ }
+
private val multiWalletSpec =
suite("multi-wallet spec")(
test("createPresentation for different wallet and isolate records") {
@@ -485,8 +908,8 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2))
for {
svc <- ZIO.service[PresentationService]
- record1 <- svc.createRecord().provide(wallet1)
- record2 <- svc.createRecord().provide(wallet2)
+ record1 <- svc.createJwtRecord().provide(wallet1)
+ record2 <- svc.createJwtRecord().provide(wallet2)
ownRecord1 <- svc.getPresentationRecord(record1.id).provide(wallet1)
ownRecord2 <- svc.getPresentationRecord(record2.id).provide(wallet2)
crossRecord1 <- svc.getPresentationRecord(record1.id).provide(wallet2)
@@ -498,4 +921,16 @@ object PresentationServiceSpec extends ZIOSpecDefault with PresentationServiceSp
}
)
+ def createTempJsonFile(jsonContent: String, fileName: String): Path = {
+ val resourceURI = this.getClass.getResource("/").toURI
+ val resourcePath = Paths.get(resourceURI)
+
+ val filePath = resourcePath.resolve(fileName + ".json")
+
+ Files.write(filePath, jsonContent.getBytes(StandardCharsets.UTF_8))
+
+ filePath.toFile.deleteOnExit()
+ filePath
+ }
+
}
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala
index 83b8da38ad..bbb32df467 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/PresentationServiceSpecHelper.scala
@@ -1,17 +1,16 @@
package io.iohk.atala.pollux.core.service
import com.nimbusds.jose.jwk.*
+import io.iohk.atala.agent.walletapi.memory.GenericSecretStorageInMemory
import io.iohk.atala.mercury.model.{AttachmentDescriptor, DidId}
import io.iohk.atala.mercury.protocol.presentproof.*
import io.iohk.atala.mercury.{AgentPeerService, PeerDID}
import io.iohk.atala.pollux.core.model.*
-import io.iohk.atala.pollux.core.repository.PresentationRepository
-import io.iohk.atala.pollux.core.repository.{
- CredentialRepository,
- CredentialRepositoryInMemory,
- PresentationRepositoryInMemory
-}
+import io.iohk.atala.pollux.core.model.error.PresentationError
+import io.iohk.atala.pollux.core.repository.*
+import io.iohk.atala.pollux.core.service.serdes.*
import io.iohk.atala.pollux.vc.jwt.*
+import io.iohk.atala.shared.models.{WalletAccessContext, WalletId}
import zio.*
import java.security.*
@@ -20,14 +19,28 @@ import java.util.UUID
trait PresentationServiceSpecHelper {
+ protected val defaultWalletLayer = ZLayer.succeed(WalletAccessContext(WalletId.default))
+
val peerDidAgentLayer =
AgentPeerService.makeLayer(PeerDID.makePeerDid(serviceEndpoint = Some("http://localhost:9099")))
- val presentationServiceLayer = ZLayer.make[PresentationService & PresentationRepository & CredentialRepository](
+ val genericSecretStorageLayer = GenericSecretStorageInMemory.layer
+ val uriDereferencerLayer = ResourceURIDereferencerImpl.layer
+ val credentialDefLayer =
+ CredentialDefinitionRepositoryInMemory.layer ++ uriDereferencerLayer >>> CredentialDefinitionServiceImpl.layer
+ val linkSecretLayer = genericSecretStorageLayer >+> LinkSecretServiceImpl.layer
+
+ val presentationServiceLayer = ZLayer.make[
+ PresentationService & CredentialDefinitionService & URIDereferencer & LinkSecretService & PresentationRepository &
+ CredentialRepository
+ ](
PresentationServiceImpl.layer,
+ credentialDefLayer,
+ uriDereferencerLayer,
+ linkSecretLayer,
PresentationRepositoryInMemory.layer,
CredentialRepositoryInMemory.layer
- )
+ ) ++ defaultWalletLayer
def createIssuer(did: DID) = {
val keyGen = KeyPairGenerator.getInstance("EC")
@@ -51,7 +64,7 @@ trait PresentationServiceSpecHelper {
attachments = Nil
)
- protected def requestPresentationJWT: RequestPresentation = {
+ protected def requestPresentation(credentialFormat: PresentCredentialRequestFormat): RequestPresentation = {
val body = RequestPresentation.Body(goal_code = Some("Presentation Request"))
val presentationAttachmentAsJson =
"""{
@@ -64,7 +77,7 @@ trait PresentationServiceSpecHelper {
val attachmentDescriptor = AttachmentDescriptor.buildJsonAttachment(
payload = presentationAttachmentAsJson,
- format = Some(PresentCredentialRequestFormat.JWT.name)
+ format = Some(credentialFormat.name)
)
RequestPresentation(
body = body,
@@ -114,14 +127,15 @@ trait PresentationServiceSpecHelper {
)
}
- protected def issueCredentialRecord = IssueCredentialRecord(
+ protected def issueCredentialRecord(credentialFormat: CredentialFormat) = IssueCredentialRecord(
id = DidCommID(),
createdAt = Instant.now,
updatedAt = None,
thid = DidCommID(),
- schemaId = None,
+ schemaUri = None,
credentialDefinitionId = None,
- credentialFormat = CredentialFormat.JWT,
+ credentialDefinitionUri = None,
+ credentialFormat = credentialFormat,
role = IssueCredentialRecord.Role.Issuer,
subjectId = None,
validityPeriod = None,
@@ -139,23 +153,63 @@ trait PresentationServiceSpecHelper {
)
extension (svc: PresentationService)
- def createRecord(
+ def createJwtRecord(
pairwiseVerifierDID: DidId = DidId("did:prism:issuer"),
pairwiseProverDID: DidId = DidId("did:prism:prover-pairwise"),
thid: DidCommID = DidCommID(),
- schemaId: String = "schemaId",
- connectionId: Option[String] = None,
- ) = {
+ schemaId: _root_.java.lang.String = "schemaId",
+ options: Option[io.iohk.atala.pollux.core.model.presentation.Options] = None
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
val proofType = ProofType(schemaId, None, None)
- svc.createPresentationRecord(
+ svc.createJwtPresentationRecord(
thid = thid,
pairwiseVerifierDID = pairwiseVerifierDID,
pairwiseProverDID = pairwiseProverDID,
connectionId = Some("connectionId"),
proofTypes = Seq(proofType),
- options = None,
- format = CredentialFormat.JWT,
+ options = options
)
}
+ def createAnoncredRecord(
+ credentialDefinitionId: String = "$CRED_DEF_ID",
+ pairwiseVerifierDID: DidId = DidId("did:prism:issuer"),
+ pairwiseProverDID: DidId = DidId("did:prism:prover-pairwise"),
+ thid: DidCommID = DidCommID()
+ ): ZIO[WalletAccessContext, PresentationError, PresentationRecord] = {
+ val anoncredPresentationRequestV1 = AnoncredPresentationRequestV1(
+ requested_attributes = Map(
+ "sex" -> AnoncredRequestedAttributeV1(
+ name = "sex",
+ restrictions = List(
+ Map(
+ ("attr::sex::value" -> "M"),
+ ("cred_def_id" -> credentialDefinitionId)
+ )
+ ),
+ non_revoked = None
+ )
+ ),
+ requested_predicates = Map(
+ "age" -> AnoncredRequestedPredicateV1(
+ name = "age",
+ p_type = ">=",
+ p_value = 18,
+ restrictions = List.empty,
+ non_revoked = None
+ )
+ ),
+ name = "proof_req_1",
+ nonce = "1103253414365527824079144",
+ version = "0.1",
+ non_revoked = None
+ )
+ svc.createAnoncredPresentationRecord(
+ thid = thid,
+ pairwiseVerifierDID = pairwiseVerifierDID,
+ pairwiseProverDID = pairwiseProverDID,
+ connectionId = Some("connectionId"),
+ anoncredPresentationRequestV1
+ )
+ }
}
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala
new file mode 100644
index 0000000000..85a18f3204
--- /dev/null
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationRequestSpec.scala
@@ -0,0 +1,99 @@
+package io.iohk.atala.pollux.core.service.serdes
+
+import zio.*
+import zio.test.*
+import zio.test.Assertion.*
+
+object AnoncredPresentationRequestSpec extends ZIOSpecDefault {
+ val json: String =
+ """
+ |{
+ | "requested_attributes": {
+ | "attribute1": {
+ | "name": "Attribute 1",
+ | "restrictions": [
+ | {
+ | "cred_def_id": "credential_definition_id_of_attribute1"
+ | }
+ | ],
+ | "non_revoked": {
+ | "from": 1635734400,
+ | "to": 1735734400
+ | }
+ | }
+ | },
+ | "requested_predicates": {
+ | "predicate1": {
+ | "name": "Predicate 1",
+ | "p_type": ">=",
+ | "p_value": 18,
+ | "restrictions": [
+ | {
+ | "schema_id": "schema_id_of_predicate1"
+ | }
+ | ],
+ | "non_revoked": {
+ | "from": 1635734400
+ | }
+ | }
+ | },
+ | "name": "Example Presentation Request",
+ | "nonce": "1234567890",
+ | "version": "1.0"
+ |}
+ |""".stripMargin
+
+ override def spec: Spec[TestEnvironment with Scope, Any] = suite("AnoncredPresentationRequestSerDes")(
+ test("should validate a correct schema") {
+ assertZIO(AnoncredPresentationRequestV1.schemaSerDes.validate(json))(isUnit)
+ },
+ test("should deserialize correctly") {
+ val expectedPresentationRequest =
+ AnoncredPresentationRequestV1(
+ requested_attributes = Map(
+ "attribute1" -> AnoncredRequestedAttributeV1(
+ "Attribute 1",
+ List(
+ Map(
+ "cred_def_id" -> "credential_definition_id_of_attribute1"
+ )
+ ),
+ Some(
+ AnoncredNonRevokedIntervalV1(
+ Some(1635734400),
+ Some(1735734400)
+ )
+ )
+ )
+ ),
+ requested_predicates = Map(
+ "predicate1" ->
+ AnoncredRequestedPredicateV1(
+ "Predicate 1",
+ ">=",
+ 18,
+ List(
+ Map(
+ "schema_id" -> "schema_id_of_predicate1"
+ )
+ ),
+ Some(
+ AnoncredNonRevokedIntervalV1(
+ Some(1635734400),
+ None
+ )
+ )
+ )
+ ),
+ name = "Example Presentation Request",
+ nonce = "1234567890",
+ version = "1.0",
+ non_revoked = None
+ )
+
+ assertZIO(AnoncredPresentationRequestV1.schemaSerDes.deserialize(json))(
+ Assertion.equalTo(expectedPresentationRequest)
+ )
+ }
+ )
+}
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala
new file mode 100644
index 0000000000..275bc181be
--- /dev/null
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/AnoncredPresentationSpec.scala
@@ -0,0 +1,213 @@
+package io.iohk.atala.pollux.core.service.serdes
+
+import io.iohk.atala.pollux.core.service.serdes.AnoncredPresentationV1.*
+import zio.*
+import zio.test.*
+import zio.test.Assertion.*
+
+object AnoncredPresentationSpec extends ZIOSpecDefault {
+ val json: String =
+ """
+ |{
+ | "proof": {
+ | "proofs": [
+ | {
+ | "primary_proof": {
+ | "eq_proof": {
+ | "revealed_attrs": {
+ | "sex": "4046863..."
+ | },
+ | "a_prime": "2329247...",
+ | "e": "1666946...",
+ | "v": "2137556...",
+ | "m": {
+ | "age": "1025474...",
+ | "master_secret": "8005118...",
+ | "name": "1031839..."
+ | },
+ | "m2": "5852217..."
+ | },
+ | "ge_proofs": [
+ | {
+ | "u": {
+ | "1": "1135835...",
+ | "0": "5209733...",
+ | "3": "6777506...",
+ | "2": "8790500..."
+ | },
+ | "r": {
+ | "DELTA": "1094759...",
+ | "3": "3908998...",
+ | "1": "3551550...",
+ | "2": "4594656...",
+ | "0": "7769940..."
+ | },
+ | "mj": "1025474...",
+ | "alpha": "4089177...",
+ | "t": {
+ | "0": "1208121...",
+ | "DELTA": "9078219...",
+ | "1": "5769003...",
+ | "3": "4089901...",
+ | "2": "2261914..."
+ | },
+ | "predicate": {
+ | "attr_name": "age",
+ | "p_type": "GE",
+ | "value": 18
+ | }
+ | }
+ | ]
+ | },
+ | "non_revoc_proof": null
+ | }
+ | ],
+ | "aggregated_proof": {
+ | "c_hash": "3880251...",
+ | "c_list": [
+ | [184, 131, 15],
+ | [95, 179],
+ | [1, 200, 254],
+ | [179, 45, 156],
+ | [1, 67, 251],
+ | [2, 207, 34, 43]
+ | ]
+ | }
+ | },
+ | "requested_proof": {
+ | "revealed_attrs": {
+ | "sex": {
+ | "sub_proof_index": 0,
+ | "raw": "M",
+ | "encoded": "4046863..."
+ | }
+ | },
+ | "self_attested_attrs":{},
+ | "unrevealed_attrs":{},
+ | "predicates": {
+ | "age": {
+ | "sub_proof_index": 0
+ | }
+ | }
+ | },
+ | "identifiers": [
+ | {
+ | "schema_id": "resource:///anoncred-presentation-schema-example.json",
+ | "cred_def_id": "resource:///anoncred-presentation-credential-definition-example.json",
+ | "rev_reg_id": null,
+ | "timestamp": null
+ | }
+ | ]
+ |}
+ |
+ |""".stripMargin
+
+ override def spec: Spec[TestEnvironment with Scope, Any] = suite("AnoncredPresentationRequestSerDes")(
+ test("should validate a correct schema") {
+ assertZIO(AnoncredPresentationV1.schemaSerDes.validate(json))(isUnit)
+ },
+ test("should deserialize correctly") {
+ val predicate = AnoncredPredicateV1(
+ attr_name = "age",
+ p_type = "GE",
+ value = 18
+ )
+
+ val geProof = AnoncredGeProofV1(
+ u = Map(
+ "1" -> "1135835...",
+ "0" -> "5209733...",
+ "3" -> "6777506...",
+ "2" -> "8790500..."
+ ),
+ r = Map(
+ "DELTA" -> "1094759...",
+ "3" -> "3908998...",
+ "1" -> "3551550...",
+ "2" -> "4594656...",
+ "0" -> "7769940..."
+ ),
+ mj = "1025474...",
+ alpha = "4089177...",
+ t = Map(
+ "0" -> "1208121...",
+ "DELTA" -> "9078219...",
+ "1" -> "5769003...",
+ "3" -> "4089901...",
+ "2" -> "2261914..."
+ ),
+ predicate = predicate
+ )
+
+ val eqProof = AnoncredEqProofV1(
+ revealed_attrs = Map("sex" -> "4046863..."),
+ a_prime = "2329247...",
+ e = "1666946...",
+ v = "2137556...",
+ m = Map(
+ "age" -> "1025474...",
+ "master_secret" -> "8005118...",
+ "name" -> "1031839..."
+ ),
+ m2 = "5852217..."
+ )
+
+ val primaryProof = AnoncredPrimaryProofV1(
+ eq_proof = eqProof,
+ ge_proofs = List(geProof)
+ )
+
+ val subProof = AnoncredSubProofV1(
+ primary_proof = primaryProof,
+ non_revoc_proof = None
+ )
+
+ val aggregatedProof = AnoncredAggregatedProofV1(
+ c_hash = "3880251...",
+ c_list = List(
+ List(184, 131, 15),
+ List(95, 179),
+ List(1, 200, 254),
+ List(179, 45, 156),
+ List(1, 67, 251),
+ List(2, 207, 34, 43)
+ )
+ )
+
+ val revealedAttr = AnoncredRevealedAttrV1(
+ sub_proof_index = 0,
+ raw = "M",
+ encoded = "4046863..."
+ )
+
+ val requestedProof = AnoncredRequestedProofV1(
+ revealed_attrs = Map("sex" -> revealedAttr),
+ self_attested_attrs = Map.empty,
+ unrevealed_attrs = Map.empty,
+ predicates = Map("age" -> AnoncredSubProofIndexV1(0))
+ )
+
+ val identifier = AnoncredIdentifierV1(
+ schema_id = "resource:///anoncred-presentation-schema-example.json",
+ cred_def_id = "resource:///anoncred-presentation-credential-definition-example.json",
+ rev_reg_id = None,
+ timestamp = None
+ )
+
+ val anoncredPresentationV1 = AnoncredPresentationV1(
+ proof = AnoncredProofV1(
+ proofs = List(subProof),
+ aggregated_proof = aggregatedProof
+ ),
+ requested_proof = requestedProof,
+ identifiers = List(identifier)
+ )
+
+ assert(AnoncredPresentationV1.schemaSerDes.serializeToJsonString(anoncredPresentationV1))(Assertion.equalTo(json))
+
+ assertZIO(AnoncredPresentationV1.schemaSerDes.deserialize(json))(
+ Assertion.equalTo(anoncredPresentationV1)
+ )
+ }
+ )
+}
diff --git a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/helper/PublicCredentialDefinitionSerDesSpec.scala b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala
similarity index 85%
rename from pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/helper/PublicCredentialDefinitionSerDesSpec.scala
rename to pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala
index 4a05b09fd5..aa10648519 100644
--- a/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/helper/PublicCredentialDefinitionSerDesSpec.scala
+++ b/pollux/lib/core/src/test/scala/io/iohk/atala/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesSpec.scala
@@ -1,16 +1,11 @@
-package io.iohk.atala.pollux.core.service.helper
+package io.iohk.atala.pollux.core.service.serdes
-import io.iohk.atala.pollux.core.service.serdes.PublicCredentialDefinitionSerDesV1
-import io.iohk.atala.pollux.core.service.serdes.PublicCredentialPrimaryPublicKeyV1
-import io.iohk.atala.pollux.core.service.serdes.PublicCredentialRevocationKeyV1
-import io.iohk.atala.pollux.core.service.serdes.PublicCredentialValueV1
import zio.*
import zio.test.*
import zio.test.Assertion.*
-import zio.test.assertZIO
-object PublicCredentialDefinitionSerDesSpec extends ZIOSpecDefault {
- val json =
+object PublicCredentialDefinitionSchemaSerDesSpec extends ZIOSpecDefault {
+ val json: String =
"""
|{
| "schemaId": "resource:///anoncred-schema-example.json",
@@ -51,7 +46,7 @@ object PublicCredentialDefinitionSerDesSpec extends ZIOSpecDefault {
override def spec: Spec[TestEnvironment with Scope, Any] = suite("PublicCredentialDefinitionSerDes")(
test("should validate a correct schema") {
- assertZIO(PublicCredentialDefinitionSerDesV1.schemaSerDes.validate(json))(isTrue)
+ assertZIO(PublicCredentialDefinitionSerDesV1.schemaSerDes.validate(json))(isUnit)
},
test("should deserialise") {
val primary = PublicCredentialPrimaryPublicKeyV1(
diff --git a/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V17__add_anoncred_credentials_to_use_columns.sql b/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V17__add_anoncred_credentials_to_use_columns.sql
new file mode 100644
index 0000000000..dde029abeb
--- /dev/null
+++ b/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V17__add_anoncred_credentials_to_use_columns.sql
@@ -0,0 +1,4 @@
+-- presentation_records
+ALTER TABLE public.presentation_records
+ ADD COLUMN "anoncred_credentials_to_use_json_schema_id" VARCHAR(64),
+ ADD COLUMN "anoncred_credentials_to_use" JSON;
\ No newline at end of file
diff --git a/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V18__issue_credential_rename_schema_id.sql b/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V18__issue_credential_rename_schema_id.sql
new file mode 100644
index 0000000000..6fc8f3c693
--- /dev/null
+++ b/pollux/lib/sql-doobie/src/main/resources/sql/pollux/V18__issue_credential_rename_schema_id.sql
@@ -0,0 +1,2 @@
+ALTER TABLE public.issue_credential_records RENAME COLUMN schema_id TO schema_uri;
+ALTER TABLE public.issue_credential_records ADD COLUMN credential_definition_uri VARCHAR(500);
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 cd3b187ba7..00ce0c9492 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
@@ -9,7 +9,7 @@ import io.circe.parser.*
import io.circe.syntax.*
import io.iohk.atala.castor.core.model.did.*
import io.iohk.atala.mercury.protocol.issuecredential.{IssueCredential, OfferCredential, RequestCredential}
-import io.iohk.atala.pollux.anoncreds.CredentialRequestMetadata
+import io.iohk.atala.pollux.anoncreds.AnoncredCredentialRequestMetadata
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError
import io.iohk.atala.pollux.core.model.error.CredentialRepositoryError.*
@@ -48,9 +48,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
given requestCredentialGet: Get[RequestCredential] = Get[String].map(decode[RequestCredential](_).getOrElse(???))
given requestCredentialPut: Put[RequestCredential] = Put[String].contramap(_.asJson.toString)
- given acRequestMetadataGet: Get[CredentialRequestMetadata] =
- Get[String].map(_.fromJson[CredentialRequestMetadata].getOrElse(???))
- given acRequestMetadataPut: Put[CredentialRequestMetadata] = Put[String].contramap(_.toJson)
+ given acRequestMetadataGet: Get[AnoncredCredentialRequestMetadata] =
+ Get[String].map(_.fromJson[AnoncredCredentialRequestMetadata].getOrElse(???))
+ given acRequestMetadataPut: Put[AnoncredCredentialRequestMetadata] = Put[String].contramap(_.toJson)
given issueCredentialGet: Get[IssueCredential] = Get[String].map(decode[IssueCredential](_).getOrElse(???))
given issueCredentialPut: Put[IssueCredential] = Put[String].contramap(_.asJson.toString)
@@ -62,8 +62,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| created_at,
| updated_at,
| thid,
- | schema_id,
+ | schema_uri,
| credential_definition_id,
+ | credential_definition_uri,
| credential_format,
| role,
| subject_id,
@@ -85,8 +86,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| ${record.createdAt},
| ${record.updatedAt},
| ${record.thid},
- | ${record.schemaId},
+ | ${record.schemaUri},
| ${record.credentialDefinitionId},
+ | ${record.credentialDefinitionUri},
| ${record.credentialFormat},
| ${record.role},
| ${record.subjectId},
@@ -129,8 +131,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| created_at,
| updated_at,
| thid,
- | schema_id,
+ | schema_uri,
| credential_definition_id,
+ | credential_definition_uri,
| credential_format,
| role,
| subject_id,
@@ -196,8 +199,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| created_at,
| updated_at,
| thid,
- | schema_id,
+ | schema_uri,
| credential_definition_id,
+ | credential_definition_uri,
| credential_format,
| role,
| subject_id,
@@ -246,8 +250,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| created_at,
| updated_at,
| thid,
- | schema_id,
+ | schema_uri,
| credential_definition_id,
+ | credential_definition_uri,
| credential_format,
| role,
| subject_id,
@@ -287,8 +292,9 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| created_at,
| updated_at,
| thid,
- | schema_id,
+ | schema_uri,
| credential_definition_id,
+ | credential_definition_uri,
| credential_format,
| role,
| subject_id,
@@ -377,7 +383,7 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
override def updateWithAnonCredsRequestCredential(
recordId: DidCommID,
request: RequestCredential,
- metadata: CredentialRequestMetadata,
+ metadata: AnoncredCredentialRequestMetadata,
protocolState: ProtocolState
): RIO[WalletAccessContext, Int] = {
val cxnIO =
@@ -426,6 +432,7 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
| SELECT
| id,
| issued_credential_raw,
+ | credential_format,
| subject_id
| FROM public.issue_credential_records
| WHERE
@@ -440,6 +447,37 @@ class JdbcCredentialRepository(xa: Transactor[ContextAwareTask], xb: Transactor[
}
+ override def getValidAnoncredIssuedCredentials(
+ recordIds: Seq[DidCommID]
+ ): RIO[WalletAccessContext, Seq[ValidFullIssuedCredentialRecord]] = {
+ val idAsStrings = recordIds.map(_.toString)
+ val nel = NonEmptyList.of(idAsStrings.head, idAsStrings.tail: _*)
+ val inClauseFragment = Fragments.in(fr"id", nel)
+
+ val cxnIO = sql"""
+ | SELECT
+ | id,
+ | issue_credential_data,
+ | credential_format,
+ | schema_uri,
+ | credential_definition_uri,
+ | subject_id
+ | FROM public.issue_credential_records
+ | WHERE 1=1
+ | AND issue_credential_data IS NOT NULL
+ | AND schema_uri IS NOT NULL
+ | AND credential_definition_uri IS NOT NULL
+ | AND credential_format = 'AnonCreds'
+ | AND $inClauseFragment
+ """.stripMargin
+ .query[ValidFullIssuedCredentialRecord]
+ .to[Seq]
+
+ cxnIO
+ .transactWallet(xa)
+
+ }
+
override def deleteIssueCredentialRecord(recordId: DidCommID): RIO[WalletAccessContext, Int] = {
val cxnIO = sql"""
| DELETE
diff --git a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcPresentationRepository.scala b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcPresentationRepository.scala
index ef0623a4e1..19acb64d76 100644
--- a/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcPresentationRepository.scala
+++ b/pollux/lib/sql-doobie/src/main/scala/io/iohk/atala/pollux/sql/repository/JdbcPresentationRepository.scala
@@ -2,9 +2,12 @@ package io.iohk.atala.pollux.sql.repository
import cats.data.NonEmptyList
import doobie.*
+import doobie.free.connection
import doobie.implicits.*
import doobie.postgres.*
import doobie.postgres.implicits.*
+import doobie.postgres.circe.json.implicits._
+import io.circe
import io.circe.*
import io.circe.parser.*
import io.circe.syntax.*
@@ -18,9 +21,11 @@ import io.iohk.atala.shared.db.Implicits.*
import io.iohk.atala.shared.models.WalletAccessContext
import io.iohk.atala.shared.utils.BytesOps
import zio.*
-import doobie.free.connection
-import java.time.Instant
import zio.interop.catz.*
+import zio.json.*
+import zio.json.ast.Json
+import zio.json.ast.Json.*
+import java.time.Instant
// TODO: replace with actual implementation
class JdbcPresentationRepository(
xa: Transactor[ContextAwareTask],
@@ -51,6 +56,31 @@ class JdbcPresentationRepository(
.transactWallet(xa)
}
+ def updateAnoncredPresentationWithCredentialsToUse(
+ recordId: DidCommID,
+ anoncredCredentialsToUseJsonSchemaId: Option[String],
+ anoncredCredentialsToUse: Option[AnoncredCredentialProofs],
+ protocolState: ProtocolState
+ ): RIO[WalletAccessContext, Int] = {
+ val cxnIO =
+ sql"""
+ | UPDATE public.presentation_records
+ | SET
+ | anoncred_credentials_to_use_json_schema_id = ${anoncredCredentialsToUseJsonSchemaId},
+ | anoncred_credentials_to_use = ${anoncredCredentialsToUse},
+ | protocol_state = $protocolState,
+ | updated_at = ${Instant.now},
+ | meta_retries = $maxRetries,
+ | meta_next_retry = ${Instant.now},
+ | meta_last_failure = null
+ | WHERE
+ | id = $recordId
+ """.stripMargin.update
+
+ cxnIO.run
+ .transactWallet(xa)
+ }
+
// deserializes from the hex string
private def deserializeInclusionProof(proof: String): MerkleInclusionProof =
MerkleInclusionProof.decode(
@@ -64,6 +94,23 @@ class JdbcPresentationRepository(
import PresentationRecord.*
+ def zioJsonToCirceJson(zioJson: Json): circe.Json = {
+ parse(zioJson.toString).getOrElse(circe.Json.Null)
+ }
+
+ def circeJsonToZioJson(circeJson: circe.Json): Json = {
+ circeJson.noSpaces.fromJson[Json].getOrElse(Json.Null)
+ }
+
+ given jsonGet: Get[AnoncredCredentialProofs] = Get[circe.Json].map { jsonString =>
+ circeJsonToZioJson(jsonString)
+ }
+
+ given jsonPut: Put[AnoncredCredentialProofs] = Put[circe.Json].contramap(zioJsonToCirceJson(_))
+
+ given didCommIDGet: Get[DidCommID] = Get[String].map(DidCommID(_))
+ given didCommIDPut: Put[DidCommID] = Put[String].contramap(_.value)
+
given protocolStateGet: Get[ProtocolState] = Get[String].map(ProtocolState.valueOf)
given protocolStatePut: Put[ProtocolState] = Put[String].contramap(_.toString)
@@ -106,6 +153,8 @@ class JdbcPresentationRepository(
| credential_format,
| request_presentation_data,
| credentials_to_use,
+ | anoncred_credentials_to_use_json_schema_id,
+ | anoncred_credentials_to_use,
| meta_retries,
| meta_next_retry,
| meta_last_failure,
@@ -123,6 +172,8 @@ class JdbcPresentationRepository(
| ${record.credentialFormat},
| ${record.requestPresentationData},
| ${record.credentialsToUse.map(_.toList)},
+ | ${record.anoncredCredentialsToUseJsonSchemaId},
+ | ${record.anoncredCredentialsToUse},
| ${record.metaRetries},
| ${record.metaNextRetry},
| ${record.metaLastFailure},
@@ -156,6 +207,8 @@ class JdbcPresentationRepository(
| propose_presentation_data,
| presentation_data,
| credentials_to_use,
+ | anoncred_credentials_to_use_json_schema_id,
+ | anoncred_credentials_to_use,
| meta_retries,
| meta_next_retry,
| meta_last_failure
@@ -202,6 +255,8 @@ class JdbcPresentationRepository(
| propose_presentation_data,
| presentation_data,
| credentials_to_use,
+ | anoncred_credentials_to_use_json_schema_id,
+ | anoncred_credentials_to_use,
| meta_retries,
| meta_next_retry,
| meta_last_failure
@@ -246,6 +301,8 @@ class JdbcPresentationRepository(
| propose_presentation_data,
| presentation_data,
| credentials_to_use,
+ | anoncred_credentials_to_use_json_schema_id,
+ | anoncred_credentials_to_use,
| meta_retries,
| meta_next_retry,
| meta_last_failure
@@ -278,6 +335,8 @@ class JdbcPresentationRepository(
| propose_presentation_data,
| presentation_data,
| credentials_to_use,
+ | anoncred_credentials_to_use_json_schema_id,
+ | anoncred_credentials_to_use,
| meta_retries,
| meta_next_retry,
| meta_last_failure
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala
index 85c13f45e4..73afd223b0 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala
@@ -165,7 +165,7 @@ object MainApp extends ZIOAppDefault {
DIDServiceImpl.layer,
EntityServiceImpl.layer,
ManagedDIDServiceWithEventNotificationImpl.layer,
- PresentationServiceImpl.layer >>> PresentationServiceNotifier.layer,
+ LinkSecretServiceImpl.layer >>> PresentationServiceImpl.layer >>> PresentationServiceNotifier.layer,
VerificationPolicyServiceImpl.layer,
WalletManagementServiceImpl.layer,
// authentication
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala
index d033da7af8..581625d808 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/jobs/IssueBackgroundJobs.scala
@@ -2,6 +2,7 @@ package io.iohk.atala.agent.server.jobs
import io.iohk.atala.agent.server.config.AppConfig
import io.iohk.atala.agent.server.jobs.BackgroundJobError.ErrorResponseReceivedFromPeerAgent
+import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError.WalletNotFoundError
import io.iohk.atala.castor.core.model.did.*
import io.iohk.atala.mercury.*
import io.iohk.atala.mercury.protocol.issuecredential.*
@@ -12,7 +13,6 @@ import io.iohk.atala.shared.utils.DurationOps.toMetricsSeconds
import io.iohk.atala.shared.utils.aspects.CustomMetricsAspect
import zio.*
import zio.metrics.*
-import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError.WalletNotFoundError
object IssueBackgroundJobs extends BackgroundJobsHelper {
@@ -148,6 +148,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
Role.Issuer,
_,
_,
@@ -202,6 +203,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
CredentialFormat.JWT,
Role.Holder,
Some(subjectId),
@@ -242,6 +244,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
CredentialFormat.AnonCreds,
Role.Holder,
None,
@@ -284,6 +287,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
Role.Holder,
_,
_,
@@ -339,6 +343,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
Role.Issuer,
_,
_,
@@ -377,6 +382,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
CredentialFormat.JWT,
Role.Issuer,
_,
@@ -421,6 +427,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
CredentialFormat.AnonCreds,
Role.Issuer,
_,
@@ -463,6 +470,7 @@ object IssueBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
Role.Issuer,
_,
_,
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 9eb3f15697..4a9405f546 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
@@ -9,7 +9,11 @@ import io.iohk.atala.agent.server.jobs.BackgroundJobError.{
InvalidState,
NotImplemented
}
+import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError.WalletNotFoundError
+import io.iohk.atala.agent.walletapi.service.ManagedDIDService
+import io.iohk.atala.agent.walletapi.storage.DIDNonSecretStorage
import io.iohk.atala.castor.core.model.did.*
+import io.iohk.atala.castor.core.service.DIDService
import io.iohk.atala.mercury.*
import io.iohk.atala.mercury.model.*
import io.iohk.atala.mercury.protocol.presentproof.*
@@ -17,11 +21,14 @@ import io.iohk.atala.mercury.protocol.reportproblem.v2.*
import io.iohk.atala.pollux.core.model.*
import io.iohk.atala.pollux.core.model.error.PresentationError.*
import io.iohk.atala.pollux.core.model.error.{CredentialServiceError, PresentationError}
+import io.iohk.atala.pollux.core.service.serdes.AnoncredCredentialProofsV1
import io.iohk.atala.pollux.core.service.{CredentialService, PresentationService}
import io.iohk.atala.pollux.vc.jwt.{JWT, JwtPresentation, DidResolver as JwtDidResolver}
+import io.iohk.atala.resolvers.DIDResolver
import io.iohk.atala.shared.utils.DurationOps.toMetricsSeconds
import io.iohk.atala.shared.utils.aspects.CustomMetricsAspect
import zio.*
+import zio.json.ast.Json
import zio.metrics.*
import zio.prelude.Validation
import zio.prelude.ZValidation.*
@@ -29,11 +36,11 @@ import io.iohk.atala.agent.walletapi.storage.DIDNonSecretStorage
import io.iohk.atala.agent.walletapi.model.error.DIDSecretStorageError.WalletNotFoundError
import io.iohk.atala.resolvers.DIDResolver
import io.iohk.atala.shared.models.WalletAccessContext
-
import java.time.{Clock, Instant, ZoneId}
import io.iohk.atala.castor.core.service.DIDService
import io.iohk.atala.agent.walletapi.service.ManagedDIDService
import io.iohk.atala.shared.http.*
+
object PresentBackgroundJobs extends BackgroundJobsHelper {
val presentProofExchanges = {
@@ -163,21 +170,41 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
// ##########################
// ### PresentationRecord ###
// ##########################
- case PresentationRecord(id, _, _, _, _, _, _, _, ProposalPending, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProposalPending, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, ProposalSent, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProposalSent, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, ProposalReceived, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProposalReceived, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, ProposalRejected, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProposalRejected, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, RequestPending, _, oRecord, _, _, _, _, _, _) => // Verifier
+ case PresentationRecord(
+ id,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ RequestPending,
+ _,
+ oRecord,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _
+ ) => // Verifier
oRecord match
case None => ZIO.fail(InvalidState("PresentationRecord 'RequestPending' with no Record"))
case Some(record) =>
val verifierReqPendingToSentFlow = for {
- _ <- ZIO.log(s"PresentationRecord: RequestPending (Send Massage)")
+ _ <- ZIO.log(s"PresentationRecord: RequestPending (Send Message)")
walletAccessContext <- buildWalletAccessContextLayer(record.from)
result <- (for {
didOps <- ZIO.service[DidOps]
@@ -211,17 +238,17 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
.gauge("present_proof_flow_verifier_req_pending_to_sent_flow_ms_gauge")
.trackDurationWith(_.toMetricsSeconds)
- case PresentationRecord(id, _, _, _, _, _, _, _, RequestSent, _, _, _, _, _, _, _, _) => // Verifier
+ case PresentationRecord(id, _, _, _, _, _, _, _, RequestSent, _, _, _, _, _, _, _, _, _, _) => // Verifier
ZIO.logDebug("PresentationRecord: RequestSent") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, RequestReceived, _, _, _, _, _, _, _, _) => // Prover
+ case PresentationRecord(id, _, _, _, _, _, _, _, RequestReceived, _, _, _, _, _, _, _, _, _, _) => // Prover
ZIO.logDebug("PresentationRecord: RequestReceived") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, RequestRejected, _, _, _, _, _, _, _, _) => // Prover
+ case PresentationRecord(id, _, _, _, _, _, _, _, RequestRejected, _, _, _, _, _, _, _, _, _, _) => // Prover
ZIO.logDebug("PresentationRecord: RequestRejected") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportPending, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportPending, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportSent, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportSent, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
- case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportReceived, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, ProblemReportReceived, _, _, _, _, _, _, _, _, _, _) =>
ZIO.fail(NotImplemented)
case PresentationRecord(
id,
@@ -233,16 +260,17 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
_,
_,
PresentationPending,
- _,
+ CredentialFormat.JWT,
oRequestPresentation,
_,
_,
credentialsToUse,
_,
_,
+ _,
+ _,
_
) => // Prover
- // signedJwtPresentation = JwtPresentation.toEncodedJwt(w3cPresentationPayload, prover)
oRequestPresentation match
case None => ZIO.fail(InvalidState("PresentationRecord 'RequestPending' with no Record"))
case Some(requestPresentation) => // TODO create build method in mercury for Presentation
@@ -252,35 +280,120 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
presentationService <- ZIO.service[PresentationService]
prover <- createPrismDIDIssuerFromPresentationCredentials(id, credentialsToUse.getOrElse(Nil))
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
- presentationPayload <- presentationService
- .createPresentationPayloadFromRecord(
- id,
- prover,
- Instant.now()
- )
- .provideSomeLayer(ZLayer.succeed(walletAccessContext))
- signedJwtPresentation = JwtPresentation.toEncodedJwt(
- presentationPayload.toW3CPresentationPayload,
- prover
- )
- presentation <- ZIO.succeed(
- Presentation(
- body = Presentation.Body(
- goal_code = requestPresentation.body.goal_code,
- comment = requestPresentation.body.comment
- ),
- attachments = Seq(
- AttachmentDescriptor
- .buildBase64Attachment(
- payload = signedJwtPresentation.value.getBytes(),
- mediaType = Some("prism/jwt")
+ presentation <-
+ for {
+ presentationPayload <-
+ presentationService
+ .createJwtPresentationPayloadFromRecord(
+ id,
+ prover,
+ Instant.now()
)
- ),
- thid = requestPresentation.thid.orElse(Some(requestPresentation.id)),
- from = requestPresentation.to,
- to = requestPresentation.from
- )
- )
+ .provideSomeLayer(ZLayer.succeed(walletAccessContext))
+ signedJwtPresentation = JwtPresentation.toEncodedJwt(
+ presentationPayload.toW3CPresentationPayload,
+ prover
+ )
+ presentation <- ZIO.succeed(
+ Presentation(
+ body = Presentation.Body(
+ goal_code = requestPresentation.body.goal_code,
+ comment = requestPresentation.body.comment
+ ),
+ attachments = Seq(
+ AttachmentDescriptor
+ .buildBase64Attachment(
+ payload = signedJwtPresentation.value.getBytes(),
+ mediaType = Some("prism/jwt")
+ )
+ ),
+ thid = requestPresentation.thid.orElse(Some(requestPresentation.id)),
+ from = requestPresentation.to,
+ to = requestPresentation.from
+ )
+ )
+ } yield presentation
+ _ <- presentationService
+ .markPresentationGenerated(id, presentation)
+ .provideSomeLayer(ZLayer.succeed(walletAccessContext))
+ } yield ()).mapError(e => (walletAccessContext, handlePresentationErrors(e)))
+ } yield result
+
+ proverPresentationPendingToGeneratedFlow
+ @@ ProverPresentationPendingToGeneratedSuccess.trackSuccess
+ @@ ProverPresentationPendingToGeneratedFailed.trackError
+ @@ ProverPresentationPendingToGenerated
+ @@ Metric
+ .gauge("present_proof_flow_prover_presentation_pending_to_generated_flow_ms_gauge")
+ .trackDurationWith(_.toMetricsSeconds)
+ case PresentationRecord(
+ id,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationPending,
+ CredentialFormat.AnonCreds,
+ oRequestPresentation,
+ _,
+ _,
+ _,
+ _,
+ None,
+ _,
+ _,
+ _
+ ) => // Prover
+ ZIO.fail(NotImplemented)
+ case PresentationRecord(
+ id,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationPending,
+ CredentialFormat.AnonCreds,
+ oRequestPresentation,
+ _,
+ _,
+ _,
+ _,
+ Some(credentialsToUseJson),
+ _,
+ _,
+ _
+ ) => // Prover
+ oRequestPresentation match
+ case None => ZIO.fail(InvalidState("PresentationRecord 'RequestPending' with no Record"))
+ case Some(requestPresentation) => // TODO create build method in mercury for Presentation
+ val proverPresentationPendingToGeneratedFlow = for {
+ walletAccessContext <- buildWalletAccessContextLayer(requestPresentation.to)
+ result <- (for {
+ presentationService <- ZIO.service[PresentationService]
+ anoncredCredentialProofs <-
+ 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()
+ )
+ .provideSomeLayer(ZLayer.succeed(walletAccessContext))
_ <- presentationService
.markPresentationGenerated(id, presentation)
.provideSomeLayer(ZLayer.succeed(walletAccessContext))
@@ -312,6 +425,8 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
_,
_,
_,
+ _,
+ _,
_
) => // Prover
ZIO.logDebug("PresentationRecord: PresentationGenerated") *> ZIO.unit
@@ -354,7 +469,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
.gauge("present_proof_flow_prover_presentation_generated_to_sent_flow_ms_gauge")
.trackDurationWith(_.toMetricsSeconds)
- case PresentationRecord(id, _, _, _, _, _, _, _, PresentationSent, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, PresentationSent, _, _, _, _, _, _, _, _, _, _) =>
ZIO.logDebug("PresentationRecord: PresentationSent") *> ZIO.unit
case PresentationRecord(
id,
@@ -366,13 +481,15 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
_,
_,
PresentationReceived,
- _,
+ CredentialFormat.JWT,
mayBeRequestPresentation,
_,
presentation,
_,
_,
_,
+ _,
+ _,
_
) => // Verifier
ZIO.logDebug("PresentationRecord: PresentationReceived") *> ZIO.unit
@@ -387,8 +504,7 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
didResolverService <- ZIO.service[JwtDidResolver]
credentialsValidationResult <- p.attachments.head.data match {
case Base64(data) =>
- // JWT verifiable presentation decided frin base64
- val base64Decoded = new String(java.util.Base64.getDecoder().decode(data))
+ val base64Decoded = new String(java.util.Base64.getDecoder.decode(data))
val maybePresentationOptions
: Either[PresentationError, Option[io.iohk.atala.pollux.core.model.presentation.Options]] =
mayBeRequestPresentation
@@ -505,14 +621,141 @@ object PresentBackgroundJobs extends BackgroundJobsHelper {
"present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_flow_ms_gauge"
)
.trackDurationWith(_.toMetricsSeconds)
-
- case PresentationRecord(id, _, _, _, _, _, _, _, PresentationVerificationFailed, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationReceived,
+ CredentialFormat.AnonCreds,
+ None,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _
+ ) =>
+ ZIO.fail(InvalidState("PresentationRecord in 'PresentationReceived' with no Presentation Request"))
+ case PresentationRecord(
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationReceived,
+ CredentialFormat.AnonCreds,
+ _,
+ _,
+ None,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _
+ ) =>
+ ZIO.fail(InvalidState("PresentationRecord in 'PresentationReceived' with no Presentation"))
+ case PresentationRecord(
+ id,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationReceived,
+ CredentialFormat.AnonCreds,
+ Some(requestPresentation),
+ _,
+ Some(presentation),
+ _,
+ _,
+ _,
+ _,
+ _,
+ _
+ ) => // Verifier
+ ZIO.logDebug("PresentationRecord: PresentationReceived") *> ZIO.unit
+ val verifierPresentationReceivedToProcessed =
+ for {
+ walletAccessContext <- buildWalletAccessContextLayer(presentation.to)
+ presReceivedToProcessedAspect = CustomMetricsAspect.endRecordingTime(
+ s"${record.id}_present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge",
+ "present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_ms_gauge"
+ )
+ result <- (for {
+ service <- ZIO.service[PresentationService]
+ _ <- (service
+ .verifyAnoncredPresentation(presentation, requestPresentation, id)
+ .provideSomeLayer(ZLayer.succeed(walletAccessContext)) @@ presReceivedToProcessedAspect)
+ .flatMapError(e =>
+ for {
+ didCommAgent <- buildDIDCommAgent(presentation.from).provideSomeLayer(
+ ZLayer.succeed(walletAccessContext)
+ )
+ reportproblem = ReportProblem.build(
+ fromDID = presentation.to,
+ toDID = presentation.from,
+ pthid = presentation.thid.getOrElse(presentation.id),
+ code = ProblemCode("e.p.presentation-verification-failed"),
+ comment = Some(e.toString)
+ )
+ _ <- MessagingService
+ .send(reportproblem.toMessage)
+ .provideSomeLayer(didCommAgent)
+ _ <- ZIO.log(s"CredentialsValidationResult: ${e.toString}")
+ } yield ()
+ ZIO.succeed(e)
+ )
+ } yield ()).mapError(e => (walletAccessContext, handlePresentationErrors(e)))
+ } yield result
+ verifierPresentationReceivedToProcessed
+ @@ VerifierPresentationReceivedToProcessedSuccess.trackSuccess
+ @@ VerifierPresentationReceivedToProcessedFailed.trackError
+ @@ VerifierPresentationReceivedToProcessed
+ @@ Metric
+ .gauge(
+ "present_proof_flow_verifier_presentation_received_to_verification_success_or_failure_flow_ms_gauge"
+ )
+ .trackDurationWith(_.toMetricsSeconds)
+ case PresentationRecord(
+ id,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ PresentationVerificationFailed,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _,
+ _
+ ) =>
ZIO.logDebug("PresentationRecord: PresentationVerificationFailed") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, PresentationAccepted, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, PresentationAccepted, _, _, _, _, _, _, _, _, _, _) =>
ZIO.logDebug("PresentationRecord: PresentationVerifiedAccepted") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, PresentationVerified, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, PresentationVerified, _, _, _, _, _, _, _, _, _, _) =>
ZIO.logDebug("PresentationRecord: PresentationVerified") *> ZIO.unit
- case PresentationRecord(id, _, _, _, _, _, _, _, PresentationRejected, _, _, _, _, _, _, _, _) =>
+ case PresentationRecord(id, _, _, _, _, _, _, _, PresentationRejected, _, _, _, _, _, _, _, _, _, _) =>
ZIO.logDebug("PresentationRecord: PresentationRejected") *> ZIO.unit
}
} yield ()
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/issue/controller/IssueControllerImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/issue/controller/IssueControllerImpl.scala
index a88521701b..bba5c66d68 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/issue/controller/IssueControllerImpl.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/issue/controller/IssueControllerImpl.scala
@@ -78,21 +78,23 @@ class IssueControllerImpl(
.mapError(_ =>
ErrorResponse.badRequest(detail = Some("Missing request parameter: credentialDefinitionId"))
)
+ credentialDefinitionId = {
+ val publicEndpointUrl = appConfig.agent.httpEndpoint.publicEndpointUrl
+ val urlSuffix =
+ s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition"
+ val urlPrefix = if (publicEndpointUrl.endsWith("/")) publicEndpointUrl else publicEndpointUrl + "/"
+ s"$urlPrefix$urlSuffix"
+ }
record <- credentialService
.createAnonCredsIssueCredentialRecord(
pairwiseIssuerDID = didIdPair.myDID,
pairwiseHolderDID = didIdPair.theirDid,
thid = DidCommID(),
credentialDefinitionGUID = credentialDefinitionGUID,
+ credentialDefinitionId = credentialDefinitionId,
claims = jsonClaims,
validityPeriod = request.validityPeriod,
- automaticIssuance = request.automaticIssuance.orElse(Some(true)), {
- val publicEndpointUrl = appConfig.agent.httpEndpoint.publicEndpointUrl
- val urlSuffix =
- s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition"
- val urlPrefix = if (publicEndpointUrl.endsWith("/")) publicEndpointUrl else publicEndpointUrl + "/"
- s"$urlPrefix$urlSuffix"
- }
+ automaticIssuance = request.automaticIssuance.orElse(Some(true))
)
} yield record
} yield IssueCredentialRecord.fromDomain(outcome)
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala
index 40af6c73d5..12b4c74c09 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofController.scala
@@ -45,6 +45,21 @@ object PresentProofController {
ErrorResponse.notFound(detail = Some(s"Thread Id not found: $thid"))
case PresentationError.InvalidFlowStateError(msg) =>
ErrorResponse.badRequest(title = "InvalidFlowState", detail = Some(msg))
+ case PresentationError.MissingAnoncredPresentationRequest(msg) =>
+ ErrorResponse.badRequest(title = "Missing Anoncred Presentation Request", detail = Some(msg))
+ case PresentationError.AnoncredPresentationCreationError(cause) =>
+ ErrorResponse.badRequest(title = "Error Creating Anoncred Presentation", detail = Some(cause.toString))
+ case PresentationError.AnoncredPresentationVerificationError(cause) =>
+ ErrorResponse.badRequest(title = "Error Verifying Prensetation", detail = Some(cause.toString))
+ case PresentationError.InvalidAnoncredPresentationRequest(msg) =>
+ ErrorResponse.badRequest(title = "Invalid Anoncred Presentation Request", detail = Some(msg))
+ case PresentationError.InvalidAnoncredPresentation(msg) =>
+ ErrorResponse.badRequest(title = "Invalid Anoncred Presentation", detail = Some(msg))
+ case PresentationError.NotMatchingPresentationCredentialFormat(cause) =>
+ ErrorResponse.badRequest(
+ title = "Presentation and Credential Format Not Matching",
+ detail = Some(cause.toString)
+ )
case PresentationError.UnexpectedError(msg) =>
ErrorResponse.internalServerError(detail = Some(msg))
case PresentationError.IssuedCredentialNotFoundError(_) =>
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofControllerImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofControllerImpl.scala
index 77d478c648..1e05838d68 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofControllerImpl.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/PresentProofControllerImpl.scala
@@ -11,7 +11,7 @@ import io.iohk.atala.mercury.protocol.presentproof.ProofType
import io.iohk.atala.pollux.core.model.error.PresentationError
import io.iohk.atala.pollux.core.model.presentation.Options
import io.iohk.atala.pollux.core.model.{CredentialFormat, DidCommID, PresentationRecord}
-import io.iohk.atala.pollux.core.service.PresentationService
+import io.iohk.atala.pollux.core.service.{PresentationService}
import io.iohk.atala.presentproof.controller.PresentProofController.toDidCommID
import io.iohk.atala.presentproof.controller.http.*
import io.iohk.atala.shared.models.WalletAccessContext
@@ -30,22 +30,41 @@ class PresentProofControllerImpl(
val result: ZIO[WalletAccessContext, ConnectionServiceError | PresentationError, PresentationStatus] = for {
didIdPair <- getPairwiseDIDs(request.connectionId).provideSomeLayer(ZLayer.succeed(connectionService))
credentialFormat = request.credentialFormat.map(CredentialFormat.valueOf).getOrElse(CredentialFormat.JWT)
- record <- presentationService
- .createPresentationRecord(
- pairwiseVerifierDID = didIdPair.myDID,
- pairwiseProverDID = didIdPair.theirDid,
- thid = DidCommID(),
- connectionId = Some(request.connectionId.toString),
- proofTypes = request.proofs.map { e =>
- ProofType(
- schema = e.schemaId, // TODO rename field to schemaId
- requiredFields = None,
- trustIssuers = Some(e.trustIssuers.map(DidId(_)))
- )
- },
- options = request.options.map(x => Options(x.challenge, x.domain)),
- format = credentialFormat,
- )
+ record <-
+ credentialFormat match {
+ case CredentialFormat.JWT =>
+ presentationService
+ .createJwtPresentationRecord(
+ pairwiseVerifierDID = didIdPair.myDID,
+ pairwiseProverDID = didIdPair.theirDid,
+ thid = DidCommID(),
+ connectionId = Some(request.connectionId.toString),
+ proofTypes = request.proofs.map { e =>
+ ProofType(
+ schema = e.schemaId,
+ requiredFields = None,
+ trustIssuers = Some(e.trustIssuers.map(DidId(_)))
+ )
+ },
+ options = request.options.map(x => Options(x.challenge, x.domain))
+ )
+ case CredentialFormat.AnonCreds =>
+ request.anoncredPresentationRequest match {
+ case Some(presentationRequest) =>
+ presentationService
+ .createAnoncredPresentationRecord(
+ pairwiseVerifierDID = didIdPair.myDID,
+ pairwiseProverDID = didIdPair.theirDid,
+ thid = DidCommID(),
+ connectionId = Some(request.connectionId.toString),
+ presentationRequest = presentationRequest
+ )
+ case None =>
+ ZIO.fail(
+ PresentationError.MissingAnoncredPresentationRequest("Anoncred presentation request is missing")
+ )
+ }
+ }
} yield PresentationStatus.fromDomain(record)
result.mapError {
@@ -92,10 +111,16 @@ class PresentProofControllerImpl(
didCommId <- ZIO.succeed(DidCommID(id.toString))
record <- requestPresentationAction.action match {
case "request-accept" =>
- presentationService.acceptRequestPresentation(
- recordId = didCommId,
- credentialsToUse = requestPresentationAction.proofId.getOrElse(Seq.empty)
- )
+ (requestPresentationAction.proofId, requestPresentationAction.anoncredPresentationRequest) match
+ case (Some(proofs), None) =>
+ presentationService.acceptRequestPresentation(recordId = didCommId, credentialsToUse = proofs)
+ case (None, Some(proofs)) =>
+ presentationService.acceptAnoncredRequestPresentation(
+ recordId = didCommId,
+ credentialsToUse = proofs
+ )
+ case _ => presentationService.acceptRequestPresentation(recordId = didCommId, credentialsToUse = Seq())
+
case "request-reject" => presentationService.rejectRequestPresentation(didCommId)
case "presentation-accept" => presentationService.acceptPresentation(didCommId)
case "presentation-reject" => presentationService.rejectPresentation(didCommId)
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationAction.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationAction.scala
index c88a97747e..114b08893a 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationAction.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationAction.scala
@@ -1,6 +1,7 @@
package io.iohk.atala.presentproof.controller.http
import io.iohk.atala.api.http.Annotation
+import io.iohk.atala.pollux.core.service.serdes.*
import io.iohk.atala.presentproof.controller.http.RequestPresentationAction.annotations
import sttp.tapir.Schema.annotations.{description, encodedExample, validate}
import sttp.tapir.{Schema, Validator}
@@ -13,7 +14,10 @@ final case class RequestPresentationAction(
action: String,
@description(annotations.proofId.description)
@encodedExample(annotations.proofId.example)
- proofId: Option[Seq[String]] = None
+ proofId: Option[Seq[String]] = None,
+ @description(annotations.anoncredProof.description)
+ @encodedExample(annotations.anoncredProof.example)
+ anoncredPresentationRequest: Option[AnoncredCredentialProofsV1],
)
object RequestPresentationAction {
@@ -37,13 +41,33 @@ object RequestPresentationAction {
"The unique identifier of the issue credential record - and hence VC - to use as the prover accepts the presentation request. Only applicable on the prover side when the action is `request-accept`.",
example = None
)
+
+ object anoncredProof
+ extends Annotation[Option[AnoncredCredentialProofsV1]](
+ description = "A list of proofs from the Anoncred library, each corresponding to a credential.",
+ example = None
+ )
+
+ object credential
+ extends Annotation[String](
+ description =
+ "The unique identifier of the issue credential record - and hence VC - to use as the prover accepts the presentation request. Only applicable on the prover side when the action is `request-accept`.",
+ example = "id"
+ )
}
- given encoder: JsonEncoder[RequestPresentationAction] =
+ given RequestPresentationActionEncoder: JsonEncoder[RequestPresentationAction] =
DeriveJsonEncoder.gen[RequestPresentationAction]
- given decoder: JsonDecoder[RequestPresentationAction] =
+ given RequestPresentationActionDecoder: JsonDecoder[RequestPresentationAction] =
DeriveJsonDecoder.gen[RequestPresentationAction]
- given schema: Schema[RequestPresentationAction] = Schema.derived
+ given RequestPresentationActionSchema: Schema[RequestPresentationAction] = Schema.derived
+
+ import AnoncredCredentialProofsV1.given
+
+ given Schema[AnoncredCredentialProofsV1] = Schema.derived
+
+ given Schema[AnoncredCredentialProofV1] = Schema.derived
+
}
diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala
index fd146ac010..9f3c677fa8 100644
--- a/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala
+++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/presentproof/controller/http/RequestPresentationInput.scala
@@ -1,9 +1,10 @@
package io.iohk.atala.presentproof.controller.http
import io.iohk.atala.api.http.Annotation
+import io.iohk.atala.pollux.core.service.serdes.*
import io.iohk.atala.presentproof.controller.http.RequestPresentationInput.annotations
-import sttp.tapir.{Schema, Validator}
import sttp.tapir.Schema.annotations.{description, encodedExample}
+import sttp.tapir.{Schema, Validator}
import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder}
import java.util.UUID
@@ -18,6 +19,9 @@ final case class RequestPresentationInput(
@description(annotations.proofs.description)
@encodedExample(annotations.proofs.example)
proofs: Seq[ProofRequestAux],
+ @description(annotations.anoncredPresentationRequest.description)
+ @encodedExample(annotations.anoncredPresentationRequest.example)
+ anoncredPresentationRequest: Option[AnoncredPresentationRequestV1],
@description(annotations.credentialFormat.description)
@encodedExample(annotations.credentialFormat.example)
credentialFormat: Option[String],
@@ -42,6 +46,54 @@ object RequestPresentationInput {
example = Seq.empty
)
+ object anoncredPresentationRequest
+ extends Annotation[Option[AnoncredPresentationRequestV1]](
+ description = "Anoncred Presentation Request",
+ example = Some(
+ AnoncredPresentationRequestV1(
+ requested_attributes = Map(
+ "attribute1" -> AnoncredRequestedAttributeV1(
+ "Attribute 1",
+ List(
+ Map(
+ "cred_def_id" -> "credential_definition_id_of_attribute1"
+ )
+ ),
+ Some(
+ AnoncredNonRevokedIntervalV1(
+ Some(1635734400),
+ Some(1735734400)
+ )
+ )
+ )
+ ),
+ requested_predicates = Map(
+ "predicate1" ->
+ AnoncredRequestedPredicateV1(
+ "Predicate 1",
+ ">=",
+ 18,
+ List(
+ Map(
+ "schema_id" -> "schema_id_of_predicate1"
+ )
+ ),
+ Some(
+ AnoncredNonRevokedIntervalV1(
+ Some(1635734400),
+ None
+ )
+ )
+ )
+ ),
+ name = "Example Presentation Request",
+ nonce = "1234567890",
+ version = "1.0",
+ non_revoked = None
+ )
+ )
+ )
+
object credentialFormat
extends Annotation[Option[String]](
description = "The credential format (default to 'JWT')",
@@ -61,5 +113,15 @@ object RequestPresentationInput {
given decoder: JsonDecoder[RequestPresentationInput] =
DeriveJsonDecoder.gen[RequestPresentationInput]
+ import AnoncredPresentationRequestV1.given
+
+ given Schema[AnoncredPresentationRequestV1] = Schema.derived
+
+ given Schema[AnoncredRequestedAttributeV1] = Schema.derived
+
+ given Schema[AnoncredRequestedPredicateV1] = Schema.derived
+
+ given Schema[AnoncredNonRevokedIntervalV1] = Schema.derived
+
given schema: Schema[RequestPresentationInput] = Schema.derived
}
diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala
index 0ae1658d10..d835a2724d 100644
--- a/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala
+++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/issue/controller/IssueControllerTestTools.scala
@@ -16,7 +16,7 @@ import io.iohk.atala.issue.controller.http.{
IssueCredentialRecord,
IssueCredentialRecordPage
}
-import io.iohk.atala.pollux.anoncreds.LinkSecretWithId
+import io.iohk.atala.pollux.anoncreds.AnoncredLinkSecretWithId
import io.iohk.atala.pollux.core.model.CredentialFormat
import io.iohk.atala.pollux.core.repository.{
CredentialDefinitionRepositoryInMemory,
diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala
index 4b53450a27..ffabec1c77 100644
--- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala
+++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala
@@ -1,7 +1,6 @@
package io.iohk.atala.pollux.credentialdefinition
-import io.iohk.atala.agent.walletapi.model.BaseEntity
-import io.iohk.atala.agent.walletapi.model.Entity
+import io.iohk.atala.agent.walletapi.model.{BaseEntity, Entity}
import io.iohk.atala.agent.walletapi.storage.GenericSecretStorage
import io.iohk.atala.api.http.ErrorResponse
import io.iohk.atala.container.util.MigrationAspects.*
@@ -112,25 +111,22 @@ object CredentialDefinitionBasicSpec extends ZIOSpecDefault with CredentialDefin
maybeValidPublicDefinition <- PublicCredentialDefinitionSerDesV1.schemaSerDes.validate(
fetchedCredentialDefinition.definition.toString()
)
- assertValidPublicDefinition = assert(maybeValidPublicDefinition)(Assertion.isTrue)
+ assertValidPublicDefinition = assert(maybeValidPublicDefinition)(Assertion.isUnit)
maybeValidKeyCorrectnessProof <- ProofKeyCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(
fetchedCredentialDefinition.keyCorrectnessProof.toString()
)
- assertValidKeyCorrectnessProof = assert(maybeValidKeyCorrectnessProof)(Assertion.isTrue)
+ assertValidKeyCorrectnessProof = assert(maybeValidKeyCorrectnessProof)(Assertion.isUnit)
storage <- ZIO.service[GenericSecretStorage]
maybeDidSecret <- storage
.get[UUID, CredentialDefinitionSecret](fetchedCredentialDefinition.guid)
.provideSomeLayer(Entity.Default.wacLayer)
maybeValidPrivateDefinitionZIO = maybeDidSecret match {
case Some(didSecret) =>
- val validPrivateDefinition =
- PrivateCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(didSecret.json.toString())
- validPrivateDefinition
- case None =>
- ZIO.succeed(false)
+ PrivateCredentialDefinitionSchemaSerDesV1.schemaSerDes.validate(didSecret.json.toString())
+ case None => ZIO.unit
}
maybeValidPrivateDefinition <- maybeValidPrivateDefinitionZIO
- assertValidPrivateDefinition = assert(maybeValidPrivateDefinition)(Assertion.isTrue)
+ assertValidPrivateDefinition = assert(maybeValidPrivateDefinition)(Assertion.isUnit)
} yield statusCodeIs201 &&
credentialDefinitionIsCreated &&
credentialDefinitionIsFetched &&