Defers NIP05 Resolver/NamecoinResolver Builder until needed

This commit is contained in:
Vitor Pamplona
2026-03-26 11:07:56 -04:00
parent d73dd662c6
commit 44c39b01ea
19 changed files with 57 additions and 50 deletions
@@ -158,29 +158,36 @@ class AppModules(
// Offers easy methods to know when connections are happening through Tor or not
val roleBasedHttpClientBuilder = RoleBasedHttpClientBuilder(okHttpClients, torPrefs.value)
// Custom fetcher that considers tor settings and avoids forwarding.
val nip05Fetcher = OkHttpNip05Fetcher(roleBasedHttpClientBuilder::okHttpClientForNip05)
val namecoinResolver =
NamecoinNameResolver(
electrumxClient =
ElectrumXClient(
socketFactory = { roleBasedHttpClientBuilder.socketFactoryForNip05() },
),
serverListProvider = {
// User-configured custom servers take priority
namecoinPrefs.customServersOrNull
?: if (roleBasedHttpClientBuilder.shouldUseTorForNIP05("https://electrumx.example.com")) {
TOR_ELECTRUMX_SERVERS
} else {
DEFAULT_ELECTRUMX_SERVERS
}
},
)
val nip05Client = Nip05Client(nip05Fetcher, namecoinResolver)
val namecoinResolver by
lazy {
Log.d("AppModules", "Namecoin Resolver Init")
NamecoinNameResolver(
electrumxClient =
ElectrumXClient(
socketFactory = { roleBasedHttpClientBuilder.socketFactoryForNip05() },
),
serverListProvider = {
// User-configured custom servers take priority
namecoinPrefs.customServersOrNull
?: if (roleBasedHttpClientBuilder.shouldUseTorForNIP05("https://electrumx.example.com")) {
TOR_ELECTRUMX_SERVERS
} else {
DEFAULT_ELECTRUMX_SERVERS
}
},
)
}
// Application-wide block height request cache
val otsBlockHeightCache by lazy { OtsBlockHeightCache() }
val nip05Client by
lazy {
Log.d("AppModules", "NIP05Client Init")
Nip05Client(
fetcher = OkHttpNip05Fetcher(roleBasedHttpClientBuilder::okHttpClientForNip05),
namecoinResolverBuilder = { namecoinResolver },
)
}
val otsResolverBuilder: TorAwareOkHttpOtsResolverBuilder =
TorAwareOkHttpOtsResolverBuilder(
@@ -280,8 +287,8 @@ class AppModules(
val sessionManager =
AccountSessionManager(
accountsCache = accountsCache,
nip05Client = nip05Client,
client = client,
nip05ClientBuilder = { nip05Client },
localPreferences = LocalPreferences,
scope = applicationIOScope,
)
@@ -119,7 +119,7 @@ open class EditPostViewModel : ViewModel() {
this.editedFromNote = edit
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountViewModel.account, accountViewModel.nip05Client)
this.userSuggestions = UserSuggestionState(accountViewModel.account, accountViewModel.nip05ClientBuilder())
}
fun sendPost() {
@@ -134,7 +134,7 @@ private fun VerifyAndDisplayNIP05OrStatusLine(
if (nip05VerifState.isExpired()) {
LaunchedEffect(key1 = nip05VerifState) {
accountViewModel.runOnIO {
nip05State.checkAndUpdate(accountViewModel.nip05Client)
nip05State.checkAndUpdate(accountViewModel.nip05ClientBuilder)
}
}
}
@@ -442,7 +442,7 @@ fun ObserveAndRenderNIP05VerifiedSymbol(
if (state.isExpired()) {
LaunchedEffect(key1 = state) {
accountViewModel.runOnIO {
nip05State.checkAndUpdate(accountViewModel.nip05Client)
nip05State.checkAndUpdate(accountViewModel.nip05ClientBuilder)
}
}
}
@@ -209,7 +209,7 @@ open class CommentPostViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -93,8 +93,8 @@ sealed class AccountState {
@Stable
class AccountSessionManager(
val accountsCache: AccountCacheState,
val nip05Client: Nip05Client,
val client: INostrClient,
val nip05ClientBuilder: () -> Nip05Client,
val localPreferences: LocalPreferences,
val scope: CoroutineScope,
) {
@@ -227,7 +227,7 @@ class AccountSessionManager(
onError("Could not parse nip05 address: $nip05")
} else {
try {
val pubkeyInfo = nip05Client.get(nip05)
val pubkeyInfo = nip05ClientBuilder().get(nip05)
if (pubkeyInfo == null) {
onError("User not found in the nip05 server: $nip05")
} else {
@@ -173,7 +173,7 @@ class AccountViewModel(
val torSettings: TorSettingsFlow,
val dataSources: RelaySubscriptionsCoordinator,
val httpClientBuilder: IRoleBasedHttpClientBuilder,
val nip05Client: INip05Client,
val nip05ClientBuilder: () -> INip05Client,
) : ViewModel(),
Dao {
var firstRoute: Route? = null
@@ -1290,7 +1290,7 @@ class AccountViewModel(
val torSettings: TorSettingsFlow,
val dataSources: RelaySubscriptionsCoordinator,
val okHttpClient: RoleBasedHttpClientBuilder,
val nip05Client: Nip05Client,
val nip05ClientBuilder: () -> Nip05Client,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T =
@@ -1300,7 +1300,7 @@ class AccountViewModel(
torSettings,
dataSources,
okHttpClient,
nip05Client,
nip05ClientBuilder,
) as T
}
@@ -1822,7 +1822,7 @@ fun mockAccountViewModel(): AccountViewModel {
torSettings = TorSettingsFlow(torType = MutableStateFlow(TorType.OFF)),
httpClientBuilder = EmptyRoleBasedHttpClientBuilder(),
dataSources = RelaySubscriptionsCoordinator(LocalCache, client, authenticator, failureTracker, scope),
nip05Client = EmptyNip05Client(),
nip05ClientBuilder = { EmptyNip05Client() },
).also {
mockedCache = it
}
@@ -1873,7 +1873,7 @@ fun mockVitorAccountViewModel(): AccountViewModel {
torSettings = TorSettingsFlow(torType = MutableStateFlow(TorType.OFF)),
httpClientBuilder = EmptyRoleBasedHttpClientBuilder(),
dataSources = RelaySubscriptionsCoordinator(LocalCache, client, authenticator, failureTracker, scope),
nip05Client = EmptyNip05Client(),
nip05ClientBuilder = { EmptyNip05Client() },
).also {
vitorCache = it
}
@@ -72,7 +72,7 @@ fun LoggedInPage(
torSettings = Amethyst.instance.torPrefs.value,
dataSources = Amethyst.instance.sources,
okHttpClient = Amethyst.instance.roleBasedHttpClientBuilder,
nip05Client = Amethyst.instance.nip05Client,
nip05ClientBuilder = { Amethyst.instance.nip05Client },
),
)
@@ -260,7 +260,7 @@ class ChatNewMessageViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -186,7 +186,7 @@ open class ChannelNewMessageViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -211,7 +211,7 @@ class LongFormPostViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -200,7 +200,7 @@ open class NewProductViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -301,7 +301,7 @@ open class ShortNotePostViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -72,7 +72,7 @@ class PeopleListViewModel : ViewModel() {
) {
if (!this::account.isInitialized || this.account != accountVM.account) {
this.account = accountVM.account
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
}
this.selectedDTag.tryEmit(selectedDTag)
@@ -72,7 +72,7 @@ class FollowPackViewModel : ViewModel() {
) {
if (!this::account.isInitialized || this.account != accountVM.account) {
this.account = accountVM.account
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
}
this.selectedDTag.tryEmit(selectedDTag)
@@ -116,7 +116,7 @@ fun ImportFollowListSelectUserScreen(
) {
val viewModel: ImportFollowListSelectUserViewModel =
viewModel(
factory = ImportFollowListSelectUserViewModel.Factory(accountViewModel.account, accountViewModel.nip05Client),
factory = ImportFollowListSelectUserViewModel.Factory(accountViewModel.account, accountViewModel.nip05ClientBuilder()),
)
Scaffold(
@@ -209,7 +209,7 @@ class NewPublicMessageViewModel :
this.canAddZapRaiser = hasLnAddress()
this.userSuggestions?.reset()
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05Client)
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
this.emojiSuggestions?.reset()
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
@@ -91,7 +91,7 @@ fun SearchScreen(
factory =
SearchBarViewModel.Factory(
accountViewModel.account,
accountViewModel.nip05Client,
accountViewModel.nip05ClientBuilder(),
),
)
@@ -49,11 +49,11 @@ sealed interface Nip05State {
fun reset() = verificationState.tryEmit(Nip05VerifState.NotStarted)
suspend fun checkAndUpdate(nip05Client: INip05Client) {
suspend fun checkAndUpdate(nip05ClientBuilder: () -> INip05Client) {
if (verificationState.value.isExpired()) {
markAsVerifying()
try {
if (nip05Client.verify(nip05, hexKey)) {
if (nip05ClientBuilder().verify(nip05, hexKey)) {
markAsVerified()
} else {
markAsInvalid()
@@ -28,7 +28,7 @@ import kotlinx.coroutines.CancellationException
@Stable
class Nip05Client(
val fetcher: Nip05Fetcher,
val namecoinResolver: NamecoinNameResolver? = null,
val namecoinResolverBuilder: (() -> NamecoinNameResolver)? = null,
) : INip05Client {
val parser = Nip05Parser()
@@ -37,8 +37,8 @@ class Nip05Client(
hexKey: HexKey,
): Boolean {
// Namecoin: route .bit domains to blockchain verification
if (namecoinResolver != null && NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue())) {
val result = namecoinResolver.resolve(nip05.toValue())
if (namecoinResolverBuilder != null && NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue())) {
val result = namecoinResolverBuilder().resolve(nip05.toValue())
return result?.pubkey == hexKey
}
@@ -61,8 +61,8 @@ class Nip05Client(
override suspend fun get(nip05: Nip05Id): Nip05KeyInfo? {
// Namecoin: route .bit domains to blockchain resolution
if (namecoinResolver != null && NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue())) {
val result = namecoinResolver.resolve(nip05.toValue()) ?: return null
if (namecoinResolverBuilder != null && NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue())) {
val result = namecoinResolverBuilder().resolve(nip05.toValue()) ?: return null
return Nip05KeyInfo(result.pubkey, result.relays)
}