Skip to content

Commit 7050f7a

Browse files
committed
feat: Embedded LND testnet4 support
1 parent 2504607 commit 7050f7a

File tree

17 files changed

+631
-104
lines changed

17 files changed

+631
-104
lines changed

components/WalletHeader.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,15 @@ export default class WalletHeader extends React.Component<
390390
const nodeAddress = SettingsStore!.host || SettingsStore!.url;
391391

392392
let infoValue: string;
393-
if (NodeInfoStore!.nodeInfo.isTestNet) {
393+
// Check Testnet4 first (more specific), then fall back to generic Testnet
394+
// For embedded LND, also check embeddedLndNetwork setting
395+
const isTestNet4 =
396+
NodeInfoStore!.nodeInfo.isTestNet4 ||
397+
(implementation === 'embedded-lnd' &&
398+
SettingsStore!.embeddedLndNetwork === 'Testnet4');
399+
if (isTestNet4) {
400+
infoValue = localeString('network.testnet4');
401+
} else if (NodeInfoStore!.nodeInfo.isTestNet) {
394402
infoValue = localeString('views.Wallet.MainPane.testnet');
395403
} else if (NodeInfoStore!.nodeInfo.isRegTest) {
396404
infoValue = localeString('views.Wallet.MainPane.regnet');

locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@
198198
"views.Settings.SocialMedia.twitter": "X / Twitter",
199199
"views.Settings.Support.store": "ZEUS merch store",
200200
"network.mainnet": "Mainnet",
201-
"network.testnet": "Testnet",
201+
"network.testnet3": "Testnet3",
202+
"network.testnet4": "Testnet4",
202203
"nostr.nostr": "Nostr",
203204
"nostr.keys": "Nostr keys",
204205
"nostr.pubkey": "Nostr pubkey",

models/NodeInfo.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,24 @@ export default class NodeInfo extends BaseModel {
2929
return this.id || this.pubkey || this.identity_pubkey || '';
3030
}
3131

32+
@computed public get isTestNet4(): boolean {
33+
return (
34+
this.network === 'testnet4' ||
35+
(this.chains &&
36+
this.chains[0] &&
37+
this.chains[0].network === 'testnet4')
38+
);
39+
}
40+
3241
@computed public get isTestNet(): boolean {
3342
return (
3443
this.testnet ||
3544
this.network === 'testnet' ||
45+
this.network === 'testnet4' ||
3646
(this.chains &&
3747
this.chains[0] &&
38-
this.chains[0].network === 'testnet')
48+
(this.chains[0].network === 'testnet' ||
49+
this.chains[0].network === 'testnet4'))
3950
);
4051
}
4152

stores/AlertStore.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ export default class AlertStore {
5656
@action
5757
public checkNeutrinoPeers = async () => {
5858
const peers =
59-
this.settingsStore.embeddedLndNetwork === 'Testnet'
59+
this.settingsStore.embeddedLndNetwork === 'Testnet3'
6060
? this.settingsStore.settings.neutrinoPeersTestnet
61+
: this.settingsStore.embeddedLndNetwork === 'Testnet4'
62+
? this.settingsStore.settings.neutrinoPeersTestnet4
6163
: this.settingsStore.settings.neutrinoPeersMainnet;
6264

6365
const results: any = [];

stores/SettingsStore.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export interface Settings {
189189
dontAllowOtherPeers: boolean;
190190
neutrinoPeersMainnet: Array<string>;
191191
neutrinoPeersTestnet: Array<string>;
192+
neutrinoPeersTestnet4: Array<string>;
192193
zeroConfPeers: Array<string>;
193194
rescan: boolean;
194195
compactDb: boolean;
@@ -376,9 +377,23 @@ export type Implementations =
376377

377378
export const EMBEDDED_NODE_NETWORK_KEYS = [
378379
{ key: 'Mainnet', translateKey: 'network.mainnet', value: 'mainnet' },
379-
{ key: 'Testnet', translateKey: 'network.testnet', value: 'testnet' }
380+
{ key: 'Testnet3', translateKey: 'network.testnet3', value: 'testnet' },
381+
{ key: 'Testnet4', translateKey: 'network.testnet4', value: 'testnet4' }
380382
];
381383

384+
// Helper to get display name for embedded LND network
385+
export const getNetworkDisplayName = (network: string): string => {
386+
const networkDisplayMap: { [key: string]: string } = {
387+
mainnet: 'Mainnet',
388+
Mainnet: 'Mainnet',
389+
testnet: 'Testnet3',
390+
Testnet3: 'Testnet3',
391+
testnet4: 'Testnet4',
392+
Testnet4: 'Testnet4'
393+
};
394+
return networkDisplayMap[network] || network;
395+
};
396+
382397
export const LNC_MAILBOX_KEYS = [
383398
{
384399
key: 'mailbox.terminal.lightning.today:443',
@@ -1346,6 +1361,11 @@ export const DEFAULT_NEUTRINO_PEERS_TESTNET = [
13461361
'testnet.blixtwallet.com'
13471362
];
13481363

1364+
export const DEFAULT_NEUTRINO_PEERS_TESTNET4 = [
1365+
'testnet4.lnolymp.us',
1366+
'testnet4.blixtwallet.com'
1367+
];
1368+
13491369
export const DEFAULT_SLIDE_TO_PAY_THRESHOLD = 10000;
13501370

13511371
export default class SettingsStore {
@@ -1427,6 +1447,7 @@ export default class SettingsStore {
14271447
dontAllowOtherPeers: false,
14281448
neutrinoPeersMainnet: DEFAULT_NEUTRINO_PEERS_MAINNET,
14291449
neutrinoPeersTestnet: DEFAULT_NEUTRINO_PEERS_TESTNET,
1450+
neutrinoPeersTestnet4: DEFAULT_NEUTRINO_PEERS_TESTNET4,
14301451
zeroConfPeers: [],
14311452
rescan: false,
14321453
compactDb: false,

stores/SyncStore.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,15 @@ export default class SyncStore {
7878

7979
private getBestBlockHeight = async () => {
8080
await new Promise((resolve, reject) => {
81+
const networkPath =
82+
this.settingsStore.embeddedLndNetwork === 'Testnet3'
83+
? 'testnet/'
84+
: this.settingsStore.embeddedLndNetwork === 'Testnet4'
85+
? 'testnet4/'
86+
: '';
8187
ReactNativeBlobUtil.fetch(
8288
'get',
83-
`https://mempool.space/${
84-
this.settingsStore.embeddedLndNetwork === 'Testnet'
85-
? 'testnet/'
86-
: ''
87-
}api/blocks/tip/height`
89+
`https://mempool.space/${networkPath}api/blocks/tip/height`
8890
)
8991
.then(async (response: any) => {
9092
const status = response.info().status;

utils/GraphSyncUtils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ describe('GraphSyncUtils', () => {
125125

126126
it('should return true and not show prompt on testnet', () => {
127127
// Temporarily set to testnet
128-
(mockSettingsStore as any).embeddedLndNetwork = 'Testnet';
128+
(mockSettingsStore as any).embeddedLndNetwork = 'Testnet3';
129129

130130
const result = checkGraphSyncBeforePayment(
131131
mockSettings,

utils/LndMobileUtils.ts

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
DEFAULT_NEUTRINO_PEERS_MAINNET,
3333
SECONDARY_NEUTRINO_PEERS_MAINNET,
3434
DEFAULT_NEUTRINO_PEERS_TESTNET,
35+
DEFAULT_NEUTRINO_PEERS_TESTNET4,
3536
DEFAULT_FEE_ESTIMATOR,
3637
DEFAULT_SPEEDLOADER
3738
} from '../stores/SettingsStore';
@@ -90,15 +91,18 @@ export function checkLndStreamErrorResponse(
9091

9192
const writeLndConfig = async ({
9293
lndDir = 'lnd',
93-
isTestnet,
94+
network,
9495
rescan,
9596
compactDb
9697
}: {
9798
lndDir: string;
98-
isTestnet?: boolean;
99+
network?: string;
99100
rescan?: boolean;
100101
compactDb?: boolean;
101102
}) => {
103+
const isMainnet = !network || network === 'mainnet';
104+
const isTestnet = network === 'testnet' || network === 'testnet3';
105+
const isTestnet4 = network === 'testnet4';
102106
const { writeConfig } = lndMobile.index;
103107

104108
const peerMode = settingsStore?.settings?.dontAllowOtherPeers
@@ -112,6 +116,41 @@ const writeLndConfig = async ({
112116

113117
console.log('persistFilters', persistFilters);
114118

119+
// Get neutrino peers based on network
120+
const getNeutrinoPeers = () => {
121+
if (isMainnet) {
122+
return settingsStore?.settings?.neutrinoPeersMainnet || [];
123+
} else if (isTestnet4) {
124+
return settingsStore?.settings?.neutrinoPeersTestnet4 || [];
125+
} else {
126+
return settingsStore?.settings?.neutrinoPeersTestnet || [];
127+
}
128+
};
129+
130+
const dbConfig = `[db]
131+
db.no-graph-cache=false
132+
133+
[bolt]
134+
db.bolt.auto-compact=${compactDb ? 'true' : 'false'}
135+
${compactDb ? 'db.bolt.auto-compact-min-age=0' : ''}`;
136+
137+
const neutrinoConfig = `[Neutrino]
138+
${getNeutrinoPeers()
139+
.map((peer: string) => `neutrino.${peerMode}=${peer}\n `)
140+
.join('')}
141+
${
142+
isMainnet
143+
? 'neutrino.assertfilterheader=230000:1308d5cfc6462f877a5587fd77d7c1ab029d45e58d5175aaf8c264cee9bde760'
144+
: ''
145+
}
146+
${
147+
isMainnet
148+
? 'neutrino.assertfilterheader=660000:08312375fabc082b17fa8ee88443feb350c19a34bb7483f94f7478fa4ad33032'
149+
: ''
150+
}
151+
neutrino.broadcasttimeout=11s
152+
neutrino.persistfilters=${persistFilters}`;
153+
115154
const config = `[Application Options]
116155
debuglevel=info
117156
maxbackoff=2s
@@ -124,46 +163,21 @@ const writeLndConfig = async ({
124163
payments-expiration-grace-period=168h
125164
${rescan ? 'reset-wallet-transactions=true' : ''}
126165
127-
[db]
128-
db.no-graph-cache=false
129-
130-
[bolt]
131-
db.bolt.auto-compact=${compactDb ? 'true' : 'false'}
132-
${compactDb ? 'db.bolt.auto-compact-min-age=0' : ''}
166+
${dbConfig}
133167
134168
[Routing]
135169
routing.assumechanvalid=1
136170
routing.strictgraphpruning=false
137171
138172
[Bitcoin]
139173
bitcoin.active=1
140-
bitcoin.mainnet=${isTestnet ? 0 : 1}
141-
bitcoin.testnet=${isTestnet ? 1 : 0}
174+
bitcoin.mainnet=${isMainnet ? true : false}
175+
bitcoin.testnet=${isTestnet ? true : false}
176+
bitcoin.testnet4=${isTestnet4 ? true : false}
142177
bitcoin.node=neutrino
143178
bitcoin.defaultchanconfs=1
144179
145-
[Neutrino]
146-
${
147-
!isTestnet
148-
? settingsStore?.settings?.neutrinoPeersMainnet
149-
.map((peer) => `neutrino.${peerMode}=${peer}\n `)
150-
.join('')
151-
: settingsStore?.settings?.neutrinoPeersTestnet
152-
.map((peer) => `neutrino.${peerMode}=${peer}\n `)
153-
.join('')
154-
}
155-
${
156-
!isTestnet
157-
? 'neutrino.assertfilterheader=230000:1308d5cfc6462f877a5587fd77d7c1ab029d45e58d5175aaf8c264cee9bde760'
158-
: ''
159-
}
160-
${
161-
!isTestnet
162-
? 'neutrino.assertfilterheader=660000:08312375fabc082b17fa8ee88443feb350c19a34bb7483f94f7478fa4ad33032'
163-
: ''
164-
}
165-
neutrino.broadcasttimeout=11s
166-
neutrino.persistfilters=${persistFilters}
180+
${neutrinoConfig}
167181
168182
[fee]
169183
fee.url=${
@@ -251,17 +265,17 @@ export async function expressGraphSync() {
251265

252266
export async function initializeLnd({
253267
lndDir = 'lnd',
254-
isTestnet,
268+
network,
255269
rescan,
256270
compactDb
257271
}: {
258272
lndDir: string;
259-
isTestnet?: boolean;
273+
network?: string;
260274
rescan?: boolean;
261275
compactDb?: boolean;
262276
}) {
263277
const { initialize } = lndMobile.index;
264-
await writeLndConfig({ lndDir, isTestnet, rescan, compactDb });
278+
await writeLndConfig({ lndDir, network, rescan, compactDb });
265279
await initialize();
266280
}
267281

@@ -402,13 +416,19 @@ export async function startLnd({
402416
}
403417

404418
export async function optimizeNeutrinoPeers(
405-
isTestnet?: boolean,
419+
network?: string,
406420
peerTargetCount: number = 3
407421
) {
408422
console.log('Optimizing Neutrino peers');
409-
let peers = isTestnet
410-
? DEFAULT_NEUTRINO_PEERS_TESTNET
411-
: DEFAULT_NEUTRINO_PEERS_MAINNET;
423+
const isMainnet = !network || network === 'mainnet';
424+
const isTestnet = network === 'testnet' || network === 'testnet3';
425+
const isTestnet4 = network === 'testnet4';
426+
427+
let peers = isMainnet
428+
? DEFAULT_NEUTRINO_PEERS_MAINNET
429+
: isTestnet4
430+
? DEFAULT_NEUTRINO_PEERS_TESTNET4
431+
: DEFAULT_NEUTRINO_PEERS_TESTNET;
412432

413433
const results: any = [];
414434
for (let i = 0; i < peers.length; i++) {
@@ -506,8 +526,8 @@ export async function optimizeNeutrinoPeers(
506526
console.log('Peers count:', selectedPeers.length);
507527
}
508528

509-
// Extra external peers
510-
if (selectedPeers.length < peerTargetCount && !isTestnet) {
529+
// Extra external peers (mainnet only)
530+
if (selectedPeers.length < peerTargetCount && isMainnet) {
511531
console.log(
512532
`Selecting Neutrino peers with ping times <${NEUTRINO_PING_THRESHOLD_MS}ms from alternate set`
513533
);
@@ -562,17 +582,17 @@ export async function optimizeNeutrinoPeers(
562582
}
563583

564584
if (selectedPeers.length > 0) {
565-
if (isTestnet) {
566-
await settingsStore.updateSettings({
567-
neutrinoPeersTestnet: selectedPeers,
568-
dontAllowOtherPeers: selectedPeers.length > 2 ? true : false
569-
});
585+
const settingsUpdate: any = {
586+
dontAllowOtherPeers: selectedPeers.length > 2 ? true : false
587+
};
588+
if (isTestnet4) {
589+
settingsUpdate.neutrinoPeersTestnet4 = selectedPeers;
590+
} else if (isTestnet) {
591+
settingsUpdate.neutrinoPeersTestnet = selectedPeers;
570592
} else {
571-
await settingsStore.updateSettings({
572-
neutrinoPeersMainnet: selectedPeers,
573-
dontAllowOtherPeers: selectedPeers.length > 2 ? true : false
574-
});
593+
settingsUpdate.neutrinoPeersMainnet = selectedPeers;
575594
}
595+
await settingsStore.updateSettings(settingsUpdate);
576596

577597
console.log('Selected the following Neutrino peers:', selectedPeers);
578598
} else {
@@ -588,13 +608,13 @@ export async function createLndWallet({
588608
lndDir,
589609
seedMnemonic,
590610
walletPassphrase,
591-
isTestnet,
611+
network,
592612
channelBackupsBase64
593613
}: {
594614
lndDir: string;
595615
seedMnemonic?: string;
596616
walletPassphrase?: string;
597-
isTestnet?: boolean;
617+
network?: string;
598618
channelBackupsBase64?: string;
599619
}) {
600620
const {
@@ -610,19 +630,21 @@ export async function createLndWallet({
610630
await excludeLndICloudBackup(lndDir);
611631
}
612632

613-
await writeLndConfig({ lndDir, isTestnet });
633+
await writeLndConfig({ lndDir, network });
614634
await initialize();
615635

616636
let status = await checkStatus();
617637
if (
618638
(status & ELndMobileStatusCodes.STATUS_PROCESS_STARTED) !==
619639
ELndMobileStatusCodes.STATUS_PROCESS_STARTED
620640
) {
641+
// Native code expects isTestnet boolean - true for any non-mainnet network
642+
const isTestnet = network === 'testnet' || network === 'testnet4';
621643
await startLnd({
622644
lndDir,
623645
walletPassword: '',
624646
isTorEnabled: false,
625-
isTestnet: isTestnet || false
647+
isTestnet
626648
});
627649
}
628650

0 commit comments

Comments
 (0)