Skip to content

Commit e05b0a3

Browse files
authored
Merge pull request #41 from synonymdev/feat/bitkit-core-regtest-and-notifs
Use bitkit-core regtest and notification methods
2 parents 1cd5c98 + be4b895 commit e05b0a3

File tree

14 files changed

+264
-193
lines changed

14 files changed

+264
-193
lines changed

app/src/main/java/to/bitkit/data/BlocktankHttpClient.kt

Lines changed: 6 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,103 +7,36 @@ import io.ktor.client.request.post
77
import io.ktor.client.request.setBody
88
import io.ktor.client.statement.HttpResponse
99
import io.ktor.http.isSuccess
10-
import kotlinx.serialization.Serializable
11-
import kotlinx.serialization.json.JsonObject
1210
import to.bitkit.env.Env
1311
import to.bitkit.models.FxRateResponse
14-
import to.bitkit.models.blocktank.RegtestCloseChannelRequest
15-
import to.bitkit.models.blocktank.RegtestDepositRequest
16-
import to.bitkit.models.blocktank.RegtestMineRequest
17-
import to.bitkit.models.blocktank.RegtestPayRequest
1812
import to.bitkit.utils.AppError
1913
import to.bitkit.utils.Logger
2014
import javax.inject.Inject
2115
import javax.inject.Singleton
2216

23-
private typealias IgnoreResponse = String
24-
2517
@Singleton
2618
class BlocktankHttpClient @Inject constructor(
2719
private val client: HttpClient,
2820
) {
29-
// region notifications
30-
suspend fun registerDevice(payload: RegisterDeviceRequest) {
31-
post<IgnoreResponse>("${Env.blocktankPushNotificationServer}/device", payload)
32-
}
33-
34-
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
35-
post<IgnoreResponse>("${Env.blocktankPushNotificationServer}/device/$deviceToken/test-notification", payload)
36-
}
37-
// endregion
38-
3921
// region rates
4022
suspend fun fetchLatestRates(): FxRateResponse {
4123
return get<FxRateResponse>(Env.btcRatesServer)
4224
}
4325
// endregion
4426

45-
// region regtest
46-
/**
47-
* Mines a number of blocks on the regtest network.
48-
* @param count Number of blocks to mine. Default is 1.
49-
*/
50-
suspend fun regtestMine(count: Int = 1) {
51-
val payload = RegtestMineRequest(count)
52-
post<IgnoreResponse>("${Env.blocktankClientServer}/regtest/chain/mine", payload)
53-
}
54-
55-
/**
56-
* Deposits a number of satoshis to an address on the regtest network.
57-
* @param address Address to deposit to.
58-
* @param amountSat Amount of satoshis to deposit. Default is 10,000,000.
59-
* @return Onchain transaction ID.
60-
*/
61-
suspend fun regtestDeposit(address: String, amountSat: Int = 10_000_000): String {
62-
val payload = RegtestDepositRequest(address = address, amountSat = amountSat)
63-
return post("${Env.blocktankClientServer}/regtest/chain/deposit", payload)
64-
}
65-
66-
/**
67-
* Pays an invoice on the regtest network.
68-
* @param invoice Invoice to pay.
69-
* @param amountSat Amount of satoshis to pay (only for 0-amount invoices).
70-
* @return Blocktank payment ID.
71-
*/
72-
suspend fun regtestPay(invoice: String, amountSat: Int? = null): String {
73-
val payload = RegtestPayRequest(invoice = invoice, amountSat = amountSat)
74-
return post("${Env.blocktankClientServer}/regtest/channel/pay", payload)
75-
}
76-
77-
/**
78-
* Closes a channel on the regtest network.
79-
* @param fundingTxId Funding transaction ID.
80-
* @param vout Funding transaction output index.
81-
* @param forceCloseAfterS Time in seconds to force-close the channel after. Default is 24 hours (86400). Set it to 0 for immediate force close.
82-
* @return Closing transaction ID.
83-
*/
84-
suspend fun regtestCloseChannel(fundingTxId: String, vout: Int, forceCloseAfterS: Int = 86400): String {
85-
val payload = RegtestCloseChannelRequest(
86-
fundingTxId = fundingTxId,
87-
vout = vout,
88-
forceCloseAfterS = forceCloseAfterS,
89-
)
90-
return post("${Env.blocktankClientServer}/regtest/channel/close", payload)
91-
}
92-
// endregion
93-
9427
// region utils
9528
private suspend inline fun <reified T> post(url: String, payload: Any? = null): T {
9629
val response = client.post(url) { payload?.let { setBody(it) } }
9730
Logger.debug("Http call: $response")
9831
return when (response.status.isSuccess()) {
9932
true -> {
10033
val responseBody = runCatching { response.body<T>() }.getOrElse {
101-
throw BlocktankErrorOld.InvalidResponse(it.message.orEmpty())
34+
throw BlocktankHttpError.InvalidResponse(it.message.orEmpty())
10235
}
10336
responseBody
10437
}
10538

106-
else -> throw BlocktankErrorOld.InvalidResponse(response.status.description)
39+
else -> throw BlocktankHttpError.InvalidResponse(response.status.description)
10740
}
10841
}
10942

