Common Patterns

Version: 1.0.0-SNAPSHOT Learn common usage patterns and best practices for TrustWeave.

Table of Contents

  1. Issuer → Holder → Verifier Workflow
  2. Batch Operations
  3. Error Recovery with Fallbacks
  4. Credential Lifecycle Management
  5. Multi-Chain Anchoring
  6. Wallet Organization Patterns

Issuer → Holder → Verifier Workflow

Complete workflow showing all three parties in a credential ecosystem. This example uses production-ready error handling patterns with fold().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.example.patterns.workflow

import com.trustweave.TrustWeave
import com.trustweave.core.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

fun main() = runBlocking {
    val trustweave = TrustWeave.create()

    // ============================================
    // 1. ISSUER: Create DID and issue credential
    // ============================================
    val issuerDidDoc = try {
        trustWeave.createDid { method("key") }
    } catch (error: Exception) {
        when (error) {
            is IllegalStateException -> {
                if (error.message?.contains("not configured") == true) {
                println("❌ DID method not registered: ${error.method}")
                println("   Available methods: ${error.availableMethods}")
            }
            else -> {
                println("❌ Failed to create issuer DID: ${error.message}")
            }
        }
        return@runBlocking
    }

    val issuerDid = issuerDidDoc.id
    val issuerKeyId = issuerDidDoc.verificationMethod.firstOrNull()?.id
        ?: error("No verification method found")

    val credential = try {
        trustWeave.issue {
            credential {
                issuer(issuerDid)
                subject {
                    id("did:key:holder-123")
                put("name", "Alice")
                put("degree", "Bachelor of Science")
            },
            config = IssuanceConfig(
                proofType = ProofType.Ed25519Signature2020,
                keyId = issuerKeyId,
                issuerDid = issuerDid
            ),
            types = listOf("VerifiableCredential", "DegreeCredential")
        )
    } catch (error: TrustWeaveError) {
        when (error) {
            is TrustWeaveError.CredentialInvalid -> {
                println("❌ Credential issuance failed: ${error.reason}")
            }
            is TrustWeaveError.InvalidDidFormat -> {
                println("❌ Invalid issuer DID format: ${error.reason}")
            }
            else -> {
                println("❌ Failed to issue credential: ${error.message}")
            }
        }
        return@runBlocking
    }

    println("✅ Issuer created credential: ${credential.id}")

    // ============================================
    // 2. HOLDER: Store credential in wallet
    // ============================================
    val holderDid = try {
        trustWeave.createDid { method("key") }
    } catch (error: Exception) {
        println("❌ Failed to create holder DID: ${error.message}")
        return@runBlocking
    }

    val wallet = try {
        trustweave.wallets.create(holderDid = holderDid)
    } catch (error: TrustWeaveError) {
        when (error) {
            is TrustWeaveError.WalletCreationFailed -> {
                println("❌ Wallet creation failed: ${error.reason}")
                println("   Provider: ${error.provider}")
            }
            else -> {
                println("❌ Failed to create wallet: ${error.message}")
            }
        }
        return@runBlocking
    }

    val credentialId = wallet.store(credential)
    println("✅ Holder stored credential: $credentialId")

    // ============================================
    // 3. VERIFIER: Verify credential
    // ============================================
    val verification = try {
        trustWeave.verify {
            credential(credential)
        }
    } catch (error: Exception) {
        when (error) {
            is IllegalStateException -> {
                println("❌ Credential validation failed: ${error.reason}")
                println("   Field: ${error.field}")
            }
            else -> {
                println("❌ Verification failed: ${error.message}")
            }
        }
        return@runBlocking
    }

    if (verification.valid) {
        println("✅ Verifier confirmed credential is valid")
        println("   Proof valid: ${verification.proofValid}")
        println("   Issuer valid: ${verification.issuerValid}")
        println("   Not revoked: ${verification.notRevoked}")

        if (verification.warnings.isNotEmpty()) {
            println("   Warnings: ${verification.warnings.joinToString()}")
        }
    } else {
        println("❌ Verifier rejected credential")
        println("   Errors: ${verification.errors.joinToString()}")
    }
}

Key Points:

  • Each party has their own DID
  • Issuer signs the credential with their key
  • Holder stores the credential in their wallet
  • Verifier checks the credential without contacting issuer

Workflow Diagram

sequenceDiagram
    participant I as Issuer
    participant VC as TrustWeave
    participant H as Holder
    participant V as Verifier

    Note over I: 1. Issuer Setup
    I->>VC: createDid()
    VC-->>I: DidDocument

    Note over I: 2. Credential Issuance
    I->>VC: issueCredential(issuerDid, claims)
    VC->>VC: Resolve issuer DID
    VC->>VC: Sign credential with issuer key
    VC-->>I: VerifiableCredential (with proof)

    Note over I,H: 3. Credential Transfer
    I->>H: Send VerifiableCredential

    Note over H: 4. Holder Storage
    H->>VC: createWallet(holderDid)
    VC-->>H: Wallet
    H->>VC: wallet.store(credential)
    VC-->>H: credentialId

    Note over H,V: 5. Credential Presentation
    H->>V: Present VerifiableCredential

    Note over V: 6. Verification
    V->>VC: verifyCredential(credential)
    VC->>VC: Resolve issuer DID
    VC->>VC: Verify proof signature
    VC->>VC: Check expiration/revocation
    VC-->>V: CredentialVerificationResult

    alt Credential Valid
        V->>V: Accept credential
    else Credential Invalid
        V->>V: Reject credential
    end

Batch Operations

Process multiple DIDs or credentials efficiently using coroutines.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.example.patterns.batch

import com.trustweave.TrustWeave
import com.trustweave.core.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val trustweave = TrustWeave.create()

    // Batch DID resolution
    val dids = listOf(
        "did:key:z6Mk...",
        "did:key:z6Mk...",
        "did:key:z6Mk..."
    )

    val results = dids.mapAsync { did ->
        runCatching {
            trustWeave.resolveDid(did)
        }
    }

    results.forEachIndexed { index, result ->
        result.fold(
            onSuccess = { resolution ->
                println("✅ DID ${index + 1} resolved: ${resolution.document?.id}")
            },
            onFailure = { error ->
                println("❌ DID ${index + 1} failed: ${error.message}")
            }
        )
    }

    // Batch credential creation
    import com.trustweave.trust.types.DidCreationResult
    import com.trustweave.trust.types.IssuanceResult
    
    val credentials = (1..10).mapAsync { index ->
        runCatching {
            val didResult = trustWeave.createDid { method("key") }
            val issuerDid = when (didResult) {
                is DidCreationResult.Success -> didResult.did
                else -> throw IllegalStateException("Failed to create DID: ${didResult.reason}")
            }
            
            val issuerResolution = trustWeave.resolveDid(issuerDid)
            val issuerDoc = when (issuerResolution) {
                is DidResolutionResult.Success -> issuerResolution.document
                else -> throw IllegalStateException("Failed to resolve issuer DID")
            }
            val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
                ?: throw IllegalStateException("No verification method found")
            
            val issuanceResult = trustWeave.issue {
                credential {
                    issuer(issuerDid.value)
                    subject {
                        id("did:key:holder-$index")
                        "index" to index
                    }
                    issued(Instant.now())
                }
                signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
            }
            
            when (issuanceResult) {
                is IssuanceResult.Success -> issuanceResult.credential
                else -> throw IllegalStateException("Failed to issue credential: ${issuanceResult.reason}")
            }
        }
    }

    val successful = credentials.filter { it.isSuccess }
    val failed = credentials.filter { it.isFailure }

    println("Created ${successful.size} credentials out of ${credentials.size}")
    if (failed.isNotEmpty()) {
        println("Failed: ${failed.size} credentials")
        failed.forEach { result ->
            result.fold(
                onSuccess = { },
                onFailure = { error ->
                    println("  Error: ${error.message}")
                }
            )
        }
    }
}

Key Points:

  • Use mapAsync for parallel operations
  • Handle each result individually
  • Filter successful results for further processing

Error Recovery with Fallbacks

Handle errors gracefully with fallback strategies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.example.patterns.recovery

import com.trustweave.TrustWeave
import com.trustweave.core.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val TrustWeave = TrustWeave.create()

    // Pattern: Try multiple DID methods with fallback
    import com.trustweave.trust.types.DidCreationResult
    
    suspend fun createDidWithFallback(methods: List<String>): Did? {
        for (method in methods) {
            val didResult = trustWeave.createDid { method(method) }
            when (didResult) {
                is DidCreationResult.Success -> return didResult.did
                is DidCreationResult.Failure.MethodNotRegistered -> {
                    println("Method '$method' not available, trying next...")
                    // Continue to next method
                }
                else -> {
                    println("Unexpected error with method '$method': ${didResult.reason}")
                    // Try next method anyway
                }
            }
        }
        return null
    }

    val did = createDidWithFallback(listOf("web", "ion", "key"))
        ?: error("All DID methods failed")

    println("✅ Created DID: ${did.id}")

    // Pattern: Retry with exponential backoff
    suspend fun resolveDidWithRetry(did: String, maxRetries: Int = 3): DidResolutionResult? {
        var lastError: Throwable? = null

        for (attempt in 1..maxRetries) {
            val resolution = trustWeave.resolveDid(did)
            when (resolution) {
                is DidResolutionResult.Success -> return resolution
                is DidResolutionResult.Failure -> {
                    lastError = Exception(resolution.reason)
                    if (attempt < maxRetries) {
                        val delay = (attempt * attempt * 100).toLong() // Exponential backoff
                        kotlinx.coroutines.delay(delay)
                        println("Retry $attempt/$maxRetries after ${delay}ms...")
                    }
                }
            )
        }

        println("Failed after $maxRetries attempts: ${lastError?.message}")
        return null
    }
}

