diff --git a/backends/LightningNodeConnect.ts b/backends/LightningNodeConnect.ts index 6e89cf5a5..d05d211a4 100644 --- a/backends/LightningNodeConnect.ts +++ b/backends/LightningNodeConnect.ts @@ -354,6 +354,16 @@ export default class LightningNodeConnect { await this.lnc.lnd.lightning .lookupInvoice({ r_hash: Base64Utils.hexToBase64(data.r_hash) }) .then((data: lnrpc.Invoice) => snakeize(data)); + channelAcceptor = (data: lnrpc.channelAcceptRequest) => + this.lnc.lnd.lightning.channelAcceptor(data); + channelAcceptorAnswer = (data: lnrpc.channelAcceptorResponse) => { + console.log('^^^backend', data); + return this.lnc.call('lnrpc.Lightning.ChannelAcceptor', { + pending_chan_id: data.pending_chan_id, + zero_conf: data.zero_conf, + accept: data.accept + }); + }; subscribeInvoice = (r_hash: string) => this.lnc.lnd.invoices.subscribeSingleInvoice({ r_hash }); subscribeInvoices = () => this.lnc.lnd.lightning.subscribeInvoices(); @@ -388,7 +398,7 @@ export default class LightningNodeConnect { supportsAddressTypeSelection = () => true; supportsTaproot = () => this.supports('v0.15.0'); supportsBumpFee = () => true; - supportsLSPs = () => false; + supportsLSPs = () => true; supportsNetworkInfo = () => false; supportsSimpleTaprootChannels = () => this.supports('v0.17.0'); supportsCustomPreimages = () => true; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d19f1cf5e..fdcbc9166 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -520,7 +520,7 @@ PODS: - RNVectorIcons (7.1.0): - React - SocketRocket (0.6.1) - - SwiftProtobuf (1.23.0) + - SwiftProtobuf (1.25.2) - TcpSockets (4.0.0): - React - Yoga (1.14.0) @@ -830,10 +830,10 @@ SPEC CHECKSUMS: RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396 RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - SwiftProtobuf: b70d65f419fbfe61a2d58003456ca5da58e337d6 + SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 TcpSockets: 4ef55305239923b343ed0a378b1fac188b1373b0 Yoga: 86fed2e4d425ee4c6eab3813ba1791101ee153c6 PODFILE CHECKSUM: 643f83a7955aa123651bac4a54204e22598914df -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/zeus.xcodeproj/project.pbxproj b/ios/zeus.xcodeproj/project.pbxproj index cf025e61d..17ebd3d04 100644 --- a/ios/zeus.xcodeproj/project.pbxproj +++ b/ios/zeus.xcodeproj/project.pbxproj @@ -2134,6 +2134,7 @@ "-ld_classic", "-Wl", "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -2211,6 +2212,7 @@ "-ld_classic", "-Wl", "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; diff --git a/lndmobile/LndMobileInjection.ts b/lndmobile/LndMobileInjection.ts index c63a745e0..9798ef63b 100644 --- a/lndmobile/LndMobileInjection.ts +++ b/lndmobile/LndMobileInjection.ts @@ -36,6 +36,7 @@ import { listPayments, listInvoices, subscribeChannelGraph, + channelAcceptorAnswer, sendKeysendPaymentV2 } from './index'; import { @@ -196,6 +197,11 @@ export interface ILndMobileInjections { ) => Promise; listPayments: () => Promise; subscribeChannelGraph: () => Promise; + channelAcceptorAnswer: ( + pending_chan_id: Uint8Array, + zero_conf: boolean, + accept: boolean + ) => void; sendKeysendPaymentV2: ({ amt, max_shard_size_msat, @@ -382,6 +388,7 @@ export default { listPayments, listInvoices, subscribeChannelGraph, + channelAcceptorAnswer, sendKeysendPaymentV2 }, channel: { diff --git a/lndmobile/index.ts b/lndmobile/index.ts index ab8dc1585..84fddd67e 100644 --- a/lndmobile/index.ts +++ b/lndmobile/index.ts @@ -841,6 +841,28 @@ export const subscribeChannelGraph = async (): Promise => { return response; }; +/** + * @throws + */ +export const channelAcceptorAnswer = async ( + pending_chan_id: Uint8Array, + zero_conf: boolean, + accept: boolean +) => { + await sendStreamCommand< + lnrpc.IChannelAcceptResponse, + lnrpc.ChannelAcceptResponse + >({ + request: lnrpc.ChannelAcceptResponse, + method: 'ChannelAcceptor', + options: { + pending_chan_id, + zero_conf, + accept + } + }); +}; + export type IReadLndLogResponse = string[]; /** * @throws diff --git a/stores/LSPStore.ts b/stores/LSPStore.ts index 6b15c14a2..bc36488ed 100644 --- a/stores/LSPStore.ts +++ b/stores/LSPStore.ts @@ -1,5 +1,6 @@ import { action, observable } from 'mobx'; import ReactNativeBlobUtil from 'react-native-blob-util'; +import { NativeEventEmitter, NativeModules } from 'react-native'; import SettingsStore from './SettingsStore'; import ChannelsStore from './ChannelsStore'; @@ -8,6 +9,7 @@ import stores from './Stores'; import lndMobile from '../lndmobile/LndMobileInjection'; const { channel } = lndMobile; +import BackendUtils from '../utils/BackendUtils'; import Base64Utils from '../utils/Base64Utils'; import { LndMobileEventEmitter } from '../utils/LndMobileUtils'; import { localeString } from '../utils/LocaleUtils'; @@ -170,22 +172,67 @@ export default class LSPStore { @action public initChannelAcceptor = async () => { + const { implementation } = this.settingsStore; if (this.channelAcceptor) return; - this.channelAcceptor = LndMobileEventEmitter.addListener( - 'ChannelAcceptor', - async (event: any) => { - try { - const channelAcceptRequest = - channel.decodeChannelAcceptRequest(event.data); - - await this.handleChannelAcceptorEvent(channelAcceptRequest); - } catch (error: any) { - console.error('channel acceptance error: ' + error.message); + + if (implementation === 'embedded-lnd') { + this.channelAcceptor = LndMobileEventEmitter.addListener( + 'ChannelAcceptor', + async (event: any) => { + try { + const result = channel.decodeChannelAcceptRequest( + event.data + ); + await this.handleChannelAcceptorEvent(result); + } catch (error: any) { + console.error( + 'channelAcceptorEvent embedded-lnd error:', + error.message + ); + } } - } - ); + ); + + await channel.channelAcceptor(); + } + + // if (implementation === 'lightning-node-connect') { + // const { LncModule } = NativeModules; + // const eventEmitter = new NativeEventEmitter(LncModule); + // console.log('hERE', eventEmitter); + // const call = BackendUtils.channelAcceptor(); + // console.log('call', call) + // this.channelAcceptor = eventEmitter.addListener( + // 'lnrpc.Lightning.ChannelAcceptor', + // (event: any) => { + // // console.log('-->', event); + // if (event.result) { + // try { + // const result = JSON.parse(event.result); + // console.log('~~RESULT', result); + // // only allow zero conf chans from the LSP + // const isZeroConfAllowed = + // result.node_pubkey === this.info.pub_key; + + // BackendUtils.channelAcceptorAnswer({ + // pending_chan_id: result.pending_chan_id, + // zero_conf: + // !result.wants_zero_conf || + // isZeroConfAllowed, + // accept: isZeroConfAllowed + // }); + // } catch (error: any) { + // console.error( + // 'channelAcceptorEvent lightning-node-connect error:', + // error.message + // ); + // } + // } + // } + // ); - await channel.channelAcceptor(); + // console.log('~~~this.channelAcceptor', this.channelAcceptor); + // } }; @action diff --git a/utils/BackendUtils.ts b/utils/BackendUtils.ts index fc310aacb..df49c5600 100644 --- a/utils/BackendUtils.ts +++ b/utils/BackendUtils.ts @@ -106,6 +106,9 @@ class BackendUtils { this.call('publishTransaction', args); bumpFee = (...args: any[]) => this.call('bumpFee', args); lookupInvoice = (...args: any[]) => this.call('lookupInvoice', args); + channelAcceptor = (...args: any[]) => this.call('channelAcceptor', args); + channelAcceptorAnswer = (...args: any[]) => + this.call('channelAcceptorAnswer', args); subscribeInvoice = (...args: any[]) => this.call('subscribeInvoice', args); subscribeInvoices = (...args: any[]) => this.call('subscribeInvoices', args); diff --git a/views/Wallet/Wallet.tsx b/views/Wallet/Wallet.tsx index 440f8c36f..62346c9c0 100644 --- a/views/Wallet/Wallet.tsx +++ b/views/Wallet/Wallet.tsx @@ -4,7 +4,9 @@ import { AppState, BackHandler, Linking, + NativeEventEmitter, NativeEventSubscription, + NativeModules, PanResponder, PanResponderInstance, Platform, @@ -120,6 +122,8 @@ export default class Wallet extends React.Component { private handleAppStateChangeSubscription: NativeEventSubscription; private backPressSubscription: NativeEventSubscription; + channelAcceptor: any; + constructor(props) { super(props); this.state = { @@ -369,12 +373,6 @@ export default class Wallet extends React.Component { embeddedLndNetwork === 'Testnet' ); } - if (BackendUtils.supportsLSPs()) { - if (SettingsStore.settings.enableLSP) { - LSPStore.getLSPInfo(); - } - LSPStore.initChannelAcceptor(); - } NodeInfoStore.getNodeInfo(); if (BackendUtils.supportsAccounts()) UTXOsStore.listAccounts(); await BalanceStore.getCombinedBalance(false); @@ -455,6 +453,53 @@ export default class Wallet extends React.Component { } } + if (BackendUtils.supportsLSPs()) { + if (SettingsStore.settings.enableLSP) { + await LSPStore.getLSPInfo(); + } + + if (implementation === 'lightning-node-connect') { + // if (this.channelAcceptor) return; + const { LncModule } = NativeModules; + const eventEmitter = new NativeEventEmitter(LncModule); + console.log('hERE~~3', eventEmitter); + const call = BackendUtils.channelAcceptor(); + console.log('call', call) + this.channelAcceptor = eventEmitter.addListener( + call, + (event: any) => { + console.log('>>event', event); + if (event.result) { + try { + const result = JSON.parse(event.result); + console.log('~~RESULT', result); + // // only allow zero conf chans from the LSP + const isZeroConfAllowed = + result.node_pubkey === LSPStore.info.pub_key; + + BackendUtils.channelAcceptorAnswer({ + pending_chan_id: result.pending_chan_id, + zero_conf: + !result.wants_zero_conf || + isZeroConfAllowed, + accept: isZeroConfAllowed + }); + } catch (error: any) { + console.error( + 'channelAcceptorEvent lightning-node-connect error:', + error.message + ); + } + } + } + ); + + console.log('~~~this.channelAcceptor', this.channelAcceptor); + } else { + LSPStore.initChannelAcceptor(); + } + } + if (connecting) { setConnectingStatus(false); }