diff --git a/locales/en.json b/locales/en.json index 45a9c06781..54326aa89c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -127,6 +127,7 @@ "general.count": "Count", "general.experimental": "Experimental", "general.defaultNodeNickname": "My Lightning Node", + "general.retry": "Retry", "restart.title": "Restart required", "restart.msg": "ZEUS has to be restarted before the new configuration is applied.", "restart.msg1": "Would you like to restart now?", diff --git a/stores/LSPStore.ts b/stores/LSPStore.ts index 93664726e5..45611cafeb 100644 --- a/stores/LSPStore.ts +++ b/stores/LSPStore.ts @@ -1,4 +1,4 @@ -import { action, observable } from 'mobx'; +import { action, observable, reaction } from 'mobx'; import ReactNativeBlobUtil from 'react-native-blob-util'; import { v4 as uuidv4 } from 'uuid'; @@ -20,25 +20,31 @@ import { LndMobileEventEmitter } from '../utils/LndMobileUtils'; import { localeString } from '../utils/LocaleUtils'; import { errorToUserFriendly } from '../utils/ErrorUtils'; +const CUSTOM_MESSAGE_TYPE = 37913; +const JSON_RPC_VERSION = '2.0'; + export default class LSPStore { @observable public info: any = {}; @observable public zeroConfFee: number | undefined; @observable public feeId: string | undefined; @observable public pubkey: string; - @observable public getInfoId: string; - @observable public createOrderId: string; - @observable public getOrderId: string; @observable public loading: boolean = true; @observable public error: boolean = false; @observable public error_msg: string = ''; @observable public showLspSettings: boolean = false; @observable public channelAcceptor: any; @observable public customMessagesSubscriber: any; + @observable public resolvedCustomMessage: boolean; + // LSPS1 + @observable public getInfoId: string; + @observable public createOrderId: string; + @observable public getOrderId: string; @observable public getInfoData: any = {}; @observable public createOrderResponse: any = {}; @observable public getOrderResponse: any = {}; - - @observable public resolvedCustomMessage: boolean; + // LSPS7 + @observable public getExtendableOrdersId: string; + @observable public getExtendableOrdersData: any = {}; settingsStore: SettingsStore; channelsStore: ChannelsStore; @@ -52,6 +58,13 @@ export default class LSPStore { this.settingsStore = settingsStore; this.channelsStore = channelsStore; this.nodeInfoStore = nodeInfoStore; + + reaction( + () => this.channelsStore.channels, + () => { + this.getExtendableChannels(); + } + ); } @action @@ -88,7 +101,7 @@ export default class LSPStore { : DEFAULT_LSPS1_PUBKEY_MAINNET; if ( BackendUtils.supportsLSPS1customMessage() && - this.getLSPS1Pubkey() == olympusPubkey + this.getLSPSPubkey() == olympusPubkey ) { return true; } else if ( @@ -101,12 +114,12 @@ export default class LSPStore { return false; }; - getLSPHost = () => + getFlowHost = () => this.nodeInfoStore!.nodeInfo.isTestNet ? this.settingsStore.settings.lspTestnet : this.settingsStore.settings.lspMainnet; - getLSPS1Pubkey = () => + getLSPSPubkey = () => this.nodeInfoStore!.nodeInfo.isTestNet ? this.settingsStore.settings.lsps1PubkeyTestnet : this.settingsStore.settings.lsps1PubkeyMainnet; @@ -123,12 +136,14 @@ export default class LSPStore { encodeMesage = (n: any) => Buffer.from(JSON.stringify(n)).toString('hex'); + // Flow 2.0 + @action public getLSPInfo = () => { return new Promise((resolve, reject) => { ReactNativeBlobUtil.fetch( 'get', - `${this.getLSPHost()}/api/v1/info`, + `${this.getFlowHost()}/api/v1/info`, { 'Content-Type': 'application/json' } @@ -188,7 +203,7 @@ export default class LSPStore { return new Promise((resolve, reject) => { ReactNativeBlobUtil.fetch( 'post', - `${this.getLSPHost()}/api/v1/fee`, + `${this.getFlowHost()}/api/v1/fee`, settings.lspAccessKey ? { 'Content-Type': 'application/json', @@ -297,7 +312,7 @@ export default class LSPStore { return new Promise((resolve, reject) => { ReactNativeBlobUtil.fetch( 'post', - `${this.getLSPHost()}/api/v1/proposal`, + `${this.getFlowHost()}/api/v1/proposal`, settings.lspAccessKey ? { 'Content-Type': 'application/json', @@ -338,6 +353,8 @@ export default class LSPStore { }); }; + // LSPS0 + @action public sendCustomMessage = ({ peer, @@ -371,8 +388,7 @@ export default class LSPStore { const peer = Base64Utils.base64ToHex(decoded.peer); const data = JSON.parse(Base64Utils.base64ToUtf8(decoded.data)); - console.log('peer', peer); - console.log('data', data); + console.log('Received custom message', { peer, data }); if (data.id === this.getInfoId) { this.getInfoData = data; @@ -398,6 +414,16 @@ export default class LSPStore { } else { this.getOrderResponse = data; } + } else if (data.id === this.getExtendableOrdersId) { + if (data.error) { + this.error = true; + this.loading = false; + this.error_msg = data?.error?.message + ? errorToUserFriendly(data?.error?.message) + : ''; + } else { + this.getExtendableOrdersData = data; + } } }; @@ -449,8 +475,10 @@ export default class LSPStore { } }; + // LSPS1 + @action - public getInfoREST = () => { + public lsps1GetInfoREST = () => { const endpoint = `${this.getLSPS1Rest()}/api/v1/get_info`; console.log('Fetching data from:', endpoint); @@ -480,7 +508,40 @@ export default class LSPStore { }; @action - public createOrderREST = (state: any) => { + public lsps1GetInfoCustomMessage = () => { + this.loading = true; + this.error = false; + this.error_msg = ''; + + this.getInfoId = uuidv4(); + const method = 'lsps1.get_info'; + + this.sendCustomMessage({ + peer: this.getLSPSPubkey(), + type: CUSTOM_MESSAGE_TYPE, + data: this.encodeMesage({ + jsonrpc: JSON_RPC_VERSION, + method, + params: {}, + id: this.getInfoId + }) + }) + .then((response) => { + console.log( + `Response for custom message (${method}) received:`, + response + ); + }) + .catch((error) => { + console.error( + `Error sending (${method}) custom message:`, + error + ); + }); + }; + + @action + public lsps1CreateOrderREST = (state: any) => { const data = JSON.stringify({ lsp_balance_sat: state.lspBalanceSat.toString(), client_balance_sat: state.clientBalanceSat.toString(), @@ -534,7 +595,53 @@ export default class LSPStore { }; @action - public getOrderREST(id: string, RESTHost: string) { + public lsps1CreateOrderCustomMessage = (state: any) => { + this.loading = true; + this.error = false; + this.error_msg = ''; + + this.createOrderId = uuidv4(); + const method = 'lsps1.create_order'; + + this.sendCustomMessage({ + peer: this.getLSPSPubkey(), + type: CUSTOM_MESSAGE_TYPE, + data: this.encodeMesage({ + jsonrpc: JSON_RPC_VERSION, + method, + params: { + lsp_balance_sat: state.lspBalanceSat.toString(), + client_balance_sat: state.clientBalanceSat.toString(), + required_channel_confirmations: parseInt( + state.requiredChannelConfirmations + ), + funding_confirms_within_blocks: parseInt( + state.confirmsWithinBlocks + ), + channel_expiry_blocks: state.channelExpiryBlocks, + token: state.token, + refund_onchain_address: state.refundOnchainAddress, + announce_channel: state.announceChannel + }, + id: this.createOrderId + }) + }) + .then((response) => { + console.log( + `Response for custom message (${method}) received:`, + response + ); + }) + .catch((error) => { + console.error( + `Error sending (${method}) custom message:`, + error + ); + }); + }; + + @action + public lsps1GetOrderREST(id: string, RESTHost: string) { this.loading = true; const endpoint = `${RESTHost}/api/v1/get_order?order_id=${id}`; @@ -562,31 +669,67 @@ export default class LSPStore { } @action - public getOrderCustomMessage(orderId: string, peer: string) { - console.log('Requesting LSPS1...'); + public lsps1GetOrderCustomMessage(orderId: string, peer: string) { this.loading = true; - const type = 37913; - const id = uuidv4(); - this.getOrderId = id; - const data = this.encodeMesage({ - jsonrpc: '2.0', - method: 'lsps1.get_order', - params: { - order_id: orderId - }, - id: this.getOrderId - }); + + this.getOrderId = uuidv4(); + const method = 'lsps1.get_order'; this.sendCustomMessage({ peer, - type, - data + type: CUSTOM_MESSAGE_TYPE, + data: this.encodeMesage({ + jsonrpc: JSON_RPC_VERSION, + method, + params: { + order_id: orderId + }, + id: this.getOrderId + }) }) .then((response) => { - console.log('Custom message sent:', response); + console.log( + `Response for custom message (${method}) received:`, + response + ); }) .catch((error) => { - console.error('Error sending custom message:', error); + console.error( + `Error sending (${method}) custom message:`, + error + ); }); } + + // LSPS7 + + @action + public getExtendableChannels = () => { + this.loading = true; + this.error = false; + this.error_msg = ''; + + this.getExtendableOrdersId = uuidv4(); + const method = 'lsps7.get_extendable_channels'; + + this.sendCustomMessage({ + peer: this.getLSPSPubkey(), + type: CUSTOM_MESSAGE_TYPE, + data: this.encodeMesage({ + jsonrpc: JSON_RPC_VERSION, + method, + params: {}, + id: this.getExtendableOrdersId + }) + }) + .then((response) => { + console.log(`Custom message (${method}) sent:`, response); + }) + .catch((error) => { + console.error( + `Error sending (${method}) custom message:`, + error + ); + }); + }; } diff --git a/views/Settings/LSPS1/Order.tsx b/views/Settings/LSPS1/Order.tsx index afd576d26c..d97af52f19 100644 --- a/views/Settings/LSPS1/Order.tsx +++ b/views/Settings/LSPS1/Order.tsx @@ -71,11 +71,11 @@ export default class Orders extends React.Component { console.log('Order found in storage->', temporaryOrder); BackendUtils.supportsLSPS1rest() - ? LSPStore.getOrderREST( + ? LSPStore.lsps1GetOrderREST( id, temporaryOrder?.endpoint ) - : LSPStore.getOrderCustomMessage( + : LSPStore.lsps1GetOrderCustomMessage( id, temporaryOrder?.peer ); diff --git a/views/Settings/LSPS1/index.tsx b/views/Settings/LSPS1/index.tsx index 1716d69602..889c019426 100644 --- a/views/Settings/LSPS1/index.tsx +++ b/views/Settings/LSPS1/index.tsx @@ -12,7 +12,6 @@ import { ButtonGroup, Icon } from 'react-native-elements'; import EncryptedStorage from 'react-native-encrypted-storage'; import Slider from '@react-native-community/slider'; import { StackNavigationProp } from '@react-navigation/stack'; -import { v4 as uuidv4 } from 'uuid'; import CaretDown from '../../../assets/images/SVG/Caret Down.svg'; import CaretRight from '../../../assets/images/SVG/Caret Right.svg'; @@ -101,13 +100,13 @@ export default class LSPS1 extends React.Component { const { LSPStore, SettingsStore, navigation } = this.props; LSPStore.resetLSPS1Data(); if (BackendUtils.supportsLSPS1rest()) { - LSPStore.getInfoREST(); + LSPStore.lsps1GetInfoREST(); } else { console.log('connecting'); await this.connectPeer(); console.log('connected'); await this.subscribeToCustomMessages(); - this.sendCustomMessage_lsps1(); + LSPStore.lsps1GetInfoCustomMessage(); } navigation.addListener('focus', () => { @@ -227,84 +226,9 @@ export default class LSPS1 extends React.Component { } } - sendCustomMessage_lsps1() { - const { LSPStore } = this.props; - LSPStore.loading = true; - LSPStore.error = false; - LSPStore.error_msg = ''; - const node_pubkey_string: string = LSPStore.getLSPS1Pubkey(); - const type = 37913; - const id = uuidv4(); - LSPStore.getInfoId = id; - const data = this.encodeMesage({ - jsonrpc: '2.0', - method: 'lsps1.get_info', - params: {}, - id: LSPStore.getInfoId - }); - - LSPStore.sendCustomMessage({ - peer: node_pubkey_string, - type, - data - }) - .then((response) => { - console.log('Custom message sent:', response); - }) - .catch((error) => { - console.error( - 'Error sending (get_info) custom message:', - error - ); - }); - } - - lsps1_createorder = () => { - const { LSPStore } = this.props; - const node_pubkey_string: string = LSPStore.getLSPS1Pubkey(); - const type = 37913; - const id = uuidv4(); - LSPStore.createOrderId = id; - LSPStore.loading = true; - LSPStore.error = false; - LSPStore.error_msg = ''; - const data = this.encodeMesage({ - jsonrpc: '2.0', - method: 'lsps1.create_order', - params: { - lsp_balance_sat: this.state.lspBalanceSat.toString(), - client_balance_sat: this.state.clientBalanceSat.toString(), - required_channel_confirmations: parseInt( - this.state.requiredChannelConfirmations - ), - funding_confirms_within_blocks: parseInt( - this.state.confirmsWithinBlocks - ), - channel_expiry_blocks: this.state.channelExpiryBlocks, - token: this.state.token, - refund_onchain_address: this.state.refundOnchainAddress, - announce_channel: this.state.announceChannel - }, - id: LSPStore.createOrderId - }); - - LSPStore.sendCustomMessage({ - peer: node_pubkey_string, - type, - data - }) - .then(() => {}) - .catch((error) => { - console.error( - 'Error sending (create_order) custom message:', - error - ); - }); - }; - connectPeer = async () => { const { ChannelsStore, LSPStore } = this.props; - const node_pubkey_string: string = LSPStore.getLSPS1Pubkey(); + const node_pubkey_string: string = LSPStore.getLSPSPubkey(); const host: string = LSPStore.getLSPS1Host(); try { return await ChannelsStore.connectPeer( @@ -473,7 +397,7 @@ export default class LSPS1 extends React.Component { const lspDisplay = isOlympus ? 'Olympus by ZEUS' : BackendUtils.supportsLSPS1customMessage() - ? LSPStore.getLSPS1Pubkey() + ? LSPStore.getLSPSPubkey() : LSPStore.getLSPS1Rest(); return ( @@ -493,7 +417,7 @@ export default class LSPS1 extends React.Component { /> {BackendUtils.supportsLSPS1customMessage() && - !LSPStore.getLSPS1Pubkey() && + !LSPStore.getLSPSPubkey() && !LSPStore.getLSPS1Host() && ( { .length === 0 ) { if (BackendUtils.supportsLSPS1rest()) { - LSPStore.createOrderREST( + LSPStore.lsps1CreateOrderREST( this.state ); } else { - this.lsps1_createorder(); + LSPStore.lsps1CreateOrderCustomMessage( + this.state + ); } } else { const orderId = result.order_id; @@ -1350,8 +1276,8 @@ export default class LSPS1 extends React.Component { BackendUtils.supportsLSPS1customMessage() ) { orderData.peer = - LSPStore.getLSPS1Pubkey(); - orderData.uri = `${LSPStore.getLSPS1Pubkey()}@${LSPStore.getLSPS1Host()}`; + LSPStore.getLSPSPubkey(); + orderData.uri = `${LSPStore.getLSPSPubkey()}@${LSPStore.getLSPS1Host()}`; } if ( BackendUtils.supportsLSPS1rest() @@ -1443,14 +1369,14 @@ export default class LSPS1 extends React.Component { }} >