Skip to content

Commit 2ed0c13

Browse files
authored
Merge pull request #72 from synonymdev/feat/external-node
External Node Channel Open Flow
2 parents d443f43 + 60905ae commit 2ed0c13

24 files changed

+1269
-223
lines changed

app/src/main/java/to/bitkit/App.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import androidx.hilt.work.HiltWorkerFactory
99
import androidx.work.Configuration
1010
import dagger.hilt.android.HiltAndroidApp
1111
import to.bitkit.env.Env
12-
import to.bitkit.utils.ResourceProvider
1312
import javax.inject.Inject
1413

1514
@HiltAndroidApp
@@ -24,7 +23,6 @@ internal open class App : Application(), Configuration.Provider {
2423

2524
override fun onCreate() {
2625
super.onCreate()
27-
ResourceProvider.init(this)
2826
currentActivity = CurrentActivity().also { registerActivityLifecycleCallbacks(it) }
2927

3028
Env.initAppStoragePath(filesDir.absolutePath)

app/src/main/java/to/bitkit/env/Env.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,5 @@ internal object Env {
105105
nodeId = "028a8910b0048630d4eb17af25668cdd7ea6f2d8ae20956e7a06e2ae46ebcb69fc",
106106
address = "34.65.86.104:9400",
107107
)
108-
val btStagingOld = LnPeer(
109-
nodeId = "03b9a456fb45d5ac98c02040d39aec77fa3eeb41fd22cf40b862b393bcfc43473a",
110-
address = "35.233.47.252:9400",
111-
)
112-
val polarToRegtest = LnPeer(
113-
nodeId = "023f6e310ff049d68c64a0eb97440b998aa15fd99162317d6743d7023519862e23",
114-
address = "10.0.2.2:9735",
115-
)
116-
val local = LnPeer(
117-
nodeId = "02faf2d1f5dc153e8931d8444c4439e46a81cb7eeadba8562e7fec3690c261ce87",
118-
address = "10.0.2.2:9738",
119-
)
120108
}
121109
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package to.bitkit.ext
2+
3+
import kotlinx.coroutines.CompletableDeferred
4+
import kotlinx.coroutines.flow.Flow
5+
import kotlinx.coroutines.flow.collect
6+
import kotlinx.coroutines.flow.takeWhile
7+
8+
/**
9+
* Suspends and collects the elements of the Flow until the provided predicate satisfies
10+
* a `WatchResult.Complete`.
11+
*
12+
* @param predicate A suspending function that processes each emitted value and returns a
13+
* `WatchResult` indicating whether to continue or complete with a result.
14+
* @return The result of type `R` when the `WatchResult.Complete` is returned by the predicate.
15+
*/
16+
suspend inline fun <T, R> Flow<T>.watchUntil(
17+
crossinline predicate: suspend (T) -> WatchResult<R>,
18+
): R {
19+
val result = CompletableDeferred<R>()
20+
21+
this.takeWhile { value ->
22+
when (val eventResult = predicate(value)) {
23+
is WatchResult.Continue -> {
24+
eventResult.result?.let { result.complete(it) }
25+
true
26+
}
27+
28+
is WatchResult.Complete -> {
29+
result.complete(eventResult.result)
30+
false
31+
}
32+
}
33+
}.collect()
34+
35+
return result.await()
36+
}
37+
38+
sealed interface WatchResult<T> {
39+
data class Continue<T>(val result: T? = null) : WatchResult<T>
40+
data class Complete<T>(val result: T) : WatchResult<T>
41+
}

app/src/main/java/to/bitkit/models/LnPeer.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,39 @@ data class LnPeer(
1717
)
1818

1919
val address get() = "$host:$port"
20+
2021
override fun toString() = "$nodeId@${address}"
2122

2223
companion object {
2324
fun PeerDetails.toLnPeer() = LnPeer(
2425
nodeId = nodeId,
2526
address = address,
2627
)
28+
29+
fun parseUri(string: String): Result<LnPeer> {
30+
val uri = string.split("@")
31+
val nodeId = uri[0]
32+
33+
if (uri.size != 2) {
34+
return Result.failure(Exception("Invalid peer uri"))
35+
}
36+
37+
val address = uri[1].split(":")
38+
39+
if (address.size < 2) {
40+
return Result.failure(Exception("Invalid peer uri"))
41+
}
42+
43+
val ip = address[0]
44+
val port = address[1]
45+
46+
return Result.success(
47+
LnPeer(
48+
nodeId = nodeId,
49+
host = ip,
50+
port = port,
51+
)
52+
)
53+
}
2754
}
2855
}

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

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.lightningdevkit.ldknode.NodeStatus
2626
import org.lightningdevkit.ldknode.PaymentDetails
2727
import org.lightningdevkit.ldknode.PaymentId
2828
import org.lightningdevkit.ldknode.Txid
29+
import org.lightningdevkit.ldknode.UserChannelId
2930
import org.lightningdevkit.ldknode.defaultConfig
3031
import to.bitkit.async.BaseCoroutineScope
3132
import to.bitkit.async.ServiceQueue
@@ -227,6 +228,26 @@ class LightningService @Inject constructor(
227228
}
228229
}
229230

