Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit 8aa4925

Browse files
committed
feat: ios pre-populated scorer download
1 parent 5ae3b9a commit 8aa4925

5 files changed

Lines changed: 138 additions & 9 deletions

File tree

lib/ios/Ldk.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
1313
RCT_EXTERN_METHOD(writeToLogFile:(NSString *)line
1414
resolve:(RCTPromiseResolveBlock)resolve
1515
reject:(RCTPromiseRejectBlock)reject)
16-
1716
RCT_EXTERN_METHOD(initChainMonitor:(RCTPromiseResolveBlock)resolve
1817
reject:(RCTPromiseRejectBlock)reject)
1918
RCT_EXTERN_METHOD(initKeysManager:(NSString *)seed
@@ -22,8 +21,13 @@ @interface RCT_EXTERN_MODULE(Ldk, NSObject)
2221
RCT_EXTERN_METHOD(initUserConfig:(NSDictionary *)userConfig
2322
resolve:(RCTPromiseResolveBlock)resolve
2423
reject:(RCTPromiseRejectBlock)reject)
24+
RCT_EXTERN_METHOD(downloadScorer:(NSString *)scorerSyncUrl
25+
skipHoursThreshold:(NSInteger *)skipHoursThreshold
26+
resolve:(RCTPromiseResolveBlock)resolve
27+
reject:(RCTPromiseRejectBlock)reject)
2528
RCT_EXTERN_METHOD(initNetworkGraph:(NSString *)network
2629
rapidGossipSyncUrl:(NSString *)rapidGossipSyncUrl
30+
skipHoursThreshold:(NSInteger *)skipHoursThreshold
2731
resolve:(RCTPromiseResolveBlock)resolve
2832
reject:(RCTPromiseRejectBlock)reject)
2933
RCT_EXTERN_METHOD(initChannelManager:(NSString *)network

lib/ios/Ldk.swift

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ enum LdkErrors: String {
7373
case backup_restore_failed = "backup_restore_failed"
7474
case backup_restore_failed_existing_files = "backup_restore_failed_existing_files"
7575
case backup_list_files_failed = "backup_list_files_failed"
76+
case scorer_download_fail = "scorer_download_fail"
7677
}
7778

7879
enum LdkCallbackResponses: String {
@@ -101,6 +102,8 @@ enum LdkCallbackResponses: String {
101102
case backup_client_setup_success = "backup_client_setup_success"
102103
case backup_restore_success = "backup_restore_success"
103104
case backup_client_check_success = "backup_client_check_success"
105+
case scorer_download_success = "scorer_download_success"
106+
case scorer_download_skip = "scorer_download_skip"
104107
}
105108

106109
enum LdkFileNames: String {
@@ -228,7 +231,43 @@ class Ldk: NSObject {
228231
}
229232

230233
@objc
231-
func initNetworkGraph(_ network: NSString, rapidGossipSyncUrl: NSString, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
234+
func downloadScorer(_ scorerSyncUrl: NSString, skipHoursThreshold: NSInteger, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
235+
guard let accountStoragePath = Ldk.accountStoragePath else {
236+
return handleReject(reject, .init_storage_path)
237+
}
238+
239+
let destinationFile = accountStoragePath.appendingPathComponent(LdkFileNames.scorer.rawValue)
240+
241+
//If old one is still recent, skip download. Else delete it.
242+
if FileManager().fileExists(atPath: destinationFile.path) {
243+
let fileAttributes = try? FileManager().attributesOfItem(atPath: destinationFile.path)
244+
if let creationDate = fileAttributes?[.creationDate] as? Date {
245+
let currentTime = Date()
246+
let timeInterval = currentTime.timeIntervalSince(creationDate)
247+
let hoursPassed = timeInterval / 3600
248+
249+
if hoursPassed <= Double(skipHoursThreshold) {
250+
return handleResolve(resolve, .scorer_download_skip)
251+
}
252+
}
253+
254+
try? FileManager().removeItem(atPath: destinationFile.path)
255+
}
256+
257+
let url = URL(string: String(scorerSyncUrl))!
258+
let task = url.downloadTask(destination: destinationFile) { error in
259+
if let error = error {
260+
return handleReject(reject, .scorer_download_fail, error)
261+
}
262+
263+
handleResolve(resolve, .scorer_download_success)
264+
}
265+
266+
task?.resume()
267+
}
268+
269+
@objc
270+
func initNetworkGraph(_ network: NSString, rapidGossipSyncUrl: NSString, skipHoursThreshold: NSInteger, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
232271
guard networkGraph == nil else {
233272
return handleReject(reject, .already_init)
234273
}
@@ -260,6 +299,7 @@ class Ldk: NSObject {
260299
}
261300

262301
print("rapidGossipSyncUrl: \(rapidGossipSyncUrl)")
302+
print("accountStoragePath: \(accountStoragePath)")
263303

264304
//Download url passed, enable rapid gossip sync
265305
do {
@@ -274,7 +314,7 @@ class Ldk: NSObject {
274314
let timestamp = networkGraph?.getLastRapidGossipSyncTimestamp() ?? 0
275315
let minutesDiffSinceLastRGS = (Calendar.current.dateComponents([.minute], from: Date.init(timeIntervalSince1970: TimeInterval(timestamp)), to: Date()).minute)!
276316

277-
guard minutesDiffSinceLastRGS > 60 else {
317+
guard minutesDiffSinceLastRGS > 60 * skipHoursThreshold else {
278318
LdkEventEmitter.shared.send(withEvent: .native_log, body: "Skipping rapid gossip sync. Last updated \(minutesDiffSinceLastRGS/60) hours ago.")
279319
return handleResolve(resolve, .network_graph_init_success)
280320
}
@@ -442,6 +482,11 @@ class Ldk: NSObject {
442482
currentBlockchainTipHash = blockHash
443483
currentBlockchainHeight = blockHeight
444484
addForegroundObserver()
485+
486+
print("Scorer:")
487+
// Task {
488+
// probabilisticScorer.debugLogLiquidityStats()
489+
// }
445490

446491
return handleResolve(resolve, .channel_manager_init_success)
447492
}

lib/src/ldk.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
TBackupServerDetails,
3636
TNodeSignReq,
3737
TBackedUpFileList,
38+
TDownloadScorer,
3839
} from './utils/types';
3940
import { extractPaymentRequest } from './utils/helpers';
4041

@@ -97,23 +98,50 @@ class LDK {
9798
}
9899
}
99100

101+
/**
102+
* Downloads a pre-populated scorer from remote server to be used on startup.
103+
* Should be called before channel manager is initialized.
104+
* @param scorerDownloadUrl
105+
* @param skipHoursThreshold
106+
*/
107+
async downloadScorer({
108+
scorerDownloadUrl,
109+
skipHoursThreshold,
110+
}: TDownloadScorer): Promise<Result<string>> {
111+
try {
112+
const res = await NativeLDK.downloadScorer(
113+
scorerDownloadUrl,
114+
skipHoursThreshold ?? 3,
115+
);
116+
this.writeDebugToLog('downloadScorer');
117+
return ok(res);
118+
} catch (e) {
119+
this.writeErrorToLog('downloadScorer', e);
120+
return err(e);
121+
}
122+
}
123+
100124
/**
101125
* Inits the network graph from previous cache or syncs from scratch.
102126
* By passing in rapidGossipSyncUrl p2p gossip sync will be disabled in favor out rapid gossip sync.
103127
* For local regtest p2p works fine but for mainnet it is better to enable rapid gossip sync.
128+
* Use skipHoursThreshold to avoid unnecessary http calls that check if there is an update.
104129
* https://docs.rs/lightning/latest/lightning/routing/network_graph/struct.NetworkGraph.html
105130
* @param network
106131
* @param rapidGossipSyncUrl
132+
* @param skipHoursThreshold
107133
* @returns {Promise<Err<unknown> | Ok<Ok<string> | Err<string>>>}
108134
*/
109135
async initNetworkGraph({
110136
network,
111137
rapidGossipSyncUrl,
138+
skipHoursThreshold,
112139
}: TInitNetworkGraphReq): Promise<Result<string>> {
113140
try {
114141
const res = await NativeLDK.initNetworkGraph(
115142
network,
116143
rapidGossipSyncUrl ?? '',
144+
skipHoursThreshold ?? 3, // default to 3 hours as that is the current RGS schedule.
117145
);
118146
this.writeDebugToLog(
119147
'initNetworkGraph',

lib/src/lightning-manager.ts

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ class LightningManager {
290290
broadcastTransaction,
291291
network,
292292
rapidGossipSyncUrl = 'https://rapidsync.lightningdevkit.org/snapshot/',
293+
scorerDownloadUrl = 'https://rapidsync.lightningdevkit.org/scoring/scorer.bin',
293294
forceCloseOnStartup,
294295
userConfig = defaultUserConfig,
295296
trustedZeroConfPeers = [],
@@ -450,15 +451,59 @@ class LightningManager {
450451
// Handled in initChannelManager below
451452

452453
if (network !== ENetworks.mainnet) {
453-
//RGS only currently working for mainnet
454+
//RGS and pre-populated scorer only available for mainnet
454455
rapidGossipSyncUrl = '';
456+
scorerDownloadUrl = '';
455457
}
456458

457-
// Step 11: Optional: Initialize the NetGraphMsgHandler
458-
const networkGraphRes = await ldk.initNetworkGraph({
459-
network,
460-
rapidGossipSyncUrl,
461-
});
459+
let promises: Promise<Result<string>>[] = [];
460+
461+
if (scorerDownloadUrl) {
462+
promises.push(
463+
ldk.downloadScorer({
464+
scorerDownloadUrl,
465+
skipHoursThreshold: 3,
466+
}),
467+
);
468+
} else {
469+
promises.push(Promise.resolve(ok('')));
470+
}
471+
472+
promises.push(
473+
ldk.initNetworkGraph({
474+
network,
475+
rapidGossipSyncUrl,
476+
skipHoursThreshold: 3,
477+
}),
478+
);
479+
480+
//
481+
//
482+
//
483+
// if (scorerDownloadUrl) {
484+
// const scorerRes = await ldk.downloadScorer({
485+
// scorerDownloadUrl,
486+
// skipHoursThreshold: 3,
487+
// });
488+
// if (scorerRes.isErr()) {
489+
// return this.handleStartError(scorerRes);
490+
// }
491+
// }
492+
//
493+
// // Step 11: Optional: Initialize the NetGraphMsgHandler
494+
// const networkGraphRes = await ldk.initNetworkGraph({
495+
// network,
496+
// rapidGossipSyncUrl,
497+
// skipHoursThreshold: 3,
498+
// });
499+
// if (networkGraphRes.isErr()) {
500+
// return this.handleStartError(networkGraphRes);
501+
// }
502+
503+
const [scorerRes, networkGraphRes] = await Promise.all(promises);
504+
if (scorerRes.isErr()) {
505+
return this.handleStartError(scorerRes);
506+
}
462507
if (networkGraphRes.isErr()) {
463508
return this.handleStartError(networkGraphRes);
464509
}

lib/src/utils/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,15 @@ export type TInitChannelManagerReq = {
310310
};
311311
};
312312

313+
export type TDownloadScorer = {
314+
scorerDownloadUrl: string;
315+
skipHoursThreshold?: number;
316+
};
317+
313318
export type TInitNetworkGraphReq = {
314319
network: ENetworks;
315320
rapidGossipSyncUrl?: string;
321+
skipHoursThreshold?: number;
316322
};
317323

318324
export type TChannelHandshakeConfig = {
@@ -553,6 +559,7 @@ export type TLdkStart = {
553559
broadcastTransaction: TBroadcastTransaction;
554560
network: ENetworks;
555561
rapidGossipSyncUrl?: string;
562+
scorerDownloadUrl?: string;
556563
forceCloseOnStartup?: TForceCloseOnStartup;
557564
userConfig?: TUserConfig;
558565
trustedZeroConfPeers?: string[];

0 commit comments

Comments
 (0)