diff --git a/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/schema/CredentialSchema.kt b/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/schema/CredentialSchema.kt index 6cebd64..696f30d 100644 --- a/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/schema/CredentialSchema.kt +++ b/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/schema/CredentialSchema.kt @@ -3,7 +3,6 @@ package com.luxoft.blockchainlab.corda.hyperledger.indy.data.schema import com.luxoft.blockchainlab.corda.hyperledger.indy.data.state.IndyCredential import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState -import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Table @@ -18,13 +17,11 @@ object CredentialSchemaV1 : MappedSchema( @Entity @Table(name = "credentials") class PersistentCredential( - @Column(name = "id") - var id: String, - @Column(name = "issuerDid") - var issuerDid: String + var issuerDid: String, + var proverDid: String ) : PersistentState() { - constructor(indyCredential: IndyCredential) : this(indyCredential.id, indyCredential.issuerDid) + constructor(indyCredential: IndyCredential) : this(indyCredential.issuerDid, indyCredential.proverDid) constructor() : this("", "") } } \ No newline at end of file diff --git a/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/state/IndyCredential.kt b/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/state/IndyCredential.kt index 3161196..b9709f4 100644 --- a/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/state/IndyCredential.kt +++ b/cordapp-contracts-states/src/main/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/data/state/IndyCredential.kt @@ -11,19 +11,19 @@ import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState /** - * A Corda record of an Indy Credential [credential] issued on request [credentialRequest] + * A Corda record of an Indy Credential issued on request [CredentialRequestInfo] * - * @param id credential persistent id * @param credentialRequestInfo indy credential request * @param credentialInfo indy credential * @param issuerDid did of an entity issued credential + * @param proverDid did of an entity received credential * @param participants corda participants */ open class IndyCredential( - val id: String, val credentialRequestInfo: CredentialRequestInfo, val credentialInfo: CredentialInfo, val issuerDid: String, + val proverDid: String, override val participants: List ) : LinearState, QueryableState { diff --git a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/FlowExtensions.kt b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/FlowExtensions.kt index 0da1ed1..30f3fae 100644 --- a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/FlowExtensions.kt +++ b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/FlowExtensions.kt @@ -14,6 +14,8 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.vaultQueryBy import net.corda.core.node.services.Vault import net.corda.core.node.services.queryBy import net.corda.core.node.services.vault.Builder.equal @@ -40,18 +42,59 @@ fun FlowLogic.indyUser(): IndyUser { /** * This method is used to get indy credential state from vault * - * @param id id of credential + * @param proverDid Did of the receiver * * @return corda state of indy credential or null if none exists */ -fun FlowLogic.getIndyCredentialState(id: String): StateAndRef? { +fun FlowLogic.getIndyCredentialsByProver(proverDid: String): List> { val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED) - val existingId = QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::id.equal(id)) + val existingId = + QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::proverDid.equal(proverDid)) val criteria = generalCriteria.and(existingId) val result = serviceHub.vaultService.queryBy(criteria) - return result.states.firstOrNull() + return result.states +} + +/** + * This method is used to get indy credential state from vault + * + * @param issuerDid Did of the issuer + * + * @return corda state of indy credential or null if none exists + */ +fun FlowLogic.getIndyCredentialsByIssuer(issuerDid: String): List> { + val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED) + val existingId = + QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::issuerDid.equal(issuerDid)) + + val criteria = generalCriteria.and(existingId) + val result = serviceHub.vaultService.queryBy(criteria) + + return result.states +} + +fun CordaRPCOps.getIndyCredentialsByIssuer(issuerDid: String): List> { + val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED) + val existingId = + QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::issuerDid.equal(issuerDid)) + + val criteria = generalCriteria.and(existingId) + val result = vaultQueryBy(criteria) + + return result.states +} + +fun CordaRPCOps.getIndyCredentialsByProver(proverDid: String): List> { + val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED) + val existingId = + QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::proverDid.equal(proverDid)) + + val criteria = generalCriteria.and(existingId) + val result = vaultQueryBy(criteria) + + return result.states } private fun FlowLogic.getUnconsumedCredentialDefinitionByCriteria( diff --git a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/IssueCredentialFlow.kt b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/IssueCredentialFlow.kt index ba5affa..fd3744c 100644 --- a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/IssueCredentialFlow.kt +++ b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/IssueCredentialFlow.kt @@ -23,11 +23,6 @@ object IssueCredentialFlow { /** * A flow to issue an Indy credential based on proposal [credProposal] * - * [identifier] must be unique for the given Indy user to allow searching Credentials by `(identifier, issuerDID)` - * - * @param identifier new unique ID for the new credential. - * Must be unique for the given Indy user to allow searching Credentials by `(identifier, issuerDID)` - * * @param credentialDefinitionId id of the credential definition to create new statement (credential) * @param credentialProposal credential JSON containing attribute values for each of requested attribute names. * Example: @@ -48,7 +43,6 @@ object IssueCredentialFlow { @InitiatingFlow @StartableByRPC open class Issuer( - private val identifier: String, private val credentialProposal: String, private val credentialDefinitionId: CredentialDefinitionId, private val proverName: CordaX500Name @@ -88,10 +82,10 @@ object IssueCredentialFlow { offer ) val credentialOut = IndyCredential( - identifier, credentialReq, credential, indyUser().did, + credentialReq.request.proverDid, listOf(ourIdentity, prover) ) StateAndContract(credentialOut, IndyCredentialContract::class.java.name) @@ -138,13 +132,10 @@ object IssueCredentialFlow { @Suspendable override fun call() { try { - val issuer = flowSession.counterparty.name - val offer = flowSession.receive().unwrap { offer -> offer } - val sessionDid = subFlow(CreatePairwiseFlow.Prover(issuer)) val credentialRequestInfo = - indyUser().createCredentialRequest(sessionDid, offer, indyUser().defaultMasterSecretId) + indyUser().createCredentialRequest(indyUser().did, offer, indyUser().defaultMasterSecretId) flowSession.send(credentialRequestInfo) val flow = object : SignTransactionFlow(flowSession) { diff --git a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/RevokeCredentialFlow.kt b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/RevokeCredentialFlow.kt index 313d5db..519ca92 100644 --- a/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/RevokeCredentialFlow.kt +++ b/cordapp/src/main/kotlin/com.luxoft.blockchainlab.corda.hyperledger.indy/flow/RevokeCredentialFlow.kt @@ -2,8 +2,10 @@ package com.luxoft.blockchainlab.corda.hyperledger.indy.flow import co.paralleluniverse.fibers.Suspendable import com.luxoft.blockchainlab.corda.hyperledger.indy.contract.IndyCredentialContract +import com.luxoft.blockchainlab.corda.hyperledger.indy.data.state.IndyCredential import com.luxoft.blockchainlab.hyperledger.indy.getRevocationRegistryId import net.corda.core.contracts.Command +import net.corda.core.contracts.StateAndRef import net.corda.core.flows.* import net.corda.core.transactions.TransactionBuilder @@ -14,19 +16,15 @@ import net.corda.core.transactions.TransactionBuilder object RevokeCredentialFlow { /** - * @param id credential id generated by [issueCredentialFlow] + * @param credentialStateIn credential generated by [IssueCredentialFlow] */ @InitiatingFlow @StartableByRPC - open class Issuer(private val id: String) : FlowLogic() { + open class Issuer(private val credentialStateIn: StateAndRef) : FlowLogic() { @Suspendable override fun call() { try { - // query vault for credential with id = credential id - val credentialStateIn = getIndyCredentialState(id) - ?: throw RuntimeException("No such credential in vault") - val credential = credentialStateIn.state.data val revRegId = credential.credentialInfo.credential.getRevocationRegistryId()!! diff --git a/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/CordentityE2E.kt b/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/CordentityE2E.kt index 2fae14a..3dbe3d9 100644 --- a/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/CordentityE2E.kt +++ b/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/CordentityE2E.kt @@ -1,13 +1,21 @@ package com.luxoft.blockchainlab.corda.hyperledger.indy +import com.luxoft.blockchainlab.corda.hyperledger.indy.data.schema.CredentialSchemaV1 +import com.luxoft.blockchainlab.corda.hyperledger.indy.data.state.IndyCredential import com.luxoft.blockchainlab.corda.hyperledger.indy.flow.* import com.luxoft.blockchainlab.hyperledger.indy.CredentialDefinitionId import com.luxoft.blockchainlab.hyperledger.indy.Interval import com.luxoft.blockchainlab.hyperledger.indy.SchemaId +import net.corda.core.contracts.StateAndRef import net.corda.core.identity.CordaX500Name +import net.corda.core.node.services.Vault +import net.corda.core.node.services.queryBy +import net.corda.core.node.services.vault.Builder.equal +import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode +import net.corda.node.services.api.VaultServiceInternal import net.corda.testing.node.internal.InternalMockNetwork.MockNode import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -63,13 +71,9 @@ class CordentityE2E : CordaTestBase() { credentialIssuer: StartedNode, credentialProposal: String, credentialDefId: CredentialDefinitionId - ): String { - - val identifier = UUID.randomUUID().toString() - + ) { val credentialFuture = credentialIssuer.services.startFlow( IssueCredentialFlow.Issuer( - identifier, credentialProposal, credentialDefId, credentialProver.getName() @@ -77,16 +81,14 @@ class CordentityE2E : CordaTestBase() { ).resultFuture credentialFuture.getOrThrow(Duration.ofSeconds(30)) - - return identifier } private fun revokeCredential( issuer: StartedNode, - credentialId: String + credentialStateIn: StateAndRef ) { val flowResult = issuer.services.startFlow( - RevokeCredentialFlow.Issuer(credentialId) + RevokeCredentialFlow.Issuer(credentialStateIn) ).resultFuture flowResult.getOrThrow(Duration.ofSeconds(30)) @@ -274,8 +276,7 @@ class CordentityE2E : CordaTestBase() { val schemaAttrInt = "1988" val credentialProposal = schemaPerson.formatProposal("John Smith", "119191919", schemaAttrInt, schemaAttrInt) - val credentialId = - issueCredential(alice, issuer, credentialProposal, credentialDefinitionId) + issueCredential(alice, issuer, credentialProposal, credentialDefinitionId) // Verify credential val attributes = listOf( @@ -300,7 +301,12 @@ class CordentityE2E : CordaTestBase() { val credentialVerified = verifyCredential(bob, alice, attributes, predicates, Interval.allTime()) assertTrue(credentialVerified) - revokeCredential(issuer, credentialId) + val states = issuer.database.transaction { + issuer.services.vaultService.getIndyCredentialsByProver(alice.getPartyDid()) + } + assert(states.size == 1) { "Invalid claims count" } + + revokeCredential(issuer, states.first()) Thread.sleep(3000) @@ -435,4 +441,15 @@ class CordentityE2E : CordaTestBase() { val credentialVerified = verifyCredential(bob, alice, attributes, emptyList(), Interval.allTime()) assertTrue(credentialVerified) } +} + +fun VaultServiceInternal.getIndyCredentialsByProver(proverDid: String): List> { + val generalCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED) + val existingId = + QueryCriteria.VaultCustomQueryCriteria(CredentialSchemaV1.PersistentCredential::proverDid.equal(proverDid)) + + val criteria = generalCriteria.and(existingId) + val result = queryBy(criteria) + + return result.states } \ No newline at end of file diff --git a/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/ReadmeExampleTest.kt b/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/ReadmeExampleTest.kt index 2688424..e907753 100644 --- a/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/ReadmeExampleTest.kt +++ b/cordapp/src/test/kotlin/com/luxoft/blockchainlab/corda/hyperledger/indy/ReadmeExampleTest.kt @@ -73,7 +73,6 @@ class ReadmeExampleTest : CordaTestBase() { ministry.services.startFlow( IssueCredentialFlow.Issuer( - UUID.randomUUID().toString(), credentialProposal, credentialDefinitionId, aliceX500