Skip to content

Commit c8fbcae

Browse files
committed
Merge branch 'master' into fix/suggested-fee-calc-boost
2 parents 67e16f1 + 82d56cd commit c8fbcae

File tree

10 files changed

+103
-67
lines changed

10 files changed

+103
-67
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,10 @@ class LightningRepo @Inject constructor(
245245
}
246246
}
247247

248-
/**Updates the shouldBlockLightning state and returns the current value*/
249248
suspend fun updateGeoBlockState() {
250249
val (isGeoBlocked, shouldBlockLightning) = coreService.checkGeoBlock()
251250
_lightningState.update {
252-
it.copy(shouldBlockLightning = shouldBlockLightning, isGeoBlocked = isGeoBlocked)
251+
it.copy(isGeoBlocked = isGeoBlocked, shouldBlockLightningReceive = shouldBlockLightning)
253252
}
254253
}
255254

@@ -432,7 +431,7 @@ class LightningRepo @Inject constructor(
432431
expirySeconds: UInt = 86_400u,
433432
): Result<String> = executeWhenNodeRunning("Create invoice") {
434433
updateGeoBlockState()
435-
if (lightningState.value.shouldBlockLightning) {
434+
if (lightningState.value.shouldBlockLightningReceive) {
436435
return@executeWhenNodeRunning Result.failure(ServiceError.GeoBlocked)
437436
}
438437

@@ -673,7 +672,7 @@ class LightningRepo @Inject constructor(
673672
Result.success(Unit)
674673
}
675674

676-
suspend fun syncState() {
675+
fun syncState() {
677676
_lightningState.update {
678677
it.copy(
679678
nodeId = getNodeId().orEmpty(),
@@ -709,8 +708,10 @@ class LightningRepo @Inject constructor(
709708
fun getChannels(): List<ChannelDetails>? =
710709
if (_lightningState.value.nodeLifecycleState.isRunning()) lightningService.channels else null
711710

712-
fun hasChannels(): Boolean =
713-
_lightningState.value.nodeLifecycleState.isRunning() && lightningService.channels?.isNotEmpty() == true
711+
fun canReceive(): Boolean {
712+
val isRunning = _lightningState.value.nodeLifecycleState.isRunning()
713+
return isRunning && lightningService.canReceive()
714+
}
714715

715716
suspend fun registerForNotifications(token: String? = null) = executeWhenNodeRunning("registerForNotifications") {
716717
return@executeWhenNodeRunning try {
@@ -856,6 +857,6 @@ data class LightningState(
856857
val peers: List<LnPeer> = emptyList(),
857858
val channels: List<ChannelDetails> = emptyList(),
858859
val isSyncingWallet: Boolean = false,
859-
val shouldBlockLightning: Boolean = false,
860+
val shouldBlockLightningReceive: Boolean = false,
860861
val isGeoBlocked: Boolean = false,
861862
)

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,9 @@ class WalletRepo @Inject constructor(
102102
suspend fun refreshBip21(force: Boolean = false): Result<Unit> = withContext(bgDispatcher) {
103103
Logger.debug("Refreshing bip21 (force: $force)", context = TAG)
104104

105-
if (coreService.checkGeoBlock().second) {
106-
_walletState.update {
107-
it.copy(receiveOnSpendingBalance = false)
108-
}
105+
val shouldBlockLightningReceive = coreService.checkGeoBlock().second
106+
_walletState.update {
107+
it.copy(receiveOnSpendingBalance = !shouldBlockLightningReceive)
109108
}
110109

111110
// Reset invoice state
@@ -383,9 +382,8 @@ class WalletRepo @Inject constructor(
383382
updateBip21AmountSats(amountSats)
384383
updateBip21Description(description)
385384

386-
val hasChannels = lightningRepo.hasChannels()
387-
388-
if (hasChannels && _walletState.value.receiveOnSpendingBalance) {
385+
val canReceive = lightningRepo.canReceive()
386+
if (canReceive && _walletState.value.receiveOnSpendingBalance) {
389387
lightningRepo.createInvoice(
390388
amountSats = _walletState.value.bip21AmountSats,
391389
description = _walletState.value.bip21Description,

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

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import to.bitkit.async.ServiceQueue
5858
import to.bitkit.data.CacheStore
5959
import to.bitkit.env.Env
6060
import to.bitkit.ext.amountSats
61-
import to.bitkit.models.LnPeer
6261
import to.bitkit.models.toCoreNetwork
6362
import to.bitkit.utils.AppError
6463
import to.bitkit.utils.Logger
@@ -143,29 +142,18 @@ class CoreService @Inject constructor(
143142
}
144143
}
145144

146-
private suspend fun getLspPeers(): List<LnPeer> {
147-
val blocktankPeers = Env.trustedLnPeers
148-
// TODO get from blocktank info when lightningService.setup sets trustedPeers0conf using BT API
149-
// pseudocode idea:
150-
// val blocktankPeers = getInfo(refresh = true)?.nodes?.map { LnPeer(nodeId = it.pubkey, address = "TO_DO") }.orEmpty()
151-
return blocktankPeers
152-
}
153-
154-
suspend fun getConnectedPeers(): List<LnPeer> = lightningService.peers.orEmpty()
155-
156-
suspend fun hasExternalNode() = getConnectedPeers().any { connectedPeer -> connectedPeer !in getLspPeers() }
157-
158145
/**
159-
* This method checks if the device is geo blocked and if should block lighting features
160-
* @return Pair(isGeoBlocked, shouldBlockLightning)*/
146+
* This method checks if the device is in a is geo blocked region and if lightning features should be blocked
147+
* @return pair of `isGeoBlocked` to `shouldBlockLightningReceive`
148+
* */
161149
suspend fun checkGeoBlock(): Pair<Boolean, Boolean> {
162150
val geoBlocked = isGeoBlocked()
163-
val shouldBlockLightning = when {
164-
hasExternalNode() -> false
151+
val shouldBlockLightningReceive = when {
152+
lightningService.hasExternalPeers() -> !lightningService.canReceive()
165153
else -> geoBlocked
166154
}
167155

168-
return Pair(geoBlocked, shouldBlockLightning)
156+
return Pair(geoBlocked, shouldBlockLightningReceive)
169157
}
170158
}
171159

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,17 @@ class LightningService @Inject constructor(
318318
Logger.warn("Peer disconnect error: $peer", LdkError(e))
319319
}
320320
}
321+
322+
private fun getLspPeers(): List<LnPeer> {
323+
val lspPeers = Env.trustedLnPeers
324+
// TODO get from blocktank info.nodes[] when setup uses it to set trustedPeers0conf
325+
// pseudocode idea:
326+
// val lspPeers = getInfo(refresh = true)?.nodes?.map { LnPeer(nodeId = it.pubkey, address = "TO_DO") }
327+
return lspPeers
328+
}
329+
330+
fun hasExternalPeers() = peers?.any { it !in getLspPeers() } == true
331+
321332
// endregion
322333

323334
// region channels
@@ -374,6 +385,21 @@ class LightningService @Inject constructor(
374385
// endregion
375386

376387
// region payments
388+
fun canReceive(): Boolean {
389+
val channels = this.channels
390+
if (channels == null) {
391+
Logger.warn("canReceive = false: Channels not available")
392+
return false
393+
}
394+
395+
if (channels.none { it.isChannelReady }) {
396+
Logger.warn("canReceive = false: Found no LN channel ready to enable receive: $channels")
397+
return false
398+
}
399+
400+
return true
401+
}
402+
377403
suspend fun receive(sat: ULong? = null, description: String, expirySecs: UInt = 3600u): String {
378404
val node = this.node ?: throw ServiceError.NodeNotSetup
379405

app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveSheet.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
1414
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1515
import androidx.navigation.compose.NavHost
1616
import androidx.navigation.compose.rememberNavController
17-
import kotlinx.coroutines.coroutineScope
18-
import kotlinx.coroutines.launch
1917
import kotlinx.serialization.Serializable
2018
import to.bitkit.repositories.LightningState
21-
import to.bitkit.ui.blocktankViewModel
2219
import to.bitkit.ui.screens.wallets.send.AddTagScreen
2320
import to.bitkit.ui.shared.modifiers.sheetHeight
2421
import to.bitkit.ui.utils.composableWithDefaultTransitions
@@ -34,8 +31,6 @@ fun ReceiveSheet(
3431
editInvoiceAmountViewModel: AmountInputViewModel = hiltViewModel(),
3532
) {
3633
val wallet = requireNotNull(walletViewModel)
37-
val blocktank = requireNotNull(blocktankViewModel)
38-
3934
val navController = rememberNavController()
4035
LaunchedEffect(Unit) { editInvoiceAmountViewModel.clearInput() }
4136

@@ -45,13 +40,7 @@ fun ReceiveSheet(
4540
val lightningState: LightningState by wallet.lightningState.collectAsStateWithLifecycle()
4641

4742
LaunchedEffect(Unit) {
48-
runCatching {
49-
// TODO move to viewModel
50-
coroutineScope {
51-
launch { wallet.refreshBip21() }
52-
launch { blocktank.refreshInfo() }
53-
}
54-
}
43+
wallet.refreshReceiveState()
5544
}
5645

5746
Column(
@@ -86,7 +75,7 @@ fun ReceiveSheet(
8675
walletState = walletState,
8776
onCjitToggle = { isOn ->
8877
when {
89-
isOn && lightningState.shouldBlockLightning -> { // TODO SHOULD BLOCK HERE
78+
isOn && lightningState.shouldBlockLightningReceive -> {
9079
navController.navigate(ReceiveRoute.GeoBlock)
9180
}
9281

app/src/main/java/to/bitkit/viewmodels/BlocktankViewModel.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ class BlocktankViewModel @Inject constructor(
4141
.distinctUntilChanged()
4242
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
4343

44-
fun refreshInfo() {
45-
viewModelScope.launch {
46-
blocktankRepo.refreshInfo()
47-
}
48-
}
49-
5044
fun refreshOrders() {
5145
viewModelScope.launch {
5246
blocktankRepo.refreshOrders()

app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import to.bitkit.models.LnPeer
2626
import to.bitkit.models.NodeLifecycleState
2727
import to.bitkit.models.Toast
2828
import to.bitkit.repositories.BackupRepo
29+
import to.bitkit.repositories.BlocktankRepo
2930
import to.bitkit.repositories.LightningRepo
3031
import to.bitkit.repositories.WalletRepo
3132
import to.bitkit.ui.onboarding.LOADING_MS
@@ -42,6 +43,7 @@ class WalletViewModel @Inject constructor(
4243
private val lightningRepo: LightningRepo,
4344
private val settingsStore: SettingsStore,
4445
private val backupRepo: BackupRepo,
46+
private val blocktankRepo: BlocktankRepo,
4547
) : ViewModel() {
4648

4749
val lightningState = lightningRepo.lightningState
@@ -240,6 +242,12 @@ class WalletViewModel @Inject constructor(
240242
}
241243
}
242244

245+
fun refreshReceiveState() = viewModelScope.launch(bgDispatcher) {
246+
launch { lightningRepo.updateGeoBlockState() }
247+
launch { walletRepo.refreshBip21() }
248+
launch { blocktankRepo.refreshInfo() }
249+
}
250+
243251
fun refreshBip21() {
244252
viewModelScope.launch {
245253
walletRepo.refreshBip21()

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,24 @@ class LightningRepoTest : BaseUnitTest() {
254254
}
255255

256256
@Test
257-
fun `hasChannels should return false when node is not running`() = test {
258-
assertFalse(sut.hasChannels())
257+
fun `canReceive should return false when node is not running`() = test {
258+
assertFalse(sut.canReceive())
259+
}
260+
261+
@Test
262+
fun `canReceive should return false when node is running but cannot receive`() = test {
263+
startNodeForTesting()
264+
whenever(lightningService.canReceive()).thenReturn(false)
265+
266+
assertFalse(sut.canReceive())
267+
}
268+
269+
@Test
270+
fun `canReceive should return true when node can receive`() = test {
271+
startNodeForTesting()
272+
whenever(lightningService.canReceive()).thenReturn(true)
273+
274+
assertTrue(sut.canReceive())
259275
}
260276

261277
@Test

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class WalletRepoTest : BaseUnitTest() {
136136
}
137137

138138
@Test
139-
fun `refreshBip21 should set receiveOnSpendingBalance as false if shouldBlockLightning is true`() = test {
139+
fun `refreshBip21 should set receiveOnSpendingBalance false when shouldBlockLightning is true`() = test {
140140
wheneverBlocking { coreService.checkGeoBlock() }.thenReturn(Pair(true, true))
141141
whenever(lightningRepo.newAddress()).thenReturn(Result.success("newAddress"))
142142
whenever(addressChecker.getAddressInfo(any())).thenReturn(mock())
@@ -147,6 +147,18 @@ class WalletRepoTest : BaseUnitTest() {
147147
assertEquals(false, sut.walletState.value.receiveOnSpendingBalance)
148148
}
149149

150+
@Test
151+
fun `refreshBip21 should set receiveOnSpendingBalance true when shouldBlockLightning is false`() = test {
152+
wheneverBlocking { coreService.checkGeoBlock() }.thenReturn(Pair(true, false))
153+
whenever(lightningRepo.newAddress()).thenReturn(Result.success("newAddress"))
154+
whenever(addressChecker.getAddressInfo(any())).thenReturn(mock())
155+
156+
val result = sut.refreshBip21()
157+
158+
assertTrue(result.isSuccess)
159+
assertEquals(true, sut.walletState.value.receiveOnSpendingBalance)
160+
}
161+
150162
@Test
151163
fun `refreshBip21 should generate new address when current has transactions`() = test {
152164
val testAddress = "testAddress"
@@ -246,10 +258,10 @@ class WalletRepoTest : BaseUnitTest() {
246258
}
247259

248260
@Test
249-
fun `updateBip21Invoice should create bolt11 when channels exist`() = test {
261+
fun `updateBip21Invoice should create bolt11 when node can receive`() = test {
250262
val testInvoice = "testInvoice"
251-
whenever(lightningRepo.hasChannels()).thenReturn(true)
252-
whenever(lightningRepo.createInvoice(1000uL, description = "test")).thenReturn(Result.success(testInvoice))
263+
whenever(lightningRepo.canReceive()).thenReturn(true)
264+
whenever(lightningRepo.createInvoice(anyOrNull(), any(), any())).thenReturn(Result.success(testInvoice))
253265

254266
sut.updateBip21Invoice(amountSats = 1000uL, description = "test").let { result ->
255267
assertTrue(result.isSuccess)
@@ -258,9 +270,7 @@ class WalletRepoTest : BaseUnitTest() {
258270
}
259271

260272
@Test
261-
fun `updateBip21Invoice should not create bolt11 when no channels exist`() = test {
262-
whenever(lightningRepo.hasChannels()).thenReturn(false)
263-
273+
fun `updateBip21Invoice should not create bolt11 when node cannot receive`() = test {
264274
sut.updateBip21Invoice(amountSats = 1000uL, description = "test").let { result ->
265275
assertTrue(result.isSuccess)
266276
assertEquals("", sut.walletState.value.bolt11)
@@ -271,7 +281,7 @@ class WalletRepoTest : BaseUnitTest() {
271281
fun `updateBip21Invoice should build correct BIP21 URL`() = test {
272282
val testAddress = "testAddress"
273283
whenever(cacheStore.data).thenReturn(flowOf(AppCacheData(onchainAddress = testAddress)))
274-
whenever(lightningRepo.hasChannels()).thenReturn(false)
284+
whenever(lightningRepo.createInvoice(anyOrNull(), any(), any())).thenReturn(Result.success("testInvoice"))
275285
sut = createSut()
276286

277287
sut.updateBip21Invoice(amountSats = 1000uL, description = "test").let { result ->

0 commit comments

Comments
 (0)