Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package to.bitkit.ldk
package to.bitkit.services

import android.content.Context
import androidx.test.core.app.ApplicationProvider
Expand Down
22 changes: 21 additions & 1 deletion app/src/main/java/to/bitkit/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package to.bitkit

import android.util.Log
import org.lightningdevkit.ldknode.PeerDetails
import to.bitkit.Tag.APP
import to.bitkit.env.Network
import to.bitkit.ext.ensureDir
Expand All @@ -13,6 +14,7 @@ import org.lightningdevkit.ldknode.Network as LdkNetwork
internal object Tag {
const val FCM = "FCM"
const val LDK = "LDK"
const val LSP = "LSP"
const val BDK = "BDK"
const val DEV = "DEV"
const val APP = "APP"
Expand Down Expand Up @@ -81,13 +83,31 @@ data class LnPeer(
val nodeId: String,
val host: String,
val port: String,
val isConnected: Boolean = false,
val isPersisted: Boolean = false,
) {
constructor(nodeId: String, address: String) : this(
constructor(
nodeId: String,
address: String,
isConnected: Boolean = false,
isPersisted: Boolean = false,
) : this(
nodeId,
address.substringBefore(":"),
address.substringAfter(":"),
isConnected,
isPersisted,
)

val address get() = "$host:$port"
override fun toString() = "$nodeId@${address}"

companion object {
fun PeerDetails.toLnPeer() = LnPeer(
nodeId = nodeId,
address = address,
isConnected = isConnected,
isPersisted = isPersisted,
)
}
}
14 changes: 9 additions & 5 deletions app/src/main/java/to/bitkit/LauncherActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ package to.bitkit

import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.runBlocking
import to.bitkit.ldk.warmupNode
import to.bitkit.ui.MainActivity
import to.bitkit.ui.SharedViewModel
import to.bitkit.ui.initNotificationChannel
import to.bitkit.ui.logFcmToken

@AndroidEntryPoint
class LauncherActivity : AppCompatActivity() {
private val sharedViewModel by viewModels<SharedViewModel>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

initNotificationChannel()
logFcmToken()
// TODO share mainViewModel in both activities, move warmupNode to it & call it suspending
runBlocking { warmupNode() }
startActivity(Intent(this, MainActivity::class.java))
sharedViewModel.warmupNode()
startActivity(Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
})
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package to.bitkit.di
package to.bitkit.async

import android.util.Log
import kotlinx.coroutines.CoroutineScope
Expand All @@ -14,7 +14,7 @@ import java.util.concurrent.ThreadFactory
import kotlin.coroutines.CoroutineContext

enum class ServiceQueue {
LDK, BDK, MIGRATION;
LDK, BDK, LSP, MIGRATION;

private val scope by lazy { CoroutineScope(dispatcher("$name-queue".lowercase()) + SupervisorJob()) }

Expand Down
57 changes: 57 additions & 0 deletions app/src/main/java/to/bitkit/data/LspApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package to.bitkit.data

import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.Serializable
import javax.inject.Inject

interface LspApi {
suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest)
suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest)
}

class BlocktankApi @Inject constructor(
private val client: HttpClient,
) : LspApi {
private val baseUrl = "https://api.stag.blocktank.to"
private val notificationsApi = "$baseUrl/notifications/api/device"

override suspend fun registerDeviceForNotifications(payload: RegisterDeviceRequest) {
post(notificationsApi, payload)
}

override suspend fun testNotification(deviceToken: String, payload: TestNotificationRequest) {
post("$notificationsApi/$deviceToken/test-notification", payload)
}

private suspend inline fun <reified T> post(url: String, payload: T) = client.post(url) { setBody(payload) }
}

@Serializable
data class RegisterDeviceRequest(
val deviceToken: String,
val publicKey: String,
val features: List<String>,
val nodeId: String,
val isoTimestamp: String,
val signature: String,
)

@Serializable
data class TestNotificationRequest(
val data: Data,
) {
@Serializable
data class Data(
val source: String,
val type: String,
val payload: Payload,
) {
@Serializable
data class Payload(
val secretMessage: String,
)
}
}
34 changes: 0 additions & 34 deletions app/src/main/java/to/bitkit/data/Models.kt

This file was deleted.

32 changes: 32 additions & 0 deletions app/src/main/java/to/bitkit/data/RestApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import to.bitkit.REST
import to.bitkit.ext.toHex
import javax.inject.Inject
Expand Down Expand Up @@ -62,3 +64,33 @@ class EsploraApi @Inject constructor(
return client.get("$REST/tx/${txid}/outspend/${outputIndex}").body()
}
}