Key Points:

  • Try multiple methods/strategies in order
  • Use exponential backoff for retries
  • Always have a fallback plan
  • Log attempts for debugging

Credential Lifecycle Management

Manage credentials through their entire lifecycle: issuance, storage, presentation, verification, and expiration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package com.example.patterns.lifecycle

import com.trustweave.TrustWeave
import com.trustweave.core.*
import com.trustweave.credential.PresentationOptions
import com.trustweave.spi.services.WalletCreationOptionsBuilder
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import java.time.Instant
import java.time.temporal.ChronoUnit

fun main() = runBlocking {
    val TrustWeave = TrustWeave.create()

    // Create issuer and holder
    val issuerDidResult = trustWeave.createDid { method("key") }
    val issuerDid = when (issuerDidResult) {
        is DidCreationResult.Success -> issuerDidResult.did
        else -> {
            println("Failed to create issuer DID: ${issuerDidResult.reason}")
            return@runBlocking
        }
    }
    
    val holderDidResult = trustWeave.createDid { method("key") }
    val holderDid = when (holderDidResult) {
        is DidCreationResult.Success -> holderDidResult.did
        else -> {
            println("Failed to create holder DID: ${holderDidResult.reason}")
            return@runBlocking
        }
    }

    // Issue credential with expiration
    val expirationDate = Instant.now().plus(1, ChronoUnit.YEARS)
    val issuerResolution = trustWeave.resolveDid(issuerDid)
    val issuerDoc = when (issuerResolution) {
        is DidResolutionResult.Success -> issuerResolution.document
        else -> throw IllegalStateException("Failed to resolve issuer DID")
    }
    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
        ?: throw IllegalStateException("No verification method found")
    
    val issuanceResult = trustWeave.issue {
        credential {
            issuer(issuerDid.value)
            subject {
                id(holderDid.value)
                "name" to "Alice"
            }
            issued(Instant.now())
            expires(expirationDate)
        }
        signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
    }
    
    val credential = when (issuanceResult) {
        is IssuanceResult.Success -> issuanceResult.credential
        else -> {
            println("❌ Failed to issue credential: ${issuanceResult.reason}")
            return@runBlocking
        }
    }

    // Store in wallet with lifecycle support
    val walletResult = trustWeave.wallet {
        holder(holderDid.value)
        enableOrganization()
        enablePresentation()
    }
    
    val wallet = when (walletResult) {
        is WalletCreationResult.Success -> walletResult.wallet
        else -> {
            println("❌ Failed to create wallet: ${walletResult.reason}")
            return@runBlocking
        }
    }

    val credentialId = wallet.store(credential)

    // Organize credential
    wallet.withOrganization { org ->
        val collectionId = org.createCollection("Education", "Academic credentials")
        org.addToCollection(credentialId, collectionId)
        org.tagCredential(credentialId, setOf("degree", "verified"))
    }

    // Create presentation when needed
    val presentation = wallet.withPresentation { pres ->
        pres.createPresentation(
            credentialIds = listOf(credentialId),
            holderDid = holderDid.id,
            options = PresentationOptions(
                holderDid = holderDid.id,
                challenge = "job-application-${System.currentTimeMillis()}"
            )
        )
    }

    // Verify before using
    val verification = try {
        trustWeave.verify {
            credential(credential)
        }
    } catch (error: Exception) {
        println("❌ Verification failed: ${error.message}")
        return@runBlocking
    }

    when (verification) {
        is VerificationResult.Valid -> {
            // Credential is valid, continue
        }
        is VerificationResult.Invalid -> {
        println("⚠️ Credential invalid: ${verification.errors.joinToString()}")
        return@runBlocking
    }

    // Check expiration
    if (credential.expirationDate != null) {
        val expiration = Instant.parse(credential.expirationDate)
        if (expiration.isBefore(Instant.now())) {
            println("⚠️ Credential expired on ${credential.expirationDate}")
            // Archive expired credential
            wallet.withLifecycle { lifecycle ->
                lifecycle.archive(credentialId)
            }
        }
    }

    println("✅ Credential lifecycle managed successfully")
}

Key Points:

  • Always set expiration dates for time-sensitive credentials
  • Organize credentials for easy retrieval
  • Verify credentials before use
  • Archive expired credentials
  • Use presentations for selective disclosure

Multi-Chain Anchoring

Anchor the same credential to multiple blockchains for redundancy and interoperability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.example.patterns.multichain

import com.trustweave.TrustWeave
import com.trustweave.anchor.BlockchainAnchorRegistry
import com.trustweave.core.*
import com.trustweave.credential.models.VerifiableCredential
import com.trustweave.testkit.anchor.InMemoryBlockchainAnchorClient
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement

fun main() = runBlocking {
    val TrustWeave = TrustWeave.create {
        blockchains {
            "algorand:testnet" to InMemoryBlockchainAnchorClient("algorand:testnet")
            "polygon:testnet" to InMemoryBlockchainAnchorClient("polygon:testnet")
        }
    }

    // Issue credential
    val issuerDidResult = trustWeave.createDid { method("key") }
    val issuerDid = when (issuerDidResult) {
        is DidCreationResult.Success -> issuerDidResult.did
        else -> {
            println("❌ Failed to create issuer DID: ${issuerDidResult.reason}")
            return@runBlocking
        }
    }

    val issuerResolution = trustWeave.resolveDid(issuerDid)
    val issuerDoc = when (issuerResolution) {
        is DidResolutionResult.Success -> issuerResolution.document
        else -> throw IllegalStateException("Failed to resolve issuer DID")
    }
    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
        ?: throw IllegalStateException("No verification method found")

    val issuanceResult = trustWeave.issue {
        credential {
            issuer(issuerDid.value)
            subject {
                id("did:key:holder")
                "data" to "important-data"
            }
            issued(Instant.now())
        }
        signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
    }
    
    val credential = when (issuanceResult) {
        is IssuanceResult.Success -> issuanceResult.credential
        else -> {
            println("❌ Failed to issue credential: ${issuanceResult.reason}")
            return@runBlocking
        }
    }

    // Anchor to multiple chains
    val chains = listOf("algorand:testnet", "polygon:testnet")
    val anchorResults = chains.mapNotNull { chainId ->
        try {
            val anchor = trustweave.blockchains.anchor(
                data = credential,
                serializer = VerifiableCredential.serializer(),
                chainId = chainId
            )
            println("✅ Anchored to $chainId: ${anchor.ref.txHash}")
            anchor
        } catch (error: TrustWeaveError) {
            println("❌ Failed to anchor to $chainId: ${error.message}")
            null
        }
    }

    println("Anchored to ${anchorResults.size} out of ${chains.size} chains")

    // Store all anchor references
    val anchorRefs = anchorResults.map { it.ref }
    // In production, store these in a database for later verification
}

Key Points:

  • Anchor to multiple chains for redundancy
  • Handle failures gracefully (some chains may be unavailable)
  • Store all anchor references for verification
  • Use different chains for different use cases

Wallet Organization Patterns

Organize credentials efficiently using collections, tags, and metadata.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package com.example.patterns.organization

import com.trustweave.TrustWeave
import com.trustweave.core.*
import com.trustweave.spi.services.WalletCreationOptionsBuilder
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

fun main() = runBlocking {
    val trustweave = TrustWeave.create()

    val holderDid = try {
        trustWeave.createDid { method("key") }
    } catch (error: Exception) {
        println("❌ Failed to create holder DID: ${error.message}")
        return@runBlocking
    }

    val wallet = try {
        trustWeave.wallet {
            holder(holderDid.value)
            type("inMemory")
        }
    } catch (error: Exception) {
        println("❌ Failed to create wallet: ${error.message}")
        return@runBlocking
    }

    // Issue multiple credentials
    val issuerDid = try {
        trustWeave.createDid { method("key") }
    } catch (error: Exception) {
        println("❌ Failed to create issuer DID: ${error.message}")
        return@runBlocking
    }

    val issuerResolution = trustWeave.resolveDid(issuerDid)
    val issuerDoc = when (issuerResolution) {
        is DidResolutionResult.Success -> issuerResolution.document
        else -> throw IllegalStateException("Failed to resolve issuer DID")
    }
    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
        ?: throw IllegalStateException("No verification method found")

    val credentials = listOf(
        "Bachelor of Science" to "education",
        "Professional License" to "professional",
        "Membership Card" to "membership"
    )

    val credentialIds = credentials.mapNotNull { (name, category) ->
        val credential = try {
            trustWeave.issue {
                credential {
                    issuer(issuerDid.value)
                    subject {
                        id(holderDid.value)
                        "credentialName" to name
                    }
                    issued(Instant.now())
                }
                signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
            }
                    issuerDid = issuerDid.id
                ),
                types = listOf("VerifiableCredential", "${category}Credential")
            )
        } catch (error: TrustWeaveError) {
            println("❌ Failed to issue credential '$name': ${error.message}")
            return@mapNotNull null
        }

        val credentialId = wallet.store(credential)

        // Organize immediately after storage
        wallet.withOrganization { org ->
            // Create collection by category
            val collectionId = org.createCollection(
                name = category.capitalize(),
                description = "$category credentials"
            )
            org.addToCollection(credentialId, collectionId)

            // Add tags
            org.tagCredential(credentialId, setOf(category, "verified", "active"))

            // Add metadata
            org.addMetadata(credentialId, mapOf(
                "category" to category,
                "storedAt" to System.currentTimeMillis()
            ))
        }

        credentialId
    }

    // Query by collection
    wallet.withOrganization { org ->
        val educationCollection = org.listCollections()
            .firstOrNull { it.name == "Education" }

        if (educationCollection != null) {
            val educationCreds = org.getCredentialsInCollection(educationCollection.id)
            println("Education credentials: ${educationCreds.size}")
        }
    }

    // Query by tag
    wallet.withOrganization { org ->
        val verifiedCreds = org.findByTag("verified")
        println("Verified credentials: ${verifiedCreds.size}")
    }

    // Get statistics
    val stats = wallet.getStatistics()
    println("Wallet stats:")
    println("  Total: ${stats.totalCredentials}")
    println("  Collections: ${stats.collectionsCount}")
    println("  Tags: ${stats.tagsCount}")
}

Key Points:

  • Organize credentials immediately after storage
  • Use collections for broad categories
  • Use tags for flexible querying
  • Add metadata for custom properties
  • Query by collection, tag, or metadata