Merge channels and payments database

There is now only one database file, phoenix.db. This
makes database backup easier.

Legacy channels closing parts have been removed (we
use a dedicated table to store closings).

The tables storing lightning outgoing payments have
been renamed for consistency by addingd a "lightning"
prefix. The related kotlin classes have been renamed
as well.
This commit is contained in:
Dominique Padiou 2024-03-18 17:21:03 +01:00
parent 9d984591bc
commit 64775d2fd5
No known key found for this signature in database
GPG Key ID: 574C8C6A1673E987
28 changed files with 185 additions and 287 deletions

View File

@ -107,13 +107,9 @@ tasks.withType<JavaExec> {
sqldelight {
databases {
create("ChannelsDatabase") {
create("PhoenixDatabase") {
packageName.set("fr.acinq.phoenix.db")
srcDirs.from("src/commonMain/sqldelight/channelsdb")
}
create("PaymentsDatabase") {
packageName.set("fr.acinq.phoenix.db")
srcDirs.from("src/commonMain/sqldelight/paymentsdb")
srcDirs.from("src/commonMain/sqldelight/phoenixdb")
}
}
}

View File

@ -6,4 +6,3 @@ import okio.Path
expect val homeDirectory: Path
expect fun createAppDbDriver(dir: Path): SqlDriver
expect fun createPaymentsDbDriver(dir: Path): SqlDriver

View File

@ -1,5 +1,6 @@
package fr.acinq.lightning.bin
import app.cash.sqldelight.EnumColumnAdapter
import co.touchlab.kermit.CommonWriter
import co.touchlab.kermit.Severity
import co.touchlab.kermit.StaticConfig
@ -27,6 +28,7 @@ import fr.acinq.lightning.bin.conf.getOrGenerateSeed
import fr.acinq.lightning.bin.conf.readConfFile
import fr.acinq.lightning.bin.db.SqliteChannelsDb
import fr.acinq.lightning.bin.db.SqlitePaymentsDb
import fr.acinq.lightning.bin.db.payments.LightningOutgoingQueries
import fr.acinq.lightning.bin.json.ApiType
import fr.acinq.lightning.bin.logs.FileLogWriter
import fr.acinq.lightning.blockchain.electrum.ElectrumClient
@ -44,6 +46,7 @@ import fr.acinq.lightning.utils.Connection
import fr.acinq.lightning.utils.ServerAddress
import fr.acinq.lightning.utils.msat
import fr.acinq.lightning.utils.sat
import fr.acinq.phoenix.db.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.cio.*
@ -210,14 +213,34 @@ class Phoenixd : CliktCommand() {
)
echo(cyan("nodeid: ${nodeParams.nodeId}"))
val driver = createAppDbDriver(datadir)
val database = PhoenixDatabase(
driver = driver,
lightning_outgoing_payment_partsAdapter = Lightning_outgoing_payment_parts.Adapter(
part_routeAdapter = LightningOutgoingQueries.hopDescAdapter,
part_status_typeAdapter = EnumColumnAdapter()
),
lightning_outgoing_paymentsAdapter = Lightning_outgoing_payments.Adapter(
status_typeAdapter = EnumColumnAdapter(),
details_typeAdapter = EnumColumnAdapter()
),
incoming_paymentsAdapter = Incoming_payments.Adapter(
origin_typeAdapter = EnumColumnAdapter(),
received_with_typeAdapter = EnumColumnAdapter()
),
channel_close_outgoing_paymentsAdapter = Channel_close_outgoing_payments.Adapter(
closing_info_typeAdapter = EnumColumnAdapter()
),
inbound_liquidity_outgoing_paymentsAdapter = Inbound_liquidity_outgoing_payments.Adapter(
lease_typeAdapter = EnumColumnAdapter()
)
)
val electrum = ElectrumClient(scope, loggerFactory)
val paymentsDb = SqlitePaymentsDb(loggerFactory, createPaymentsDbDriver(datadir))
val peer = Peer(
nodeParams = nodeParams, walletParams = lsp.walletParams, watcher = ElectrumWatcher(electrum, scope, loggerFactory), db = object : Databases {
override val channels: ChannelsDb
get() = SqliteChannelsDb(createAppDbDriver(datadir))
override val payments: PaymentsDb
get() = paymentsDb
override val channels: ChannelsDb get() = SqliteChannelsDb(driver, database)
override val payments: PaymentsDb get() = SqlitePaymentsDb(database)
}, socketBuilder = TcpSocket.Builder(), scope
)

View File

@ -1,3 +1,19 @@
/*
* Copyright 2020 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.acinq.lightning.bin.db
import app.cash.sqldelight.db.SqlDriver
@ -6,14 +22,13 @@ import fr.acinq.lightning.CltvExpiry
import fr.acinq.lightning.channel.states.PersistedChannelState
import fr.acinq.lightning.db.ChannelsDb
import fr.acinq.lightning.serialization.Serialization
import fr.acinq.phoenix.db.ChannelsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
internal class SqliteChannelsDb(private val driver: SqlDriver) : ChannelsDb {
internal class SqliteChannelsDb(val driver: SqlDriver, val database: PhoenixDatabase) : ChannelsDb {
private val database = ChannelsDatabase(driver)
private val queries = database.channelsDatabaseQueries
private val queries = database.channelsQueries
override suspend fun addOrUpdateChannel(state: PersistedChannelState) {
val channelId = state.channelId.toByteArray()

View File

@ -16,8 +16,6 @@
package fr.acinq.lightning.bin.db
import app.cash.sqldelight.EnumColumnAdapter
import app.cash.sqldelight.db.SqlDriver
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Crypto
import fr.acinq.bitcoin.TxId
@ -27,8 +25,6 @@ import fr.acinq.lightning.bin.db.payments.LinkTxToPaymentQueries
import fr.acinq.lightning.bin.db.payments.PaymentsMetadataQueries
import fr.acinq.lightning.channel.ChannelException
import fr.acinq.lightning.db.*
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.logging.info
import fr.acinq.lightning.payment.FinalFailure
import fr.acinq.lightning.utils.*
import fr.acinq.lightning.wire.FailureMessage
@ -36,40 +32,10 @@ import fr.acinq.phoenix.db.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class SqlitePaymentsDb(
loggerFactory: LoggerFactory,
private val driver: SqlDriver,
) : PaymentsDb {
private val log = loggerFactory.newLogger(this::class)
private val database = PaymentsDatabase(
driver = driver,
outgoing_payment_partsAdapter = Outgoing_payment_parts.Adapter(
part_routeAdapter = OutgoingQueries.hopDescAdapter,
part_status_typeAdapter = EnumColumnAdapter()
),
outgoing_paymentsAdapter = Outgoing_payments.Adapter(
status_typeAdapter = EnumColumnAdapter(),
details_typeAdapter = EnumColumnAdapter()
),
incoming_paymentsAdapter = Incoming_payments.Adapter(
origin_typeAdapter = EnumColumnAdapter(),
received_with_typeAdapter = EnumColumnAdapter()
),
outgoing_payment_closing_tx_partsAdapter = Outgoing_payment_closing_tx_parts.Adapter(
part_closing_info_typeAdapter = EnumColumnAdapter()
),
channel_close_outgoing_paymentsAdapter = Channel_close_outgoing_payments.Adapter(
closing_info_typeAdapter = EnumColumnAdapter()
),
inbound_liquidity_outgoing_paymentsAdapter = Inbound_liquidity_outgoing_payments.Adapter(
lease_typeAdapter = EnumColumnAdapter()
)
)
class SqlitePaymentsDb(val database: PhoenixDatabase) : PaymentsDb {
private val inQueries = IncomingQueries(database)
private val outQueries = OutgoingQueries(database)
private val lightningOutgoingQueries = LightningOutgoingQueries(database)
private val spliceOutQueries = SpliceOutgoingQueries(database)
private val channelCloseQueries = ChannelCloseOutgoingQueries(database)
private val cpfpQueries = SpliceCpfpOutgoingQueries(database)
@ -82,7 +48,7 @@ class SqlitePaymentsDb(
parts: List<LightningOutgoingPayment.Part>
) {
withContext(Dispatchers.Default) {
outQueries.addLightningParts(parentId, parts)
lightningOutgoingQueries.addLightningParts(parentId, parts)
}
}
@ -93,7 +59,7 @@ class SqlitePaymentsDb(
database.transaction {
when (outgoingPayment) {
is LightningOutgoingPayment -> {
outQueries.addLightningOutgoingPayment(outgoingPayment)
lightningOutgoingQueries.addLightningOutgoingPayment(outgoingPayment)
}
is SpliceOutgoingPayment -> {
spliceOutQueries.addSpliceOutgoingPayment(outgoingPayment)
@ -128,7 +94,7 @@ class SqlitePaymentsDb(
completedAt: Long
) {
withContext(Dispatchers.Default) {
outQueries.completePayment(id, LightningOutgoingPayment.Status.Completed.Succeeded.OffChain(preimage, completedAt))
lightningOutgoingQueries.completePayment(id, LightningOutgoingPayment.Status.Completed.Succeeded.OffChain(preimage, completedAt))
}
}
@ -138,7 +104,7 @@ class SqlitePaymentsDb(
completedAt: Long
) {
withContext(Dispatchers.Default) {
outQueries.completePayment(id, LightningOutgoingPayment.Status.Completed.Failed(finalFailure, completedAt))
lightningOutgoingQueries.completePayment(id, LightningOutgoingPayment.Status.Completed.Failed(finalFailure, completedAt))
}
}
@ -148,7 +114,7 @@ class SqlitePaymentsDb(
completedAt: Long
) {
withContext(Dispatchers.Default) {
outQueries.updateLightningPart(partId, preimage, completedAt)
lightningOutgoingQueries.updateLightningPart(partId, preimage, completedAt)
}
}
@ -158,16 +124,16 @@ class SqlitePaymentsDb(
completedAt: Long
) {
withContext(Dispatchers.Default) {
outQueries.updateLightningPart(partId, failure, completedAt)
lightningOutgoingQueries.updateLightningPart(partId, failure, completedAt)
}
}
override suspend fun getLightningOutgoingPayment(id: UUID): LightningOutgoingPayment? = withContext(Dispatchers.Default) {
outQueries.getPayment(id)
lightningOutgoingQueries.getPayment(id)
}
override suspend fun getLightningOutgoingPaymentFromPartId(partId: UUID): LightningOutgoingPayment? = withContext(Dispatchers.Default) {
outQueries.getPaymentFromPartId(partId)
lightningOutgoingQueries.getPaymentFromPartId(partId)
}
// ---- list outgoing
@ -175,7 +141,7 @@ class SqlitePaymentsDb(
override suspend fun listLightningOutgoingPayments(
paymentHash: ByteVector32
): List<LightningOutgoingPayment> = withContext(Dispatchers.Default) {
outQueries.listLightningOutgoingPayments(paymentHash)
lightningOutgoingQueries.listLightningOutgoingPayments(paymentHash)
}
// ---- incoming payments

View File

@ -21,9 +21,9 @@ import fr.acinq.lightning.db.ChannelCloseOutgoingPayment
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.sat
import fr.acinq.lightning.utils.toByteVector32
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
class ChannelCloseOutgoingQueries(val database: PaymentsDatabase) {
class ChannelCloseOutgoingQueries(val database: PhoenixDatabase) {
private val channelCloseQueries = database.channelCloseOutgoingPaymentsQueries
fun getChannelCloseOutgoingPayment(id: UUID): ChannelCloseOutgoingPayment? {
@ -74,7 +74,7 @@ class ChannelCloseOutgoingQueries(val database: PaymentsDatabase) {
confirmed_at: Long?,
locked_at: Long?,
channel_id: ByteArray,
closing_info_type: OutgoingPartClosingInfoTypeVersion,
closing_info_type: ClosingInfoTypeVersion,
closing_info_blob: ByteArray
): ChannelCloseOutgoingPayment {
return ChannelCloseOutgoingPayment(
@ -88,7 +88,7 @@ class ChannelCloseOutgoingQueries(val database: PaymentsDatabase) {
confirmedAt = confirmed_at,
lockedAt = locked_at,
channelId = channel_id.toByteVector32(),
closingType = OutgoingPartClosingInfoData.deserialize(closing_info_type, closing_info_blob),
closingType = ClosingInfoData.deserialize(closing_info_type, closing_info_blob),
)
}
}

View File

@ -25,24 +25,24 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
enum class OutgoingPartClosingInfoTypeVersion {
enum class ClosingInfoTypeVersion {
// basic type, containing only a [ChannelClosingType] field
CLOSING_INFO_V0,
}
sealed class OutgoingPartClosingInfoData {
sealed class ClosingInfoData {
@Serializable
data class V0(val closingType: ChannelClosingType)
companion object {
fun deserialize(typeVersion: OutgoingPartClosingInfoTypeVersion, blob: ByteArray): ChannelClosingType = DbTypesHelper.decodeBlob(blob) { json, format ->
fun deserialize(typeVersion: ClosingInfoTypeVersion, blob: ByteArray): ChannelClosingType = DbTypesHelper.decodeBlob(blob) { json, format ->
when (typeVersion) {
OutgoingPartClosingInfoTypeVersion.CLOSING_INFO_V0 -> format.decodeFromString<V0>(json).closingType
ClosingInfoTypeVersion.CLOSING_INFO_V0 -> format.decodeFromString<V0>(json).closingType
}
}
}
}
fun ChannelCloseOutgoingPayment.mapClosingTypeToDb() = OutgoingPartClosingInfoTypeVersion.CLOSING_INFO_V0 to
Json.encodeToString(OutgoingPartClosingInfoData.V0(this.closingType)).toByteArray(Charsets.UTF_8)
fun ChannelCloseOutgoingPayment.mapClosingTypeToDb() = ClosingInfoTypeVersion.CLOSING_INFO_V0 to
Json.encodeToString(ClosingInfoData.V0(this.closingType)).toByteArray(Charsets.UTF_8)

View File

@ -21,10 +21,10 @@ import fr.acinq.lightning.db.InboundLiquidityOutgoingPayment
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.sat
import fr.acinq.lightning.utils.toByteVector32
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
class InboundLiquidityQueries(val database: PaymentsDatabase) {
private val queries = database.inboundLiquidityOutgoingQueries
class InboundLiquidityQueries(val database: PhoenixDatabase) {
private val queries = database.inboundLiquidityOutgoingPaymentsQueries
fun add(payment: InboundLiquidityOutgoingPayment) {
database.transaction {

View File

@ -21,13 +21,12 @@ import app.cash.sqldelight.coroutines.mapToList
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.byteVector32
import fr.acinq.lightning.db.IncomingPayment
import fr.acinq.lightning.utils.msat
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.Flow
class IncomingQueries(private val database: PaymentsDatabase) {
class IncomingQueries(private val database: PhoenixDatabase) {
private val queries = database.incomingPaymentsQueries

View File

@ -35,46 +35,46 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
enum class OutgoingDetailsTypeVersion {
enum class LightningOutgoingDetailsTypeVersion {
NORMAL_V0,
KEYSEND_V0,
SWAPOUT_V0,
}
sealed class OutgoingDetailsData {
sealed class LightningOutgoingDetailsData {
sealed class Normal : OutgoingDetailsData() {
sealed class Normal : LightningOutgoingDetailsData() {
@Serializable
data class V0(val paymentRequest: String) : Normal()
}
sealed class KeySend : OutgoingDetailsData() {
sealed class KeySend : LightningOutgoingDetailsData() {
@Serializable
data class V0(@Serializable val preimage: ByteVector32) : KeySend()
}
sealed class SwapOut : OutgoingDetailsData() {
sealed class SwapOut : LightningOutgoingDetailsData() {
@Serializable
data class V0(val address: String, val paymentRequest: String, @Serializable val swapOutFee: Satoshi) : SwapOut()
}
companion object {
/** Deserialize the details of an outgoing payment. Return null if the details is for a legacy channel closing payment (see [deserializeLegacyClosingDetails]). */
fun deserialize(typeVersion: OutgoingDetailsTypeVersion, blob: ByteArray): LightningOutgoingPayment.Details? = DbTypesHelper.decodeBlob(blob) { json, format ->
fun deserialize(typeVersion: LightningOutgoingDetailsTypeVersion, blob: ByteArray): LightningOutgoingPayment.Details = DbTypesHelper.decodeBlob(blob) { json, format ->
when (typeVersion) {
OutgoingDetailsTypeVersion.NORMAL_V0 -> format.decodeFromString<Normal.V0>(json).let { LightningOutgoingPayment.Details.Normal(Bolt11Invoice.read(it.paymentRequest).get()) }
OutgoingDetailsTypeVersion.KEYSEND_V0 -> format.decodeFromString<KeySend.V0>(json).let { LightningOutgoingPayment.Details.KeySend(it.preimage) }
OutgoingDetailsTypeVersion.SWAPOUT_V0 -> format.decodeFromString<SwapOut.V0>(json).let { LightningOutgoingPayment.Details.SwapOut(it.address, Bolt11Invoice.read(it.paymentRequest).get(), it.swapOutFee) }
LightningOutgoingDetailsTypeVersion.NORMAL_V0 -> format.decodeFromString<Normal.V0>(json).let { LightningOutgoingPayment.Details.Normal(Bolt11Invoice.read(it.paymentRequest).get()) }
LightningOutgoingDetailsTypeVersion.KEYSEND_V0 -> format.decodeFromString<KeySend.V0>(json).let { LightningOutgoingPayment.Details.KeySend(it.preimage) }
LightningOutgoingDetailsTypeVersion.SWAPOUT_V0 -> format.decodeFromString<SwapOut.V0>(json).let { LightningOutgoingPayment.Details.SwapOut(it.address, Bolt11Invoice.read(it.paymentRequest).get(), it.swapOutFee) }
}
}
}
}
fun LightningOutgoingPayment.Details.mapToDb(): Pair<OutgoingDetailsTypeVersion, ByteArray> = when (this) {
is LightningOutgoingPayment.Details.Normal -> OutgoingDetailsTypeVersion.NORMAL_V0 to
Json.encodeToString(OutgoingDetailsData.Normal.V0(paymentRequest.write())).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Details.KeySend -> OutgoingDetailsTypeVersion.KEYSEND_V0 to
Json.encodeToString(OutgoingDetailsData.KeySend.V0(preimage)).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Details.SwapOut -> OutgoingDetailsTypeVersion.SWAPOUT_V0 to
Json.encodeToString(OutgoingDetailsData.SwapOut.V0(address, paymentRequest.write(), swapOutFee)).toByteArray(Charsets.UTF_8)
fun LightningOutgoingPayment.Details.mapToDb(): Pair<LightningOutgoingDetailsTypeVersion, ByteArray> = when (this) {
is LightningOutgoingPayment.Details.Normal -> LightningOutgoingDetailsTypeVersion.NORMAL_V0 to
Json.encodeToString(LightningOutgoingDetailsData.Normal.V0(paymentRequest.write())).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Details.KeySend -> LightningOutgoingDetailsTypeVersion.KEYSEND_V0 to
Json.encodeToString(LightningOutgoingDetailsData.KeySend.V0(preimage)).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Details.SwapOut -> LightningOutgoingDetailsTypeVersion.SWAPOUT_V0 to
Json.encodeToString(LightningOutgoingDetailsData.SwapOut.V0(address, paymentRequest.write(), swapOutFee)).toByteArray(Charsets.UTF_8)
}

View File

@ -31,33 +31,33 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
enum class OutgoingPartStatusTypeVersion {
enum class LightningOutgoingPartStatusTypeVersion {
SUCCEEDED_V0,
FAILED_V0,
}
sealed class OutgoingPartStatusData {
sealed class LightningOutgoingPartStatusData {
sealed class Succeeded : OutgoingPartStatusData() {
sealed class Succeeded : LightningOutgoingPartStatusData() {
@Serializable
data class V0(@Serializable val preimage: ByteVector32) : Succeeded()
}
sealed class Failed : OutgoingPartStatusData() {
sealed class Failed : LightningOutgoingPartStatusData() {
@Serializable
data class V0(val remoteFailureCode: Int?, val details: String) : Failed()
}
companion object {
fun deserialize(
typeVersion: OutgoingPartStatusTypeVersion,
typeVersion: LightningOutgoingPartStatusTypeVersion,
blob: ByteArray, completedAt: Long
): LightningOutgoingPayment.Part.Status = DbTypesHelper.decodeBlob(blob) { json, format ->
when (typeVersion) {
OutgoingPartStatusTypeVersion.SUCCEEDED_V0 -> format.decodeFromString<Succeeded.V0>(json).let {
LightningOutgoingPartStatusTypeVersion.SUCCEEDED_V0 -> format.decodeFromString<Succeeded.V0>(json).let {
LightningOutgoingPayment.Part.Status.Succeeded(it.preimage, completedAt)
}
OutgoingPartStatusTypeVersion.FAILED_V0 -> format.decodeFromString<Failed.V0>(json).let {
LightningOutgoingPartStatusTypeVersion.FAILED_V0 -> format.decodeFromString<Failed.V0>(json).let {
LightningOutgoingPayment.Part.Status.Failed(it.remoteFailureCode, it.details, completedAt)
}
}
@ -65,8 +65,8 @@ sealed class OutgoingPartStatusData {
}
}
fun LightningOutgoingPayment.Part.Status.Succeeded.mapToDb() = OutgoingPartStatusTypeVersion.SUCCEEDED_V0 to
Json.encodeToString(OutgoingPartStatusData.Succeeded.V0(preimage)).toByteArray(Charsets.UTF_8)
fun LightningOutgoingPayment.Part.Status.Succeeded.mapToDb() = LightningOutgoingPartStatusTypeVersion.SUCCEEDED_V0 to
Json.encodeToString(LightningOutgoingPartStatusData.Succeeded.V0(preimage)).toByteArray(Charsets.UTF_8)
fun LightningOutgoingPayment.Part.Status.Failed.mapToDb() = OutgoingPartStatusTypeVersion.FAILED_V0 to
Json.encodeToString(OutgoingPartStatusData.Failed.V0(remoteFailureCode, details)).toByteArray(Charsets.UTF_8)
fun LightningOutgoingPayment.Part.Status.Failed.mapToDb() = LightningOutgoingPartStatusTypeVersion.FAILED_V0 to
Json.encodeToString(LightningOutgoingPartStatusData.Failed.V0(remoteFailureCode, details)).toByteArray(Charsets.UTF_8)

View File

@ -25,16 +25,15 @@ import fr.acinq.lightning.ShortChannelId
import fr.acinq.lightning.channel.ChannelException
import fr.acinq.lightning.db.HopDesc
import fr.acinq.lightning.db.LightningOutgoingPayment
import fr.acinq.lightning.db.OutgoingPayment
import fr.acinq.lightning.payment.OutgoingPaymentFailure
import fr.acinq.lightning.utils.*
import fr.acinq.lightning.wire.FailureMessage
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import fr.acinq.secp256k1.Hex
class OutgoingQueries(val database: PaymentsDatabase) {
class LightningOutgoingQueries(val database: PhoenixDatabase) {
private val queries = database.outgoingPaymentsQueries
private val queries = database.lightningOutgoingPaymentsQueries
fun addLightningParts(parentId: UUID, parts: List<LightningOutgoingPayment.Part>) {
if (parts.isEmpty()) return
@ -135,12 +134,10 @@ class OutgoingQueries(val database: PaymentsDatabase) {
return result
}
/** This method will ignore any parts that are not proper [LightningOutgoingPayment]. */
fun getPaymentFromPartId(partId: UUID): LightningOutgoingPayment? {
return queries.getLightningPart(part_id = partId.toString()).executeAsOneOrNull()?.let { part ->
queries.getPayment(id = part.part_parent_id, Companion::mapLightningOutgoingPayment).executeAsList()
}?.filterIsInstance<LightningOutgoingPayment>()?.let {
// first ignore any legacy channel closing, then group by parent id
}?.let {
groupByRawLightningOutgoing(it).firstOrNull()
}?.let {
filterUselessParts(it)
@ -156,17 +153,13 @@ class OutgoingQueries(val database: PaymentsDatabase) {
id = id.toString(),
mapper = Companion::mapLightningOutgoingPayment
).executeAsList().let { parts ->
// only take regular LN payments parts, and group them
parts.filterIsInstance<LightningOutgoingPayment>().let {
groupByRawLightningOutgoing(it).firstOrNull()
}?.let {
groupByRawLightningOutgoing(parts).firstOrNull()?.let {
filterUselessParts(it)
}
}
fun listLightningOutgoingPayments(paymentHash: ByteVector32): List<LightningOutgoingPayment> {
return queries.listPaymentsForPaymentHash(paymentHash.toByteArray(), Companion::mapLightningOutgoingPayment).executeAsList()
.filterIsInstance<LightningOutgoingPayment>()
.let { groupByRawLightningOutgoing(it) }
}
@ -195,16 +188,15 @@ class OutgoingQueries(val database: PaymentsDatabase) {
recipient_amount_msat: Long,
recipient_node_id: String,
payment_hash: ByteArray,
details_type: OutgoingDetailsTypeVersion,
details_type: LightningOutgoingDetailsTypeVersion,
details_blob: ByteArray,
created_at: Long,
completed_at: Long?,
status_type: OutgoingStatusTypeVersion?,
status_type: LightningOutgoingStatusTypeVersion?,
status_blob: ByteArray?
): LightningOutgoingPayment {
val details = OutgoingDetailsData.deserialize(details_type, details_blob)
return if (details != null) {
LightningOutgoingPayment(
val details = LightningOutgoingDetailsData.deserialize(details_type, details_blob)
return LightningOutgoingPayment(
id = UUID.fromString(id),
recipientAmount = MilliSatoshi(recipient_amount_msat),
recipient = PublicKey.parse(Hex.decode(recipient_node_id)),
@ -213,7 +205,6 @@ class OutgoingQueries(val database: PaymentsDatabase) {
status = mapPaymentStatus(status_type, status_blob, completed_at),
createdAt = created_at
)
} else throw IllegalArgumentException("cannot handle closing payment at this stage, use LegacyChannelCloseHelper")
}
@Suppress("UNUSED_PARAMETER")
@ -222,11 +213,11 @@ class OutgoingQueries(val database: PaymentsDatabase) {
recipient_amount_msat: Long,
recipient_node_id: String,
payment_hash: ByteArray,
details_type: OutgoingDetailsTypeVersion,
details_type: LightningOutgoingDetailsTypeVersion,
details_blob: ByteArray,
created_at: Long,
completed_at: Long?,
status_type: OutgoingStatusTypeVersion?,
status_type: LightningOutgoingStatusTypeVersion?,
status_blob: ByteArray?,
// lightning parts data, may be null
lightning_part_id: String?,
@ -234,16 +225,9 @@ class OutgoingQueries(val database: PaymentsDatabase) {
lightning_part_route: List<HopDesc>?,
lightning_part_created_at: Long?,
lightning_part_completed_at: Long?,
lightning_part_status_type: OutgoingPartStatusTypeVersion?,
lightning_part_status_type: LightningOutgoingPartStatusTypeVersion?,
lightning_part_status_blob: ByteArray?,
// closing tx parts data, may be null
closingtx_part_id: String?,
closingtx_part_tx_id: ByteArray?,
closingtx_part_amount_sat: Long?,
closingtx_part_closing_info_type: OutgoingPartClosingInfoTypeVersion?,
closingtx_part_closing_info_blob: ByteArray?,
closingtx_part_created_at: Long?
): OutgoingPayment {
): LightningOutgoingPayment {
val parts = if (lightning_part_id != null && lightning_part_amount_msat != null && lightning_part_route != null && lightning_part_created_at != null) {
listOf(
@ -281,7 +265,7 @@ class OutgoingQueries(val database: PaymentsDatabase) {
route: List<HopDesc>,
createdAt: Long,
completedAt: Long?,
statusType: OutgoingPartStatusTypeVersion?,
statusType: LightningOutgoingPartStatusTypeVersion?,
statusBlob: ByteArray?
): LightningOutgoingPayment.Part {
return LightningOutgoingPayment.Part(
@ -298,22 +282,22 @@ class OutgoingQueries(val database: PaymentsDatabase) {
}
private fun mapPaymentStatus(
statusType: OutgoingStatusTypeVersion?,
statusType: LightningOutgoingStatusTypeVersion?,
statusBlob: ByteArray?,
completedAt: Long?,
): LightningOutgoingPayment.Status = when {
completedAt == null && statusType == null && statusBlob == null -> LightningOutgoingPayment.Status.Pending
completedAt != null && statusType != null && statusBlob != null -> OutgoingStatusData.deserialize(statusType, statusBlob, completedAt)
completedAt != null && statusType != null && statusBlob != null -> LightningOutgoingStatusData.deserialize(statusType, statusBlob, completedAt)
else -> throw UnhandledOutgoingStatus(completedAt, statusType, statusBlob)
}
private fun mapLightningPartStatus(
statusType: OutgoingPartStatusTypeVersion?,
statusType: LightningOutgoingPartStatusTypeVersion?,
statusBlob: ByteArray?,
completedAt: Long?,
): LightningOutgoingPayment.Part.Status = when {
completedAt == null && statusType == null && statusBlob == null -> LightningOutgoingPayment.Part.Status.Pending
completedAt != null && statusType != null && statusBlob != null -> OutgoingPartStatusData.deserialize(statusType, statusBlob, completedAt)
completedAt != null && statusType != null && statusBlob != null -> LightningOutgoingPartStatusData.deserialize(statusType, statusBlob, completedAt)
else -> throw UnhandledOutgoingPartStatus(statusType, statusBlob, completedAt)
}
@ -336,8 +320,8 @@ class OutgoingQueries(val database: PaymentsDatabase) {
}
}
data class UnhandledOutgoingStatus(val completedAt: Long?, val statusTypeVersion: OutgoingStatusTypeVersion?, val statusData: ByteArray?) :
data class UnhandledOutgoingStatus(val completedAt: Long?, val statusTypeVersion: LightningOutgoingStatusTypeVersion?, val statusData: ByteArray?) :
RuntimeException("cannot map outgoing payment status data with completed_at=$completedAt status_type=$statusTypeVersion status=$statusData")
data class UnhandledOutgoingPartStatus(val status_type: OutgoingPartStatusTypeVersion?, val status_blob: ByteArray?, val completedAt: Long?) :
data class UnhandledOutgoingPartStatus(val status_type: LightningOutgoingPartStatusTypeVersion?, val status_blob: ByteArray?, val completedAt: Long?) :
RuntimeException("cannot map outgoing part status data [ completed_at=$completedAt status_type=$status_type status_blob=$status_blob]")

View File

@ -22,7 +22,6 @@
package fr.acinq.lightning.bin.db.payments
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Satoshi
import fr.acinq.lightning.bin.db.payments.DbTypesHelper.decodeBlob
import fr.acinq.lightning.bin.db.serializers.v1.ByteVector32Serializer
import fr.acinq.lightning.bin.db.serializers.v1.SatoshiSerializer
@ -32,54 +31,35 @@ import io.ktor.utils.io.charsets.*
import io.ktor.utils.io.core.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
enum class OutgoingStatusTypeVersion {
enum class LightningOutgoingStatusTypeVersion {
SUCCEEDED_OFFCHAIN_V0,
FAILED_V0,
}
sealed class OutgoingStatusData {
sealed class LightningOutgoingStatusData {
sealed class SucceededOffChain : OutgoingStatusData() {
sealed class SucceededOffChain : LightningOutgoingStatusData() {
@Serializable
data class V0(@Serializable val preimage: ByteVector32) : SucceededOffChain()
}
sealed class SucceededOnChain : OutgoingStatusData() {
@Serializable
data class V0(
val txIds: List<@Serializable ByteVector32>,
@Serializable val claimed: Satoshi,
val closingType: String
) : SucceededOnChain()
@Serializable
object V1 : SucceededOnChain()
}
sealed class Failed : OutgoingStatusData() {
sealed class Failed : LightningOutgoingStatusData() {
@Serializable
data class V0(val reason: String) : Failed()
}
companion object {
/** Extract valuable data from old outgoing payments status that represent closing transactions. */
fun deserializeLegacyClosingStatus(blob: ByteArray): SucceededOnChain.V0 = decodeBlob(blob) { json, format ->
val data = format.decodeFromString<SucceededOnChain.V0>(json)
data
}
fun deserialize(typeVersion: OutgoingStatusTypeVersion, blob: ByteArray, completedAt: Long): LightningOutgoingPayment.Status = decodeBlob(blob) { json, format ->
fun deserialize(typeVersion: LightningOutgoingStatusTypeVersion, blob: ByteArray, completedAt: Long): LightningOutgoingPayment.Status = decodeBlob(blob) { json, format ->
@Suppress("DEPRECATION")
when (typeVersion) {
OutgoingStatusTypeVersion.SUCCEEDED_OFFCHAIN_V0 -> format.decodeFromString<SucceededOffChain.V0>(json).let {
LightningOutgoingStatusTypeVersion.SUCCEEDED_OFFCHAIN_V0 -> format.decodeFromString<SucceededOffChain.V0>(json).let {
LightningOutgoingPayment.Status.Completed.Succeeded.OffChain(it.preimage, completedAt)
}
OutgoingStatusTypeVersion.FAILED_V0 -> format.decodeFromString<Failed.V0>(json).let {
LightningOutgoingStatusTypeVersion.FAILED_V0 -> format.decodeFromString<Failed.V0>(json).let {
LightningOutgoingPayment.Status.Completed.Failed(deserializeFinalFailure(it.reason), completedAt)
}
}
@ -101,10 +81,9 @@ sealed class OutgoingStatusData {
}
}
fun LightningOutgoingPayment.Status.Completed.mapToDb(): Pair<OutgoingStatusTypeVersion, ByteArray> = when (this) {
is LightningOutgoingPayment.Status.Completed.Succeeded.OffChain -> OutgoingStatusTypeVersion.SUCCEEDED_OFFCHAIN_V0 to
Json.encodeToString(OutgoingStatusData.SucceededOffChain.V0(preimage)).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Status.Completed.Failed -> OutgoingStatusTypeVersion.FAILED_V0 to
Json.encodeToString(OutgoingStatusData.Failed.V0(OutgoingStatusData.serializeFinalFailure(reason))).toByteArray(Charsets.UTF_8)
fun LightningOutgoingPayment.Status.Completed.mapToDb(): Pair<LightningOutgoingStatusTypeVersion, ByteArray> = when (this) {
is LightningOutgoingPayment.Status.Completed.Succeeded.OffChain -> LightningOutgoingStatusTypeVersion.SUCCEEDED_OFFCHAIN_V0 to
Json.encodeToString(LightningOutgoingStatusData.SucceededOffChain.V0(preimage)).toByteArray(Charsets.UTF_8)
is LightningOutgoingPayment.Status.Completed.Failed -> LightningOutgoingStatusTypeVersion.FAILED_V0 to
Json.encodeToString(LightningOutgoingStatusData.Failed.V0(LightningOutgoingStatusData.serializeFinalFailure(reason))).toByteArray(Charsets.UTF_8)
}

View File

@ -20,12 +20,12 @@ import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import fr.acinq.bitcoin.TxId
import fr.acinq.lightning.bin.db.WalletPaymentId
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.*
class LinkTxToPaymentQueries(val database: PaymentsDatabase) {
class LinkTxToPaymentQueries(val database: PhoenixDatabase) {
private val linkTxQueries = database.linkTxToPaymentQueries
fun listUnconfirmedTxs(): Flow<List<ByteArray>> {

View File

@ -3,9 +3,9 @@ package fr.acinq.lightning.bin.db.payments
import fr.acinq.lightning.bin.db.PaymentMetadata
import fr.acinq.lightning.bin.db.WalletPaymentId
import fr.acinq.lightning.utils.currentTimestampMillis
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
class PaymentsMetadataQueries(private val database: PaymentsDatabase) {
class PaymentsMetadataQueries(private val database: PhoenixDatabase) {
private val queries = database.paymentsMetadataQueries

View File

@ -21,9 +21,9 @@ import fr.acinq.lightning.db.SpliceCpfpOutgoingPayment
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.sat
import fr.acinq.lightning.utils.toByteVector32
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
class SpliceCpfpOutgoingQueries(val database: PaymentsDatabase) {
class SpliceCpfpOutgoingQueries(val database: PhoenixDatabase) {
private val cpfpQueries = database.spliceCpfpOutgoingPaymentsQueries
fun addCpfpPayment(payment: SpliceCpfpOutgoingPayment) {

View File

@ -21,9 +21,9 @@ import fr.acinq.lightning.db.SpliceOutgoingPayment
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.sat
import fr.acinq.lightning.utils.toByteVector32
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
class SpliceOutgoingQueries(val database: PaymentsDatabase) {
class SpliceOutgoingQueries(val database: PhoenixDatabase) {
private val spliceOutQueries = database.spliceOutgoingPaymentsQueries
fun addSpliceOutgoingPayment(payment: SpliceOutgoingPayment) {

View File

@ -1,4 +1,4 @@
import fr.acinq.lightning.bin.db.payments.OutgoingPartClosingInfoTypeVersion;
import fr.acinq.lightning.bin.db.payments.ClosingInfoTypeVersion;
-- Store in a flat row outgoing payments standing for channel-closing.
-- There are no complex json columns like in the outgoing_payments table.
@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS channel_close_outgoing_payments (
confirmed_at INTEGER DEFAULT NULL,
locked_at INTEGER DEFAULT NULL,
channel_id BLOB NOT NULL,
closing_info_type TEXT AS OutgoingPartClosingInfoTypeVersion NOT NULL,
closing_info_type TEXT AS ClosingInfoTypeVersion NOT NULL,
closing_info_blob BLOB NOT NULL
);

View File

@ -1,36 +1,35 @@
import fr.acinq.lightning.db.HopDesc;
import fr.acinq.lightning.bin.db.payments.OutgoingDetailsTypeVersion;
import fr.acinq.lightning.bin.db.payments.OutgoingPartClosingInfoTypeVersion;
import fr.acinq.lightning.bin.db.payments.OutgoingPartStatusTypeVersion;
import fr.acinq.lightning.bin.db.payments.OutgoingStatusTypeVersion;
import fr.acinq.lightning.bin.db.payments.LightningOutgoingDetailsTypeVersion;
import fr.acinq.lightning.bin.db.payments.LightningOutgoingPartStatusTypeVersion;
import fr.acinq.lightning.bin.db.payments.LightningOutgoingStatusTypeVersion;
import kotlin.collections.List;
PRAGMA foreign_keys = 1;
-- outgoing payments
-- Stores an outgoing payment in a flat row. Some columns can be null.
CREATE TABLE IF NOT EXISTS outgoing_payments (
CREATE TABLE IF NOT EXISTS lightning_outgoing_payments (
id TEXT NOT NULL PRIMARY KEY,
recipient_amount_msat INTEGER NOT NULL,
recipient_node_id TEXT NOT NULL,
payment_hash BLOB NOT NULL,
created_at INTEGER NOT NULL,
-- details
details_type TEXT AS OutgoingDetailsTypeVersion NOT NULL,
details_type TEXT AS LightningOutgoingDetailsTypeVersion NOT NULL,
details_blob BLOB NOT NULL,
-- status
completed_at INTEGER DEFAULT NULL,
status_type TEXT AS OutgoingStatusTypeVersion DEFAULT NULL,
status_type TEXT AS LightningOutgoingStatusTypeVersion DEFAULT NULL,
status_blob BLOB DEFAULT NULL
);
-- Create indexes to optimize the queries in AggregatedQueries.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX IF NOT EXISTS outgoing_payments_filter_idx
ON outgoing_payments(completed_at);
ON lightning_outgoing_payments(completed_at);
-- Stores the lightning parts that make up a lightning payment
CREATE TABLE IF NOT EXISTS outgoing_payment_parts (
CREATE TABLE IF NOT EXISTS lightning_outgoing_payment_parts (
part_id TEXT NOT NULL PRIMARY KEY,
part_parent_id TEXT NOT NULL,
part_amount_msat INTEGER NOT NULL,
@ -38,24 +37,10 @@ CREATE TABLE IF NOT EXISTS outgoing_payment_parts (
part_created_at INTEGER NOT NULL,
-- status
part_completed_at INTEGER DEFAULT NULL,
part_status_type TEXT AS OutgoingPartStatusTypeVersion DEFAULT NULL,
part_status_type TEXT AS LightningOutgoingPartStatusTypeVersion DEFAULT NULL,
part_status_blob BLOB DEFAULT NULL,
FOREIGN KEY(part_parent_id) REFERENCES outgoing_payments(id)
);
-- !! This table is legacy, and will only contain old payments. See ChannelCloseOutgoingPayment.sq for the new table.
-- Stores the transactions that close a channel
CREATE TABLE IF NOT EXISTS outgoing_payment_closing_tx_parts (
part_id TEXT NOT NULL PRIMARY KEY,
part_parent_id TEXT NOT NULL,
part_tx_id BLOB NOT NULL,
part_amount_sat INTEGER NOT NULL,
part_closing_info_type TEXT AS OutgoingPartClosingInfoTypeVersion NOT NULL,
part_closing_info_blob BLOB NOT NULL,
part_created_at INTEGER NOT NULL,
FOREIGN KEY(part_parent_id) REFERENCES outgoing_payments(id)
FOREIGN KEY(part_parent_id) REFERENCES lightning_outgoing_payments(id)
);
-- A FOREIGN KEY does NOT create an implicit index.
@ -64,17 +49,16 @@ CREATE TABLE IF NOT EXISTS outgoing_payment_closing_tx_parts (
-- > Indices are not required for child key columns but they are almost always beneficial.
-- > [...] So, in most real systems, an index should be created on the child key columns
-- > of each foreign key constraint.
CREATE INDEX IF NOT EXISTS parent_id_idx ON outgoing_payment_parts(part_parent_id);
CREATE INDEX IF NOT EXISTS parent_id_idx ON outgoing_payment_closing_tx_parts(part_parent_id);
CREATE INDEX IF NOT EXISTS parent_id_idx ON lightning_outgoing_payment_parts(part_parent_id);
-- queries for outgoing payments
hasPayment:
SELECT COUNT(*) FROM outgoing_payments
SELECT COUNT(*) FROM lightning_outgoing_payments
WHERE id = ?;
insertPayment:
INSERT INTO outgoing_payments (
INSERT INTO lightning_outgoing_payments (
id,
recipient_amount_msat,
recipient_node_id,
@ -85,23 +69,23 @@ INSERT INTO outgoing_payments (
VALUES (?, ?, ?, ?, ?, ?, ?);
updatePayment:
UPDATE outgoing_payments SET completed_at=?, status_type=?, status_blob=? WHERE id=?;
UPDATE lightning_outgoing_payments SET completed_at=?, status_type=?, status_blob=? WHERE id=?;
scanCompleted:
SELECT id, completed_at
FROM outgoing_payments
FROM lightning_outgoing_payments
WHERE completed_at IS NOT NULL;
deletePayment:
DELETE FROM outgoing_payments WHERE id = ?;
DELETE FROM lightning_outgoing_payments WHERE id = ?;
-- queries for lightning parts
countLightningPart:
SELECT COUNT(*) FROM outgoing_payment_parts WHERE part_id = ?;
SELECT COUNT(*) FROM lightning_outgoing_payment_parts WHERE part_id = ?;
insertLightningPart:
INSERT INTO outgoing_payment_parts (
INSERT INTO lightning_outgoing_payment_parts (
part_id,
part_parent_id,
part_amount_msat,
@ -110,33 +94,17 @@ INSERT INTO outgoing_payment_parts (
VALUES (?, ?, ?, ?, ?);
updateLightningPart:
UPDATE outgoing_payment_parts
UPDATE lightning_outgoing_payment_parts
SET part_status_type=?,
part_status_blob=?,
part_completed_at=?
WHERE part_id=?;
getLightningPart:
SELECT * FROM outgoing_payment_parts WHERE part_id=?;
SELECT * FROM lightning_outgoing_payment_parts WHERE part_id=?;
deleteLightningPartsForParentId:
DELETE FROM outgoing_payment_parts WHERE part_parent_id = ?;
-- queries for closing tx parts
countClosingTxPart:
SELECT COUNT(*) FROM outgoing_payment_closing_tx_parts WHERE part_id = ?;
insertClosingTxPart:
INSERT INTO outgoing_payment_closing_tx_parts (
part_id,
part_parent_id,
part_tx_id,
part_amount_sat,
part_closing_info_type,
part_closing_info_blob,
part_created_at
) VALUES (:id, :parent_id, :tx_id, :amount_msat, :closing_info_type, :closing_info_blob, :created_at);
DELETE FROM lightning_outgoing_payment_parts WHERE part_parent_id = ?;
-- queries mixing outgoing payments and parts
@ -151,12 +119,12 @@ SELECT id,
completed_at,
status_type,
status_blob
FROM outgoing_payments
FROM lightning_outgoing_payments
WHERE id=?;
getOldestCompletedDate:
SELECT completed_at
FROM outgoing_payments AS o
FROM lightning_outgoing_payments AS o
WHERE completed_at IS NOT NULL
ORDER BY o.completed_at ASC
LIMIT 1;
@ -179,17 +147,9 @@ SELECT parent.id,
lightning_parts.part_created_at AS lightning_part_created_at,
lightning_parts.part_completed_at AS lightning_part_completed_at,
lightning_parts.part_status_type AS lightning_part_status_type,
lightning_parts.part_status_blob AS lightning_part_status_blob,
-- closing tx parts
closing_parts.part_id AS closingtx_part_id,
closing_parts.part_tx_id AS closingtx_tx_id,
closing_parts.part_amount_sat AS closingtx_amount_sat,
closing_parts.part_closing_info_type AS closingtx_info_type,
closing_parts.part_closing_info_blob AS closingtx_info_blob,
closing_parts.part_created_at AS closingtx_created_at
FROM outgoing_payments AS parent
LEFT OUTER JOIN outgoing_payment_parts AS lightning_parts ON lightning_parts.part_parent_id = parent.id
LEFT OUTER JOIN outgoing_payment_closing_tx_parts AS closing_parts ON closing_parts.part_parent_id = parent.id
lightning_parts.part_status_blob AS lightning_part_status_blob
FROM lightning_outgoing_payments AS parent
LEFT OUTER JOIN lightning_outgoing_payment_parts AS lightning_parts ON lightning_parts.part_parent_id = parent.id
WHERE parent.id=?;
listPaymentsForPaymentHash:
@ -210,17 +170,9 @@ SELECT parent.id,
lightning_parts.part_created_at AS lightning_part_created_at,
lightning_parts.part_completed_at AS lightning_part_completed_at,
lightning_parts.part_status_type AS lightning_part_status_type,
lightning_parts.part_status_blob AS lightning_part_status_blob,
-- closing tx parts
closing_parts.part_id AS closingtx_part_id,
closing_parts.part_tx_id AS closingtx_tx_id,
closing_parts.part_amount_sat AS closingtx_amount_sat,
closing_parts.part_closing_info_type AS closingtx_info_type,
closing_parts.part_closing_info_blob AS closingtx_info_blob,
closing_parts.part_created_at AS closingtx_created_at
FROM outgoing_payments AS parent
LEFT OUTER JOIN outgoing_payment_parts AS lightning_parts ON lightning_parts.part_parent_id = parent.id
LEFT OUTER JOIN outgoing_payment_closing_tx_parts AS closing_parts ON closing_parts.part_parent_id = parent.id
lightning_parts.part_status_blob AS lightning_part_status_blob
FROM lightning_outgoing_payments AS parent
LEFT OUTER JOIN lightning_outgoing_payment_parts AS lightning_parts ON lightning_parts.part_parent_id = parent.id
WHERE payment_hash=?;
-- use this in a `transaction` block to know how many rows were changed after an UPDATE

View File

@ -2,8 +2,7 @@ package fr.acinq.lightning.bin
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import fr.acinq.phoenix.db.ChannelsDatabase
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import okio.Path
import okio.Path.Companion.toPath
@ -12,13 +11,6 @@ actual val homeDirectory: Path = System.getProperty("user.home").toPath()
actual fun createAppDbDriver(dir: Path): SqlDriver {
val path = dir / "phoenix.db"
val driver = JdbcSqliteDriver("jdbc:sqlite:$path")
ChannelsDatabase.Schema.create(driver)
return driver
}
actual fun createPaymentsDbDriver(dir: Path): SqlDriver {
val path = dir / "payments.db"
val driver = JdbcSqliteDriver("jdbc:sqlite:$path")
PaymentsDatabase.Schema.create(driver)
PhoenixDatabase.Schema.create(driver)
return driver
}

View File

@ -2,8 +2,7 @@ package fr.acinq.lightning.bin
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.native.NativeSqliteDriver
import fr.acinq.phoenix.db.ChannelsDatabase
import fr.acinq.phoenix.db.PaymentsDatabase
import fr.acinq.phoenix.db.PhoenixDatabase
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.toKString
import okio.Path
@ -15,13 +14,7 @@ import platform.posix.setenv
actual val homeDirectory: Path = setenv("KTOR_LOG_LEVEL", "WARN", 1).let { getenv("HOME")?.toKString()!!.toPath() }
actual fun createAppDbDriver(dir: Path): SqlDriver {
return NativeSqliteDriver(ChannelsDatabase.Schema, "phoenix.db",
onConfiguration = { it.copy(extendedConfig = it.extendedConfig.copy(basePath = dir.toString())) }
)
}
actual fun createPaymentsDbDriver(dir: Path): SqlDriver {
return NativeSqliteDriver(PaymentsDatabase.Schema, "payments.db",
return NativeSqliteDriver(PhoenixDatabase.Schema, "phoenix.db",
onConfiguration = { it.copy(extendedConfig = it.extendedConfig.copy(basePath = dir.toString())) }
)
}