Skip to content

Commit 184a033

Browse files
committed
fix: delegate wallet ids to core
1 parent 93f66fb commit 184a033

6 files changed

Lines changed: 41 additions & 31 deletions

File tree

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package to.bitkit.models
22

3-
import java.security.MessageDigest
43
import java.util.Locale
54

65
enum class ActivityWalletType {
@@ -15,15 +14,6 @@ enum class ActivityWalletType {
1514

1615
fun scopedId(value: String): String = "$prefix$value"
1716

18-
fun deriveId(keys: Collection<String>): String {
19-
val normalizedKeys = keys.filter { it.isNotBlank() }
20-
if (normalizedKeys.isEmpty()) return ""
21-
22-
val hash = MessageDigest.getInstance("SHA-256")
23-
.digest(normalizedKeys.sorted().joinToString("\n").toByteArray(Charsets.UTF_8))
24-
return scopedId(hash.joinToString("") { "%02x".format(it) })
25-
}
26-
2717
private val prefix: String
2818
get() = "$id:"
2919
}

app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import to.bitkit.ext.rawId
3838
import to.bitkit.ext.runSuspendCatching
3939
import to.bitkit.ext.scopedId
4040
import to.bitkit.ext.walletId
41-
import to.bitkit.models.ActivityWalletType
4241
import to.bitkit.models.HwFundingAccount
4342
import to.bitkit.models.HwFundingAddressType
4443
import to.bitkit.models.HwFundingBroadcastResult
@@ -415,7 +414,7 @@ class HwWalletRepo @Inject constructor(
415414
private fun List<KnownDevice>.toWatcherSpecs(watcherSettings: WatcherSettings): List<WatcherSpec> =
416415
flatMap { device ->
417416
val walletId = device.walletId.takeIf { it.isNotBlank() }
418-
?: ActivityWalletType.TREZOR.deriveId(device.xpubs.values)
417+
?: trezorRepo.deriveWalletId(device.xpubs.values)
419418
if (walletId.isBlank()) return@flatMap emptyList()
420419

421420
device.xpubs

app/src/main/java/to/bitkit/repositories/TrezorRepo.kt

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,12 @@ class TrezorRepo @Inject constructor(
764764
}
765765
}
766766

767+
fun deriveWalletId(xpubs: Collection<String>): String {
768+
val xpubList = xpubs.toList()
769+
if (xpubList.isEmpty() || xpubList.any { it.isBlank() }) return ""
770+
return trezorService.deriveWalletId(ActivityWalletType.TREZOR.id, xpubList)
771+
}
772+
767773
suspend fun stopWatcher(watcherId: String): Result<Unit> = withContext(ioDispatcher) {
768774
runCatching {
769775
awaitSetup()
@@ -888,7 +894,7 @@ class TrezorRepo @Inject constructor(
888894
lastConnectedAt = clock.nowMs(),
889895
xpubs = xpubs,
890896
customLabel = previous?.customLabel,
891-
walletId = knownDevices.findHardwareWalletId(deviceInfo.id, xpubs),
897+
walletId = knownDevices.findHardwareWalletId(deviceInfo.id, xpubs, ::deriveWalletId),
892898
)
893899
val updated = knownDevices.filter { it.id != known.id } + known
894900
saveKnownDevices(updated)
@@ -918,7 +924,7 @@ class TrezorRepo @Inject constructor(
918924

919925
private suspend fun loadKnownDevices(): List<KnownDevice> = runCatching {
920926
val devices = hwWalletStore.loadKnownDevices()
921-
val migrated = devices.withHardwareWalletIds()
927+
val migrated = devices.withHardwareWalletIds(::deriveWalletId)
922928
if (migrated != devices) {
923929
hwWalletStore.saveKnownDevices(migrated)
924930
}
@@ -1088,30 +1094,32 @@ private val KnownDevice.walletKey: String
10881094
private fun walletKey(xpubs: Map<String, String>, fallback: String): String =
10891095
xpubs.values.sorted().joinToString().ifEmpty { fallback }
10901096

1091-
private fun List<KnownDevice>.findHardwareWalletId(deviceId: String, xpubs: Map<String, String>): String {
1097+
private fun List<KnownDevice>.findHardwareWalletId(
1098+
deviceId: String,
1099+
xpubs: Map<String, String>,
1100+
deriveWalletId: (Collection<String>) -> String,
1101+
): String {
10921102
val walletKey = walletKey(xpubs, deviceId)
10931103
firstOrNull { it.walletKey == walletKey }?.walletId?.takeIf { it.isNotBlank() }?.let { return it }
1094-
if (xpubs.values.any { it.isNotBlank() }) return deriveHardwareWalletId(xpubs)
1104+
if (xpubs.values.any { it.isNotBlank() }) return deriveWalletId(xpubs.values)
10951105

10961106
return firstOrNull { it.id == deviceId }?.walletId?.takeIf { it.isNotBlank() }.orEmpty()
10971107
}
10981108

1099-
private fun List<KnownDevice>.withHardwareWalletIds(): List<KnownDevice> {
1109+
private fun List<KnownDevice>.withHardwareWalletIds(
1110+
deriveWalletId: (Collection<String>) -> String,
1111+
): List<KnownDevice> {
11001112
val existingByWallet = filter { it.walletId.isNotBlank() }
11011113
.associate { it.walletKey to it.walletId }
11021114
val generatedByWallet = mutableMapOf<String, String>()
11031115

11041116
return map {
11051117
val walletId = existingByWallet[it.walletKey]
1106-
?: generatedByWallet.getOrPut(it.walletKey) { deriveHardwareWalletId(it.xpubs) }
1118+
?: generatedByWallet.getOrPut(it.walletKey) { deriveWalletId(it.xpubs.values) }
11071119
if (it.walletId == walletId) it else it.copy(walletId = walletId)
11081120
}
11091121
}
11101122

1111-
private fun deriveHardwareWalletId(xpubs: Map<String, String>): String {
1112-
return ActivityWalletType.TREZOR.deriveId(xpubs.values)
1113-
}
1114-
11151123
private const val DEFAULT_GAP_LIMIT = 20u
11161124

11171125
private fun KnownDevice.toDeviceInfo() = TrezorDeviceInfo(

app/src/main/java/to/bitkit/services/TrezorService.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import to.bitkit.async.ServiceQueue
5050
import javax.inject.Inject
5151
import javax.inject.Singleton
5252
import com.synonym.bitkitcore.Network as BitkitCoreNetwork
53+
import com.synonym.bitkitcore.deriveWalletId as deriveCoreWalletId
5354

5455
@Suppress("TooManyFunctions")
5556
@Singleton
@@ -301,4 +302,8 @@ class TrezorService @Inject constructor(
301302
onchainStopAllWatchers()
302303
}
303304
}
305+
306+
fun deriveWalletId(deviceType: String, xpubs: Collection<String>): String {
307+
return deriveCoreWalletId(deviceType = deviceType, xpubs = xpubs.toList())
308+
}
304309
}

app/src/test/java/to/bitkit/repositories/HwWalletRepoTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import org.mockito.kotlin.never
2525
import org.mockito.kotlin.times
2626
import org.mockito.kotlin.verify
2727
import org.mockito.kotlin.whenever
28-
import org.mockito.kotlin.wheneverBlocking
2928
import to.bitkit.data.HwWalletData
3029
import to.bitkit.data.HwWalletStore
3130
import to.bitkit.data.SettingsData
@@ -84,11 +83,12 @@ class HwWalletRepoTest : BaseUnitTest() {
8483
whenever(settingsStore.data).thenReturn(settingsData)
8584
whenever(trezorRepo.state).thenReturn(trezorState)
8685
whenever(trezorRepo.watcherEvents).thenReturn(watcherEvents)
87-
wheneverBlocking {
86+
whenever(trezorRepo.deriveWalletId(any())).thenReturn(trezorWalletId)
87+
whenever {
8888
trezorRepo.startWatcher(any(), any(), any(), any(), anyOrNull(), anyOrNull(), any())
8989
}.thenReturn(Result.success(Unit))
90-
wheneverBlocking { activityRepo.persistHardwareActivities(any(), any()) }.thenReturn(Result.success(Unit))
91-
wheneverBlocking { activityRepo.deleteActivitiesForWallet(any()) }.thenReturn(Result.success(Unit))
90+
whenever { activityRepo.persistHardwareActivities(any(), any()) }.thenReturn(Result.success(Unit))
91+
whenever { activityRepo.deleteActivitiesForWallet(any()) }.thenReturn(Result.success(Unit))
9292
}
9393

9494
private fun createRepo() = HwWalletRepo(
@@ -180,7 +180,7 @@ class HwWalletRepoTest : BaseUnitTest() {
180180
@Test
181181
fun `transactions changed event is not exposed when persistence fails`() = test {
182182
val activity = onchainActivity(txid = "t1", amount = 10_562_411uL)
183-
wheneverBlocking { activityRepo.persistHardwareActivities(listOf(activity), emptyList()) }
183+
whenever { activityRepo.persistHardwareActivities(listOf(activity), emptyList()) }
184184
.thenReturn(Result.failure(AppError("persist failed")))
185185
val sut = createRepo()
186186

app/src/test/java/to/bitkit/repositories/TrezorRepoTest.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class TrezorRepoTest : BaseUnitTest() {
102102
whenever(settingsStore.data).thenReturn(settingsData)
103103
whenever(context.filesDir).thenReturn(tempFolder.root)
104104
whenever { hwWalletStore.loadKnownDevices() }.thenReturn(emptyList())
105+
whenever(trezorService.deriveWalletId(any(), any())).thenAnswer {
106+
walletIdFor(*it.getArgument<Collection<String>>(1).toTypedArray())
107+
}
105108
}
106109

107110
private fun createSut(): TrezorRepo = TrezorRepo(
@@ -115,6 +118,9 @@ class TrezorRepoTest : BaseUnitTest() {
115118
ioDispatcher = testDispatcher,
116119
)
117120

121+
private fun walletIdFor(vararg xpubs: String): String =
122+
ActivityWalletType.TREZOR.scopedId(xpubs.sorted().joinToString("|"))
123+
118124
@Suppress("LongParameterList")
119125
private fun mockDeviceInfo(
120126
id: String = DEVICE_ID,
@@ -208,7 +214,8 @@ class TrezorRepoTest : BaseUnitTest() {
208214
val saved = savedCaptor.firstValue.single()
209215
assertEquals(knownDevice.id, saved.id)
210216
assertTrue(ActivityWalletType.TREZOR.owns(saved.walletId))
211-
assertEquals(ActivityWalletType.TREZOR.deriveId(listOf("zpubNS")), saved.walletId)
217+
assertEquals(walletIdFor("zpubNS"), saved.walletId)
218+
verify(trezorService).deriveWalletId(ActivityWalletType.TREZOR.id, listOf("zpubNS"))
212219
assertEquals(listOf(saved), sut.state.value.knownDevices)
213220
}
214221

@@ -625,7 +632,8 @@ class TrezorRepoTest : BaseUnitTest() {
625632
val captor = argumentCaptor<List<KnownDevice>>()
626633
verify(hwWalletStore).saveKnownDevices(captor.capture())
627634
val saved = captor.firstValue.single()
628-
assertEquals(ActivityWalletType.TREZOR.deriveId(listOf("captured-native-xpub")), saved.walletId)
635+
assertEquals(walletIdFor("captured-native-xpub"), saved.walletId)
636+
verify(trezorService).deriveWalletId(ActivityWalletType.TREZOR.id, listOf("captured-native-xpub"))
629637
}
630638

631639
@Test
@@ -671,8 +679,8 @@ class TrezorRepoTest : BaseUnitTest() {
671679

672680
@Test
673681
fun `connect derives new wallet id when same device id has different xpub identity`() = test {
674-
val oldWalletId = ActivityWalletType.TREZOR.deriveId(listOf("old-native-xpub"))
675-
val newWalletId = ActivityWalletType.TREZOR.deriveId(listOf("new-native-xpub"))
682+
val oldWalletId = walletIdFor("old-native-xpub")
683+
val newWalletId = walletIdFor("new-native-xpub")
676684
val nativeSegwitPath = "m/84'/1'/0'"
677685
val previousDevice = mockKnownDevice(
678686
id = DEVICE_ID,

0 commit comments

Comments
 (0)