Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions app/src/main/java/to/bitkit/data/PrivatePaykitStores.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,14 @@ data class PrivatePaykitCacheData(
val contacts: Map<String, PrivatePaykitContactCacheData> = emptyMap(),
val cleanupPending: Boolean = false,
val deletedContactCleanupPendingPublicKeys: Set<String> = emptySet(),
val profileRecoveryPending: Boolean = false,
)

@Serializable
data class PrivatePaykitContactCacheData(
val remoteEndpoints: List<PrivatePaykitStoredPaymentEntryData> = emptyList(),
val localInvoice: PrivatePaykitStoredInvoiceData? = null,
val receivedInvoicePaymentHashes: List<String> = emptyList(),
val lastLocalPayloadHash: String? = null,
val linkCompletedAt: Long? = null,
val handshakeUpdatedAt: Long? = null,
val recoveryStartedAt: Long? = null,
val mainRecoveryAttemptId: String? = null,
val responderRecoveryAttemptId: String? = null,
val lastCompletedRecoveryAttemptId: String? = null,
val awaitingRecoveredRemoteEndpoints: Boolean = false,
val linkFailureCount: Int = 0,
val hasPublishedPrivatePaymentList: Boolean = false,
)

@Serializable
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/to/bitkit/data/PubkyStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.Serializable
import to.bitkit.data.serializers.PubkyStoreSerializer
import to.bitkit.models.PubkyProfileData
import javax.inject.Inject
import javax.inject.Singleton

Expand Down Expand Up @@ -36,4 +37,5 @@ class PubkyStore @Inject constructor(
data class PubkyStoreData(
val cachedName: String? = null,
val cachedImageUri: String? = null,
val contactProfileOverrides: Map<String, PubkyProfileData> = emptyMap(),
)
26 changes: 20 additions & 6 deletions app/src/main/java/to/bitkit/data/keychain/Keychain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@ class Keychain @Inject constructor(
} catch (t: Throwable) {
throw KeychainError.FailedToSave(key, cause = t)
}
Logger.info("Saved to keychain: $key")
Logger.info("Saved value for key '$key'", context = TAG)
}

/** Inserts or replaces a string value associated with a given key in the keychain. */
@Suppress("TooGenericExceptionCaught")
suspend fun upsertString(key: String, value: String) {
try {
Expand All @@ -94,7 +93,22 @@ class Keychain @Inject constructor(
} catch (t: Throwable) {
throw KeychainError.FailedToSave(key, cause = t)
}
Logger.info("Upsert in keychain: $key")
Logger.info("Upserted value for key '$key'", context = TAG)
}

@Suppress("TooGenericExceptionCaught")
fun upsert(key: String, value: ByteArray) {
try {
val encryptedValue = keyStore.encrypt(value)
runBlocking(this.coroutineContext) {
keychain.edit { it[key.indexed] = encryptedValue.toBase64() }
}
} catch (c: CancellationException) {
throw c
} catch (t: Throwable) {
throw KeychainError.FailedToSave(key, cause = t)
}
Logger.info("Upserted value for key '$key'", context = TAG)
}

@Suppress("TooGenericExceptionCaught")
Expand All @@ -106,7 +120,7 @@ class Keychain @Inject constructor(
} catch (t: Throwable) {
throw KeychainError.FailedToDelete(key, cause = t)
}
Logger.debug("Deleted from keychain: $key")
Logger.debug("Deleted value for key '$key'", context = TAG)
}

