Skip to content

Commit

Permalink
45 enable tracking of blockchain txs (#47)
Browse files Browse the repository at this point in the history
* enable tracking of logs and blockchain txs

* update gradle-properties version

* fix bugs

- replace ifBlank with ifEmpty
- fix getDidPublishOperationStatus call

* resolve PR #47 comments
  • Loading branch information
Essbante authored Sep 17, 2022
1 parent 93557ce commit e02d29b
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 77 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version=2.0.4-SNAPSHOT
version=2.0.5-SNAPSHOT

kotlin.code.style=official
57 changes: 18 additions & 39 deletions src/main/kotlin/com/rootsid/wal/library/dlt/Dlt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import io.iohk.atala.prism.api.VerificationResult
import io.iohk.atala.prism.api.models.AtalaOperationId
import io.iohk.atala.prism.api.models.AtalaOperationInfo
import io.iohk.atala.prism.api.models.AtalaOperationStatus
import io.iohk.atala.prism.api.models.AtalaOperationStatusEnum
import io.iohk.atala.prism.api.node.NodeAuthApiImpl
import io.iohk.atala.prism.api.node.NodePayloadGenerator
import io.iohk.atala.prism.api.node.NodePublicApi
Expand Down Expand Up @@ -48,32 +47,12 @@ class Dlt {
return response.transactionId
}

fun getDidPublishOperationInfo(did: Did): AtalaOperationStatusEnum {
val operationId = did.publishedOperationId
if ("" == operationId) {
fun getDidLastOperationInfo(did: Did): AtalaOperationInfo {
if (did.operationId.isEmpty()) {
throw Exception("Unable to find operation information because operation id was empty.")
}
val operationInfo = getOperationInfo(AtalaOperationId.fromHex(operationId))
operationInfo.transactionId?.let {
BlockchainTxLogEntry(
it,
BlockchainTxAction.PUBLISH_DID,
"${TESTNET_URL}$it",
did.alias
)
}
return operationInfo.status
}

/**
* Get operation information using the operationId
*
* @param operationId operation identifier
* @return operation information from the blockchain transaction
*/
private fun getOperationInfo(operationId: AtalaOperationId): AtalaOperationInfo {
val operationId = AtalaOperationId.fromHex(did.operationId.last())
val nodeAuthApi = NodeAuthApiImpl(GrpcConfig.options())

return runBlocking { nodeAuthApi.getOperationInfo(operationId) }
}

Expand All @@ -86,7 +65,7 @@ class Dlt {
* @param description additional details
* @return Log Entry containing details of the operation and the blockchain transaction
*/
private fun waitForSubmission(nodePublicApi: NodePublicApi, operationId: AtalaOperationId, action: BlockchainTxAction, description: String): BlockchainTxLogEntry {
private fun waitForSubmission(nodePublicApi: NodePublicApi, operationId: AtalaOperationId): String {
var status = runBlocking {
nodePublicApi.getOperationInfo(operationId).status
}
Expand All @@ -97,9 +76,9 @@ class Dlt {
nodePublicApi.getOperationInfo(operationId).status
}
}
val tid = transactionId(operationId)
println("Track the transaction in:\n- ${Constant.TESTNET_URL}$tid\n")
return BlockchainTxLogEntry(tid, action, "${Constant.TESTNET_URL}$tid", description)
val txId = transactionId(operationId)
println("Track the transaction in:\n- ${Constant.TESTNET_URL}$txId\n")
return txId
}

/**
Expand Down Expand Up @@ -344,8 +323,8 @@ class Dlt {
PrismDid.DEFAULT_MASTER_KEY_ID
)
}
did.operationHash = createDidInfo.operationHash.hexValue
did.operationId = createDidOperationId.hexValue()
did.operationHash.add(createDidInfo.operationHash.hexValue)
did.operationId.add(createDidOperationId.hexValue())

return did
}
Expand Down Expand Up @@ -388,7 +367,7 @@ class Dlt {
DidPublicKey(keyId, PublicKeyUsage.fromProto(keyUsage), newKeyPair.publicKey)
)
val updateDidInfo = nodePayloadGenerator.updateDid(
previousHash = Sha256Digest.fromHex(did.operationHash),
previousHash = Sha256Digest.fromHex(did.operationHash.last()),
masterKeyId = PrismDid.DEFAULT_MASTER_KEY_ID,
keysToAdd = arrayOf(newKeyInfo)
)
Expand All @@ -397,14 +376,14 @@ class Dlt {
payload = updateDidInfo.payload,
did = PrismDid.fromString(did.uriCanonical).asCanonical(),
masterKeyId = PrismDid.DEFAULT_MASTER_KEY_ID,
previousOperationHash = Sha256Digest.fromHex(did.operationHash),
previousOperationHash = Sha256Digest.fromHex(did.operationHash.last()),
keysToAdd = arrayOf(newKeyInfo),
keysToRevoke = arrayOf()
)
}
did.operationHash = updateDidInfo.operationHash.hexValue
did.operationHash.add(updateDidInfo.operationHash.hexValue)
did.keyPaths.add(newKeyPairData)
did.operationId = updateDidOperationId.hexValue()
did.operationId.add(updateDidOperationId.hexValue())
return did
}

Expand All @@ -430,7 +409,7 @@ class Dlt {
mapOf(PrismDid.DEFAULT_MASTER_KEY_ID to masterKeyPair.privateKey)
)
val updateDidInfo = nodePayloadGenerator.updateDid(
previousHash = Sha256Digest.fromHex(did.operationHash),
previousHash = Sha256Digest.fromHex(did.operationHash.last()),
masterKeyId = PrismDid.DEFAULT_MASTER_KEY_ID,
keysToRevoke = arrayOf(keyId)
)
Expand All @@ -439,16 +418,16 @@ class Dlt {
payload = updateDidInfo.payload,
did = PrismDid.fromString(did.uriCanonical).asCanonical(),
masterKeyId = PrismDid.DEFAULT_MASTER_KEY_ID,
previousOperationHash = Sha256Digest.fromHex(did.operationHash),
previousOperationHash = Sha256Digest.fromHex(did.operationHash.last()),
keysToAdd = arrayOf(),
keysToRevoke = arrayOf(keyId)
)
}
// Key revocation flag
keyPairList[0].revoked = true
// Update DID last operation hash
did.operationHash = updateDidInfo.operationHash.hexValue
did.operationId = updateDidOperationId.hexValue()
did.operationHash.add(updateDidInfo.operationHash.hexValue)
did.operationId.add(updateDidOperationId.hexValue())
return did
} else {
throw NoSuchElementException("Key identifier '$keyId' not found.")
Expand Down Expand Up @@ -498,7 +477,7 @@ class Dlt {
)
}
// Update DID last operation hash
issuerDid.operationHash = credentialsInfo.operationHash.hexValue
issuerDid.operationHash.add(credentialsInfo.operationHash.hexValue)
issuedCredential.operationId = issueCredentialsOperationId.hexValue()
return Pair(issuerDid, issuedCredential)
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/rootsid/wal/library/dlt/model/Did.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ data class Did(
val uriCanonical: String,
val uriLongForm: String,
var keyPaths: MutableList<KeyPath> = mutableListOf(),
var operationId: String = "",
var operationHash: String = "",
var publishedStatus: AtalaOperationStatusEnum = AtalaOperationStatus.UNKNOWN_OPERATION,
var publishedOperationId: String = ""
var operationId: MutableList<String> = mutableListOf(),
var operationHash: MutableList<String> = mutableListOf(),
// var publishedStatus: AtalaOperationStatusEnum = AtalaOperationStatus.UNKNOWN_OPERATION
// var publishedOperationId: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.rootsid.wal.library.mongoimpl

import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import com.rootsid.wal.library.mongoimpl.config.DefaultMongoDbConn
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 org.litote.kmongo.*
import java.util.*

class BlockchainTxLogDocStorage(db: MongoDatabase? = null, collectionName: String = "tx_logs") : BlockchainTxLogStorage {
private val txLogCollection: MongoCollection<BlockchainTxLogDocument>

init {
val mongoConn = db ?: DefaultMongoDbConn.open()

this.txLogCollection = mongoConn.getCollection<BlockchainTxLogDocument>(collectionName)
}

override fun createTxLogObject(txLogId: String, walletId: String, action: BlockchainTxAction, description: String?): BlockchainTxLog {
return BlockchainTxLogDocument(txLogId, walletId, action, description)
}

override fun insert(txLog: BlockchainTxLog): BlockchainTxLog {
val result = txLogCollection.insertOne(txLog as BlockchainTxLogDocument)

if (result.wasAcknowledged()) {
return txLog
}
throw Exception("Failed to insert blockchain transaction log")
}

override fun list(): List<BlockchainTxLog> {
return txLogCollection.find().toList()
}

/**
* Update tx log with new entry *
* @param txLog updated tx log data object
* @return true if the operation was acknowledged
*/
override fun update(txLog: BlockchainTxLog): Boolean {
val result = txLogCollection.updateOne(txLog as BlockchainTxLogDocument, upsert())
return result.wasAcknowledged()
}

override fun findById(txLogId: String): BlockchainTxLog {
return txLogCollection.findOneById(txLogId) ?: throw NoSuchElementException("No tx log found with id $txLogId")
}

override fun exists(txLogId: String): Boolean {
return txLogCollection.findOneById(txLogId) != null
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.rootsid.wal.library.wallet.storage.WalletStorage
import org.litote.kmongo.*
import java.util.*


class WalletDocStorage(db: MongoDatabase? = null, collectionName: String = "wallet") : WalletStorage {
private val walletCollection: MongoCollection<WalletDocument>

Expand Down Expand Up @@ -82,7 +81,8 @@ class WalletDocStorage(db: MongoDatabase? = null, collectionName: String = "wall
override fun findDidByAlias(walletId: String, alias: String): Optional<Did> {
return Optional.ofNullable(
walletCollection.findOne("{_id:'$walletId','dids':{${MongoOperator.elemMatch}: {'alias':'$alias'}}}")
?.dids?.firstOrNull { it.alias.equals(alias, true) })
?.dids?.firstOrNull { it.alias.equals(alias, true) }
)
}

override fun listDids(walletId: String): List<Did> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.rootsid.wal.library.mongoimpl.document

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

data class BlockchainTxLogDocument(
override val _id: String,
override val walletId: String,
override val action: BlockchainTxAction,
override var description: String? = null,
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
49 changes: 30 additions & 19 deletions src/main/kotlin/com/rootsid/wal/library/wallet/WalletService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package com.rootsid.wal.library.wallet
import com.rootsid.wal.library.Constant
import com.rootsid.wal.library.dlt.Dlt
import com.rootsid.wal.library.dlt.model.Did
import com.rootsid.wal.library.wallet.model.Wallet
import com.rootsid.wal.library.wallet.model.addDid
import com.rootsid.wal.library.wallet.model.*
import com.rootsid.wal.library.wallet.storage.BlockchainTxLogStorage
import com.rootsid.wal.library.wallet.storage.WalletStorage
import io.iohk.atala.prism.api.models.AtalaOperationStatus
import io.iohk.atala.prism.api.models.AtalaOperationStatusEnum
import io.iohk.atala.prism.api.node.PrismDidState
import io.iohk.atala.prism.common.PrismSdkInternal
Expand All @@ -17,7 +16,7 @@ import io.iohk.atala.prism.protos.DIDData
import kotlinx.serialization.json.JsonObject
import java.util.*

class WalletService(private val walletStorage: WalletStorage, private val dlt: Dlt) {
class WalletService(private val walletStorage: WalletStorage, private val txLogStorage: BlockchainTxLogStorage, private val dlt: Dlt) {
/**
* New wallet
*
Expand All @@ -27,7 +26,7 @@ class WalletService(private val walletStorage: WalletStorage, private val dlt: D
* @return a new wallet
*/
fun createWallet(id: String, mnemonic: String, passphrase: String): Wallet {
if (walletStorage.exists(id)) {
if (walletStorage.exists(id) || txLogStorage.exists(id)) {
throw RuntimeException("Duplicated Wallet identifier")
}

Expand Down Expand Up @@ -88,15 +87,15 @@ class WalletService(private val walletStorage: WalletStorage, private val dlt: D
*/
fun createDid(walletId: String, didAlias: String = UUID.randomUUID().toString(), issuer: Boolean = false): Did {
this.findWalletById(walletId)
.let { w ->
if (w.dids.any { it.alias.equals(didAlias, true) }) {
.let { wallet ->
if (wallet.dids.any { it.alias.equals(didAlias, true) }) {
throw RuntimeException("Duplicated DID alias")
}

val newDid = dlt.newDid(didAlias, w.dids.size, BytesOps.hexToBytes(w.seed), issuer)
w.addDid(newDid)
val newDid = dlt.newDid(didAlias, wallet.dids.size, BytesOps.hexToBytes(wallet.seed), issuer)
wallet.addDid(newDid)

walletStorage.update(w)
walletStorage.update(wallet)
println("DID created")

return newDid
Expand Down Expand Up @@ -128,13 +127,21 @@ class WalletService(private val walletStorage: WalletStorage, private val dlt: D
?.let { did ->
val didUpdate = dlt.publishDid(did, BytesOps.hexToBytes(wallet.seed))

did.publishedStatus = AtalaOperationStatus.PENDING_SUBMISSION
//did.publishedStatus = AtalaOperationStatus.PENDING_SUBMISSION
did.operationHash =
didUpdate.operationHash.ifBlank { throw RuntimeException("Unable to find operation id.") }
didUpdate.operationHash.ifEmpty { throw RuntimeException("Unable to find operation id.") }
did.operationId =
didUpdate.operationId.ifBlank { throw RuntimeException("Unable to find operation id.") }
didUpdate.operationId.ifEmpty { throw RuntimeException("Unable to find operation id.") }

walletStorage.update(wallet)
txLogStorage.insert(
txLogStorage.createTxLogObject(
did.operationId.toString(),
walletId,
BlockchainTxAction.PUBLISH_DID,
"Publish DID: $didAlias"
)
)
println("DID '$didAlias' published.")

return did
Expand All @@ -161,15 +168,19 @@ class WalletService(private val walletStorage: WalletStorage, private val dlt: D
}

fun getDidPublishOperationStatus(walletId: String, didAlias: String): AtalaOperationStatusEnum {
return this.getDidPublishOperationStatus(findWalletById(walletId), didAlias)
return this.getDidLastOperationStatus(findWalletById(walletId), didAlias)
}

fun getDidPublishOperationStatus(wallet: Wallet, didAlias: String): AtalaOperationStatusEnum {
fun getDidLastOperationStatus(wallet: Wallet, didAlias: String): AtalaOperationStatusEnum {
wallet.dids.firstOrNull { it.alias.equals(didAlias, true) }
?.let { d ->
val publishOperationInfo = dlt.getDidPublishOperationInfo(d)
d.publishedStatus = publishOperationInfo
return publishOperationInfo
?.let { did ->
val publishOperationInfo = dlt.getDidLastOperationInfo(did)
// did.publishedStatus = publishOperationInfo.status
return publishOperationInfo.status
} ?: throw RuntimeException("Did alias '$didAlias' not found")
}

fun updateTxLogOperationInfo(operationId: String) {
// TODO: update txLog operation info
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
package com.rootsid.wal.library.wallet.model

interface BlockchainTxLog {
import io.iohk.atala.prism.api.models.AtalaOperationStatusEnum
import java.io.Serializable

interface BlockchainTxLog : Serializable {
// use operationId as the primary key
val _id: String
val walletId: String
var logEntries: MutableList<BlockchainTxLogEntry>

// var logEntries: MutableList<BlockchainTxLogEntry>
val action: BlockchainTxAction
var status: AtalaOperationStatusEnum
var description: String?
var txId: String?
var url: String?
}

/**
* Add blockchain tx log
*
* @param entry blockchain transaction log entry
*/
fun BlockchainTxLog.addBlockchainTxLog(entry: BlockchainTxLogEntry) {
logEntries.add(entry)
}
// fun BlockchainTxLog.addBlockchainTxLog(entry: BlockchainTxLogEntry) {
// logEntries.add(entry)
// }
Loading

0 comments on commit e02d29b

Please sign in to comment.