Skip to content

Commit a10d0d2

Browse files
authored
Implement Keystore Cleanup Service (#1801)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent 12362e2 commit a10d0d2

90 files changed

Lines changed: 6714 additions & 282 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/tokengen/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ require (
122122
go.uber.org/zap v1.28.0 // indirect
123123
go.yaml.in/yaml/v3 v3.0.4 // indirect
124124
golang.org/x/crypto v0.52.0 // indirect
125+
golang.org/x/exp v0.0.0-20260527015227-08cc5374adb3 // indirect
125126
golang.org/x/mod v0.36.0 // indirect
126127
golang.org/x/net v0.55.0 // indirect
127128
golang.org/x/sync v0.20.0 // indirect

docs/configuration.md

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ token:
4242

4343
# When we are interested in knowing when a transaction reaches finality, we subscribe to the Finality Listener Manager for the finality event of that transaction.
4444
# This configuration specifies the way the manager is instantiated (i.e., how it gets notified about the finality events, how often it checks).
45-
finality:
45+
finality:
4646
# Only applicable for fabric networks.
4747
# The manager subscribes to the delivery service and receives all final transactions.
4848
# This manager keeps two structures: an LRU cache of recently finalized transactions, and a list of listeners that are waiting for future transactions.
@@ -209,6 +209,56 @@ token:
209209
# on every sweep until it either resolves or an operator intervenes.
210210
notFoundGracePeriod: 30m
211211

212+
# storage service configuration
213+
storage:
214+
# cleanup config controls automatic deletion of cryptographic keys from the keystore
215+
# for tokens that have been deleted (spent, expired, or invalidated).
216+
# If omitted, the cleanup manager uses its built-in defaults (disabled by default).
217+
cleanup:
218+
# enabled determines whether keystore cleanup runs. Default: false.
219+
# Must be explicitly enabled. This is a conservative default to prevent
220+
# unexpected key deletion in existing deployments.
221+
enabled: false
222+
223+
# ttl is the minimum age of deleted tokens before their keys are eligible for cleanup. Default: 24h.
224+
# This ensures tokens are truly finalized before key deletion.
225+
# Increase this value for additional safety margin in high-latency networks.
226+
# Relationship: Should be significantly greater than transaction finality time.
227+
ttl: 24h
228+
229+
# scanInterval is how often the cleanup manager scans for deleted tokens. Default: 1h.
230+
# Lower values provide faster cleanup but increase database load.
231+
# Higher values reduce overhead but delay key removal.
232+
# Relationship: Should be less than ttl to ensure timely cleanup.
233+
# Performance impact: Each scan queries the token database for deleted tokens.
234+
scanInterval: 1h
235+
236+
# batchSize is the maximum number of deleted tokens processed per scan. Default: 100.
237+
# Limits the number of tokens processed in a single cleanup sweep.
238+
# Increase for high-volume environments with many deleted tokens.
239+
# Performance impact: Larger batches reduce scan overhead but increase memory usage and processing time per sweep.
240+
batchSize: 100
241+
242+
# workerCount is the number of local workers that process tokens in parallel. Default: 1.
243+
# Increase to improve cleanup throughput in high-volume scenarios.
244+
# Decrease to reduce resource consumption on constrained systems.
245+
# Performance impact: More workers increase CPU utilization during cleanup sweeps.
246+
workerCount: 1
247+
248+
# advisoryLockID is the PostgreSQL advisory lock identifier used for cleanup leader election.
249+
# This ensures only one replica performs cleanup sweeps at a time in multi-instance deployments.
250+
# Default: 8389190333894887277 (hex: 0x74746b636c65616e, ASCII: "ttkclean")
251+
# The default value is derived from the ASCII encoding of "ttkclean" (Token Transaction Keystore Cleanup).
252+
# Only change this if you need to run multiple independent cleanup managers on the same database.
253+
# Note: PostgreSQL advisory locks use 64-bit integers. This value must be unique across your application.
254+
advisoryLockID: 8389190333894887277
255+
256+
# instanceID identifies this replica in logs and monitoring.
257+
# If empty, a unique identifier is generated automatically at startup.
258+
# Set this explicitly in containerized environments for consistent identity across restarts.
259+
# This helps with debugging and tracking which instance performed cleanup operations.
260+
instanceID:
261+
212262
# auditor-specific settings
213263
auditor:
214264
# locker configures the distributed locking strategy for the auditor's
@@ -445,6 +495,79 @@ Default values:
445495
- Increase `workerCount` to 8-16 to improve parallel processing
446496
- Decrease `scanInterval` to 2-3s for faster recovery detection
447497

498+
499+
### Optional: token.tms.<name>.services.storage.cleanup
500+
501+
If not specified, the default configuration is:
502+
503+
```yaml
504+
token:
505+
tms:
506+
<name>:
507+
services:
508+
storage:
509+
cleanup:
510+
enabled: false
511+
ttl: 24h
512+
scanInterval: 1h
513+
batchSize: 100
514+
workerCount: 1
515+
advisoryLockID: 8389190333894887277
516+
instanceID:
517+
```
518+
519+
Default values:
520+
521+
- enabled: false
522+
- ttl: 24h
523+
- scanInterval: 1h
524+
- batchSize: 100
525+
- workerCount: 1
526+
- advisoryLockID: 8389190333894887277 (`0x74746b636c65616e`)
527+
- instanceID: empty, auto-generated when the cleanup manager starts
528+
529+
**Parameter Relationships and Tuning:**
530+
531+
- **Cleanup is disabled by default** and must be explicitly enabled. This is a conservative default to prevent unexpected key deletion in existing deployments.
532+
- **Only deleted tokens older than `ttl` are considered for cleanup** to ensure tokens are truly finalized before key deletion.
533+
- **The manager validates** that `ttl`, `scanInterval`, `batchSize`, and `workerCount` are all greater than zero.
534+
- **`advisoryLockID`** is used to acquire PostgreSQL advisory-lock leadership so that only one replica performs a cleanup sweep at a time. The default value (8389190333894887277 or 0x74746b636c65616e) represents the ASCII string "ttkclean" (Token Transaction Keystore Cleanup) encoded as a 64-bit integer.
535+
- **`instanceID`** is used to identify this replica in logs and monitoring; if omitted, the manager generates a unique identifier automatically at startup.
536+
537+
**Tuning Recommendations:**
538+
539+
1. **For High-Volume Environments:**
540+
- Increase `batchSize` to 200-500 to process more tokens per sweep
541+
- Increase `workerCount` to 8-16 to improve parallel key deletion
542+
- Decrease `scanInterval` to 30m for more frequent cleanup
543+
544+
2. **For Resource-Constrained Systems:**
545+
- Decrease `workerCount` to 2 to reduce CPU usage
546+
- Increase `scanInterval` to 2-4h to reduce database load
547+
- Keep default `batchSize` to limit memory usage
548+
549+
3. **For Security-Sensitive Deployments:**
550+
- Decrease `ttl` to 12h for faster key removal
551+
- Decrease `scanInterval` to 30m for more frequent cleanup
552+
- Monitor cleanup metrics to ensure timely processing
553+
554+
4. **For Multi-Instance Deployments:**
555+
- **PostgreSQL Required**: Multi-instance deployments require PostgreSQL for distributed coordination via advisory locks
556+
- Keep default `advisoryLockID` unless running multiple independent cleanup systems
557+
- Consider setting explicit `instanceID` values for easier debugging and monitoring
558+
559+
5. **For Single-Node Deployments:**
560+
- **SQLite Supported**: SQLite can be used for single-node deployments and handles node restarts gracefully
561+
- Cleanup works automatically after node restarts by scanning for eligible tokens
562+
- **Important**: Do not use SQLite with multiple replicas as it lacks the advisory lock mechanism for leader election
563+
564+
**Performance Considerations:**
565+
- Each scan queries the token database, so `scanInterval` directly affects database load
566+
- `workerCount` affects CPU utilization during cleanup sweeps
567+
- `batchSize` affects memory usage and the duration of each cleanup sweep
568+
- The relationship `scanInterval < ttl` ensures timely cleanup without premature processing
569+
570+
---
448571
---
449572

450573
### Optional: token.tms.<name>.auditor.lock

docs/evolution_summary.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The Token Transaction service, a core component for orchestrating token lifecycl
5252
- **Network Integration:** Instantiated by both Fabric and FabricX network services
5353
- **Dual Database Support:** Operates on TTXDB for regular transactions and AuditDB for auditor nodes
5454

55-
* *See also:* [**TTX Service Documentation**](services/ttx.md), [**Transaction Recovery Service**](services/recovery.md)
55+
* *See also:* [**TTX Service Documentation**](services/ttx.md), [**Transaction Recovery Service**](services/storage/recovery.md)
5656

5757
## Core Service Enhancements
5858
The SDK's foundational services have been matured:
@@ -63,7 +63,8 @@ The SDK's foundational services have been matured:
6363
- **Database Indexing:** Added missing indexes for token queries, significantly improving query performance
6464
- **Query Enhancements:** New `SearchDirection` support in `QueryTransactionsParams` for flexible result ordering
6565
- **Recovery Service Integration:** Built-in transaction recovery capabilities for handling finality listener failures
66-
* *See also:* [**Storage Service**](services/storage.md)
66+
- **Keystore Cleanup Service:** Automatic deletion of cryptographic keys for deleted tokens, improving security and reducing storage overhead
67+
* *See also:* [**Storage Service**](services/storage.md), [**Keystore Cleanup Service**](services/storage/keystore_cleanup.md)
6768
- **Network Service:** Expanded to handle more complex Fabric network interactions and better integration with the Fabric Smart Client. A major change was the **removal of the Orion-based implementation**, which has been replaced by the introduction of **FabricX support**, providing a more modern and integrated approach for advanced ledger interactions.
6869
* *See also:* [**Network Service**](services/network.md)
6970

docs/imgs/storage_db.png

5.69 KB
Loading

docs/imgs/storage_db.puml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,18 @@ package "Token Store (TokenDB)" {
115115
created_at : TIMESTAMP
116116
}
117117

118+
entity "TokenSKICleanups" as tkn_ski_cleanups {
119+
* tx_id : TEXT <<PK, NOT NULL, FK>>
120+
* idx : INT <<PK, NOT NULL, FK>>
121+
--
122+
cleaned_at : TIMESTAMP <<NOT NULL>>
123+
cleaned_by : TEXT <<NOT NULL>>
124+
}
125+
118126
tkn_own }o--|| tokens : "(tx_id, idx)"
119127
tkn_crts }o--|| tokens : "(tx_id, idx)"
120128
tkn_locks |o--o| tokens : "(tx_id, idx)"
129+
tkn_ski_cleanups }o--|| tokens : "(tx_id, idx)"
121130
}
122131

