Moves the ContactList cache lists to AccountViewModel, where it can be disposed more efficiently.

This commit is contained in:
Vitor Pamplona
2024-08-06 15:36:04 -04:00
parent e5328d7975
commit 5fdff97cf8
11 changed files with 74 additions and 100 deletions
@@ -225,6 +225,7 @@ class Account(
@Immutable @Immutable
class LiveFollowLists( class LiveFollowLists(
val users: Set<String> = emptySet(), val users: Set<String> = emptySet(),
val usersPlusMe: Set<String>,
val hashtags: Set<String> = emptySet(), val hashtags: Set<String> = emptySet(),
val geotags: Set<String> = emptySet(), val geotags: Set<String> = emptySet(),
val communities: Set<String> = emptySet(), val communities: Set<String> = emptySet(),
@@ -423,17 +424,29 @@ class Account(
val liveKind3FollowsFlow: Flow<LiveFollowLists> = val liveKind3FollowsFlow: Flow<LiveFollowLists> =
userProfile().flow().follows.stateFlow.transformLatest { userProfile().flow().follows.stateFlow.transformLatest {
checkNotInMainThread() checkNotInMainThread()
// makes sure the output include only valid p tags
val verifiedFollowingUsers = it.user.latestContactList?.verifiedFollowKeySet() ?: emptySet()
emit( emit(
LiveFollowLists( LiveFollowLists(
it.user.cachedFollowingKeySet(), verifiedFollowingUsers,
it.user.cachedFollowingTagSet(), verifiedFollowingUsers + keyPair.pubKeyHex,
it.user.cachedFollowingGeohashSet(), it.user.latestContactList
it.user.cachedFollowingCommunitiesSet(), ?.unverifiedFollowTagSet()
?.map { it.lowercase() }
?.toSet() ?: emptySet(),
it.user.latestContactList
?.unverifiedFollowGeohashSet()
?.toSet() ?: emptySet(),
it.user.latestContactList
?.verifiedFollowAddressSet()
?.toSet() ?: emptySet(),
), ),
) )
} }
val liveKind3Follows = liveKind3FollowsFlow.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) val liveKind3Follows = liveKind3FollowsFlow.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveHomeList: Flow<ListNameNotePair> by lazy { private val liveHomeList: Flow<ListNameNotePair> by lazy {
@@ -465,11 +478,11 @@ class Account(
} else if (peopleListFollows.listName == KIND3_FOLLOWS) { } else if (peopleListFollows.listName == KIND3_FOLLOWS) {
emit(kind3Follows) emit(kind3Follows)
} else if (peopleListFollows.event == null) { } else if (peopleListFollows.event == null) {
emit(LiveFollowLists()) emit(LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} else { } else {
val result = waitToDecrypt(peopleListFollows.event) val result = waitToDecrypt(peopleListFollows.event)
if (result == null) { if (result == null) {
emit(LiveFollowLists()) emit(LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} else { } else {
emit(result) emit(result)
} }
@@ -481,7 +494,7 @@ class Account(
} }
val liveHomeFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveHomeFollowLists: StateFlow<LiveFollowLists?> by lazy {
liveHomeFollowListFlow.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) liveHomeFollowListFlow.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
fun relaysFromPeopleListFlows( fun relaysFromPeopleListFlows(
@@ -540,7 +553,7 @@ class Account(
val liveNotificationFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveNotificationFollowLists: StateFlow<LiveFollowLists?> by lazy {
combinePeopleListFlows(liveKind3FollowsFlow, liveNotificationList) combinePeopleListFlows(liveKind3FollowsFlow, liveNotificationList)
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@@ -552,7 +565,7 @@ class Account(
val liveStoriesFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveStoriesFollowLists: StateFlow<LiveFollowLists?> by lazy {
combinePeopleListFlows(liveKind3FollowsFlow, liveStoriesList) combinePeopleListFlows(liveKind3FollowsFlow, liveStoriesList)
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@@ -564,7 +577,7 @@ class Account(
val liveDiscoveryFollowLists: StateFlow<LiveFollowLists?> by lazy { val liveDiscoveryFollowLists: StateFlow<LiveFollowLists?> by lazy {
combinePeopleListFlows(liveKind3FollowsFlow, liveDiscoveryList) combinePeopleListFlows(liveKind3FollowsFlow, liveDiscoveryList)
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists()) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
private fun decryptLiveFollows( private fun decryptLiveFollows(
@@ -572,10 +585,11 @@ class Account(
onReady: (LiveFollowLists) -> Unit, onReady: (LiveFollowLists) -> Unit,
) { ) {
listEvent.privateTags(signer) { privateTagList -> listEvent.privateTags(signer) { privateTagList ->
val users = (listEvent.bookmarkedPeople() + listEvent.filterUsers(privateTagList)).toSet()
onReady( onReady(
LiveFollowLists( LiveFollowLists(
users = users = users,
(listEvent.bookmarkedPeople() + listEvent.filterUsers(privateTagList)).toSet(), usersPlusMe = users + userProfile().pubkeyHex,
hashtags = hashtags =
(listEvent.hashtags() + listEvent.filterHashtags(privateTagList)).toSet(), (listEvent.hashtags() + listEvent.filterHashtags(privateTagList)).toSet(),
geotags = geotags =
@@ -759,6 +773,10 @@ class Account(
} }
} }
suspend fun countFollowersOf(pubkey: HexKey): Int = LocalCache.users.count { _, it -> it.latestContactList?.isTaggedUser(pubkey) ?: false }
suspend fun followerCount(): Int = countFollowersOf(keyPair.pubKeyHex)
fun sendNewUserMetadata( fun sendNewUserMetadata(
name: String? = null, name: String? = null,
picture: String? = null, picture: String? = null,
@@ -2813,9 +2831,7 @@ class Account(
flowHiddenUsers.value.hiddenUsers.contains(userHex) || flowHiddenUsers.value.hiddenUsers.contains(userHex) ||
flowHiddenUsers.value.spammers.contains(userHex) flowHiddenUsers.value.spammers.contains(userHex)
fun followingKeySet(): Set<HexKey> = userProfile().cachedFollowingKeySet() fun followingKeySet(): Set<HexKey> = liveKind3Follows.value.users
fun followingTagSet(): Set<HexKey> = userProfile().cachedFollowingTagSet()
fun isAcceptable(user: User): Boolean { fun isAcceptable(user: User): Boolean {
if (userProfile().pubkeyHex == user.pubkeyHex) { if (userProfile().pubkeyHex == user.pubkeyHex) {
@@ -2865,8 +2881,6 @@ class Account(
} }
fun getRelevantReports(note: Note): Set<Note> { fun getRelevantReports(note: Note): Set<Note> {
val followsPlusMe = userProfile().latestContactList?.verifiedFollowKeySetAndMe ?: emptySet()
val innerReports = val innerReports =
if (note.event is RepostEvent || note.event is GenericRepostEvent) { if (note.event is RepostEvent || note.event is GenericRepostEvent) {
note.replyTo?.map { getRelevantReports(it) }?.flatten() ?: emptyList() note.replyTo?.map { getRelevantReports(it) }?.flatten() ?: emptyList()
@@ -2875,8 +2889,8 @@ class Account(
} }
return ( return (
note.reportsBy(followsPlusMe) + note.reportsBy(liveKind3Follows.value.usersPlusMe) +
(note.author?.reportsBy(followsPlusMe) ?: emptyList()) + (note.author?.reportsBy(liveKind3Follows.value.usersPlusMe) ?: emptyList()) +
innerReports innerReports
).toSet() ).toSet()
} }
@@ -336,52 +336,24 @@ class User(
fun isFollowing(user: User): Boolean = latestContactList?.isTaggedUser(user.pubkeyHex) ?: false fun isFollowing(user: User): Boolean = latestContactList?.isTaggedUser(user.pubkeyHex) ?: false
fun isFollowingHashtag(tag: String): Boolean = latestContactList?.isTaggedHash(tag) ?: false fun isFollowingHashtag(tag: String): Boolean {
return latestContactList?.unverifiedFollowTagSet()?.map { it.lowercase() }?.toSet()?.let {
fun isFollowingHashtagCached(tag: String): Boolean {
return latestContactList?.verifiedFollowTagSet?.let {
return tag.lowercase() in it return tag.lowercase() in it
} }
?: false ?: false
} }
fun isFollowingGeohashCached(geoTag: String): Boolean { fun isFollowingGeohash(geoTag: String): Boolean {
return latestContactList?.verifiedFollowGeohashSet?.let { return latestContactList?.unverifiedFollowAddressSet()?.toSet()?.let {
return geoTag.lowercase() in it return geoTag.lowercase() in it
} }
?: false ?: false
} }
fun isFollowingCached(user: User): Boolean {
return latestContactList?.verifiedFollowKeySet?.let {
return user.pubkeyHex in it
}
?: false
}
fun isFollowingCached(userHex: String): Boolean {
return latestContactList?.verifiedFollowKeySet?.let {
return userHex in it
}
?: false
}
fun transientFollowCount(): Int? = latestContactList?.unverifiedFollowKeySet()?.size fun transientFollowCount(): Int? = latestContactList?.unverifiedFollowKeySet()?.size
suspend fun transientFollowerCount(): Int = LocalCache.users.count { _, it -> it.latestContactList?.isTaggedUser(pubkeyHex) ?: false } suspend fun transientFollowerCount(): Int = LocalCache.users.count { _, it -> it.latestContactList?.isTaggedUser(pubkeyHex) ?: false }
fun cachedFollowingKeySet(): Set<HexKey> = latestContactList?.verifiedFollowKeySet ?: emptySet()
fun cachedFollowingTagSet(): Set<String> = latestContactList?.verifiedFollowTagSet ?: emptySet()
fun cachedFollowingGeohashSet(): Set<HexKey> = latestContactList?.verifiedFollowGeohashSet ?: emptySet()
fun cachedFollowingCommunitiesSet(): Set<HexKey> = latestContactList?.verifiedFollowCommunitySet ?: emptySet()
fun cachedFollowCount(): Int? = latestContactList?.verifiedFollowKeySet?.size
suspend fun cachedFollowerCount(): Int = LocalCache.users.count { _, it -> it.latestContactList?.isTaggedUser(pubkeyHex) ?: false }
fun hasSentMessagesTo(key: ChatroomKey?): Boolean { fun hasSentMessagesTo(key: ChatroomKey?): Boolean {
val messagesToUser = privateChatrooms[key] ?: return false val messagesToUser = privateChatrooms[key] ?: return false
@@ -763,9 +763,11 @@ class FollowListViewModel(
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val liveKind3FollowsFlow: Flow<List<CodeName>> = val liveKind3FollowsFlow: Flow<List<CodeName>> =
account.userProfile().flow().follows.stateFlow.transformLatest { account.liveKind3FollowsFlow.transformLatest {
checkNotInMainThread()
val communities = val communities =
it.user.cachedFollowingCommunitiesSet().mapNotNull { it.communities.mapNotNull {
LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote -> LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote ->
CodeName( CodeName(
"Community/${communityNote.idHex}", "Community/${communityNote.idHex}",
@@ -776,12 +778,12 @@ class FollowListViewModel(
} }
val hashtags = val hashtags =
it.user.cachedFollowingTagSet().map { it.hashtags.map {
CodeName("Hashtag/$it", HashtagName(it), CodeNameType.ROUTE) CodeName("Hashtag/$it", HashtagName(it), CodeNameType.ROUTE)
} }
val geotags = val geotags =
it.user.cachedFollowingGeohashSet().map { it.geotags.map {
CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE) CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE)
} }
@@ -83,6 +83,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.FeatureSetType import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.actions.mediaServers.MediaServersListView import com.vitorpamplona.amethyst.ui.actions.mediaServers.MediaServersListView
@@ -114,7 +115,6 @@ import com.vitorpamplona.ammolite.relays.RelayPoolStatus
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@@ -151,7 +151,7 @@ fun DrawerContent(
EditStatusBoxes(accountViewModel.account.userProfile(), accountViewModel, drawerState) EditStatusBoxes(accountViewModel.account.userProfile(), accountViewModel, drawerState)
} }
FollowingAndFollowerCounts(accountViewModel.account.userProfile(), onClickUser) FollowingAndFollowerCounts(accountViewModel.account, onClickUser)
HorizontalDivider( HorizontalDivider(
thickness = DividerThickness, thickness = DividerThickness,
@@ -380,18 +380,12 @@ fun UserStatusDeleteButton(onClick: () -> Unit) {
@Composable @Composable
private fun FollowingAndFollowerCounts( private fun FollowingAndFollowerCounts(
baseAccountUser: User, baseAccountUser: Account,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
var followingCount by remember { mutableStateOf("--") } val followingCount = baseAccountUser.liveKind3Follows.collectAsStateWithLifecycle()
var followerCount by remember { mutableStateOf("--") } var followerCount by remember { mutableStateOf("--") }
WatchFollow(baseAccountUser = baseAccountUser) { newFollowing ->
if (followingCount != newFollowing) {
followingCount = newFollowing
}
}
WatchFollower(baseAccountUser = baseAccountUser) { newFollower -> WatchFollower(baseAccountUser = baseAccountUser) { newFollower ->
if (followerCount != newFollower) { if (followerCount != newFollower) {
followerCount = newFollower followerCount = newFollower
@@ -402,7 +396,9 @@ private fun FollowingAndFollowerCounts(
modifier = drawerSpacing.clickable(onClick = onClick), modifier = drawerSpacing.clickable(onClick = onClick),
) { ) {
Text( Text(
text = followingCount, text =
followingCount.value.users.size
.toString(),
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
) )
@@ -419,31 +415,19 @@ private fun FollowingAndFollowerCounts(
} }
} }
@Composable
fun WatchFollow(
baseAccountUser: User,
onReady: (String) -> Unit,
) {
val accountUserFollowsState by baseAccountUser.live().follows.observeAsState()
LaunchedEffect(key1 = accountUserFollowsState) {
launch(Dispatchers.IO) {
onReady(accountUserFollowsState?.user?.cachedFollowCount()?.toString() ?: "--")
}
}
}
@Composable @Composable
fun WatchFollower( fun WatchFollower(
baseAccountUser: User, baseAccountUser: Account,
onReady: (String) -> Unit, onReady: (String) -> Unit,
) { ) {
val accountUserFollowersState by baseAccountUser.live().followers.observeAsState() val accountUserFollowersState by baseAccountUser
.userProfile()
.live()
.followers
.observeAsState()
LaunchedEffect(key1 = accountUserFollowersState) { LaunchedEffect(key1 = accountUserFollowersState) {
launch(Dispatchers.IO) { onReady(baseAccountUser.followerCount().toString() ?: "--")
onReady(accountUserFollowersState?.user?.cachedFollowerCount()?.toString() ?: "--")
}
} }
} }
@@ -676,7 +676,7 @@ fun LoadModerators(
val newParticipantUsers = val newParticipantUsers =
if (followingKeySet == null) { if (followingKeySet == null) {
val allFollows = accountViewModel.account.userProfile().cachedFollowingKeySet() val allFollows = accountViewModel.account.liveKind3Follows.value.users
val followingParticipants = val followingParticipants =
ParticipantListBuilder().followsThatParticipateOn(baseNote, allFollows).minus(hosts) ParticipantListBuilder().followsThatParticipateOn(baseNote, allFollows).minus(hosts)
@@ -732,7 +732,7 @@ private fun LoadParticipants(
val newParticipantUsers = val newParticipantUsers =
if (followingKeySet == null) { if (followingKeySet == null) {
val allFollows = accountViewModel.account.userProfile().cachedFollowingKeySet() val allFollows = accountViewModel.account.liveKind3Follows.value.users
val followingParticipants = val followingParticipants =
ParticipantListBuilder() ParticipantListBuilder()
.followsThatParticipateOn(baseNote, allFollows) .followsThatParticipateOn(baseNote, allFollows)
@@ -889,7 +889,7 @@ fun RenderChannelThumb(
val newParticipantUsers = val newParticipantUsers =
if (followingKeySet == null) { if (followingKeySet == null) {
val allFollows = accountViewModel.account.userProfile().cachedFollowingKeySet() val allFollows = accountViewModel.account.liveKind3Follows.value.users
val followingParticipants = val followingParticipants =
ParticipantListBuilder().followsThatParticipateOn(baseNote, allFollows).toList() ParticipantListBuilder().followsThatParticipateOn(baseNote, allFollows).toList()
@@ -367,11 +367,10 @@ fun WatchUserFollows(
remember { remember {
accountViewModel.userFollows accountViewModel.userFollows
.map { .map {
it.user.isFollowingCached(userHex) || accountViewModel.isFollowing(userHex) || (userHex == accountViewModel.account.userProfile().pubkeyHex)
(userHex == accountViewModel.account.userProfile().pubkeyHex)
}.distinctUntilChanged() }.distinctUntilChanged()
}.observeAsState( }.observeAsState(
accountViewModel.account.userProfile().isFollowingCached(userHex) || accountViewModel.isFollowing(userHex) ||
(userHex == accountViewModel.account.userProfile().pubkeyHex), (userHex == accountViewModel.account.userProfile().pubkeyHex),
) )
@@ -29,12 +29,12 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -44,12 +44,11 @@ fun DisplayFollowingHashtagsInPost(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: (String) -> Unit, nav: (String) -> Unit,
) { ) {
val userFollowState by accountViewModel.userFollows.observeAsState() val userFollowState by accountViewModel.account.liveKind3Follows.collectAsStateWithLifecycle()
var firstTag by remember(baseNote) { mutableStateOf<String?>(null) } var firstTag by remember(baseNote) { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = userFollowState) { LaunchedEffect(key1 = userFollowState) {
val followingTags = userFollowState?.user?.cachedFollowingTagSet() ?: emptySet() val newFirstTag = baseNote.event?.firstIsTaggedHashes(userFollowState.hashtags)
val newFirstTag = baseNote.event?.firstIsTaggedHashes(followingTags)
if (firstTag != newFirstTag) { if (firstTag != newFirstTag) {
firstTag = newFirstTag firstTag = newFirstTag
@@ -807,10 +807,10 @@ class AccountViewModel(
fun isFollowing(user: User?): Boolean { fun isFollowing(user: User?): Boolean {
if (user == null) return false if (user == null) return false
return account.userProfile().isFollowingCached(user) return account.isFollowing(user)
} }
fun isFollowing(user: HexKey): Boolean = account.userProfile().isFollowingCached(user) fun isFollowing(user: HexKey): Boolean = account.isFollowing(user)
val hideDeleteRequestDialog: Boolean val hideDeleteRequestDialog: Boolean
get() = account.hideDeleteRequestDialog get() = account.hideDeleteRequestDialog
@@ -188,7 +188,7 @@ fun GeoHashActionOptions(
.observeAsState() .observeAsState()
val isFollowingTag by val isFollowingTag by
remember(userState) { remember(userState) {
derivedStateOf { userState?.user?.isFollowingGeohashCached(tag) ?: false } derivedStateOf { userState?.user?.isFollowingGeohash(tag) ?: false }
} }
if (isFollowingTag) { if (isFollowingTag) {
@@ -167,7 +167,7 @@ fun HashtagActionOptions(
.observeAsState() .observeAsState()
val isFollowingTag by val isFollowingTag by
remember(userState) { remember(userState) {
derivedStateOf { userState?.user?.isFollowingHashtagCached(tag) ?: false } derivedStateOf { userState?.user?.isFollowingHashtag(tag) ?: false }
} }
if (isFollowingTag) { if (isFollowingTag) {
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.quartz.crypto package com.vitorpamplona.quartz.crypto
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey import com.vitorpamplona.quartz.encoders.toHexKey
class KeyPair( class KeyPair(
@@ -29,6 +30,7 @@ class KeyPair(
) { ) {
val privKey: ByteArray? val privKey: ByteArray?
val pubKey: ByteArray val pubKey: ByteArray
val pubKeyHex: HexKey
init { init {
if (privKey == null) { if (privKey == null) {
@@ -52,6 +54,8 @@ class KeyPair(
this.pubKey = pubKey this.pubKey = pubKey
} }
} }
this.pubKeyHex = this.pubKey.toHexKey().intern()
} }
override fun toString(): String = "KeyPair(privateKey=${privKey?.toHexKey()}, publicKey=${pubKey.toHexKey()}" override fun toString(): String = "KeyPair(privateKey=${privKey?.toHexKey()}, publicKey=${pubKey.toHexKey()}"