Anchor to Blockchain
This guide shows you how to anchor data to blockchains for tamper evidence and timestamping. You’ll learn how to choose a blockchain, anchor data, read anchored data, and implement multi-chain anchoring.
Quick Example
Here’s a complete example that anchors a credential digest to a blockchain:
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
import com.trustweave.trust.TrustWeave
import com.trustweave.anchor.AnchorResult
import com.trustweave.anchor.AnchorRef
import com.trustweave.anchor.BlockchainAnchorClient
import com.trustweave.anchor.exceptions.BlockchainException
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class CredentialDigest(
val credentialId: String,
val digest: String,
val issuer: String
)
fun main() = runBlocking {
try {
// Create TrustWeave with blockchain support
val trustWeave = TrustWeave.build {
keys {
provider("inMemory")
algorithm("Ed25519")
}
anchor {
chain("algorand:testnet") {
inMemory()
}
}
}
// Create credential digest
val digest = CredentialDigest(
credentialId = "cred-123",
digest = "z6Mk...", // SHA-256 digest
issuer = "did:key:issuer"
)
// Anchor to blockchain - get client from registry
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
// Serialize and anchor data
val json = kotlinx.serialization.json.Json.encodeToJsonElement(
CredentialDigest.serializer(),
digest
)
val anchorResult = anchorClient.writePayload(json)
println("✅ Anchored at: ${anchorResult.ref.txHash}")
println(" Chain: ${anchorResult.ref.chainId}")
println(" Timestamp: ${anchorResult.timestamp}")
} catch (error: BlockchainException) {
when (error) {
is BlockchainException.ChainNotRegistered -> {
println("❌ Chain not registered: ${error.chainId}")
}
else -> {
println("❌ Error: ${error.message}")
}
}
}
}
Expected Output:
1
2
3
✅ Anchored at: tx-abc123...
Chain: algorand:testnet
Timestamp: 2024-01-01T00:00:00Z
Step-by-Step Guide
Step 1: Configure Blockchain Support
Register blockchain clients in TrustWeave:
1
2
3
4
5
6
7
8
9
10
11
12
val trustWeave = TrustWeave.build {
keys {
provider("inMemory")
algorithm("Ed25519")
}
anchor {
chain("algorand:testnet") {
inMemory()
}
// Add more chains as needed
}
}
Step 2: Prepare Data for Anchoring
Create the data structure to anchor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import kotlinx.serialization.Serializable
@Serializable
data class CredentialDigest(
val credentialId: String,
val digest: String,
val issuer: String
)
val digest = CredentialDigest(
credentialId = "cred-123",
digest = "z6Mk...",
issuer = "did:key:issuer"
)
Step 3: Anchor the Data
Anchor data to the blockchain:
1
2
3
4
5
6
7
8
9
10
import kotlinx.serialization.json.Json
// Get anchor client from registry
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
// Serialize and anchor
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
val anchorResult = anchorClient.writePayload(json)
Step 4: Store Anchor Reference
Save the anchor reference for later verification:
1
2
val anchorRef = anchorResult.ref
// Store in database: anchorRef.chainId, anchorRef.txHash, anchorRef.blockNumber
Choosing a Blockchain
Select a blockchain based on your requirements:
| Blockchain | Use Case | Latency | Cost | Notes |
|---|---|---|---|---|
| Algorand | Production, fast finality | ~4s | Low | Recommended for most use cases |
| Polygon | Ethereum-compatible, low cost | ~2s | Very low | Good for high-volume anchoring |
| Ethereum | Maximum security | ~15s | High | Use for high-value credentials |
| Base | Layer 2, low cost | ~2s | Very low | Ethereum-compatible |
| Arbitrum | Layer 2, fast | ~1s | Low | Ethereum-compatible |
Testnet vs Mainnet
- Testnet: Use for development and testing (free, no real value)
- Mainnet: Use for production (costs real tokens)
Anchoring Data
Basic Anchoring
Anchor any serializable data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import kotlinx.serialization.json.Json
@Serializable
data class MyData(val id: String, val value: String)
val data = MyData(id = "123", value = "test")
// Get anchor client from registry
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
// Serialize and anchor
val json = Json.encodeToJsonElement(MyData.serializer(), data)
val anchorResult = anchorClient.writePayload(json)
Anchoring Credentials
Anchor credential digests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import kotlinx.serialization.json.Json
// Issue credential first
val credential = trustWeave.issue { ... }
// Create digest
val digest = CredentialDigest(
credentialId = credential.id,
digest = computeDigest(credential),
issuer = credential.issuer
)
// Get anchor client and anchor digest
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
val anchorResult = anchorClient.writePayload(json)
What Gets Stored On-Chain?
Important: Only the digest (hash) is stored on-chain, not the full data. The data is:
- Serialized to JSON
- Canonicalized using JCS
- Hashed (SHA-256)
- The hash is stored on-chain
The original data must be stored separately (database, IPFS, etc.) if you need to retrieve it later.
Reading Anchored Data
Read by Anchor Reference
Read anchored data using the anchor reference:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import kotlinx.serialization.json.Json
val anchorRef = AnchorRef(
chainId = "algorand:testnet",
txHash = "tx-abc123..."
)
// Get anchor client and read
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(anchorRef.chainId)
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
val result = anchorClient.readPayload(anchorRef)
val data = Json.decodeFromJsonElement(CredentialDigest.serializer(), result.payload)
Verify Integrity
Verify that the data matches the on-chain digest:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import kotlinx.serialization.json.Json
try {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(anchorRef.chainId)
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
val result = anchorClient.readPayload(anchorRef)
val data = Json.decodeFromJsonElement(CredentialDigest.serializer(), result.payload)
// Verify digest matches
val computedDigest = computeDigest(data)
val isIntact = computedDigest == data.digest
if (isIntact) {
println("✅ Data verified against on-chain digest")
} else {
println("❌ Data verification failed - possible tampering!")
}
} catch (error: Exception) {
println("❌ Error reading anchor: ${error.message}")
}
Multi-Chain Anchoring
Anchor the same data to multiple blockchains for redundancy:
Sequential Anchoring
Anchor to multiple chains one at a time:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import kotlinx.serialization.json.Json
val chains = listOf("algorand:testnet", "polygon:testnet")
val anchorResults = chains.mapNotNull { chainId ->
try {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(chainId)
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found for $chainId")
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
val anchor = anchorClient.writePayload(json)
println("✅ Anchored to $chainId: ${anchor.ref.txHash}")
anchor
} catch (error: Exception) {
println("❌ Failed to anchor to $chainId: ${error.message}")
null
}
}
Parallel Anchoring
Anchor to multiple chains concurrently:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.serialization.json.Json
val chains = listOf("algorand:testnet", "polygon:testnet")
val anchorResults = chains.map { chainId ->
async {
try {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(chainId)
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found for $chainId")
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
anchorClient.writePayload(json)
} catch (error: Exception) {
println("Failed to anchor to $chainId: ${error.message}")
null
}
}
}.awaitAll().filterNotNull()
Store All References
Store all anchor references for verification:
1
2
3
val anchorRefs = anchorResults.map { it.ref }
// Store in database for later verification
database.saveAnchorRefs(credentialId, anchorRefs)
Common Patterns
Pattern 1: Anchor with Retry
Retry anchoring on failure:
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
import kotlinx.coroutines.delay
import kotlinx.serialization.json.Json
suspend fun anchorWithRetry(
data: CredentialDigest,
chainId: String,
maxRetries: Int = 3
): AnchorResult? {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(chainId)
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found for $chainId")
repeat(maxRetries) { attempt ->
try {
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), data)
return anchorClient.writePayload(json)
} catch (error: Exception) {
if (attempt == maxRetries - 1) {
println("Failed after $maxRetries attempts: ${error.message}")
return null
}
delay(1000 * (attempt + 1)) // Exponential backoff
}
}
return null
}
Pattern 2: Anchor with Timeout
Add timeout to anchoring operations:
1
2
3
4
5
6
7
8
9
10
11
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.json.Json
val anchorResult = withTimeout(30000) { // 30 second timeout
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
anchorClient.writePayload(json)
}
Pattern 3: Verify Anchor Exists
Check if an anchor exists before reading:
1
2
3
4
5
6
7
8
9
10
11
12
suspend fun verifyAnchorExists(ref: AnchorRef): Boolean {
return try {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get(ref.chainId)
as? BlockchainAnchorClient
?: return false
anchorClient.readPayload(ref)
true
} catch (error: Exception) {
false
}
}
Error Handling
Handle anchoring errors gracefully:
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
import kotlinx.serialization.json.Json
try {
val anchorClient = trustWeave.configuration.registries.blockchainRegistry.get("algorand:testnet")
as? BlockchainAnchorClient
?: throw IllegalStateException("Blockchain client not found")
val json = Json.encodeToJsonElement(CredentialDigest.serializer(), digest)
val anchorResult = anchorClient.writePayload(json)
// Use anchor result
} catch (error: BlockchainException) {
when (error) {
is BlockchainException.ChainNotRegistered -> {
println("Chain not registered: ${error.chainId}")
println("Available chains: ${error.availableChains}")
}
is BlockchainException.TransactionFailed -> {
println("Transaction failed: ${error.reason}")
}
else -> {
println("Blockchain error: ${error.message}")
// Could be network issue, insufficient funds, etc.
}
}
} catch (error: Exception) {
println("Error: ${error.message}")
error.printStackTrace()
}
Integration Guides
For production use, see integration guides for specific blockchains:
- Algorand Integration - Algorand anchoring setup
- Ethereum Integration - Ethereum anchoring setup
- Polygon Integration - Polygon anchoring setup
- Base Integration - Base anchoring setup
API Reference
For complete API documentation, see:
- Core API - anchor() - Complete parameter reference
- Core API - readAnchor() - Reading anchored data
Related Concepts
- Blockchain Anchoring - Understanding anchoring concepts
- JSON Canonicalization - How data is canonicalized
Related How-To Guides
- Issue Credentials - Issue credentials to anchor
- Create DIDs - Create issuer DIDs
Next Steps
Ready to integrate?
- Algorand Integration - Set up Algorand anchoring
- Ethereum Integration - Set up Ethereum anchoring
Want to learn more?
- Blockchain Anchoring Concept - Deep dive into anchoring
- Your First Application - Complete anchoring example