fix: build a release apk, by dropping the app's copies of what the library already ships

`:composeApp:assembleRelease` dies in `mergeDexRelease`:

    Type com.machankura.compose.ui.composable.widgets.nfc.ComposableSingletons$HceMonitorKt
    is defined multiple times:
      composeApp/build/intermediates/project_dex_archive/release/dexBuilderRelease/out/...
      lightning-kmp-app/library/build/.transforms/.../bundleLibRuntimeToDirAndroidMain_dex/...

Both paths are ours. composeApp compiles a class, lightning-kmp-app's `:library`
compiles the same fully-qualified name, and D8 will not merge the two into one
apk. Debug never objected, because it packages the per-project dex archives as
they stand and only the release merge walks the whole set looking for
collisions -- so this has been true for a while and surfaced the first time
anyone asked for a release. There were two such copies, and fixing the first
only uncovered the second.

**The NFC widgets were renamed by directory, not by package.** 9abdf42
("Refactor torch to mantra", 2026-07-15) moved three files under
`composeApp/src/androidMain/kotlin/press/mantra/compose/ui/composable/widgets/nfc/`
and left their `package com.machankura.compose.ui.composable.widgets.nfc` line
alone. The library holds the same three at
`fr/acinq/phoenix/compose/ui/widgets/nfc/`, also declaring `com.machankura...`.
So three directories say three different things and the package -- the only one
of them D8 reads -- says one. `NfcState.kt` is byte-identical across the two;
`HceMonitor.kt` and `NfcReaderMonitor.kt` differ in a single import line,
`press.mantra` against `fr.acinq.phoenix` for `ModalBottomSheet`.

Deleted the app's three rather than renaming their package to `press.mantra`,
which was the other way out and is the worse one. `NfcStateRepository` is an
`object`. The library's own `HceService` and `NfcReaderCallback` import it under
the `com.machankura` name, and `MainActivity:50-52` reaches for it fully
qualified. Renaming the app's copy would have compiled, dexed and run -- with
two singletons: one that `MainActivity` sets back to `Inactive` on a new intent,
and a different one the HCE service is collecting. Nothing would warn, because
by then the names genuinely differ; the tag emulation would just not stop.
Deleting leaves one object, and `MainActivity`'s existing fully-qualified
references land on it untouched. The two composables went along with it as dead
weight -- nothing outside their own files names `HceMonitor` or
`NfcReaderMonitor` anywhere in composeApp.

**The sqldelight databases were a second copy of the same kind.** With the NFC
clash gone, `mergeDexRelease` came back with
`fr.acinq.phoenix.db.sqldelight.AppDatabase$Companion`, out of the same pair of
directories. composeApp's build.gradle.kts declared `ChannelsDatabase`,
`PaymentsDatabase` and `AppDatabase` at packageName
`fr.acinq.phoenix.db.sqldelight` from `.sq` sources under
`src/commonMain/sqldelight`; the library's build.gradle.kts declares the same
three names, the same package and the same layout, over a tree `diff -rq` reports
as identical file for file. Both plugins ran, and both generated the same
classes.

Removed the plugin alias and the whole `sqldelight { }` block from composeApp
and deleted its 32 `.sq`/`.sqm` files, rather than moving the app's generated
package to `press.mantra`. Nothing under `press.mantra` imports
`fr.acinq.phoenix.db` -- grep finds no file -- and nothing there touches
`app.cash.sqldelight` either, against 18 files in the library that do. The app
was generating a database layer it has never opened. Renaming would have kept
generating it, and kept a second identical schema in the tree for someone to
edit and then wonder why nothing moved. A comment sits where the plugin alias
was, since the absence is the load-bearing part and an alias is a one-line thing
to add back by reflex.

**The sqldelight driver dependencies stay.** `-android-driver`,
`-sqlite-driver`, `-native-driver`, `-runtime` and `-coroutines-extensions` are
still declared in composeApp. They are runtime drivers rather than code
generation, and are very likely redundant -- the library declares the same five
as `implementation`, which still carries them onto the runtime classpath. But
they are not what D8 objected to, and a missing driver fails when the app opens
a database on one platform, not when it builds, so retiring them wants more
evidence than a green build and is its own change.

**What this does not fix.** Anything else copied into both trees fails exactly
this way, and only on release. A scan of the two source sets for colliding
fully-qualified top-level types is clean now -- the three NFC files were the
only ones -- but that scan reads `.kt`, and the sqldelight collision had no
`.kt` to find. Generated code is where the next one hides: Room and
compose-resources generate into composeApp, and the library generates
compose-resources too, which is why settings.gradle.kts keeps the submodule's
project named `:library` and renames only the coordinate.

**Verified.** `:composeApp:assembleRelease` produces the unsigned 36MB apk;
`:composeApp:assembleDebug` builds. 906 tests pass, 576 jvm over 69 classes and
330 android over 41 classes, unchanged from before the change since it adds
none. The apk was not installed, so that NFC still works on a device is read
from the code -- one `NfcStateRepository`, the one both sides were already
naming -- rather than watched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-06 22:13:32 +02:00
parent 224d6009dc
commit f301a924fd
36 changed files with 5 additions and 1373 deletions

View File

