Skip to content

Commit 96c8b69

Browse files
JOHNJOHN
authored andcommitted
feat: scope all paykit storage classes by identity
1 parent e90b9c9 commit 96c8b69

File tree

8 files changed

+106
-57
lines changed

8 files changed

+106
-57
lines changed

app/src/main/java/to/bitkit/paykit/storage/ContactStorage.kt

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,43 @@ package to.bitkit.paykit.storage
33
import kotlinx.serialization.decodeFromString
44
import kotlinx.serialization.encodeToString
55
import kotlinx.serialization.json.Json
6+
import to.bitkit.paykit.KeyManager
67
import to.bitkit.paykit.models.Contact
78
import to.bitkit.utils.Logger
89
import javax.inject.Inject
910
import javax.inject.Singleton
1011

1112
/**
12-
* Manages persistent storage of contacts
13+
* Manages persistent storage of contacts.
14+
*
15+
* Storage is scoped by the current identity pubkey.
1316
*/
1417
@Singleton
1518
class ContactStorage @Inject constructor(
16-
private val keychain: PaykitKeychainStorage
19+
private val keychain: PaykitKeychainStorage,
20+
private val keyManager: KeyManager,
1721
) {
1822
companion object {
1923
private const val TAG = "ContactStorage"
2024
}
2125

22-
private var contactsCache: List<Contact>? = null
23-
private val identityName: String = "default"
26+
private var contactsCache: MutableMap<String, List<Contact>> = mutableMapOf()
27+
28+
private val currentIdentity: String
29+
get() = keyManager.getCurrentPublicKeyZ32() ?: "default"
2430

2531
private val contactsKey: String
26-
get() = "contacts.$identityName"
32+
get() = "contacts.$currentIdentity"
2733

2834
fun listContacts(): List<Contact> {
29-
if (contactsCache != null) {
30-
return contactsCache!!
31-
}
35+
val identity = currentIdentity
36+
contactsCache[identity]?.let { return it }
3237

3338
return try {
3439
val data = keychain.retrieve(contactsKey) ?: return emptyList()
3540
val json = String(data)
3641
val contacts = Json.decodeFromString<List<Contact>>(json)
37-
contactsCache = contacts
42+
contactsCache[identity] = contacts
3843
contacts
3944
} catch (e: Exception) {
4045
Logger.error("ContactStorage: Failed to load contacts", e, context = TAG)
@@ -82,7 +87,9 @@ class ContactStorage @Inject constructor(
8287
}
8388

8489
suspend fun clearAll() {
90+
val identity = currentIdentity
8591
persistContacts(emptyList())
92+
contactsCache.remove(identity)
8693
}
8794

8895
suspend fun importContacts(newContacts: List<Contact>) {
@@ -101,10 +108,11 @@ class ContactStorage @Inject constructor(
101108
}
102109

103110
private suspend fun persistContacts(contacts: List<Contact>) {
111+
val identity = currentIdentity
104112
try {
105113
val json = Json.encodeToString(contacts)
106114
keychain.store(contactsKey, json.toByteArray())
107-
contactsCache = contacts
115+
contactsCache[identity] = contacts
108116
} catch (e: Exception) {
109117
Logger.error("ContactStorage: Failed to persist contacts", e, context = TAG)
110118
throw PaykitStorageException.SaveFailed(contactsKey)

app/src/main/java/to/bitkit/paykit/storage/PaymentRequestStorage.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package to.bitkit.paykit.storage
33
import kotlinx.serialization.decodeFromString
44
import kotlinx.serialization.encodeToString
55
import kotlinx.serialization.json.Json
6+
import to.bitkit.paykit.KeyManager
67
import to.bitkit.paykit.models.PaymentRequest
78
import to.bitkit.paykit.models.PaymentRequestStatus
89
import to.bitkit.paykit.models.RequestDirection
@@ -12,36 +13,40 @@ import javax.inject.Singleton
1213

1314
/**
1415
* Manages persistent storage of payment requests using Keychain.
16+
*
17+
* Storage is scoped by the current identity pubkey.
1518
*/
1619
@Singleton
1720
class PaymentRequestStorage @Inject constructor(
18-
private val keychain: PaykitKeychainStorage
21+
private val keychain: PaykitKeychainStorage,
22+
private val keyManager: KeyManager,
1923
) {
2024
companion object {
2125
private const val TAG = "PaymentRequestStorage"
2226
private const val MAX_REQUESTS_TO_KEEP = 200
2327
}
2428

25-
private var requestsCache: List<PaymentRequest>? = null
26-
private val identityName: String = "default"
29+
private var requestsCache: MutableMap<String, List<PaymentRequest>> = mutableMapOf()
30+
31+
private val currentIdentity: String
32+
get() = keyManager.getCurrentPublicKeyZ32() ?: "default"
2733

2834
private val requestsKey: String
29-
get() = "payment_requests.$identityName"
35+
get() = "payment_requests.$currentIdentity"
3036

3137
/**
3238
* Get all requests (newest first)
3339
*/
3440
fun listRequests(): List<PaymentRequest> {
35-
if (requestsCache != null) {
36-
return requestsCache!!
37-
}
41+
val identity = currentIdentity
42+
requestsCache[identity]?.let { return it }
3843

3944
return try {
4045
val data = keychain.retrieve(requestsKey) ?: return emptyList()
4146
val json = String(data)
4247
val requests = Json.decodeFromString<List<PaymentRequest>>(json)
4348
.sortedByDescending { it.createdAt }
44-
requestsCache = requests
49+
requestsCache[identity] = requests
4550
requests
4651
} catch (e: Exception) {
4752
Logger.error("PaymentRequestStorage: Failed to load requests", e, context = TAG)
@@ -199,7 +204,9 @@ class PaymentRequestStorage @Inject constructor(
199204
* Clear all requests
200205
*/
201206
suspend fun clearAll() {
207+
val identity = currentIdentity
202208
persistRequests(emptyList())
209+
requestsCache.remove(identity)
203210
}
204211

205212
// MARK: - Statistics
@@ -232,10 +239,11 @@ class PaymentRequestStorage @Inject constructor(
232239
// MARK: - Private
233240

234241
private suspend fun persistRequests(requests: List<PaymentRequest>) {
242+
val identity = currentIdentity
235243
try {
236244
val json = Json.encodeToString(requests)
237245
keychain.store(requestsKey, json.toByteArray())
238-
requestsCache = requests
246+
requestsCache[identity] = requests
239247
} catch (e: Exception) {
240248
Logger.error("PaymentRequestStorage: Failed to persist requests", e, context = TAG)
241249
throw PaykitStorageException.SaveFailed(requestsKey)

app/src/main/java/to/bitkit/paykit/storage/PrivateEndpointStorage.kt

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ import kotlinx.serialization.Serializable
44
import kotlinx.serialization.decodeFromString
55
import kotlinx.serialization.encodeToString
66
import kotlinx.serialization.json.Json
7+
import to.bitkit.paykit.KeyManager
78
import to.bitkit.paykit.models.PrivateEndpointOffer
89
import to.bitkit.utils.Logger
910
import javax.inject.Inject
1011
import javax.inject.Singleton
1112

1213
/**
1314
* Manages persistent storage of private payment endpoints using Keychain.
15+
*
16+
* Storage is scoped by the current identity pubkey.
1417
*/
1518
@Singleton
1619
class PrivateEndpointStorage @Inject constructor(
17-
private val keychain: PaykitKeychainStorage
20+
private val keychain: PaykitKeychainStorage,
21+
private val keyManager: KeyManager,
1822
) {
1923
companion object {
2024
private const val TAG = "PrivateEndpointStorage"
@@ -23,14 +27,16 @@ class PrivateEndpointStorage @Inject constructor(
2327
@Serializable
2428
private data class StoredEndpoint(
2529
val methodId: String,
26-
val endpoint: String
30+
val endpoint: String,
2731
)
2832

29-
private var endpointsCache: Map<String, List<PrivateEndpointOffer>>? = null
30-
private val identityName: String = "default"
33+
private var endpointsCache: MutableMap<String, Map<String, List<PrivateEndpointOffer>>> = mutableMapOf()
34+
35+
private val currentIdentity: String
36+
get() = keyManager.getCurrentPublicKeyZ32() ?: "default"
3137

3238
private val endpointsKey: String
33-
get() = "private_endpoints.$identityName"
39+
get() = "private_endpoints.$currentIdentity"
3440

3541
/**
3642
* Get all private endpoints for a peer
@@ -101,7 +107,9 @@ class PrivateEndpointStorage @Inject constructor(
101107
* Clear all endpoints
102108
*/
103109
suspend fun clearAll() {
110+
val identity = currentIdentity
104111
persistAllEndpoints(emptyMap())
112+
endpointsCache.remove(identity)
105113
}
106114

107115
/**
@@ -114,9 +122,8 @@ class PrivateEndpointStorage @Inject constructor(
114122
// MARK: - Private
115123

116124
private fun loadAllEndpoints(): Map<String, List<PrivateEndpointOffer>> {
117-
if (endpointsCache != null) {
118-
return endpointsCache!!
119-
}
125+
val identity = currentIdentity
126+
endpointsCache[identity]?.let { return it }
120127

121128
return try {
122129
val data = keychain.retrieve(endpointsKey) ?: return emptyMap()
@@ -130,12 +137,12 @@ class PrivateEndpointStorage @Inject constructor(
130137
storedList.map { stored ->
131138
PrivateEndpointOffer(
132139
methodId = stored.methodId,
133-
endpoint = stored.endpoint
140+
endpoint = stored.endpoint,
134141
)
135142
}
136143
}
137144

138-
endpointsCache = endpoints
145+
endpointsCache[identity] = endpoints
139146
endpoints
140147
} catch (e: Exception) {
141148
Logger.error("PrivateEndpointStorage: Failed to load endpoints", e, context = TAG)
@@ -144,20 +151,21 @@ class PrivateEndpointStorage @Inject constructor(
144151
}
145152

146153
private suspend fun persistAllEndpoints(endpoints: Map<String, List<PrivateEndpointOffer>>) {
154+
val identity = currentIdentity
147155
try {
148156
// Convert PrivateEndpointOffer to StoredEndpoint for serialization
149157
val stored = endpoints.mapValues { (_, endpointList) ->
150158
endpointList.map { endpoint ->
151159
StoredEndpoint(
152160
methodId = endpoint.methodId,
153-
endpoint = endpoint.endpoint
161+
endpoint = endpoint.endpoint,
154162
)
155163
}
156164
}
157165

158166
val json = Json.encodeToString(stored)
159167
keychain.store(endpointsKey, json.toByteArray())
160-
endpointsCache = endpoints
168+
endpointsCache[identity] = endpoints
161169
} catch (e: Exception) {
162170
Logger.error("PrivateEndpointStorage: Failed to persist endpoints", e, context = TAG)
163171
throw PaykitStorageException.SaveFailed(endpointsKey)

app/src/main/java/to/bitkit/paykit/storage/ReceiptStorage.kt

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package to.bitkit.paykit.storage
33
import kotlinx.serialization.decodeFromString
44
import kotlinx.serialization.encodeToString
55
import kotlinx.serialization.json.Json
6+
import to.bitkit.paykit.KeyManager
67
import to.bitkit.paykit.models.PaymentDirection
78
import to.bitkit.paykit.models.PaymentStatus
89
import to.bitkit.paykit.models.Receipt
@@ -11,34 +12,38 @@ import javax.inject.Inject
1112
import javax.inject.Singleton
1213

1314
/**
14-
* Manages persistent storage of payment receipts
15+
* Manages persistent storage of payment receipts.
16+
*
17+
* Storage is scoped by the current identity pubkey.
1518
*/
1619
@Singleton
1720
class ReceiptStorage @Inject constructor(
18-
private val keychain: PaykitKeychainStorage
21+
private val keychain: PaykitKeychainStorage,
22+
private val keyManager: KeyManager,
1923
) {
2024
companion object {
2125
private const val TAG = "ReceiptStorage"
2226
private const val MAX_RECEIPTS_TO_KEEP = 500
2327
}
2428

25-
private var receiptsCache: List<Receipt>? = null
26-
private val identityName: String = "default"
29+
private var receiptsCache: MutableMap<String, List<Receipt>> = mutableMapOf()
30+
31+
private val currentIdentity: String
32+
get() = keyManager.getCurrentPublicKeyZ32() ?: "default"
2733

2834
private val receiptsKey: String
29-
get() = "receipts.$identityName"
35+
get() = "receipts.$currentIdentity"
3036

3137
fun listReceipts(): List<Receipt> {
32-
if (receiptsCache != null) {
33-
return receiptsCache!!
34-
}
38+
val identity = currentIdentity
39+
receiptsCache[identity]?.let { return it }
3540

3641
return try {
3742
val data = keychain.retrieve(receiptsKey) ?: return emptyList()
3843
val json = String(data)
3944
val receipts = Json.decodeFromString<List<Receipt>>(json)
4045
val sorted = receipts.sortedByDescending { it.createdAt }
41-
receiptsCache = sorted
46+
receiptsCache[identity] = sorted
4247
sorted
4348
} catch (e: Exception) {
4449
Logger.error("ReceiptStorage: Failed to load receipts", e, context = TAG)
@@ -105,7 +110,9 @@ class ReceiptStorage @Inject constructor(
105110
}
106111

107112
suspend fun clearAll() {
113+
val identity = currentIdentity
108114
persistReceipts(emptyList())
115+
receiptsCache.remove(identity)
109116
}
110117

111118
fun totalSent(): Long {
@@ -129,10 +136,11 @@ class ReceiptStorage @Inject constructor(
129136
}
130137

131138
private suspend fun persistReceipts(receipts: List<Receipt>) {
139+
val identity = currentIdentity
132140
try {
133141
val json = Json.encodeToString(receipts)
134142
keychain.store(receiptsKey, json.toByteArray())
135-
receiptsCache = receipts
143+
receiptsCache[identity] = receipts
136144
} catch (e: Exception) {
137145
Logger.error("ReceiptStorage: Failed to persist receipts", e, context = TAG)
138146
throw PaykitStorageException.SaveFailed(receiptsKey)

app/src/main/java/to/bitkit/paykit/storage/RotationSettingsStorage.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,28 @@ data class RotationEvent(
6868
)
6969

7070
/**
71-
* Manages rotation settings and history persistence
71+
* Manages rotation settings and history persistence.
72+
*
73+
* Storage is scoped by the current identity pubkey.
7274
*/
7375
@Singleton
7476
class RotationSettingsStorage @Inject constructor(
75-
private val keychain: PaykitKeychainStorage
77+
private val keychain: PaykitKeychainStorage,
78+
private val keyManager: to.bitkit.paykit.KeyManager,
7679
) {
7780
companion object {
7881
private const val TAG = "RotationSettingsStorage"
7982
private const val MAX_HISTORY_EVENTS = 100
8083
}
8184

82-
private val identityName: String = "default"
85+
private val currentIdentity: String
86+
get() = keyManager.getCurrentPublicKeyZ32() ?: "default"
8387

8488
private val settingsKey: String
85-
get() = "rotation_settings.$identityName"
89+
get() = "rotation_settings.$currentIdentity"
8690

8791
private val historyKey: String
88-
get() = "rotation_history.$identityName"
92+
get() = "rotation_history.$currentIdentity"
8993

9094
// MARK: - Settings
9195

0 commit comments

Comments
 (0)