Skip to content

Commit

Permalink
48 improve txlog capabilities (#49)
Browse files Browse the repository at this point in the history
* Improve txLog capabilities

Add a function to refresh the status of the operations logged in the tx log
Add timestamps to the tx logs
Fix Dlt.publishDid function so it can publish holder and issuer DIDs

* add issueCredential, revokeCredential and verifyCredential functionality to WalletService

* Update src/main/kotlin/com/rootsid/wal/library/mongoimpl/document/BlockchainTxLogDocument.kt

Co-authored-by: Rogelio Blanco <[email protected]>

* fix issues with LocaleDateTime

* remove storage identifier validation

Co-authored-by: Rogelio Blanco <[email protected]>
  • Loading branch information
Essbante and rogelio-blanco authored Sep 24, 2022
1 parent e02d29b commit 7b68746
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 96 deletions.
110 changes: 45 additions & 65 deletions src/main/kotlin/com/rootsid/wal/library/dlt/Dlt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.iohk.atala.prism.crypto.EC
import io.iohk.atala.prism.crypto.MerkleInclusionProof
import io.iohk.atala.prism.crypto.Sha256Digest
import io.iohk.atala.prism.crypto.keys.ECKeyPair
import io.iohk.atala.prism.crypto.keys.ECPrivateKey
import io.iohk.atala.prism.identity.*
import io.iohk.atala.prism.protos.*
import io.ipfs.multibase.Base58
Expand All @@ -30,9 +31,9 @@ import kotlinx.serialization.json.*
import pbandk.ByteArr
import pbandk.json.encodeToJsonString

private const val TESTNET_URL = "https://explorer.cardano-testnet.iohkdev.io/en/transaction?id="

class Dlt {
private val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())

/**
* Use this function to get the blockchain transaction ID associated with an operationID
* @param operationId operation identifier
Expand All @@ -47,13 +48,18 @@ class Dlt {
return response.transactionId
}

fun getDidLastOperationInfo(did: Did): AtalaOperationInfo {
if (did.operationId.isEmpty()) {
throw Exception("Unable to find operation information because operation id was empty.")
}
val operationId = AtalaOperationId.fromHex(did.operationId.last())
val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
return runBlocking { nodeAuthApi.getOperationInfo(operationId) }
// fun getDidLastOperationInfo(did: Did): AtalaOperationInfo {
// if (did.operationId.isEmpty()) {
// throw Exception("Unable to find operation information because operation id was empty.")
// }
// val operationId = AtalaOperationId.fromHex(did.operationId.last())
// val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
// return runBlocking { nodeAuthApi.getOperationInfo(operationId) }
// }

fun getOperationInfo(operationId: String): AtalaOperationInfo {
// val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
return runBlocking { nodeAuthApi.getOperationInfo(AtalaOperationId.fromHex(operationId)) }
}

/**
Expand Down Expand Up @@ -126,6 +132,20 @@ class Dlt {
}
}

@OptIn(PrismSdkInternal::class)
private fun privateKeyMap(keyPaths: MutableList<KeyPath>, seed: ByteArray): Map<String, ECPrivateKey> {
val keyMap = mutableMapOf<String, ECPrivateKey>()
keyPaths.forEach {
keyMap[it.keyId] = KeyGenerator.deriveKeyFromFullPath(
seed,
it.didIdx,
PublicKeyUsage.fromProto(KeyUsage.fromValue(it.keyTypeValue)),
it.keyIdx
).privateKey
}
return keyMap
}

/**
* New did
*
Expand Down Expand Up @@ -302,18 +322,10 @@ class Dlt {
fun publishDid(did: Did, seed: ByteArray): Did {
val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
val prismDid = PrismDid.fromString(did.uriLongForm)
// Key pairs to get private keys
val masterKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_MASTER_KEY_ID)
val issuingKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_ISSUING_KEY_ID)
val revocationKeyPair = deriveKeyPair(did.keyPaths, seed, PrismDid.DEFAULT_REVOCATION_KEY_ID)
// TODO: refactor to allow publishing of DIDs with other key pairs arrangements (e.g. with no revocation key, multiple master keys, etc.)

val nodePayloadGenerator = NodePayloadGenerator(
prismDid as LongFormPrismDid,
mapOf(
PrismDid.DEFAULT_MASTER_KEY_ID to masterKeyPair.privateKey,
PrismDid.DEFAULT_ISSUING_KEY_ID to issuingKeyPair.privateKey,
PrismDid.DEFAULT_REVOCATION_KEY_ID to revocationKeyPair.privateKey
)
privateKeyMap(did.keyPaths, seed)
)
val createDidInfo = nodePayloadGenerator.createDid()
val createDidOperationId = runBlocking {
Expand Down Expand Up @@ -478,7 +490,8 @@ class Dlt {
}
// Update DID last operation hash
issuerDid.operationHash.add(credentialsInfo.operationHash.hexValue)
issuedCredential.operationId = issueCredentialsOperationId.hexValue()
issuerDid.operationId.add(issueCredentialsOperationId.hexValue())
issuedCredential.operationId.add(issueCredentialsOperationId.hexValue())
return Pair(issuerDid, issuedCredential)
}

Expand Down Expand Up @@ -515,33 +528,25 @@ class Dlt {
arrayOf(Sha256Digest.fromHex(credential.credentialHash))
)
}
credential.revoked = true
credential.operationId = revokeOperationId.hexValue()
issuerDid.operationHash.add(revokeInfo.operationHash.hexValue)
issuerDid.operationId.add(revokeOperationId.hexValue())
credential.operationId.add(revokeOperationId.hexValue())
return Pair(issuerDid, credential)
}

/**
* Verify issued credential
* Verify credential
*
* @param wallet Wallet containing the credential
* @param credentialAlias Alias of Credential to verify
* @return Verification result
*/
fun verifyIssuedCredential(wallet: Wallet, credentialAlias: String): List<String> {
val credentials = wallet.issuedCredentials.filter { it.alias == credentialAlias }
if (credentials.isNotEmpty()) {
val credential = credentials[0]
val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
val signed = JsonBasedCredential.fromString(credential.verifiedCredential.encodedSignedCredential)
// Use encodeDefaults to generate empty siblings field on proof
val format = Json { encodeDefaults = true }
val proof = MerkleInclusionProof.decode(format.encodeToString(credential.verifiedCredential.proof))

return runBlocking {
nodeAuthApi.verify(signed, proof).toMessageArray()
}
} else {
throw Exception("Credential '$credentialAlias' not found.")
fun verifyCredential(credential: Credential): List<String> {
val signed = JsonBasedCredential.fromString(credential.verifiedCredential.encodedSignedCredential)
// Use encodeDefaults to generate empty siblings field on proof
val format = Json { encodeDefaults = true }
val proof = MerkleInclusionProof.decode(format.encodeToString(credential.verifiedCredential.proof))

return runBlocking {
nodeAuthApi.verify(signed, proof).toMessageArray()
}
}

Expand All @@ -553,31 +558,6 @@ class Dlt {
return messages
}

/**
* Verify imported credential
*
* @param wallet Wallet containing the credential
* @param credentialAlias Alias of credential to verify
* @return Verification result
*/
fun verifyImportedCredential(wallet: Wallet, credentialAlias: String): List<String> {
val credentials = wallet.importedCredentials.filter { it.alias == credentialAlias }
if (credentials.isNotEmpty()) {
val credential = credentials[0]
val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())
val signed = JsonBasedCredential.fromString(credential.verifiedCredential.encodedSignedCredential)
// Use encodeDefaults to generate empty siblings field on proof
val format = Json { encodeDefaults = true }
val proof = MerkleInclusionProof.decode(format.encodeToString(credential.verifiedCredential.proof))

return runBlocking {
nodeAuthApi.verify(signed, proof).toMessageArray()
}
} else {
throw Exception("Credential '$credentialAlias' not found.")
}
}

/**
* Grpc config
* Done this way to allow programmatic override of the grpc config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import com.rootsid.wal.library.mongoimpl.document.BlockchainTxLogDocument
import com.rootsid.wal.library.wallet.model.BlockchainTxAction
import com.rootsid.wal.library.wallet.model.BlockchainTxLog
import com.rootsid.wal.library.wallet.storage.BlockchainTxLogStorage
import io.iohk.atala.prism.api.models.AtalaOperationStatus
import org.litote.kmongo.*
import java.time.LocalDateTime
import java.util.*

class BlockchainTxLogDocStorage(db: MongoDatabase? = null, collectionName: String = "tx_logs") : BlockchainTxLogStorage {
Expand All @@ -20,7 +22,8 @@ class BlockchainTxLogDocStorage(db: MongoDatabase? = null, collectionName: Strin
}

override fun createTxLogObject(txLogId: String, walletId: String, action: BlockchainTxAction, description: String?): BlockchainTxLog {
return BlockchainTxLogDocument(txLogId, walletId, action, description)
val now = LocalDateTime.now()
return BlockchainTxLogDocument(txLogId, walletId, action, description, now, now)
}

override fun insert(txLog: BlockchainTxLog): BlockchainTxLog {
Expand All @@ -36,6 +39,11 @@ class BlockchainTxLogDocStorage(db: MongoDatabase? = null, collectionName: Strin
return txLogCollection.find().toList()
}

override fun listPending(): List<BlockchainTxLog> {
val pending = listOf(AtalaOperationStatus.AWAIT_CONFIRMATION, AtalaOperationStatus.PENDING_SUBMISSION)
return txLogCollection.find(BlockchainTxLog::status `in` pending).toList()
}

/**
* Update tx log with new entry *
* @param txLog updated tx log data object
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package com.rootsid.wal.library.mongoimpl.document

import com.rootsid.wal.library.utils.LocalDateTimeSerializer
import com.rootsid.wal.library.wallet.model.BlockchainTxAction
import com.rootsid.wal.library.wallet.model.BlockchainTxLog
import io.iohk.atala.prism.api.models.AtalaOperationStatus
import io.iohk.atala.prism.api.models.AtalaOperationStatusEnum
import kotlinx.serialization.Serializable
import java.time.LocalDateTime

data class BlockchainTxLogDocument(
override val _id: String,
override val walletId: String,
override val action: BlockchainTxAction,
override var description: String? = null,
@Serializable(with = LocalDateTimeSerializer::class)
override val createdAt: LocalDateTime = LocalDateTime.now(),
@Serializable(with = LocalDateTimeSerializer::class)
override var updatedAt: LocalDateTime = LocalDateTime.now(),
override var status: AtalaOperationStatusEnum = AtalaOperationStatus.PENDING_SUBMISSION,
override var txId: String? = null,
override var url: String? = null
// val walletId: String
// override var logEntries: MutableList<BlockchainTxLogEntry> = mutableListOf()
) : BlockchainTxLog
Loading

0 comments on commit 7b68746

Please sign in to comment.