@@ -117,39 +50,17 @@ class BlocktankHttpClient @Inject constructor(
11750
return when (response.status.isSuccess()) {
11851
true -> {
11952
val responseBody = runCatching { response.body<T>() }.getOrElse {
120-
throw BlocktankErrorOld.InvalidResponse(it.message.orEmpty())
53+
throw BlocktankHttpError.InvalidResponse(it.message.orEmpty())
12154
}
12255
responseBody
12356
}
12457

125-
else -> throw BlocktankErrorOld.InvalidResponse(response.status.description)
58+
else -> throw BlocktankHttpError.InvalidResponse(response.status.description)
12659
}
12760
}
12861
// endregion
12962
}
13063

131-
sealed class BlocktankErrorOld(message: String) : AppError(message) {
132-
data class InvalidResponse(override val message: String) : BlocktankErrorOld(message)
133-
}
134-
135-
@Serializable
136-
data class RegisterDeviceRequest(
137-
val deviceToken: String,
138-
val publicKey: String,
139-
val features: List<String>,
140-
val nodeId: String,
141-
val isoTimestamp: String,
142-
val signature: String,
143-
)
144-
145-
@Serializable
146-
data class TestNotificationRequest(
147-
val data: Data,
148-
) {
149-
@Serializable
150-
data class Data(
151-
val source: String,
152-
val type: String,
153-
val payload: JsonObject,
154-
)
64+
sealed class BlocktankHttpError(message: String) : AppError(message) {
65+
data class InvalidResponse(override val message: String) : BlocktankHttpError(message)
15566
}

app/src/main/java/to/bitkit/models/blocktank/Regtest.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.

app/src/main/java/to/bitkit/services/BlocktankServiceOld.kt renamed to app/src/main/java/to/bitkit/services/BlocktankNotificationsService.kt

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package to.bitkit.services
22

33
import kotlinx.coroutines.CoroutineDispatcher
4-
import kotlinx.serialization.json.JsonObject
5-
import kotlinx.serialization.json.JsonPrimitive
64
import to.bitkit.async.BaseCoroutineScope
75
import to.bitkit.async.ServiceQueue
8-
import to.bitkit.data.BlocktankHttpClient
9-
import to.bitkit.data.RegisterDeviceRequest
10-
import to.bitkit.data.TestNotificationRequest
116
import to.bitkit.data.keychain.Keychain
127
import to.bitkit.data.keychain.Keychain.Key
138
import to.bitkit.di.BgDispatcher
@@ -22,15 +17,13 @@ import javax.inject.Inject
2217
import javax.inject.Singleton
2318

2419
@Singleton
25-
class BlocktankServiceOld @Inject constructor(
20+
class BlocktankNotificationsService @Inject constructor(
2621
@BgDispatcher bgDispatcher: CoroutineDispatcher,
27-
private val blocktankHttpClient: BlocktankHttpClient,
2822
private val lightningService: LightningService,
2923
private val keychain: Keychain,
3024
private val crypto: Crypto,
3125
) : BaseCoroutineScope(bgDispatcher) {
3226

33-
// region notifications
3427
suspend fun registerDevice(deviceToken: String) {
3528
val nodeId = lightningService.nodeId ?: throw ServiceError.NodeNotStarted
3629

@@ -51,17 +44,16 @@ class BlocktankServiceOld @Inject constructor(
5144
}
5245
keychain.save(Key.PUSH_NOTIFICATION_PRIVATE_KEY.name, keypair.privateKey)
5346

54-
val payload = RegisterDeviceRequest(
55-
deviceToken = deviceToken,
56-
publicKey = publicKey,
57-
features = Env.pushNotificationFeatures.map { it.toString() },
58-
nodeId = nodeId,
59-
isoTimestamp = "$timestamp",
60-
signature = signature,
61-
)
62-
6347
ServiceQueue.CORE.background {
64-
blocktankHttpClient.registerDevice(payload)
48+
uniffi.bitkitcore.registerDevice(
49+
deviceToken = deviceToken,
50+
publicKey = publicKey,
51+
features = Env.pushNotificationFeatures.map { it.toString() },
52+
nodeId = nodeId,
53+
isoTimestamp = "$timestamp",
54+
signature = signature,
55+
customUrl = Env.blocktankPushNotificationServer,
56+
)
6557
}
6658

6759
// Cache token so we can avoid re-registering
@@ -76,21 +68,13 @@ class BlocktankServiceOld @Inject constructor(
7668
suspend fun testNotification(deviceToken: String) {
7769
Logger.debug("Sending test notification to self…")
7870

79-
val payload = TestNotificationRequest(
80-
data = TestNotificationRequest.Data(
81-
source = "blocktank",
82-
type = "incomingHtlc",
83-
payload = JsonObject(
84-
mapOf(
85-
"secretMessage" to JsonPrimitive("hello")
86-
)
87-
)
88-
)
89-
)
90-
9171
ServiceQueue.CORE.background {
92-
blocktankHttpClient.testNotification(deviceToken, payload)
72+
uniffi.bitkitcore.testNotification(
73+
deviceToken = deviceToken,
74+
secretMessage = "hello",
75+
notificationType = "incomingHtlc",
76+
customUrl = Env.blocktankPushNotificationServer,
77+
)
9378
}
9479
}
95-
// endregion
9680
}

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ class CoreService @Inject constructor(
8484
}
8585

8686
try {
87-
updateBlocktankUrl(newUrl = Env.blocktankClientServer)
88-
Logger.info("Blocktank URL updated to ${Env.blocktankClientServer}")
87+
val blocktankUrl = Env.blocktankClientServer
88+
updateBlocktankUrl(newUrl = blocktankUrl)
89+
Logger.info("Blocktank URL updated to $blocktankUrl")
8990
} catch (e: Exception) {
9091
Logger.error("Failed to update Blocktank URL", e)
9192
}
@@ -405,9 +406,7 @@ class BlocktankService(
405406
options: CreateOrderOptions,
406407
): IBtOrder {
407408
return ServiceQueue.CORE.background {
408-
createOrder(
409-
lspBalanceSat = lspBalanceSat, channelExpiryWeeks = channelExpiryWeeks, options = options,
410-
)
409+
createOrder(lspBalanceSat = lspBalanceSat, channelExpiryWeeks = channelExpiryWeeks, options = options)
411410
}
412411
}
413412

app/src/main/java/to/bitkit/ui/settings/BlocktankRegtestScreen.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ fun BlocktankRegtestScreen(
107107
Logger.debug("Initiating regtest deposit with address: $depositAddress, amount: $depositAmount")
108108
isDepositing = true
109109
try {
110-
val amount = depositAmount.toIntOrNull() ?: error("Invalid deposit amount: $depositAmount")
110+
val amount = depositAmount.toULongOrNull() ?: error("Invalid deposit amount: $depositAmount")
111111
val txId = viewModel.regtestDeposit(depositAddress, amount)
112112
Logger.debug("Deposit successful with txId: $txId")
113113
app.toast(
@@ -155,7 +155,7 @@ fun BlocktankRegtestScreen(
155155
isMining = true
156156
try {
157157
val count =
158-
mineBlockCount.toIntOrNull() ?: error("Invalid block count: $mineBlockCount")
158+
mineBlockCount.toUIntOrNull() ?: error("Invalid block count: $mineBlockCount")
159159
viewModel.regtestMine(count)
160160
Logger.debug("Successfully mined $count blocks")
161161
app.toast(
@@ -206,7 +206,7 @@ fun BlocktankRegtestScreen(
206206
coroutineScope.launch {
207207
Logger.debug("Initiating regtest payment with invoice: $paymentInvoice, amount: $paymentAmount")
208208
try {
209-
val amount = if (paymentAmount.isEmpty()) null else paymentAmount.toIntOrNull()
209+
val amount = if (paymentAmount.isEmpty()) null else paymentAmount.toULongOrNull()
210210
val paymentId = viewModel.regtestPay(paymentInvoice, amount)
211211
Logger.debug("Payment successful with ID: $paymentId")
212212
app.toast(
@@ -264,8 +264,8 @@ fun BlocktankRegtestScreen(
264264
coroutineScope.launch {
265265
Logger.debug("Initiating channel close with fundingTxId: $fundingTxId, vout: $vout, forceCloseAfter: $forceCloseAfter")
266266
try {
267-
val voutNum = vout.toIntOrNull() ?: error("Invalid Vout: $vout")
268-
val closeAfter = forceCloseAfter.toIntOrNull()
267+
val voutNum = vout.toUIntOrNull() ?: error("Invalid Vout: $vout")
268+
val closeAfter = forceCloseAfter.toULongOrNull()
269269
?: error("Invalid Force Close After: $forceCloseAfter")
270270
val closingTxId = viewModel.regtestCloseChannel(
271271
fundingTxId = fundingTxId,

app/src/main/java/to/bitkit/ui/settings/BlocktankRegtestViewModel.kt

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,58 @@ package to.bitkit.ui.settings
22

33
import androidx.lifecycle.ViewModel
44
import dagger.hilt.android.lifecycle.HiltViewModel
5-
import to.bitkit.data.BlocktankHttpClient
65
import javax.inject.Inject
76

87
@HiltViewModel
98
class BlocktankRegtestViewModel @Inject constructor(
10-
private val blocktankHttpClient: BlocktankHttpClient
119
) : ViewModel() {
1210

13-
suspend fun regtestMine(count: Int = 1) {
14-
blocktankHttpClient.regtestMine(count)
11+
/**
12+
* Mines a number of blocks on the regtest network.
13+
* @param count Number of blocks to mine. Default is 1.
14+
*/
15+
suspend fun regtestMine(count: UInt = 1u) {
16+
uniffi.bitkitcore.regtestMine(count = count)
1517
}
1618

17-
suspend fun regtestDeposit(address: String, amountSat: Int = 10_000_000): String {
18-
return blocktankHttpClient.regtestDeposit(address, amountSat)
19+
/**
20+
* Deposits a number of satoshis to an address on the regtest network.
21+
* @param address Address to deposit to.
22+
* @param amountSat Amount of satoshis to deposit. Default is 10,000,000.
23+
* @return Onchain transaction ID.
24+
*/
25+
suspend fun regtestDeposit(address: String, amountSat: ULong = 10_000_000uL): String {
26+
return uniffi.bitkitcore.regtestDeposit(
27+
address = address,
28+
amountSat = amountSat,
29+
)
1930
}
2031

21-
suspend fun regtestPay(invoice: String, amountSat: Int? = null): String {
22-
return blocktankHttpClient.regtestPay(invoice, amountSat)
32+
/**
33+
* Pays an invoice on the regtest network.
34+
* @param invoice Invoice to pay.
35+
* @param amountSat Amount of satoshis to pay (only for 0-amount invoices).
36+
* @return Blocktank payment ID.
37+
*/
38+
suspend fun regtestPay(invoice: String, amountSat: ULong? = null): String {
39+
return uniffi.bitkitcore.regtestPay(
40+
invoice = invoice,
41+
amountSat = amountSat,
42+
)
2343
}
2444

25-
suspend fun regtestCloseChannel(fundingTxId: String, vout: Int, forceCloseAfterS: Int = 86400): String {
26-
return blocktankHttpClient.regtestCloseChannel(fundingTxId, vout, forceCloseAfterS)
45+
/**
46+
* Closes a channel on the regtest network.
47+
* @param fundingTxId Funding transaction ID.
48+
* @param vout Funding transaction output index.
49+
* @param forceCloseAfterS Time in seconds to force-close the channel after. Default is 24 hours (86400). Set it to 0 for immediate force close.
50+
* @return Closing transaction ID.
51+
*/
52+
suspend fun regtestCloseChannel(fundingTxId: String, vout: UInt, forceCloseAfterS: ULong = 86_400uL): String {
53+
return uniffi.bitkitcore.regtestCloseChannel(
54+
fundingTxId = fundingTxId,
55+
vout = vout,
56+
forceCloseAfterS = forceCloseAfterS,
57+
)
2758
}
2859
}

0 commit comments

Comments
 (0)