Skip to content

Commit 019f89f

Browse files
authored
Merge pull request #270 from synonymdev/update-bitkit-ldk-node-versions
Update bitkit-core v0.1.33 and ldk-node v0.7.0-rc.1
2 parents 2e51400 + 531ab1d commit 019f89f

File tree

10 files changed

+225
-91
lines changed

10 files changed

+225
-91
lines changed

Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Bitkit/Services/CoreService.swift

Lines changed: 136 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,99 @@ class ActivityService {
6161
}
6262
}
6363

64+
private func mapToCoreTransactionDetails(txid: String, _ details: LDKNode.TransactionDetails) -> BitkitCore.TransactionDetails {
65+
let inputs = details.inputs.map { input in
66+
BitkitCore.TxInput(
67+
txid: input.txid,
68+
vout: input.vout,
69+
scriptsig: input.scriptsig,
70+
witness: input.witness,
71+
sequence: input.sequence
72+
)
73+
}
74+
75+
let outputs = details.outputs.map { output in
76+
BitkitCore.TxOutput(
77+
scriptpubkey: output.scriptpubkey,
78+
scriptpubkeyType: output.scriptpubkeyType,
79+
scriptpubkeyAddress: output.scriptpubkeyAddress,
80+
value: output.value,
81+
n: output.n
82+
)
83+
}
84+
85+
return BitkitCore.TransactionDetails(
86+
txId: txid,
87+
amountSats: details.amountSats,
88+
inputs: inputs,
89+
outputs: outputs
90+
)
91+
}
92+
93+
private func fetchTransactionDetails(txid: String) async -> BitkitCore.TransactionDetails? {
94+
do {
95+
return try await getTransactionDetails(txid: txid)
96+
} catch {
97+
Logger.warn("Failed to fetch stored transaction details for \(txid): \(error)", context: "ActivityService")
98+
return nil
99+
}
100+
}
101+
102+
func getTransactionDetails(txid: String) async throws -> BitkitCore.TransactionDetails? {
103+
try await ServiceQueue.background(.core) {
104+
try BitkitCore.getTransactionDetails(txId: txid)
105+
}
106+
}
107+
108+
// MARK: - Seen Tracking
109+
110+
func isActivitySeen(id: String) async -> Bool {
111+
do {
112+
if let activity = try await getActivityById(activityId: id) {
113+
switch activity {
114+
case let .onchain(onchain):
115+
return onchain.seenAt != nil
116+
case let .lightning(lightning):
117+
return lightning.seenAt != nil
118+
}
119+
}
120+
} catch {
121+
Logger.error("Failed to check seen status for activity \(id): \(error)", context: "ActivityService")
122+
}
123+
return false
124+
}
125+
126+
func isOnchainActivitySeen(txid: String) async -> Bool {
127+
if let activity = try? await getOnchainActivityByTxId(txid: txid) {
128+
return activity.seenAt != nil
129+
}
130+
return false
131+
}
132+
133+
func markActivityAsSeen(id: String, seenAt: UInt64? = nil) async {
134+
let timestamp = seenAt ?? UInt64(Date().timeIntervalSince1970)
135+
136+
do {
137+
try await ServiceQueue.background(.core) {
138+
try BitkitCore.markActivityAsSeen(activityId: id, seenAt: timestamp)
139+
self.activitiesChangedSubject.send()
140+
}
141+
} catch {
142+
Logger.error("Failed to mark activity \(id) as seen: \(error)", context: "ActivityService")
143+
}
144+
}
145+
146+
func markOnchainActivityAsSeen(txid: String, seenAt: UInt64? = nil) async {
147+
do {
148+
guard let activity = try await getOnchainActivityByTxId(txid: txid) else {
149+
return
150+
}
151+
await markActivityAsSeen(id: activity.id, seenAt: seenAt)
152+
} catch {
153+
Logger.error("Failed to mark onchain activity for \(txid) as seen: \(error)", context: "ActivityService")
154+
}
155+
}
156+
64157
// MARK: - Transaction Status Checks
65158

