Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

45 enable tracking of blockchain txs #47

Merged
merged 5 commits into from
Sep 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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