@@ -11,7 +11,11 @@ plugins {
alias(libs.plugins.composeHotReload)
alias(libs.plugins.kotlinPluginSerialization)
alias(libs.plugins.ksp)
alias(libs.plugins.sqldelight)
// No sqldelight plugin here on purpose. The fr.acinq.phoenix.db.sqldelight databases this
// module used to generate are the same three lightning-kmp-app's :library generates from an
// identical copy of the .sq sources, and nothing under press.mantra touches them. Generating
// them on both sides put every generated class in the apk twice, which debug tolerates and
// mergeDexRelease rejects as duplicate types. The library's copies are the ones that count.
}
kotlin {
@@ -206,23 +210,6 @@ room3 {
schemaDirectory("$projectDir/schemas")
}
sqldelight {
databases {
create("ChannelsDatabase") {
packageName.set("fr.acinq.phoenix.db.sqldelight")
srcDirs.from("src/commonMain/sqldelight/channelsdb")
}
create("PaymentsDatabase") {
packageName.set("fr.acinq.phoenix.db.sqldelight")
srcDirs.from("src/commonMain/sqldelight/paymentsdb")
}
create("AppDatabase") {
packageName.set("fr.acinq.phoenix.db.sqldelight")
srcDirs.from("src/commonMain/sqldelight/appdb")
}
}
}
compose.desktop {
application {
mainClass = "press.mantra.desktop.MainKt"

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2025 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 com.machankura.compose.ui.composable.widgets.nfc
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import press.mantra.compose.ui.composable.widgets.dialogs.ModalBottomSheet
import fr.acinq.phoenix.utils.extensions.findActivitySafe
/** Displays a blocking bottom sheet when the HCE service is started, using the state in [NfcStateRepository]. */
@Composable
fun HceMonitorDialog() {
val context = LocalContext.current
val activity = context.findActivitySafe() ?: return
val state by NfcStateRepository.state.collectAsState(initial = null)
val onDone = { activity.stopHceService() }
when (state) {
is NfcState.EmulatingTag -> ModalBottomSheet(
onDismiss = onDone,
scrimAlpha = .5f,
horizontalAlignment = Alignment.CenterHorizontally,
internalPadding = PaddingValues(horizontal = 24.dp),
dismissOnScrimClick = false,
) {
Text(text = "HCE Emulation", style = MaterialTheme.typography.headlineSmall)
// TODO: Spacer(Modifier.height(16.dp))
// Image(
// painter = painterResource(id = R.drawable.ic_nfc),
// contentDescription = stringResource(R.string.nfc_button),
// modifier = Modifier.size(64.dp),
// colorFilter = ColorFilter.tint(MaterialTheme.colors.primary)
// )
Spacer(Modifier.height(24.dp))
FilledTonalButton(
// TODO: icon = R.drawable.ic_cross,
onClick = onDone,
modifier = Modifier.fillMaxWidth()
) {
Text("Cancel")
}
Spacer(Modifier.height(24.dp))
}
is NfcState.ShowReader -> {}
is NfcState.Inactive -> {}
null -> {}
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2025 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 com.machankura.compose.ui.composable.widgets.nfc
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import press.mantra.compose.ui.composable.widgets.dialogs.ModalBottomSheet
import fr.acinq.phoenix.utils.extensions.findActivitySafe
@Composable
fun NfcReaderMonitor() {
val context = LocalContext.current
val state by NfcStateRepository.state.collectAsState(initial = null)
val activity = context.findActivitySafe() ?: return
val onDismiss = { activity.stopNfcReader() }
when (state) {
null -> {}
is NfcState.Inactive -> {}
is NfcState.EmulatingTag -> {}
is NfcState.ShowReader -> {
ModalBottomSheet(
onDismiss = onDismiss,
scrimAlpha = .5f,
horizontalAlignment = Alignment.CenterHorizontally,
internalPadding = PaddingValues(horizontal = 24.dp),
dismissOnScrimClick = false,
) {
Text(text = "NFC Reader", style = MaterialTheme.typography.headlineSmall)
// TODO: Spacer(Modifier.height(16.dp))
// Image(
// painter = painterResource(id = R.drawable.ic_nfc),
// contentDescription = stringResource(R.string.nfc_button),
// modifier = Modifier.size(64.dp),
// colorFilter = ColorFilter.tint(MaterialTheme.colors.primary)
// )
Spacer(Modifier.height(16.dp))
// Text(text = stringResource(R.string.nfc_reader_desc), style = MaterialTheme.typography.subtitle2)
Spacer(Modifier.height(24.dp))
FilledTonalButton(
// TODO: icon = R.drawable.ic_cross,
onClick = onDismiss,
modifier = Modifier.fillMaxWidth()
) {
Text("Cancel")
}
Spacer(Modifier.height(24.dp))
}
}
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2025 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 com.machankura.compose.ui.composable.widgets.nfc
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
sealed class NfcState {
data object Inactive: NfcState()
sealed class Busy : NfcState()
data object ShowReader: Busy()
data class EmulatingTag(val paymentRequest: String): Busy()
}
object NfcStateRepository {
private val _state = MutableStateFlow<NfcState?>(null)
val state = _state.asStateFlow()
fun isReading() = state.value is NfcState.ShowReader
fun isEmulating() = state.value is NfcState.EmulatingTag
fun updateState(s: NfcState) {
_state.value = s
}
}

View File

@@ -1,26 +0,0 @@
import fr.acinq.phoenix.data.ExchangeRate;
CREATE TABLE IF NOT EXISTS exchange_rates (
fiat TEXT NOT NULL PRIMARY KEY,
price REAL NOT NULL,
type TEXT AS ExchangeRate.Type NOT NULL,
source TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
insert:
INSERT INTO exchange_rates(
fiat, price, type, source, updated_at
) VALUES (?, ?, ?, ?, ?);
update:
UPDATE exchange_rates SET price=?, type=?, source=?, updated_at=? WHERE fiat=?;
get:
SELECT * FROM exchange_rates WHERE fiat=?;
list:
SELECT * FROM exchange_rates;
delete:
DELETE FROM exchange_rates WHERE fiat=?;

View File

@@ -1,22 +0,0 @@
-- Generic key/value store
CREATE TABLE IF NOT EXISTS key_value_store (
key TEXT NOT NULL PRIMARY KEY,
value BLOB NOT NULL,
updated_at INTEGER NOT NULL
);
get:
SELECT * FROM key_value_store WHERE key = ?;
exists:
SELECT COUNT(*) FROM key_value_store WHERE key = ?;
insert:
INSERT INTO key_value_store(key, value, updated_at) VALUES (?, ?, ?);
update:
UPDATE key_value_store SET value = ?, updated_at = ? WHERE key = ?;
delete:
DELETE FROM key_value_store WHERE key = ?;

View File

@@ -1,42 +0,0 @@
-- This table stores notifications of all kinds.
-- * id => UUID of a notification
-- * type_version => string tracking the type/version of a notification
-- * data_json => json-serialized blob containing the notification details
-- * created_at => when the notification was created, in millis
-- * read_at => when the notification was read, in millis. Read notifications are typically not shown anymore.
CREATE TABLE IF NOT EXISTS notifications (
id TEXT NOT NULL PRIMARY KEY,
type_version TEXT NOT NULL,
data_json BLOB NOT NULL,
created_at INTEGER NOT NULL,
read_at INTEGER DEFAULT NULL,
node_id_hash TEXT DEFAULT NULL
);
-- Create index to optimize the `listUnread` query.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX notifications_source ON notifications(node_id_hash) WHERE read_at IS NULL;
listUnread:
SELECT id, group_concat(id, ';') AS grouped_ids, type_version, data_json, max(created_at)
FROM notifications
WHERE read_at IS NULL AND node_id_hash = ?
GROUP BY type_version, data_json
ORDER BY created_at DESC;
get:
SELECT id, type_version, data_json, created_at, read_at FROM notifications WHERE id=?;
insert:
INSERT INTO notifications (
id, type_version, data_json, created_at, node_id_hash
) VALUES (?, ?, ?, ?, ?);
markAsRead:
UPDATE notifications SET read_at=? WHERE id IN ?;
markAllAsRead:
UPDATE notifications SET read_at=? WHERE read_at IS NULL;
initializeNodeIdHashColumn:
UPDATE notifications SET node_id_hash=? WHERE node_id_hash IS NULL;

View File

@@ -1,10 +0,0 @@
-- Migration: v1 -> v2
--
-- Changes:
-- * Added table key_value_store
CREATE TABLE IF NOT EXISTS key_value_store (
key TEXT NOT NULL PRIMARY KEY,
value BLOB NOT NULL,
updated_at INTEGER NOT NULL
);

View File

@@ -1,15 +0,0 @@
-- Migration: v2 -> v3
--
-- Changes:
-- * Deleted table bitcoin_price_rates
-- * Added table exchange_rates
DROP TABLE IF EXISTS bitcoin_price_rates;
CREATE TABLE IF NOT EXISTS exchange_rates (
fiat TEXT NOT NULL PRIMARY KEY,
price REAL NOT NULL,
type TEXT NOT NULL,
source TEXT NOT NULL,
updated_at INTEGER NOT NULL
);

View File

@@ -1,12 +0,0 @@
-- Migration: v3 -> v4
--
-- Changes:
-- * add notifications table
CREATE TABLE IF NOT EXISTS notifications (
id TEXT NOT NULL PRIMARY KEY,
type_version TEXT AS NotificationTypeVersion NOT NULL,
data_json BLOB NOT NULL,
created_at INTEGER NOT NULL,
read_at INTEGER DEFAULT NULL
);

View File

@@ -1,22 +0,0 @@
-- Migration: v4 -> v5
--
-- Changes:
-- * add contacts table
-- * add contact_offers table
CREATE TABLE IF NOT EXISTS contacts (
id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
photo_uri TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS contact_offers (
offer_id BLOB NOT NULL PRIMARY KEY,
contact_id TEXT NOT NULL,
offer TEXT NOT NULL,
created_at INTEGER NOT NULL,
FOREIGN KEY(contact_id) REFERENCES contacts(id)
);

View File

@@ -1,7 +0,0 @@
-- Migration: v5 -> v6
--
-- Changes:
-- * add use_offer_key flag to the contacts table. By default, a contact is trusted.
ALTER TABLE contacts
ADD COLUMN use_offer_key INTEGER AS Boolean DEFAULT 1 NOT NULL;

View File

@@ -1,23 +0,0 @@
-- Migration: v6 -> v7
--
-- Changes:
-- * Added table cloudkit_contacts_metadata
-- * Added index on table cloudkit_contacts_metadata
-- * Added table cloudkit_contacts_queue
--
-- See CloudKitContacts.sq for more details.
CREATE TABLE IF NOT EXISTS cloudkit_contacts_metadata (
id TEXT NOT NULL PRIMARY KEY,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL
);
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_contacts_metadata(record_creation);
CREATE TABLE IF NOT EXISTS cloudkit_contacts_queue (
rowid INTEGER PRIMARY KEY,
id TEXT NOT NULL,
date_added INTEGER NOT NULL
);

View File

@@ -1,26 +0,0 @@
-- Migration: v7 -> v8
--
-- Changes:
-- * Migrating contacts to paymentsDb
--
DROP INDEX IF EXISTS contact_id_index;
--- The original `contact_offers` table has a FOREIGN KEY constraint.
--- This is going to cause a problem when we rename the contacts table.
--- And there's no way to drop a constraint in sqlite.
--- So we need to migrate the old table to a new one without the constraint.
CREATE TABLE IF NOT EXISTS contact_offers_old (
offer_id BLOB NOT NULL PRIMARY KEY,
contact_id TEXT NOT NULL,
offer TEXT NOT NULL,
created_at INTEGER NOT NULL
);
INSERT INTO contact_offers_old SELECT * FROM contact_offers;
DROP TABLE IF EXISTS contact_offers;
ALTER TABLE contacts RENAME TO contacts_old;
DROP INDEX IF EXISTS record_creation_idx;
ALTER TABLE cloudkit_contacts_metadata RENAME TO cloudkit_contacts_metadata_old;
DROP TABLE IF EXISTS cloudkit_contacts_queue;

View File

@@ -1,10 +0,0 @@
-- Migration: v8 -> v9
--
-- Changes:
-- * Adding column `node_id_hash` to notifications table
--
ALTER TABLE notifications
ADD COLUMN node_id_hash TEXT DEFAULT NULL;
CREATE INDEX notifications_source ON notifications(node_id_hash) WHERE read_at IS NULL;

View File

@@ -1,48 +0,0 @@
import fr.acinq.bitcoin.ByteVector32;
import fr.acinq.lightning.channel.states.PersistedChannelState;
import kotlin.Boolean;
-- channels table
-- note: boolean are stored as INTEGER, with 0=false
CREATE TABLE local_channels (
channel_id BLOB AS ByteVector32 NOT NULL PRIMARY KEY,
data BLOB AS PersistedChannelState NOT NULL,
is_closed INTEGER AS Boolean DEFAULT 0 NOT NULL
);
-- htlcs info table
CREATE TABLE htlc_infos (
channel_id BLOB AS ByteVector32 NOT NULL,
commitment_number INTEGER NOT NULL,
payment_hash BLOB AS ByteVector32 NOT NULL,
cltv_expiry INTEGER NOT NULL,
FOREIGN KEY(channel_id) REFERENCES local_channels(channel_id)
);
CREATE INDEX htlc_infos_idx ON htlc_infos(channel_id, commitment_number);
-- channels queries
getChannel:
SELECT * FROM local_channels WHERE channel_id=?;
updateChannel:
UPDATE local_channels SET data=? WHERE channel_id=?;
insertChannel:
INSERT INTO local_channels VALUES (?, ?, 0);
closeLocalChannel:
UPDATE local_channels SET is_closed=1 WHERE channel_id=?;
listLocalChannels:
SELECT data FROM local_channels WHERE is_closed=0;
-- htlcs info queries
insertHtlcInfo:
INSERT INTO htlc_infos VALUES (?, ?, ?, ?);
listHtlcInfos:
SELECT payment_hash, cltv_expiry FROM htlc_infos WHERE channel_id=? AND commitment_number=?;
deleteHtlcInfo:
DELETE FROM htlc_infos WHERE channel_id=?;

View File

@@ -1,85 +0,0 @@
-- This table stores the CKRecord metadata corresponding to a synced contact.
-- * id => stores the primary key of the contact row
--
CREATE TABLE IF NOT EXISTS cloudkit_contacts_metadata (
id TEXT NOT NULL PRIMARY KEY,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL
);
-- When resuming the download process (e.g. after app relaunch),
-- we need to fetch the earliest creationDate.
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_contacts_metadata(record_creation);
-- This table stores the queue of items that need to be pushed to the cloud.
-- * rowid => because we might store the same `id` multiple times
-- * id => stores the primary key of the contact row
--
CREATE TABLE IF NOT EXISTS cloudkit_contacts_queue (
rowid INTEGER PRIMARY KEY,
id TEXT NOT NULL,
date_added INTEGER NOT NULL
);
-- ########## cloudkit_contacts_metadata ##########
addMetadata:
INSERT INTO cloudkit_contacts_metadata (
id,
record_creation,
record_blob)
VALUES (?, ?, ?);
updateMetadata:
UPDATE cloudkit_contacts_metadata
SET record_blob = ?
WHERE id = ?;
existsMetadata:
SELECT COUNT(*) FROM cloudkit_contacts_metadata
WHERE id = ?;
fetchMetadata:
SELECT * FROM cloudkit_contacts_metadata
WHERE id = ?;
scanMetadata:
SELECT id FROM cloudkit_contacts_metadata;
fetchOldestCreation_Contacts:
SELECT id, record_creation FROM cloudkit_contacts_metadata
ORDER BY record_creation ASC
LIMIT 1;
deleteMetadata:
DELETE FROM cloudkit_contacts_metadata
WHERE id = ?;
deleteAllFromMetadata:
DELETE FROM cloudkit_contacts_metadata;
-- ########## cloudkit_contacts_queue ##########
addToQueue:
INSERT INTO cloudkit_contacts_queue (
id,
date_added)
VALUES (?, ?);
fetchQueueBatch:
SELECT * FROM cloudkit_contacts_queue
ORDER BY date_added ASC
LIMIT :limit;
fetchQueueCount:
SELECT COUNT(*) FROM cloudkit_contacts_queue;
deleteFromQueue:
DELETE FROM cloudkit_contacts_queue
WHERE rowid = ?;
deleteAllFromQueue:
DELETE FROM cloudkit_contacts_queue;

View File

@@ -1,90 +0,0 @@
import fr.acinq.lightning.utils.UUID;
-- This table stores the CKRecord metadata corresponding to a synced payment.
-- * id => stores the primary key of the payment row
--
CREATE TABLE IF NOT EXISTS cloudkit_payments_metadata (
id BLOB AS UUID NOT NULL PRIMARY KEY,
unpadded_size INTEGER NOT NULL,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL
);
-- When resuming the download process (e.g. after app relaunch),
-- we need to fetch the earliest creationDate.
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_payments_metadata(record_creation);
-- This table stores the queue of items that need to be pushed to the cloud.
-- * id => stores the primary key of the payment row
--
CREATE TABLE IF NOT EXISTS cloudkit_payments_queue (
rowid INTEGER PRIMARY KEY,
id BLOB AS UUID NOT NULL,
date_added INTEGER NOT NULL
);
-- ########## cloudkit_payments_metadata ##########
addMetadata:
INSERT INTO cloudkit_payments_metadata (
id,
unpadded_size,
record_creation,
record_blob)
VALUES (?, ?, ?, ?);
updateMetadata:
UPDATE cloudkit_payments_metadata
SET unpadded_size = ?,
record_blob = ?
WHERE id = ?;
existsMetadata:
SELECT COUNT(*) FROM cloudkit_payments_metadata
WHERE id = ?;
fetchMetadata:
SELECT * FROM cloudkit_payments_metadata
WHERE id = ?;
scanMetadata:
SELECT id FROM cloudkit_payments_metadata;
listNonZeroSizes:
SELECT id, unpadded_size FROM cloudkit_payments_metadata WHERE unpadded_size > 0;
fetchOldestCreation:
SELECT id, record_creation FROM cloudkit_payments_metadata
ORDER BY record_creation ASC
LIMIT 1;
deleteMetadata:
DELETE FROM cloudkit_payments_metadata
WHERE id = ?;
deleteAllFromMetadata:
DELETE FROM cloudkit_payments_metadata;
-- ########## cloudkit_payments_queue ##########
addToQueue:
INSERT INTO cloudkit_payments_queue (
id,
date_added)
VALUES (?, ?);
fetchQueueBatch:
SELECT * FROM cloudkit_payments_queue
ORDER BY date_added ASC
LIMIT :limit;
fetchQueueCount:
SELECT COUNT(*) FROM cloudkit_payments_queue;
deleteFromQueue:
DELETE FROM cloudkit_payments_queue
WHERE rowid = ?;
deleteAllFromQueue:
DELETE FROM cloudkit_payments_queue;

View File

@@ -1,36 +0,0 @@
import fr.acinq.lightning.utils.UUID;
import fr.acinq.phoenix.data.ContactInfo;
CREATE TABLE IF NOT EXISTS contacts (
id BLOB AS UUID NOT NULL PRIMARY KEY,
data BLOB AS ContactInfo NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER DEFAULT NULL
);
listContacts:
SELECT data
FROM contacts;
getContact:
SELECT data
FROM contacts
WHERE id = :contactId;
scanContacts:
SELECT id, created_at FROM contacts;
existsContact:
SELECT COUNT(*) FROM contacts
WHERE id = ?;
insertContact:
INSERT INTO contacts(id, data, created_at, updated_at)
VALUES (:id, :data, :createdAt, :updatedAt);
updateContact:
UPDATE contacts SET data=:data, updated_at=:updatedAt
WHERE id=:contactId;
deleteContact:
DELETE FROM contacts WHERE id=:contactId;

View File

@@ -1,35 +0,0 @@
import fr.acinq.bitcoin.ByteVector32;
import fr.acinq.bitcoin.TxId;
import fr.acinq.lightning.utils.UUID;
CREATE TABLE on_chain_txs (
payment_id BLOB AS UUID NOT NULL PRIMARY KEY,
tx_id BLOB AS TxId NOT NULL,
confirmed_at INTEGER,
locked_at INTEGER
);
CREATE INDEX on_chain_txs_tx_id ON on_chain_txs(tx_id);
insert:
INSERT INTO on_chain_txs(
payment_id,
tx_id,
confirmed_at,
locked_at)
VALUES (?, ?, ?, ?);
setConfirmed:
UPDATE on_chain_txs
SET confirmed_at=?
WHERE tx_id=?;
setLocked:
UPDATE on_chain_txs
SET locked_at=?
WHERE tx_id=?;
listUnconfirmed:
SELECT tx_id
FROM on_chain_txs
WHERE confirmed_at IS NULL;

View File

@@ -1,59 +0,0 @@
-- This view returns all outgoing payments, but only successful incoming payments.
CREATE VIEW payments
AS SELECT id, created_at, completed_at, succeeded_at, order_ts, data
FROM (
SELECT id, created_at, received_at AS completed_at, received_at AS succeeded_at, received_at AS order_ts, data
FROM payments_incoming
WHERE received_at IS NOT NULL -- we only consider completed incoming payments
UNION ALL
SELECT id, created_at, completed_at, succeeded_at, created_at AS order_ts, data
FROM payments_outgoing
);
get:
SELECT data
FROM payments
WHERE id=:id;
list:
SELECT p.data, pm.*
FROM payments AS p
LEFT OUTER JOIN payments_metadata AS pm ON pm.payment_id = p.id
ORDER BY order_ts DESC
LIMIT :limit OFFSET :offset;
listSuccessful:
SELECT p.data, pm.*
FROM payments AS p
LEFT OUTER JOIN payments_metadata AS pm ON pm.payment_id = p.id
WHERE
succeeded_at BETWEEN :succeeded_at_from AND :succeeded_at_to
ORDER BY order_ts
LIMIT :limit OFFSET :offset;
listInFlight:
SELECT p.data, pm.*
FROM payments AS p
LEFT OUTER JOIN payments_metadata AS pm ON pm.payment_id = p.id
WHERE
completed_at IS NULL
ORDER BY order_ts DESC
LIMIT :limit OFFSET :offset;
listRecent:
SELECT p.data, pm.*
FROM payments AS p
LEFT OUTER JOIN payments_metadata AS pm ON pm.payment_id = p.id
WHERE
order_ts >= :min_ts
ORDER BY order_ts DESC
LIMIT :limit OFFSET :offset;
getOldestCompletedAt:
SELECT min(completed_at) AS completed_at
FROM payments;
countCompletedInRange:
SELECT count(id)
FROM payments
WHERE completed_at BETWEEN :completed_at_from AND :completed_at_to;

View File

@@ -1,90 +0,0 @@
import fr.acinq.bitcoin.ByteVector32;
import fr.acinq.bitcoin.TxId;
import fr.acinq.lightning.db.IncomingPayment;
import fr.acinq.lightning.utils.UUID;
-- incoming payments
CREATE TABLE payments_incoming (
id BLOB AS UUID NOT NULL PRIMARY KEY,
payment_hash BLOB AS ByteVector32 UNIQUE,
tx_id BLOB AS TxId,
created_at INTEGER NOT NULL,
received_at INTEGER,
data BLOB AS IncomingPayment NOT NULL
);
CREATE INDEX payments_incoming_payment_hash_idx ON payments_incoming(payment_hash);
CREATE INDEX payments_incoming_tx_id_idx ON payments_incoming(tx_id);
-- Create indexes to optimize the queries in AggregatedQueries.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX payments_incoming_filter_idx
ON payments_incoming(received_at)
WHERE received_at IS NOT NULL;
-- queries
insert:
INSERT INTO payments_incoming (
id,
payment_hash,
tx_id,
created_at,
received_at,
data)
VALUES (?, ?, ?, ?, ?, ?);
update:
UPDATE payments_incoming
SET received_at=:receivedAt,
tx_id=:txId,
data=:data
WHERE id=:id;
get:
SELECT data
FROM payments_incoming
WHERE id=?;
getByPaymentHash:
SELECT data
FROM payments_incoming
WHERE payment_hash=?;
list:
SELECT payment.data
FROM payments_incoming AS payment
WHERE
payment.created_at BETWEEN :created_at_from AND :created_at_to
ORDER BY payment.created_at DESC
LIMIT :limit OFFSET :offset;
listSuccessful:
SELECT payment.data
FROM payments_incoming AS payment
WHERE
payment.received_at BETWEEN :received_at_from AND :received_at_to
ORDER BY payment.received_at DESC
LIMIT :limit OFFSET :offset;
listByTxId:
SELECT data
FROM payments_incoming
WHERE tx_id=?;
listSuccessfulIds:
SELECT id, received_at
FROM payments_incoming
WHERE received_at IS NOT NULL;
deleteById:
DELETE FROM payments_incoming
WHERE id = ?;
deleteByPaymentHash:
DELETE FROM payments_incoming
WHERE payment_hash = ?;
-- use this in a `transaction` block to know how many rows were changed after an UPDATE
changes:
SELECT changes();

View File

@@ -1,65 +0,0 @@
import fr.acinq.phoenix.db.payments.LnurlBase;
import fr.acinq.phoenix.db.payments.LnurlMetadata;
import fr.acinq.phoenix.db.payments.LnurlSuccessAction;
import fr.acinq.lightning.utils.UUID;
-- This table stores metadata corresponding to a payment.
-- * payment_id => uuid of an incoming or an outgoing payments
-- * lnurl_base => serialized lnurl object (e.g. LNUrl.Pay), excluding metadata content
-- * lnurl_metadata => serialized lnurl metadata (e.g. LNUrl.Pay.Metadata)
-- * lnurl_successAction => serialized LUD-09 (e.g. LNUrl.PayInvoice.SuccessAction.Message)
-- * user_description => user-customized short description
-- * user_notes => user-customized notes (can be much longer than description)
-- * modified_at => last time this DB entry was modified (i.e. within payments_metadata table)
-- * original_fiat => stores original fiat price (via conversion rate) at time of transaction
--
CREATE TABLE IF NOT EXISTS payments_metadata (
payment_id BLOB AS UUID NOT NULL PRIMARY KEY,
lnurl_base_type TEXT AS LnurlBase.TypeVersion,
lnurl_base_blob BLOB,
lnurl_description TEXT,
lnurl_metadata_type TEXT AS LnurlMetadata.TypeVersion,
lnurl_metadata_blob BLOB,
lnurl_successAction_type TEXT AS LnurlSuccessAction.TypeVersion,
lnurl_successAction_blob BLOB,
user_description TEXT,
user_notes TEXT,
modified_at INTEGER,
original_fiat_type TEXT,
original_fiat_rate REAL,
lightning_address TEXT
);
-- queries for payments_metadata table
hasMetadata:
SELECT COUNT(*) FROM payments_metadata
WHERE payment_id = ?;
addMetadata:
INSERT INTO payments_metadata (
payment_id,
lnurl_base_type, lnurl_base_blob,
lnurl_description,
lnurl_metadata_type, lnurl_metadata_blob,
lnurl_successAction_type, lnurl_successAction_blob,
user_description, user_notes,
modified_at,
original_fiat_type, original_fiat_rate,
lightning_address)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
updateUserInfo:
UPDATE payments_metadata
SET user_description = ?,
user_notes = ?,
modified_at = ?
WHERE payment_id = ?;
get:
SELECT * FROM payments_metadata
WHERE payment_id = ?;
-- use this in a `transaction` block to know how many rows were changed after an UPDATE
changes:
SELECT changes();

View File

@@ -1,99 +0,0 @@
import fr.acinq.bitcoin.ByteVector32;
import fr.acinq.bitcoin.TxId;
import fr.acinq.lightning.db.OutgoingPayment;
import fr.acinq.lightning.utils.UUID;
CREATE TABLE payments_outgoing (
id BLOB AS UUID NOT NULL PRIMARY KEY,
payment_hash BLOB AS ByteVector32,
tx_id BLOB AS TxId,
created_at INTEGER NOT NULL,
completed_at INTEGER, -- a completed outgoing payment can be a success or a failure
succeeded_at INTEGER, -- will only be set for a successful payment
data BLOB AS OutgoingPayment NOT NULL
);
CREATE INDEX payments_outgoing_payment_hash_idx ON payments_outgoing(payment_hash);
CREATE INDEX payments_outgoing_tx_id_idx ON payments_outgoing(tx_id);
-- Create indexes to optimize the queries in AggregatedQueries.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX payments_outgoing_filter_idx ON payments_outgoing(completed_at) WHERE completed_at IS NOT NULL;
CREATE TABLE link_lightning_outgoing_payment_parts (
part_id BLOB AS UUID NOT NULL PRIMARY KEY,
parent_id BLOB AS UUID NOT NULL
);
-- queries
insert:
INSERT INTO payments_outgoing (
id,
payment_hash,
tx_id,
created_at,
completed_at,
succeeded_at,
data)
VALUES (?, ?, ?, ?, ?, ?, ?);
insertPartLink:
INSERT INTO link_lightning_outgoing_payment_parts(
part_id,
parent_id)
VALUES (?, ?);
update:
UPDATE payments_outgoing
SET completed_at=?,
succeeded_at=?,
data=:data
WHERE id = :id;
get:
SELECT data
FROM payments_outgoing
WHERE id=?;
getParentId:
SELECT parent_id
FROM link_lightning_outgoing_payment_parts
WHERE part_id=?;
listByPaymentHash:
SELECT data
FROM payments_outgoing
WHERE payment_hash=?;
listByTxId:
SELECT data
FROM payments_outgoing
WHERE tx_id=?;
deleteById:
DELETE FROM payments_outgoing
WHERE id = ?;
deleteByPaymentHash:
DELETE FROM payments_outgoing
WHERE payment_hash = ?;
list:
SELECT data
FROM payments_outgoing
WHERE
created_at BETWEEN :created_at_from AND :created_at_to
LIMIT :limit OFFSET :offset;
listSuccessful:
SELECT data
FROM payments_outgoing
WHERE
succeeded_at BETWEEN :succeeded_at_from AND :succeeded_at_to
LIMIT :limit OFFSET :offset;
listSuccessfulIds:
SELECT id, completed_at
FROM payments_outgoing
WHERE completed_at IS NOT NULL;

View File

@@ -1,24 +0,0 @@
-- Migration: v1 -> v2
--
-- Changes:
-- * Added table cloudkit_payments_metadata
-- * Added table cloudkit_payments_queue
CREATE TABLE IF NOT EXISTS cloudkit_payments_metadata (
type INTEGER NOT NULL,
id TEXT NOT NULL,
unpadded_size INTEGER NOT NULL,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL,
PRIMARY KEY (type, id)
);
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_payments_metadata(record_creation);
CREATE TABLE IF NOT EXISTS cloudkit_payments_queue (
rowid INTEGER PRIMARY KEY,
type INTEGER NOT NULL,
id TEXT NOT NULL,
date_added INTEGER NOT NULL
);

View File

@@ -1,22 +0,0 @@
-- Migration: v10 -> v11
--
-- This is a code migration, see AfterVersion10.kt
-- incoming payments
CREATE TABLE payments_incoming (
id BLOB AS UUID NOT NULL PRIMARY KEY,
payment_hash BLOB AS ByteVector32 UNIQUE,
tx_id BLOB AS TxId,
created_at INTEGER NOT NULL,
received_at INTEGER,
data BLOB AS IncomingPayment NOT NULL
);
CREATE INDEX payments_incoming_payment_hash_idx ON payments_incoming(payment_hash);
CREATE INDEX payments_incoming_tx_id_idx ON payments_incoming(tx_id);
-- Create indexes to optimize the queries in AggregatedQueries.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX payments_incoming_filter_idx
ON payments_incoming(received_at)
WHERE received_at IS NOT NULL;

View File

@@ -1,92 +0,0 @@
-- Migration: v11 -> v12
--
-- Changes:
-- * add table payments_outgoing, which stores all types of outgoing payments.
-- * add table link_lightning_outgoing_payment_parts, which links lightning parts with the parent.
--
-- /!\ There is also a code migration, see AfterVersion11.kt.
-- This code migration moves existing data to the new tables and drops the old tables.
CREATE TABLE payments_outgoing (
id BLOB AS UUID NOT NULL PRIMARY KEY,
payment_hash BLOB AS ByteVector32,
tx_id BLOB AS TxId,
created_at INTEGER NOT NULL,
completed_at INTEGER, -- a completed outgoing payment can be a success or a failure
succeeded_at INTEGER, -- will only be set for a successful payment
data BLOB AS OutgoingPayment NOT NULL
);
CREATE INDEX payments_outgoing_payment_hash_idx ON payments_outgoing(payment_hash);
CREATE INDEX payments_outgoing_tx_id_idx ON payments_outgoing(tx_id);
-- Create indexes to optimize the queries in AggregatedQueries.
-- Tip: Use "explain query plan" to ensure they're actually being used.
CREATE INDEX payments_outgoing_filter_idx ON payments_outgoing(completed_at) WHERE completed_at IS NOT NULL;
CREATE TABLE link_lightning_outgoing_payment_parts (
part_id BLOB AS UUID NOT NULL PRIMARY KEY,
parent_id BLOB AS UUID NOT NULL
);
-- This table will be read then deleted in the code migration
ALTER TABLE payments_metadata RENAME TO payments_metadata_old;
CREATE TABLE IF NOT EXISTS payments_metadata (
payment_id BLOB AS UUID NOT NULL PRIMARY KEY,
lnurl_base_type TEXT AS LnurlBase.TypeVersion,
lnurl_base_blob BLOB,
lnurl_description TEXT,
lnurl_metadata_type TEXT AS LnurlMetadata.TypeVersion,
lnurl_metadata_blob BLOB,
lnurl_successAction_type TEXT AS LnurlSuccessAction.TypeVersion,
lnurl_successAction_blob BLOB,
user_description TEXT,
user_notes TEXT,
modified_at INTEGER,
original_fiat_type TEXT,
original_fiat_rate REAL
);
CREATE TABLE on_chain_txs (
payment_id BLOB AS UUID NOT NULL PRIMARY KEY,
tx_id BLOB AS TxId NOT NULL,
confirmed_at INTEGER,
locked_at INTEGER
);
CREATE INDEX on_chain_txs_tx_id ON on_chain_txs(tx_id);
CREATE VIEW payments
AS SELECT id, created_at, completed_at, succeeded_at, order_ts, data
FROM (
SELECT id, created_at, received_at AS completed_at, received_at AS succeeded_at, received_at AS order_ts, data
FROM payments_incoming
WHERE received_at IS NOT NULL -- we only consider completed incoming payments
UNION ALL
SELECT id, created_at, completed_at, succeeded_at, created_at AS order_ts, data
FROM payments_outgoing
);
-- This table will be read then deleted in the code migration
ALTER TABLE cloudkit_payments_metadata RENAME TO cloudkit_payments_metadata_old;
DROP INDEX record_creation_idx;
CREATE TABLE IF NOT EXISTS cloudkit_payments_metadata (
id BLOB AS UUID NOT NULL PRIMARY KEY,
unpadded_size INTEGER NOT NULL,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL
);
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_payments_metadata(record_creation);
-- This table will be read then deleted in the code migration
ALTER TABLE cloudkit_payments_queue RENAME TO cloudkit_payments_queue_old;
CREATE TABLE IF NOT EXISTS cloudkit_payments_queue (
rowid INTEGER PRIMARY KEY,
id BLOB AS UUID NOT NULL,
date_added INTEGER NOT NULL
);

View File

@@ -1,37 +0,0 @@
-- Migration: v12 -> v13
--
-- Changes:
-- * Added a new column [lightning_address] in table [payments_metadata]
-- * Migration of contacts from appDb to paymentsDb:
-- * Added new table: contacts
-- * Added new table: cloudkit_contacts_metadata
-- * Added new table: cloudkit_contacts_queue
-- * Added new index: record_creation_idx
import fr.acinq.lightning.utils.UUID;
import fr.acinq.phoenix.data.ContactInfo;
ALTER TABLE payments_metadata
ADD COLUMN lightning_address TEXT;
CREATE TABLE IF NOT EXISTS contacts (
id BLOB AS UUID NOT NULL PRIMARY KEY,
data BLOB AS ContactInfo NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS cloudkit_contacts_metadata (
id TEXT NOT NULL PRIMARY KEY,
record_creation INTEGER NOT NULL,
record_blob BLOB NOT NULL
);
CREATE INDEX IF NOT EXISTS record_creation_idx
ON cloudkit_contacts_metadata(record_creation);
CREATE TABLE IF NOT EXISTS cloudkit_contacts_queue (
rowid INTEGER PRIMARY KEY,
id TEXT NOT NULL,
date_added INTEGER NOT NULL
);

View File

@@ -1,22 +0,0 @@
-- Migration: v2 -> v3
--
-- Changes:
-- * Added table payments_metadata
import fr.acinq.phoenix.db.payments.LnurlBase;
import fr.acinq.phoenix.db.payments.LnurlMetadata;
import fr.acinq.phoenix.db.payments.LnurlSuccessAction;
CREATE TABLE IF NOT EXISTS payments_metadata (
type INTEGER NOT NULL,
id TEXT NOT NULL,
lnurl_base_type TEXT AS LnurlBase.TypeVersion,
lnurl_base_blob BLOB,
lnurl_description TEXT,
lnurl_metadata_type TEXT AS LnurlMetadata.TypeVersion,
lnurl_metadata_blob BLOB,
lnurl_successAction_type TEXT AS LnurlSuccessAction.TypeVersion,
lnurl_successAction_blob BLOB,
user_description TEXT,
PRIMARY KEY (type, id)
);

View File

@@ -1,12 +0,0 @@
-- Migration: v3 -> v4
--
-- Changes:
-- * Added column: payments_metadata.user_notes
-- * Added column: payments_metadata.modified_at
-- * Added indexes to improve performance of listAllPaymentsOrder query
ALTER TABLE payments_metadata ADD COLUMN user_notes TEXT DEFAULT NULL;
ALTER TABLE payments_metadata ADD COLUMN modified_at INTEGER DEFAULT NULL;
CREATE INDEX IF NOT EXISTS incoming_payments_created_at_idx ON incoming_payments(created_at);
CREATE INDEX IF NOT EXISTS outgoing_payments_created_at_idx ON outgoing_payments(created_at);

View File

@@ -1,8 +0,0 @@
-- Migration: v4 -> v5
--
-- Changes:
-- * Added column: payments_metadata.original_fiat_type
-- * Added column: payments_metadata.original_fiat_rate
ALTER TABLE payments_metadata ADD COLUMN original_fiat_type TEXT DEFAULT NULL;
ALTER TABLE payments_metadata ADD COLUMN original_fiat_rate REAL DEFAULT NULL;

View File

@@ -1,22 +0,0 @@
import fr.acinq.phoenix.db.payments.OutgoingPartClosingInfoTypeVersion;
-- Migration: v5 -> v6
--
-- Changes:
-- * Add a new table that stores the transactions closing a lightning channel, represented
-- as parts of an outgoing payment.
-- * Add an index on parent_id in the closing txs table.
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)
);
CREATE INDEX IF NOT EXISTS parent_id_idx ON outgoing_payment_closing_tx_parts(part_parent_id);

View File

@@ -1,17 +0,0 @@
-- Migration: v6 -> v7
--
-- Changes:
-- * Removed index: received_amount_msat_idx
-- * Replaced index: incoming_payments_created_at_idx -> incoming_payments_filter_idx
-- * Replaced index: outgoing_payments_created_at_idx -> outgoing_payments_filter_idx
DROP INDEX IF EXISTS received_amount_msat_idx;
DROP INDEX IF EXISTS incoming_payments_created_at_idx;
CREATE INDEX IF NOT EXISTS incoming_payments_filter_idx
ON incoming_payments(received_at)
WHERE received_at IS NOT NULL;
DROP INDEX IF EXISTS outgoing_payments_created_at_idx;
CREATE INDEX IF NOT EXISTS outgoing_payments_filter_idx
ON outgoing_payments(completed_at);

View File

@@ -1,57 +0,0 @@
import fr.acinq.phoenix.db.payments.OutgoingPartClosingInfoTypeVersion;
-- Migration: v7 -> v8
--
-- Changes:
-- * add a new splice_outgoing_payments table to store outgoing splices
-- * add a new channel_close_outgoing_payments table to store channel closings
-- * add a new link_tx_to_payments table that links payments to on-chain transactions
-- * add a new expected_amount_msat column in the incoming_payments table
CREATE TABLE IF NOT EXISTS splice_outgoing_payments (
id TEXT NOT NULL PRIMARY KEY,
recipient_amount_sat INTEGER NOT NULL,
address TEXT NOT NULL,
mining_fees_sat INTEGER NOT NULL,
tx_id BLOB NOT NULL,
channel_id BLOB NOT NULL,
created_at INTEGER NOT NULL,
confirmed_at INTEGER DEFAULT NULL,
locked_at INTEGER DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS channel_close_outgoing_payments (
id TEXT NOT NULL PRIMARY KEY,
recipient_amount_sat INTEGER NOT NULL,
address TEXT NOT NULL,
is_default_address INTEGER NOT NULL,
mining_fees_sat INTEGER NOT NULL,
tx_id BLOB NOT NULL,
created_at INTEGER NOT NULL,
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_blob BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS link_tx_to_payments (
tx_id BLOB NOT NULL,
type INTEGER NOT NULL,
id TEXT NOT NULL,
confirmed_at INTEGER DEFAULT NULL,
locked_at INTEGER DEFAULT NULL,
PRIMARY KEY (tx_id, type, id)
);
CREATE TABLE IF NOT EXISTS splice_cpfp_outgoing_payments (
id TEXT NOT NULL PRIMARY KEY,
mining_fees_sat INTEGER NOT NULL,
channel_id BLOB NOT NULL,
tx_id BLOB NOT NULL,
created_at INTEGER NOT NULL,
confirmed_at INTEGER DEFAULT NULL,
locked_at INTEGER DEFAULT NULL
);
CREATE INDEX IF NOT EXISTS link_tx_to_payments_txid ON link_tx_to_payments(tx_id);

View File

@@ -1,18 +0,0 @@
import fr.acinq.phoenix.db.payments.InboundLiquidityLeaseTypeVersion;
-- Migration: v8 -> v9
--
-- Changes:
-- * add a new inbound_liquidity_outgoing_payments table to store inbound liquidity payments
CREATE TABLE IF NOT EXISTS inbound_liquidity_outgoing_payments (
id TEXT NOT NULL PRIMARY KEY,
mining_fees_sat INTEGER NOT NULL,
channel_id BLOB NOT NULL,
tx_id BLOB NOT NULL,
lease_type TEXT AS InboundLiquidityLeaseTypeVersion NOT NULL,
lease_blob BLOB NOT NULL,
created_at INTEGER NOT NULL,
confirmed_at INTEGER DEFAULT NULL,
locked_at INTEGER DEFAULT NULL
);

View File

@@ -1,8 +0,0 @@
import fr.acinq.phoenix.db.payments.InboundLiquidityLeaseTypeVersion;
-- Migration: v9 -> v10
--
-- Changes:
-- * Added a new column [payment_details_type] in table [inbound_liquidity_outgoing_payments]
ALTER TABLE inbound_liquidity_outgoing_payments ADD COLUMN payment_details_type TEXT DEFAULT NULL;