66159
func wasTransactionReplaced(txid: String) async -> Bool {
@@ -84,17 +177,15 @@ class ActivityService {
84177
return false
85178
}
86179

87-
do {
88-
// Check if this transaction's activity has boostTxIds (meaning it replaced other transactions)
89-
// If any of the replaced transactions have the same value, don't show the sheet
90-
guard let onchain = try? await getOnchainActivityByTxId(txid: txid),
91-
!onchain.boostTxIds.isEmpty
92-
else {
93-
return true
94-
}
180+
let onchainActivity = try? await getOnchainActivityByTxId(txid: txid)
181+
182+
if let onchainActivity, onchainActivity.seenAt != nil {
183+
return false
184+
}
95185

96-
// This transaction replaced others - check if any have the same value
97-
for replacedTxid in onchain.boostTxIds {
186+
// If this is a replacement transaction with same value as original, skip the sheet
187+
if let boostTxIds = onchainActivity?.boostTxIds, !boostTxIds.isEmpty {
188+
for replacedTxid in boostTxIds {
98189
if let replaced = try? await getOnchainActivityByTxId(txid: replacedTxid),
99190
replaced.value == value
100191
{
@@ -105,8 +196,6 @@ class ActivityService {
105196
return false
106197
}
107198
}
108-
} catch {
109-
Logger.error("Failed to check existing activities for replacement: \(error)", context: "CoreService.shouldShowReceivedSheet")
110199
}
111200

112201
return true
@@ -213,7 +302,7 @@ class ActivityService {
213302

214303
private func processOnchainPayment(
215304
_ payment: PaymentDetails,
216-
transactionDetails: TransactionDetails? = nil
305+
transactionDetails: BitkitCore.TransactionDetails? = nil
217306
) async throws {
218307
guard case let .onchain(txid, _) = payment.kind else { return }
219308

@@ -258,6 +347,7 @@ class ActivityService {
258347
let feeRate = existingOnchain?.feeRate ?? 1
259348
let preservedAddress = existingOnchain?.address ?? "Loading..."
260349
let doesExist = existingOnchain?.doesExist ?? true
350+
let seenAt = existingOnchain?.seenAt
261351

262352
// Check if this transaction is a channel transfer
263353
if channelId == nil || !isTransfer {
@@ -309,7 +399,8 @@ class ActivityService {
309399
channelId: channelId,
310400
transferTxId: transferTxId,
311401
createdAt: UInt64(payment.creationTime.timeIntervalSince1970),
312-
updatedAt: paymentTimestamp
402+
updatedAt: paymentTimestamp,
403+
seenAt: seenAt
313404
)
314405

315406
if existingActivity != nil {
@@ -321,7 +412,7 @@ class ActivityService {
321412

322413
// MARK: - Onchain Event Handlers
323414

324-
private func processOnchainTransaction(txid: String, details: TransactionDetails, context: String) async throws {
415+
private func processOnchainTransaction(txid: String, details: BitkitCore.TransactionDetails, context: String) async throws {
325416
guard let payments = LightningService.shared.payments else {
326417
Logger.warn("No payments available for transaction \(txid)", context: context)
327418
return
@@ -340,15 +431,21 @@ class ActivityService {
340431
try await processOnchainPayment(payment, transactionDetails: details)
341432
}
342433

343-
func handleOnchainTransactionReceived(txid: String, details: TransactionDetails) async throws {
434+
func handleOnchainTransactionReceived(txid: String, details: LDKNode.TransactionDetails) async throws {
435+
let coreDetails = mapToCoreTransactionDetails(txid: txid, details)
436+
344437
try await ServiceQueue.background(.core) {
345-
try await self.processOnchainTransaction(txid: txid, details: details, context: "CoreService.handleOnchainTransactionReceived")
438+
try BitkitCore.upsertTransactionDetails(detailsList: [coreDetails])
439+
try await self.processOnchainTransaction(txid: txid, details: coreDetails, context: "CoreService.handleOnchainTransactionReceived")
346440
}
347441
}
348442

349-
func handleOnchainTransactionConfirmed(txid: String, details: TransactionDetails) async throws {
443+
func handleOnchainTransactionConfirmed(txid: String, details: LDKNode.TransactionDetails) async throws {
444+
let coreDetails = mapToCoreTransactionDetails(txid: txid, details)
445+
350446
try await ServiceQueue.background(.core) {
351-
try await self.processOnchainTransaction(txid: txid, details: details, context: "CoreService.handleOnchainTransactionConfirmed")
447+
try BitkitCore.upsertTransactionDetails(detailsList: [coreDetails])
448+
try await self.processOnchainTransaction(txid: txid, details: coreDetails, context: "CoreService.handleOnchainTransactionConfirmed")
352449
}
353450
}
354451

@@ -497,13 +594,11 @@ class ActivityService {
497594

498595
let paymentTimestamp = UInt64(payment.latestUpdateTimestamp)
499596
let existingActivity = try getActivityById(activityId: payment.id)
597+
let existingLightning: LightningActivity? = if let existingActivity, case let .lightning(ln) = existingActivity { ln } else { nil }
500598

501599
// Skip if existing activity has newer timestamp to avoid overwriting local data
502-
if let existingActivity, case let .lightning(existing) = existingActivity {
503-
let existingUpdatedAt = existing.updatedAt ?? 0
504-
if existingUpdatedAt > paymentTimestamp {
505-
return
506-
}
600+
if let existingUpdatedAt = existingLightning?.updatedAt, existingUpdatedAt > paymentTimestamp {
601+
return
507602
}
508603

509604
let state: BitkitCore.PaymentState = switch payment.status {
@@ -523,7 +618,8 @@ class ActivityService {
523618
timestamp: paymentTimestamp,
524619
preimage: preimage,
525620
createdAt: paymentTimestamp,
526-
updatedAt: paymentTimestamp
621+
updatedAt: paymentTimestamp,
622+
seenAt: existingLightning?.seenAt
527623
)
528624

529625
if existingActivity != nil {
@@ -598,7 +694,8 @@ class ActivityService {
598694

599695
/// Marks replacement transactions (with originalTxId in boostTxIds) as doesExist = false when original confirms
600696
/// Finds the channel ID associated with a transaction based on its direction
601-
private func findChannelForTransaction(txid: String, direction: PaymentDirection, transactionDetails: TransactionDetails? = nil) async -> String?
697+
private func findChannelForTransaction(txid: String, direction: PaymentDirection,
698+
transactionDetails: BitkitCore.TransactionDetails? = nil) async -> String?
602699
{
603700
switch direction {
604701
case .inbound:
@@ -611,13 +708,13 @@ class ActivityService {
611708
}
612709

613710
/// Check if a transaction spends a closed channel's funding UTXO
614-
private func findClosedChannelForTransaction(txid: String, transactionDetails: TransactionDetails? = nil) async -> String? {
711+
private func findClosedChannelForTransaction(txid: String, transactionDetails: BitkitCore.TransactionDetails? = nil) async -> String? {
615712
do {
616713
let closedChannels = try await getAllClosedChannels(sortDirection: .desc)
617714
guard !closedChannels.isEmpty else { return nil }
618715

619-
// Use provided transaction details if available, otherwise try node
620-
guard let details = transactionDetails ?? LightningService.shared.getTransactionDetails(txid: txid) else {
716+
let details = if let provided = transactionDetails { provided } else { await fetchTransactionDetails(txid: txid) }
717+
guard let details else {
621718
Logger.warn("Transaction details not available for \(txid)", context: "CoreService.findClosedChannelForTransaction")
622719
return nil
623720
}
@@ -686,7 +783,7 @@ class ActivityService {
686783
}
687784

688785
/// Check pre-activity metadata for addresses in the transaction
689-
private func findAddressInPreActivityMetadata(details: TransactionDetails, value: UInt64) async -> String? {
786+
private func findAddressInPreActivityMetadata(details: BitkitCore.TransactionDetails, value: UInt64) async -> String? {
690787
for output in details.outputs {
691788
guard let address = output.scriptpubkeyAddress else { continue }
692789
if let metadata = try? await getPreActivityMetadata(searchKey: address, searchByAddress: true),
@@ -700,9 +797,11 @@ class ActivityService {
700797
}
701798

702799
/// Find the receiving address for an onchain transaction
703-
private func findReceivingAddress(for txid: String, value: UInt64, transactionDetails: TransactionDetails? = nil) async throws -> String? {
704-
// Use provided transaction details if available, otherwise try node
705-
guard let details = transactionDetails ?? LightningService.shared.getTransactionDetails(txid: txid) else {
800+
private func findReceivingAddress(for txid: String, value: UInt64,
801+
transactionDetails: BitkitCore.TransactionDetails? = nil) async throws -> String?
802+
{
803+
let details = if let provided = transactionDetails { provided } else { await fetchTransactionDetails(txid: txid) }
804+
guard let details else {
706805
Logger.warn("Transaction details not available for \(txid)", context: "CoreService.findReceivingAddress")
707806
return nil
708807
}
@@ -1048,7 +1147,8 @@ class ActivityService {
10481147
timestamp: timestamp,
10491148
preimage: template.status == .succeeded ? "preimage\(activityId)" : nil,
10501149
createdAt: timestamp,
1051-
updatedAt: timestamp
1150+
updatedAt: timestamp,
1151+
seenAt: nil
10521152
)
10531153
)
10541154
case .onchain:
@@ -1071,7 +1171,8 @@ class ActivityService {
10711171
channelId: nil,
10721172
transferTxId: nil,
10731173
createdAt: timestamp,
1074-
updatedAt: timestamp
1174+
updatedAt: timestamp,
1175+
seenAt: nil
10751176
)
10761177
)
10771178
}

Bitkit/Services/LightningService.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,19 @@ class LightningService {
7878
Logger.debug("Building ldk-node with vssUrl: '\(vssUrl)'")
7979
Logger.debug("Building ldk-node with lnurlAuthServerUrl: '\(lnurlAuthServerUrl)'")
8080

81-
// Create NodeEntropy from mnemonic
82-
let nodeEntropy = NodeEntropy.fromBip39Mnemonic(mnemonic: mnemonic, passphrase: passphrase)
81+
// Set entropy from mnemonic on builder
82+
builder.setEntropyBip39Mnemonic(mnemonic: mnemonic, passphrase: passphrase)
8383

8484
try await ServiceQueue.background(.ldk) {
8585
if !lnurlAuthServerUrl.isEmpty {
8686
self.node = try builder.buildWithVssStore(
87-
nodeEntropy: nodeEntropy,
8887
vssUrl: vssUrl,
8988
storeId: storeId,
9089
lnurlAuthServerUrl: lnurlAuthServerUrl,
9190
fixedHeaders: [:]
9291
)
9392
} else {
9493
self.node = try builder.buildWithVssStoreAndFixedHeaders(
95-
nodeEntropy: nodeEntropy,
9694
vssUrl: vssUrl,
9795
storeId: storeId,
9896
fixedHeaders: [:]
@@ -623,12 +621,6 @@ extension LightningService {
623621
var channels: [ChannelDetails]? { node?.listChannels() }
624622
var payments: [PaymentDetails]? { node?.listPayments() }
625623

626-
/// Get transaction details from the node for a given transaction ID
627-
/// Returns nil if the transaction is not found in the wallet
628-
func getTransactionDetails(txid: String) -> TransactionDetails? {
629-
return node?.getTransactionDetails(txid: txid)
630-
}
631-
632624
/// Get balance for a specific address in satoshis
633625
/// - Parameter address: The Bitcoin address to check
634626
/// - Returns: The current balance in satoshis

0 commit comments

Comments
 (0)