fun exists(key: String): Boolean {
Expand All @@ -119,7 +133,7 @@ class Keychain @Inject constructor(
keyStore.resetEncryptionKey()
val count = keys.size

Logger.info("Reset keychain encryption key and deleted all '$count' entries")
Logger.info("Reset keychain encryption key and deleted all '$count' entries", context = TAG)
}

private val String.indexed: Preferences.Key<String>
Expand Down Expand Up @@ -174,7 +188,7 @@ class Keychain @Inject constructor(
PIN,
PIN_ATTEMPTS_REMAINING,
PAYKIT_SESSION,
PRIVATE_PAYKIT_SECRET_STATE,
PAYKIT_SDK_STATE,
PUBKY_SECRET_KEY,
}
}
Expand Down
24 changes: 0 additions & 24 deletions app/src/main/java/to/bitkit/env/Env.kt
Original file line number Diff line number Diff line change
Expand Up @@ -158,21 +158,6 @@ internal object Env {
const val BITREFILL_APP = "Bitkit"
const val BITREFILL_REF = "AL6dyZYt"

private val pubkyDomain: String
get() = when (network) {
Network.BITCOIN -> "bitkit.to"
else -> "staging.bitkit.to"
}

val pubkyCapabilities: String
get() {
val prefix = when (network) {
Network.BITCOIN -> ""
else -> "staging."
}
return "/pub/$pubkyDomain/:rw,/pub/${prefix}pubky.app/:r,/pub/paykit/v0/:rw"
}

val homegateUrl: String
get() {
if (isLocalE2eBackend) {
Expand All @@ -185,15 +170,6 @@ internal object Env {
}
}

val profilePath: String
get() = "/pub/$pubkyDomain/profile.json"

val contactsBasePath: String
get() = "/pub/$pubkyDomain/contacts/"

val blobsBasePath: String
get() = "/pub/$pubkyDomain/blobs/"

val rnBackupServerHost: String
get() = when (network) {
Network.BITCOIN -> "https://blocktank.synonym.to/backups-ldk"
Expand Down
17 changes: 2 additions & 15 deletions app/src/main/java/to/bitkit/models/BackupPayloads.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,7 @@ data class WalletBackupV1(
val createdAt: Long,
val transfers: List<TransferEntity>,
val privatePaykitHighestReservedReceiveIndexByAddressType: Map<String, Int>? = null,
val privatePaykitContactLinks: Map<String, PrivatePaykitContactLinkBackupV1>? = null,
)

@Serializable
data class PrivatePaykitContactLinkBackupV1(
val publicKey: String,
val linkSnapshotHex: String? = null,
val handshakeSnapshotHex: String? = null,
val remoteEndpoints: Map<String, String> = emptyMap(),
val linkCompletedAt: Long? = null,
val handshakeUpdatedAt: Long? = null,
val recoveryStartedAt: Long? = null,
val mainRecoveryAttemptId: String? = null,
val responderRecoveryAttemptId: String? = null,
val awaitingRecoveredRemoteEndpoints: Boolean = false,
val paykitSdkBackupState: String? = null,
)

@Serializable
Expand All @@ -44,6 +30,7 @@ data class MetadataBackupV1(
val tagMetadata: List<PreActivityMetadata>,
val cache: AppCacheData,
val pubkySession: PubkySessionBackupV1? = null,
val pubkyContactProfileOverrides: Map<String, PubkyProfileData>? = null,
)

@Serializable
Expand Down
41 changes: 32 additions & 9 deletions app/src/main/java/to/bitkit/models/PubkyProfile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import androidx.compose.runtime.Stable
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import to.bitkit.ext.ellipsisMiddle
import com.synonym.bitkitcore.PubkyProfile as CorePubkyProfile
import com.synonym.paykit.PaykitProfile as SdkPaykitProfile
import com.synonym.paykit.PubkyProfile as SdkPubkyProfile

private val pubkyProfileJson = Json { ignoreUnknownKeys = true }

@Immutable
data class PubkyProfileLink(val label: String, val url: String)
Expand All @@ -23,18 +26,21 @@ data class PubkyProfile(
companion object {
private const val TRUNCATED_PK_LENGTH = 11

fun fromFfi(publicKey: String, ffiProfile: CorePubkyProfile): PubkyProfile {
fun fromPubkyProfile(publicKey: String, sdkProfile: SdkPubkyProfile): PubkyProfile {
return PubkyProfile(
publicKey = publicKey,
name = ffiProfile.name,
bio = ffiProfile.bio ?: "",
imageUrl = ffiProfile.image,
links = ffiProfile.links.orEmpty().map { PubkyProfileLink(label = it.title, url = it.url) },
name = sdkProfile.name,
bio = sdkProfile.bio ?: "",
imageUrl = sdkProfile.image,
links = sdkProfile.links.map { PubkyProfileLink(label = it.title, url = it.url) },
tags = emptyList(),
status = ffiProfile.status,
status = sdkProfile.status,
)
}

fun fromPaykitProfile(publicKey: String, profile: SdkPaykitProfile): PubkyProfile =
PubkyProfileData.fromPaykitProfile(profile).toPubkyProfile(publicKey)

fun placeholder(publicKey: String) = PubkyProfile(
publicKey = publicKey,
name = publicKey.ellipsisMiddle(TRUNCATED_PK_LENGTH),
Expand Down Expand Up @@ -85,11 +91,28 @@ data class PubkyProfileData(
) {
companion object {
fun decode(json: String): PubkyProfileData =
Json { ignoreUnknownKeys = true }.decodeFromString(json)
pubkyProfileJson.decodeFromString(json)

fun fromPaykitProfile(profile: SdkPaykitProfile): PubkyProfileData {
val extra = profile.extraJson?.let { runCatching { decode(it) }.getOrNull() }
return PubkyProfileData(
name = profile.displayName ?: extra?.name.orEmpty(),
bio = extra?.bio.orEmpty(),
image = profile.imageUri ?: extra?.image,
links = extra?.links.orEmpty(),
tags = extra?.tags.orEmpty(),
)
}
}

fun encode(): ByteArray =
Json.encodeToString(this).toByteArray(Charsets.UTF_8)
pubkyProfileJson.encodeToString(this).toByteArray(Charsets.UTF_8)

fun toPaykitProfile() = SdkPaykitProfile(
displayName = name,
imageUri = image,
extraJson = encode().toString(Charsets.UTF_8),
)

fun toPubkyProfile(publicKey: String) = PubkyProfile(
publicKey = publicKey,
Expand Down
14 changes: 2 additions & 12 deletions app/src/main/java/to/bitkit/models/PubkyPublicKeyFormat.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package to.bitkit.models

import com.synonym.paykit.PaykitPublicKeys
import to.bitkit.ext.ellipsisMiddle
import java.util.Locale

object PubkyPublicKeyFormat {
private const val pubkyPrefix = "pubky"
private const val rawKeyLength = 52
private const val redactedLength = 16
const val maximumInputLength = 57

private val zBase32Regex = Regex("^[ybndrfg8ejkmcpqxot1uwisza345h769]+$")

fun bounded(input: String): String {
return input
.trim()
Expand All @@ -19,14 +16,7 @@ object PubkyPublicKeyFormat {
}

fun normalized(input: String): String? {
val normalizedInput = input.trim().lowercase(Locale.US)
val rawKey = normalizedInput.removePrefix(pubkyPrefix)

if (rawKey.length != rawKeyLength || !zBase32Regex.matches(rawKey)) {
return null
}

return "$pubkyPrefix$rawKey"
return runCatching { PaykitPublicKeys.normalize(bounded(input)) }.getOrNull()
}

fun matches(lhs: String?, rhs: String?): Boolean {
Expand Down
Loading
Loading