Skip to content

Commit 40d265e

Browse files
authored
bug(storage): ski cleaner service should apply only to owned tokens LFDT-Panurus#1902 (LFDT-Panurus#1903)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent 8462aef commit 40d265e

5 files changed

Lines changed: 56 additions & 6 deletions

File tree

docs/services/storage/keystore_cleanup.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ type Storage interface {
8383
// AcquireCleanupLeadership obtains distributed lock for leader election
8484
AcquireCleanupLeadership(ctx context.Context, lockID int64) (Leadership, bool, error)
8585

86-
// GetDeletedTokensPendingSKICleanup queries deleted tokens older than TTL that haven't been cleaned
86+
// GetDeletedTokensPendingSKICleanup queries deleted, owned tokens older than TTL that haven't been cleaned.
87+
// Tokens for which this node is only an auditor or issuer are excluded, since this node
88+
// never holds the secret keys for those tokens.
8789
GetDeletedTokensPendingSKICleanup(ctx context.Context, olderThan time.Duration, limit int) ([]DeletedToken, error)
8890

8991
// MarkTokenCleaned records successful key cleanup to prevent reprocessing
@@ -303,10 +305,12 @@ A token transitions through the following states related to cleanup:
303305

304306
- **Active**: Token is unspent and in use
305307
- **Deleted**: Token marked as deleted (`is_deleted=true`, `spent_at` set)
306-
- **Eligible for Cleanup**: Deleted token older than TTL without a cleanup record in `token_ski_cleanups`
308+
- **Eligible for Cleanup**: Deleted, **owned** token (`owner=true`) older than TTL without a cleanup record in `token_ski_cleanups`
307309
- **Cleaned**: Keys deleted from keystore (record exists in `token_ski_cleanups`)
308310

309-
The cleanup service only processes tokens in the "Eligible for Cleanup" state.
311+
The cleanup service only processes tokens in the "Eligible for Cleanup" state. Tokens for which
312+
this node is only an auditor or issuer (`owner=false`) are never selected, since this node
313+
never holds the secret keys for tokens it does not own.
310314

311315
## Database Schema
312316

token/services/storage/db/dbtest/tokens.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,4 +1546,45 @@ func TGetDeletedTokensPendingSKICleanup(t *testing.T, db TestTokenDB) {
15461546

15471547
assert.Equal(t, 3, count, "should return all indices for the same transaction")
15481548
})
1549+
1550+
// Test 12: Non-owned tokens (auditor-only, issuer-only) must be excluded,
1551+
// since this node never holds the secret keys for tokens it does not own.
1552+
t.Run("ExcludeNonOwnedTokens", func(t *testing.T) {
1553+
createAndDeleteNonOwnedToken := func(txID string, index uint64, ownerType string, auditor, issuer bool) {
1554+
tr := driver2.TokenRecord{
1555+
TxID: txID,
1556+
Index: index,
1557+
OwnerRaw: []byte{1, 2, 3},
1558+
OwnerType: ownerType,
1559+
OwnerIdentity: fmt.Appendf(nil, "owner_%s_%d", txID, index),
1560+
Ledger: []byte("ledger"),
1561+
LedgerMetadata: []byte{},
1562+
Quantity: "0x01",
1563+
Type: ABC,
1564+
Amount: 1,
1565+
Owner: false,
1566+
Auditor: auditor,
1567+
Issuer: issuer,
1568+
}
1569+
require.NoError(t, db.StoreToken(ctx, tr, nil))
1570+
require.NoError(t, db.DeleteTokens(ctx, "deleter_tx", &token.ID{TxId: txID, Index: index}))
1571+
}
1572+
1573+
createAndDeleteNonOwnedToken("auditor_only", 0, "idemix", true, false)
1574+
createAndDeleteNonOwnedToken("issuer_only", 0, "idemix", false, true)
1575+
createAndDeleteToken("owned_control", 0, "idemix")
1576+
1577+
tokens, err := db.GetDeletedTokensPendingSKICleanup(ctx, 0, 100)
1578+
require.NoError(t, err, "query should not error")
1579+
1580+
foundOwnedControl := false
1581+
for _, tok := range tokens {
1582+
assert.NotEqual(t, "auditor_only", tok.TxID, "auditor-only token should not be returned")
1583+
assert.NotEqual(t, "issuer_only", tok.TxID, "issuer-only token should not be returned")
1584+
if tok.TxID == "owned_control" {
1585+
foundOwnedControl = true
1586+
}
1587+
}
1588+
assert.True(t, foundOwnedControl, "owned token should still be returned")
1589+
})
15491590
}