231+
suspend fun connectPeer(peer: LnPeer): Result<Unit> {
232+
val node = this.node ?: throw ServiceError.NodeNotSetup
233+
234+
return ServiceQueue.LDK.background {
235+
try {
236+
Logger.debug("Connecting peer: $peer")
237+
238+
node.connect(peer.nodeId, peer.address, persist = true)
239+
240+
Logger.info("Peer connected: $peer")
241+
242+
Result.success(Unit)
243+
} catch (e: NodeException) {
244+
val error = LdkError(e)
245+
Logger.error("Peer connect error: $peer", error)
246+
Result.failure(error)
247+
}
248+
}
249+
}
250+
230251
suspend fun disconnectPeer(peer: LnPeer) {
231252
val node = this.node ?: throw ServiceError.NodeNotSetup
232253
Logger.debug("Disconnecting peer: $peer")
@@ -238,25 +259,36 @@ class LightningService @Inject constructor(
238259
} catch (e: NodeException) {
239260
Logger.warn("Peer disconnect error: $peer", LdkError(e))
240261
}
241-
}
242-
// endregion
262+
} // endregion
243263

244264
// region channels
245-
suspend fun openChannel(peer: LnPeer, channelAmountSats: ULong, pushToCounterpartySats: ULong? = null) {
246-
val node = this@LightningService.node ?: throw ServiceError.NodeNotSetup
265+
suspend fun openChannel(
266+
peer: LnPeer,
267+
channelAmountSats: ULong,
268+
pushToCounterpartySats: ULong? = null,
269+
) : Result<UserChannelId> {
270+
val node = this.node ?: throw ServiceError.NodeNotSetup
247271

248-
try {
249-
ServiceQueue.LDK.background {
250-
node.openChannel(
272+
return ServiceQueue.LDK.background {
273+
try {
274+
Logger.debug("Initiating channel open (sats: $channelAmountSats) with peer: $peer")
275+
276+
val userChannelId = node.openChannel(
251277
nodeId = peer.nodeId,
252278
address = peer.address,
253279
channelAmountSats = channelAmountSats,
254280
pushToCounterpartyMsat = pushToCounterpartySats?.millis,
255281
channelConfig = null,
256282
)
283+
284+
Logger.info("Channel open initiated, userChannelId: $userChannelId")
285+
286+
Result.success(userChannelId)
287+
} catch (e: NodeException) {
288+
val error = LdkError(e)
289+
Logger.error("Error initiating channel open", error)
290+
Result.failure(error)
257291
}
258-
} catch (e: NodeException) {
259-
throw LdkError(e)
260292
}
261293
}
262294

0 commit comments

Comments
 (0)