123132
package "Wallet & Identity Store (WalletDB / IdentityDB)" {

docs/services/storage.md

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ This store serves as the authoritative registry for all tokens (UTXOs) known to
3939
* **PublicParameters**: A cache for the network's cryptographic public parameters and their hashes.
4040
* **TokenCertifications**: Stores third-party certifications for tokens, often required by privacy-preserving drivers.
4141
* **TokenLocks**: Manages short-lived pessimistic locks on tokens to prevent double-spending during transaction assembly.
42+
* **TokenSKICleanups**: Tracks keystore cleanup operations for deleted tokens. Records when cryptographic keys were removed from the keystore and which instance performed the cleanup, preventing reprocessing and enabling audit trails in multi-instance deployments.
4243

4344
### Wallet & Identity Store (WalletDB / IdentityDB)
4445
Manages the cryptographic identities and logical wallet groupings used by the node.
@@ -49,7 +50,7 @@ Manages the cryptographic identities and logical wallet groupings used by the no
4950
* **IdentitySigners**: Tracks which identities have locally available signing keys and their associated metadata.
5051

5152
### Generic Store
52-
* **KeyStore**: A secure, generic key-value store used for persisting various cryptographic materials and small sensitive states.
53+
* **KeyStore**: A secure, generic key-value store used for persisting various cryptographic materials and small sensitive states. The keystore uses identifiers that are expected to be the hexadecimal representation of the key's Subject Key Identifier (SKI). This convention is relied upon by other packages, particularly for operations like keystore cleanup where SKIs are derived from owner identities to locate and manage keys.
5354

5455
## Internal Databases
5556

@@ -88,7 +89,7 @@ This ensures that the local view of the "Token Landscape" always reflects the gr
8889

8990
The Storage Service includes a **Transaction Recovery Service** that provides the core recovery mechanism for handling pending transactions that may have lost their finality listeners due to node restarts, network interruptions, or other failures.
9091

91-
For detailed documentation on the recovery service architecture, configuration, and usage, see [**Transaction Recovery Service**](recovery.md).
92+
For detailed documentation on the recovery service architecture, configuration, and usage, see [**Transaction Recovery Service**](storage/recovery.md).
9293

9394
### Architecture
9495

@@ -153,4 +154,55 @@ The recovery service supports both PostgreSQL and SQLite backends, with differen
153154
### Configuration
154155

155156
Recovery behavior is controlled by the `token.tms.<name>.services.network.fabric.recovery` configuration section.
157+
158+
## Keystore Cleanup Service
159+
160+
The Storage Service includes a **Keystore Cleanup Service** that provides automatic deletion of cryptographic keys from the keystore for tokens that have been deleted (spent, expired, or invalidated). This ensures that the keystore doesn't accumulate stale keys indefinitely, improving security and reducing storage overhead.
161+
162+
For detailed documentation on the cleanup service architecture, configuration, and usage, see [**Keystore Cleanup Service**](storage/keystore_cleanup.md).
163+
164+
### Architecture
165+
166+
The cleanup service operates on the token database and keystore, scanning for deleted tokens that are eligible for key cleanup. It uses a distributed locking mechanism (PostgreSQL advisory locks) to ensure only one replica in a multi-instance deployment performs cleanup at a time.
167+
168+
### Cleanup Manager
169+
170+
The cleanup manager runs in the background and periodically scans for deleted tokens whose cryptographic keys can be safely removed from the keystore.
171+
172+
**Key Features:**
173+
- **Automatic Key Deletion**: Removes keys for deleted tokens after a configurable TTL period
174+
- **SKI Derivation**: Derives Subject Key Identifiers (SKIs) from owner identities to locate keys
175+
- **Multi-Database Support**:
176+
- **PostgreSQL**: Recommended for production multi-instance deployments. Uses advisory locks for distributed coordination and leader election
177+
- **SQLite**: Supported for single-node deployments and development. Handles node restarts gracefully but is not designed for multi-replica scenarios
178+
- **Configurable Behavior**: Cleanup parameters can be tuned via configuration (see [Configuration](../configuration.md))
179+
180+
### Cleanup Process
181+
182+
The cleanup service follows this workflow:
183+
184+
1. **Leadership Acquisition**: The cleanup manager acquires an advisory lock to become the leader
185+
2. **Scan Phase**: Scans the token database for deleted tokens older than the configured TTL (default: 24 hours) that haven't had their keys cleaned
186+
3. **SKI Derivation**: For each eligible token, derives SKIs from the owner identity
187+
4. **Key Deletion**: Deletes the derived keys from the keystore using parallel workers
188+
5. **Tracking**: Marks tokens as cleaned in the database to prevent reprocessing
189+
190+
### Database Backend Considerations
191+
192+
The cleanup service supports both PostgreSQL and SQLite backends, with different characteristics:
193+
194+
**PostgreSQL:**
195+
- **Multi-Instance Support**: Uses advisory locks for distributed coordination
196+
- **Leader Election**: Only one replica performs cleanup sweeps at a time
197+
- **High Availability**: Multiple replicas can share the same database
198+
199+
**SQLite:**
200+
- **Single-Node Only**: Suitable for development and single-node deployments
201+
- **Node Restart Support**: Cleanup resumes automatically after restart
202+
- **No Multi-Replica Support**: Lacks advisory lock mechanism for leader election
203+
204+
### Configuration
205+
206+
Cleanup behavior is controlled by the configuration section. See the [Configuration Guide](../configuration.md) for detailed parameter descriptions and tuning recommendations.
207+
156208
See the [Configuration Guide](../configuration.md), Section `Optional: token.tms.<name>.services.network.fabric.recovery`, for detailed parameter descriptions and tuning recommendations.

0 commit comments

Comments
 (0)