token/services/storage/db/driver/token.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ type TokenStore interface {
269269
Notifier() (TokenNotifier, error)
270270
// GetDeletedTokensPendingSKICleanup returns deleted tokens older than the specified duration that haven't had their SKI keys cleaned yet.
271271
// This is used by the keystore cleanup service to identify tokens whose cryptographic keys can be safely removed.
272-
// Only tokens without a record in the token_ski_cleanups table are returned.
272+
// Only tokens without a record in the token_ski_cleanups table are returned, and only tokens owned by this node,
273+
// since this node only holds the secret keys for tokens it owns, not for tokens it merely audited or issued.
273274
GetDeletedTokensPendingSKICleanup(ctx context.Context, olderThan time.Duration, limit int) ([]DeletedToken, error)
274275
// MarkTokenCleaned marks a token as having its cryptographic keys cleaned up.
275276
// This prevents the cleanup service from processing the same token multiple times.

token/services/storage/db/sql/common/tokens.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1183,8 +1183,10 @@ func (db *TokenStore) GetCertifications(ctx context.Context, ids []*token.ID) ([
11831183
return certifications, nil
11841184
}
11851185

1186-
// GetDeletedTokensPendingSKICleanup returns deleted tokens older than the specified duration
1186+
// GetDeletedTokensPendingSKICleanup returns deleted, owned tokens older than the specified duration
11871187
// that haven't had their SKI keys cleaned up yet (no record in token_ski_cleanups table).
1188+
// Only tokens owned by this node are considered, since this node only holds the secret keys
1189+
// for tokens it owns, not for tokens it merely audited or issued.
11881190
func (db *TokenStore) GetDeletedTokensPendingSKICleanup(ctx context.Context, olderThan time.Duration, limit int) ([]driver.DeletedToken, error) {
11891191
cutoffTime := time.Now().UTC().Add(-olderThan)
11901192

@@ -1210,6 +1212,7 @@ func (db *TokenStore) GetDeletedTokensPendingSKICleanup(ctx context.Context, old
12101212
))).
12111213
Where(cond.And(
12121214
cond.CmpVal(tokenTable.Field("is_deleted"), "=", true),
1215+
cond.CmpVal(tokenTable.Field("owner"), "=", true),
12131216
cond.CmpVal(tokenTable.Field("spent_at"), "<", cutoffTime),
12141217
cond.IsNil(cleanupTable.Field("tx_id")),
12151218
)).

token/services/storage/services/cleanup/manager.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ type DeletedToken struct {
3232
type Storage interface {
3333
// AcquireCleanupLeadership acquires an advisory lock for cleanup leadership
3434
AcquireCleanupLeadership(ctx context.Context, lockID int64) (Leadership, bool, error)
35-
// GetDeletedTokensPendingSKICleanup returns deleted tokens older than the specified duration that haven't had their SKI keys cleaned
35+
// GetDeletedTokensPendingSKICleanup returns deleted tokens older than the specified duration that haven't had their SKI keys cleaned.
36+
// Only tokens owned by this node are returned, since this node only holds the secret keys for tokens it owns.
3637
GetDeletedTokensPendingSKICleanup(ctx context.Context, olderThan time.Duration, limit int) ([]DeletedToken, error)
3738
// MarkTokenCleaned marks a token as having its SKI keys cleaned up
3839
MarkTokenCleaned(ctx context.Context, txID string, index uint64, cleanedBy string) error

0 commit comments

Comments
 (0)