@Serializable
data class Tx(
val txid: String,
val status: TxStatus,
)

@Serializable
data class TxStatus(
@SerialName("confirmed")
val isConfirmed: Boolean,
@SerialName("block_height")
val blockHeight: Int? = null,
@SerialName("block_hash")
val blockHash: String? = null,
)

@Serializable
data class OutputSpent(
val spent: Boolean,
)

@Serializable
data class MerkleProof(
@SerialName("block_height")
val blockHeight: Int,
@Suppress("ArrayInDataClass")
val merkle: Array<String>,
val pos: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,6 @@ class AndroidKeyStore(
val cipher = Cipher.getInstance(transformation).apply { init(Cipher.DECRYPT_MODE, secretKey, spec) }

val decryptedDataBytes = cipher.doFinal(actualEncryptedData)
return decryptedDataBytes.toString(Charsets.UTF_8)
return decryptedDataBytes.decodeToString()
}
}
26 changes: 21 additions & 5 deletions app/src/main/java/to/bitkit/di/HttpModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,34 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.ANDROID
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import to.bitkit.data.BlocktankApi
import to.bitkit.data.EsploraApi
import to.bitkit.data.LspApi
import to.bitkit.data.RestApi
import javax.inject.Singleton

val json = Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
}

@Module
@InstallIn(SingletonComponent::class)
object HttpModule {
@Provides
@Singleton
fun provideJson(): Json {
return Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
}
return json
}

@Provides
Expand All @@ -40,9 +47,18 @@ object HttpModule {
install(ContentNegotiation) {
json(json = json)
}
defaultRequest { // Set default request properties
contentType(ContentType.Application.Json)
}
}
}

@Provides
@Singleton
fun provideLspApi(blocktankApi: BlocktankApi): LspApi {
return blocktankApi
}

@Provides
@Singleton
fun provideRestApi(esploraApi: EsploraApi): RestApi {
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/to/bitkit/di/ServicesModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import to.bitkit.bdk.BitcoinService
import to.bitkit.ldk.LightningService
import to.bitkit.services.BitcoinService
import to.bitkit.services.LightningService

@Module
@InstallIn(SingletonComponent::class)
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/to/bitkit/di/ViewModelModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package to.bitkit.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import to.bitkit.services.BlocktankService
import to.bitkit.ui.SharedViewModel
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object ViewModelModule {
@Singleton
@Provides
fun provideSharedViewModel(
@BgDispatcher bgDispatcher: CoroutineDispatcher,
blocktankService: BlocktankService,
): SharedViewModel {
return SharedViewModel(
bgDispatcher,
blocktankService,
)
}
}
2 changes: 2 additions & 0 deletions app/src/main/java/to/bitkit/ext/ByteArray.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ fun Any.convertToByteArray(): ByteArray {
fun ByteArray.toBase64(flags: Int = Base64.DEFAULT): String = Base64.encodeToString(this, flags)

fun String.fromBase64(flags: Int = Base64.DEFAULT): ByteArray = Base64.decode(this, flags)

val String.uByteList get() = this.toByteArray(Charsets.UTF_8).map { it.toUByte() }
2 changes: 1 addition & 1 deletion app/src/main/java/to/bitkit/ext/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal fun toast(
text: String,
duration: Int = Toast.LENGTH_SHORT,
) {
with(currentActivity<MainActivity>()) {
currentActivity<MainActivity>().run {
Toast.makeText(this, text, duration).show()
}
}
Expand Down
Loading