From 1f003cc2923123846d260e547dba70dad788bd59 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 5 Jul 2023 11:06:51 +0200 Subject: [PATCH 1/8] [lightning] save node features as stringified json array in db --- backend/src/api/database-migration.ts | 7 +- backend/src/api/explorer/nodes.api.ts | 17 ++- .../clightning/clightning-convert.ts | 112 +++++++++++++++++- .../tasks/lightning/network-sync.service.ts | 1 - backend/src/utils/format.ts | 33 ++++++ 5 files changed, 164 insertions(+), 6 deletions(-) diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index a9266a016..7c7608aff 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 63; + private static currentVersion = 64; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -543,6 +543,11 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `blocks_audits` ADD fullrbf_txs JSON DEFAULT "[]"'); await this.updateToSchemaVersion(63); } + + if (databaseSchemaVersion < 64 && isBitcoin === true) { + await this.$executeQuery('ALTER TABLE `nodes` ADD features text NULL'); + await this.updateToSchemaVersion(64); + } } /** diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index d429299e1..f5c87fbd1 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -656,10 +656,19 @@ class NodesApi { alias_search, color, sockets, - status + status, + features ) - VALUES (?, NOW(), FROM_UNIXTIME(?), ?, ?, ?, ?, 1) - ON DUPLICATE KEY UPDATE updated_at = FROM_UNIXTIME(?), alias = ?, alias_search = ?, color = ?, sockets = ?, status = 1`; + VALUES (?, NOW(), FROM_UNIXTIME(?), ?, ?, ?, ?, 1, ?) + ON DUPLICATE KEY UPDATE + updated_at = FROM_UNIXTIME(?), + alias = ?, + alias_search = ?, + color = ?, + sockets = ?, + status = 1, + features = ? + `; await DB.query(query, [ node.pub_key, @@ -668,11 +677,13 @@ class NodesApi { this.aliasToSearchText(node.alias), node.color, sockets, + JSON.stringify(node.features), node.last_update, node.alias, this.aliasToSearchText(node.alias), node.color, sockets, + JSON.stringify(node.features), ]); } catch (e) { logger.err('$saveNode() error: ' + (e instanceof Error ? e.message : e)); diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 084965383..75296ded2 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -2,8 +2,94 @@ import { ILightningApi } from '../lightning-api.interface'; import FundingTxFetcher from '../../../tasks/lightning/sync-tasks/funding-tx-fetcher'; import logger from '../../../logger'; import { Common } from '../../common'; +import { hex2bin } from '../../../utils/format'; import config from '../../../config'; +// https://github.com/lightningnetwork/lnd/blob/master/lnwire/features.go +enum FeatureBits { + DataLossProtectRequired = 0, + DataLossProtectOptional = 1, + InitialRoutingSync = 3, + UpfrontShutdownScriptRequired = 4, + UpfrontShutdownScriptOptional = 5, + GossipQueriesRequired = 6, + GossipQueriesOptional = 7, + TLVOnionPayloadRequired = 8, + TLVOnionPayloadOptional = 9, + StaticRemoteKeyRequired = 12, + StaticRemoteKeyOptional = 13, + PaymentAddrRequired = 14, + PaymentAddrOptional = 15, + MPPRequired = 16, + MPPOptional = 17, + WumboChannelsRequired = 18, + WumboChannelsOptional = 19, + AnchorsRequired = 20, + AnchorsOptional = 21, + AnchorsZeroFeeHtlcTxRequired = 22, + AnchorsZeroFeeHtlcTxOptional = 23, + ShutdownAnySegwitRequired = 26, + ShutdownAnySegwitOptional = 27, + AMPRequired = 30, + AMPOptional = 31, + ExplicitChannelTypeRequired = 44, + ExplicitChannelTypeOptional = 45, + ScidAliasRequired = 46, + ScidAliasOptional = 47, + PaymentMetadataRequired = 48, + PaymentMetadataOptional = 49, + ZeroConfRequired = 50, + ZeroConfOptional = 51, + KeysendRequired = 54, + KeysendOptional = 55, + ScriptEnforcedLeaseRequired = 2022, + ScriptEnforcedLeaseOptional = 2023, + MaxBolt11Feature = 5114, +}; + +// Features is a mapping of known feature bits to a descriptive name. All known +// feature bits must be assigned a name in this mapping, and feature bit pairs +// must be assigned together for correct behavior. +const FeaturesMap = new Map([ + [FeatureBits.DataLossProtectRequired, 'data-loss-protect'], + [FeatureBits.DataLossProtectOptional, 'data-loss-protect'], + [FeatureBits.InitialRoutingSync, 'initial-routing-sync'], + [FeatureBits.UpfrontShutdownScriptRequired, 'upfront-shutdown-script'], + [FeatureBits.UpfrontShutdownScriptOptional, 'upfront-shutdown-script'], + [FeatureBits.GossipQueriesRequired, 'gossip-queries'], + [FeatureBits.GossipQueriesOptional, 'gossip-queries'], + [FeatureBits.TLVOnionPayloadRequired, 'tlv-onion'], + [FeatureBits.TLVOnionPayloadOptional, 'tlv-onion'], + [FeatureBits.StaticRemoteKeyOptional, 'static-remote-key'], + [FeatureBits.StaticRemoteKeyRequired, 'static-remote-key'], + [FeatureBits.PaymentAddrOptional, 'payment-addr'], + [FeatureBits.PaymentAddrRequired, 'payment-addr'], + [FeatureBits.MPPOptional, 'multi-path-payments'], + [FeatureBits.MPPRequired, 'multi-path-payments'], + [FeatureBits.AnchorsRequired, 'anchor-commitments'], + [FeatureBits.AnchorsOptional, 'anchor-commitments'], + [FeatureBits.AnchorsZeroFeeHtlcTxRequired, 'anchors-zero-fee-htlc-tx'], + [FeatureBits.AnchorsZeroFeeHtlcTxOptional, 'anchors-zero-fee-htlc-tx'], + [FeatureBits.WumboChannelsRequired, 'wumbo-channels'], + [FeatureBits.WumboChannelsOptional, 'wumbo-channels'], + [FeatureBits.AMPRequired, 'amp'], + [FeatureBits.AMPOptional, 'amp'], + [FeatureBits.PaymentMetadataOptional, 'payment-metadata'], + [FeatureBits.PaymentMetadataRequired, 'payment-metadata'], + [FeatureBits.ExplicitChannelTypeOptional, 'explicit-commitment-type'], + [FeatureBits.ExplicitChannelTypeRequired, 'explicit-commitment-type'], + [FeatureBits.KeysendOptional, 'keysend'], + [FeatureBits.KeysendRequired, 'keysend'], + [FeatureBits.ScriptEnforcedLeaseRequired, 'script-enforced-lease'], + [FeatureBits.ScriptEnforcedLeaseOptional, 'script-enforced-lease'], + [FeatureBits.ScidAliasRequired, 'scid-alias'], + [FeatureBits.ScidAliasOptional, 'scid-alias'], + [FeatureBits.ZeroConfRequired, 'zero-conf'], + [FeatureBits.ZeroConfOptional, 'zero-conf'], + [FeatureBits.ShutdownAnySegwitRequired, 'shutdown-any-segwit'], + [FeatureBits.ShutdownAnySegwitOptional, 'shutdown-any-segwit'], +]); + /** * Convert a clightning "listnode" entry to a lnd node entry */ @@ -17,10 +103,34 @@ export function convertNode(clNode: any): ILightningApi.Node { custom_records = undefined; } } + + const nodeFeatures: ILightningApi.Feature[] = []; + const nodeFeaturesBinary = hex2bin(clNode.features).split('').reverse().join(''); + + for (let i = 0; i < nodeFeaturesBinary.length; i++) { + if (nodeFeaturesBinary[i] === '0') { + continue; + } + const feature = FeaturesMap.get(i); + if (!feature) { + nodeFeatures.push({ + name: 'unknown', + is_required: i % 2 === 0, + is_known: false + }); + } else { + nodeFeatures.push({ + name: feature, + is_required: i % 2 === 0, + is_known: true + }); + } + } + return { alias: clNode.alias ?? '', color: `#${clNode.color ?? ''}`, - features: [], // TODO parse and return clNode.feature + features: nodeFeatures, pub_key: clNode.nodeid, addresses: clNode.addresses?.map((addr) => { let address = addr.address; diff --git a/backend/src/tasks/lightning/network-sync.service.ts b/backend/src/tasks/lightning/network-sync.service.ts index 6785b0e2d..963b9e8c2 100644 --- a/backend/src/tasks/lightning/network-sync.service.ts +++ b/backend/src/tasks/lightning/network-sync.service.ts @@ -3,7 +3,6 @@ import logger from '../../logger'; import channelsApi from '../../api/explorer/channels.api'; import bitcoinApi from '../../api/bitcoin/bitcoin-api-factory'; import config from '../../config'; -import { IEsploraApi } from '../../api/bitcoin/esplora-api.interface'; import { ILightningApi } from '../../api/lightning/lightning-api.interface'; import { $lookupNodeLocation } from './sync-tasks/node-locations'; import lightningApi from '../../api/lightning/lightning-api-factory'; diff --git a/backend/src/utils/format.ts b/backend/src/utils/format.ts index a18ce1892..9017f349f 100644 --- a/backend/src/utils/format.ts +++ b/backend/src/utils/format.ts @@ -26,4 +26,37 @@ export function formatBytes(bytes: number, toUnit: string, skipUnit = false): st } return `${bytes.toFixed(2)}${skipUnit ? '' : ' ' + byteUnits[unitIndex]}`; +} + +// https://stackoverflow.com/a/64235212 +export function hex2bin(hex): string { + if (!hex) { + return ''; + } + + hex = hex.replace('0x', '').toLowerCase(); + let out = ''; + + for (const c of hex) { + switch (c) { + case '0': out += '0000'; break; + case '1': out += '0001'; break; + case '2': out += '0010'; break; + case '3': out += '0011'; break; + case '4': out += '0100'; break; + case '5': out += '0101'; break; + case '6': out += '0110'; break; + case '7': out += '0111'; break; + case '8': out += '1000'; break; + case '9': out += '1001'; break; + case 'a': out += '1010'; break; + case 'b': out += '1011'; break; + case 'c': out += '1100'; break; + case 'd': out += '1101'; break; + case 'e': out += '1110'; break; + case 'f': out += '1111'; break; + default: return ''; + } + } + return out; } \ No newline at end of file From 32d46ad7ac4f2434219712d9024abe1e89400c3c Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 5 Jul 2023 11:17:04 +0200 Subject: [PATCH 2/8] [lightning] save bit number when converting features from clightning --- backend/src/api/explorer/nodes.api.ts | 5 ++++- backend/src/api/lightning/clightning/clightning-convert.ts | 2 ++ backend/src/api/lightning/lightning-api.interface.ts | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index f5c87fbd1..dc359f914 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -56,7 +56,8 @@ class NodesApi { UNIX_TIMESTAMP(updated_at) AS updated_at, color, sockets as sockets, as_number, city_id, country_id, subdivision_id, longitude, latitude, geo_names_iso.names as iso_code, geo_names_as.names as as_organization, geo_names_city.names as city, - geo_names_country.names as country, geo_names_subdivision.names as subdivision + geo_names_country.names as country, geo_names_subdivision.names as subdivision, + features FROM nodes LEFT JOIN geo_names geo_names_as on geo_names_as.id = as_number LEFT JOIN geo_names geo_names_city on geo_names_city.id = city_id @@ -75,6 +76,8 @@ class NodesApi { node.subdivision = JSON.parse(node.subdivision); node.city = JSON.parse(node.city); node.country = JSON.parse(node.country); + + node.features = JSON.parse(node.features); // Active channels and capacity const activeChannelsStats: any = await this.$getActiveChannelsStats(public_key); diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 75296ded2..84510d0fd 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -114,12 +114,14 @@ export function convertNode(clNode: any): ILightningApi.Node { const feature = FeaturesMap.get(i); if (!feature) { nodeFeatures.push({ + bit: i, name: 'unknown', is_required: i % 2 === 0, is_known: false }); } else { nodeFeatures.push({ + bit: i, name: feature, is_required: i % 2 === 0, is_known: true diff --git a/backend/src/api/lightning/lightning-api.interface.ts b/backend/src/api/lightning/lightning-api.interface.ts index cd5cb973d..ef26646a0 100644 --- a/backend/src/api/lightning/lightning-api.interface.ts +++ b/backend/src/api/lightning/lightning-api.interface.ts @@ -79,6 +79,7 @@ export namespace ILightningApi { } export interface Feature { + bit: number; name: string; is_required: boolean; is_known: boolean; From 4d41d36fe768493ef98716502b741decdcd8799a Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 5 Jul 2023 11:28:13 +0200 Subject: [PATCH 3/8] [lightning] save feature bit number when using lnd describegraph --- backend/src/api/lightning/lnd/lnd-api.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/backend/src/api/lightning/lnd/lnd-api.ts b/backend/src/api/lightning/lnd/lnd-api.ts index b4c91d36e..eb48b5f96 100644 --- a/backend/src/api/lightning/lnd/lnd-api.ts +++ b/backend/src/api/lightning/lnd/lnd-api.ts @@ -41,8 +41,23 @@ class LndApi implements AbstractLightningApi { } async $getNetworkGraph(): Promise { - return axios.get(config.LND.REST_API_URL + '/v1/graph', this.axiosConfig) + const graph = await axios.get(config.LND.REST_API_URL + '/v1/graph', this.axiosConfig) .then((response) => response.data); + + for (const node of graph.nodes) { + const nodeFeatures: ILightningApi.Feature[] = []; + for (const bit in node.features) { + nodeFeatures.push({ + bit: parseInt(bit, 10), + name: node.features[bit].name, + is_required: node.features[bit].is_required, + is_known: node.features[bit].is_known, + }); + } + node.features = nodeFeatures; + } + + return graph; } } From 556eb65320d358d9d4812cac27b3ea497774dc4e Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 5 Jul 2023 14:00:30 +0200 Subject: [PATCH 4/8] [lightning] start integrating features bits in the node page --- backend/src/api/explorer/nodes.api.ts | 13 ++++++- .../clightning/clightning-convert.ts | 7 ++-- backend/src/api/lightning/lnd/lnd-api.ts | 4 +-- backend/src/utils/format.ts | 35 ++++++++++++++++++- .../app/lightning/node/node.component.html | 19 ++++++++-- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index dc359f914..40e106345 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -3,6 +3,7 @@ import DB from '../../database'; import { ResultSetHeader } from 'mysql2'; import { ILightningApi } from '../lightning/lightning-api.interface'; import { ITopNodesPerCapacity, ITopNodesPerChannels } from '../../mempool.interfaces'; +import { bin2hex } from '../../utils/format'; class NodesApi { public async $getWorldNodes(): Promise { @@ -76,8 +77,18 @@ class NodesApi { node.subdivision = JSON.parse(node.subdivision); node.city = JSON.parse(node.city); node.country = JSON.parse(node.country); - + + // Features node.features = JSON.parse(node.features); + let maxBit = 0; + for (const feature of node.features) { + maxBit = Math.max(maxBit, feature.bit); + } + node.featuresBits = new Array(maxBit + 1).fill(0); + for (const feature of node.features) { + node.featuresBits[feature.bit] = 1; + } + node.featuresBits = bin2hex(node.featuresBits.reverse().join('')); // Active channels and capacity const activeChannelsStats: any = await this.$getActiveChannelsStats(public_key); diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 84510d0fd..771dabcd7 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -6,7 +6,7 @@ import { hex2bin } from '../../../utils/format'; import config from '../../../config'; // https://github.com/lightningnetwork/lnd/blob/master/lnwire/features.go -enum FeatureBits { +export enum FeatureBits { DataLossProtectRequired = 0, DataLossProtectOptional = 1, InitialRoutingSync = 3, @@ -47,10 +47,7 @@ enum FeatureBits { MaxBolt11Feature = 5114, }; -// Features is a mapping of known feature bits to a descriptive name. All known -// feature bits must be assigned a name in this mapping, and feature bit pairs -// must be assigned together for correct behavior. -const FeaturesMap = new Map([ +export const FeaturesMap = new Map([ [FeatureBits.DataLossProtectRequired, 'data-loss-protect'], [FeatureBits.DataLossProtectOptional, 'data-loss-protect'], [FeatureBits.InitialRoutingSync, 'initial-routing-sync'], diff --git a/backend/src/api/lightning/lnd/lnd-api.ts b/backend/src/api/lightning/lnd/lnd-api.ts index eb48b5f96..f4099e82b 100644 --- a/backend/src/api/lightning/lnd/lnd-api.ts +++ b/backend/src/api/lightning/lnd/lnd-api.ts @@ -46,10 +46,10 @@ class LndApi implements AbstractLightningApi { for (const node of graph.nodes) { const nodeFeatures: ILightningApi.Feature[] = []; - for (const bit in node.features) { + for (const bit in node.features) { nodeFeatures.push({ bit: parseInt(bit, 10), - name: node.features[bit].name, + name: node.features[bit].name, is_required: node.features[bit].is_required, is_known: node.features[bit].is_known, }); diff --git a/backend/src/utils/format.ts b/backend/src/utils/format.ts index 9017f349f..63dc07ae4 100644 --- a/backend/src/utils/format.ts +++ b/backend/src/utils/format.ts @@ -29,7 +29,7 @@ export function formatBytes(bytes: number, toUnit: string, skipUnit = false): st } // https://stackoverflow.com/a/64235212 -export function hex2bin(hex): string { +export function hex2bin(hex: string): string { if (!hex) { return ''; } @@ -58,5 +58,38 @@ export function hex2bin(hex): string { default: return ''; } } + return out; +} + +export function bin2hex(bin: string): string { + if (!bin) { + return ''; + } + + let out = ''; + + for (let i = 0; i < bin.length; i += 4) { + const c = bin.substring(i, i + 4); + switch (c) { + case '0000': out += '0'; break; + case '0001': out += '1'; break; + case '0010': out += '2'; break; + case '0011': out += '3'; break; + case '0100': out += '4'; break; + case '0101': out += '5'; break; + case '0110': out += '6'; break; + case '0111': out += '7'; break; + case '1000': out += '8'; break; + case '1001': out += '9'; break; + case '1010': out += 'a'; break; + case '1011': out += 'b'; break; + case '1100': out += 'c'; break; + case '1101': out += 'd'; break; + case '1110': out += 'e'; break; + case '1111': out += 'f'; break; + default: return ''; + } + } + return out; } \ No newline at end of file diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 2a74a68aa..c3903e915 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -21,7 +21,6 @@
-
@@ -59,6 +58,9 @@ + + +
Avg channel distance {{ avgDistance | amountShortener: 1 }} km ·{{ kmToMiles(avgDistance) | amountShortener: 1 }} mi
@@ -100,13 +102,26 @@ + + + + + +
- + + Features + + {{ bits }} + + + +
From 6336c529edcf297909142627b5f75be7ab55fe40 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Sat, 8 Jul 2023 10:43:37 +0200 Subject: [PATCH 5/8] [lightning] show decoded features in node page --- .../app/lightning/node/node.component.html | 28 ++++++++++++++++++- .../src/app/lightning/node/node.component.ts | 7 ++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index c3903e915..0ed1c78c9 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -118,10 +118,36 @@ Features {{ bits }} - + +
+
+
+
+
Raw bits
+ {{ node.featuresBits }} +
+
Decoded
+ + + + + + + + + + + + + +
BitNameRequired
{{ feature.bit }}{{ feature.name }}{{ feature.is_required }}
+
+
+
+
diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index 47f65007f..719136d79 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -37,7 +37,7 @@ export class NodeComponent implements OnInit { liquidityAd: ILiquidityAd; tlvRecords: CustomRecord[]; avgChannelDistance$: Observable; - + showFeatures = false; kmToMiles = kmToMiles; constructor( @@ -164,4 +164,9 @@ export class NodeComponent implements OnInit { onLoadingEvent(e) { this.channelListLoading = e; } + + toggleFeatures() { + this.showFeatures = !this.showFeatures; + return false; + } } From 8fb67a914cc3eb12e9d6e74b9f22b08b56ce419a Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Sat, 8 Jul 2023 10:59:43 +0200 Subject: [PATCH 6/8] [lightning] fix node features binary conversion --- backend/src/api/explorer/nodes.api.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 40e106345..7d78559c4 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -84,6 +84,8 @@ class NodesApi { for (const feature of node.features) { maxBit = Math.max(maxBit, feature.bit); } + maxBit = Math.ceil(maxBit / 4) * 4 - 1; + node.featuresBits = new Array(maxBit + 1).fill(0); for (const feature of node.features) { node.featuresBits[feature.bit] = 1; From 6fe32cdd191259f53d95a3448ef912a6d319ed89 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Sun, 16 Jul 2023 18:24:42 +0900 Subject: [PATCH 7/8] [lightning] fix issue during initial node.features indexing --- backend/src/api/explorer/nodes.api.ts | 23 +++++++++++-------- .../app/lightning/node/node.component.html | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/backend/src/api/explorer/nodes.api.ts b/backend/src/api/explorer/nodes.api.ts index 7d78559c4..22f9ca48a 100644 --- a/backend/src/api/explorer/nodes.api.ts +++ b/backend/src/api/explorer/nodes.api.ts @@ -80,17 +80,20 @@ class NodesApi { // Features node.features = JSON.parse(node.features); - let maxBit = 0; - for (const feature of node.features) { - maxBit = Math.max(maxBit, feature.bit); + node.featuresBits = null; + if (node.features) { + let maxBit = 0; + for (const feature of node.features) { + maxBit = Math.max(maxBit, feature.bit); + } + maxBit = Math.ceil(maxBit / 4) * 4 - 1; + + node.featuresBits = new Array(maxBit + 1).fill(0); + for (const feature of node.features) { + node.featuresBits[feature.bit] = 1; + } + node.featuresBits = bin2hex(node.featuresBits.reverse().join('')); } - maxBit = Math.ceil(maxBit / 4) * 4 - 1; - - node.featuresBits = new Array(maxBit + 1).fill(0); - for (const feature of node.features) { - node.featuresBits[feature.bit] = 1; - } - node.featuresBits = bin2hex(node.featuresBits.reverse().join('')); // Active channels and capacity const activeChannelsStats: any = await this.$getActiveChannelsStats(public_key); diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 0ed1c78c9..47b10184e 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -102,10 +102,10 @@ - + - + From 6ab3b89884c8d7631e45e23a0f1a91070e783556 Mon Sep 17 00:00:00 2001 From: softsimon Date: Mon, 17 Jul 2023 17:41:38 +0900 Subject: [PATCH 8/8] Change to a Details-button --- frontend/src/app/lightning/node/node.component.html | 2 +- frontend/src/styles.scss | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/lightning/node/node.component.html b/frontend/src/app/lightning/node/node.component.html index 47b10184e..c6c693a3a 100644 --- a/frontend/src/app/lightning/node/node.component.html +++ b/frontend/src/app/lightning/node/node.component.html @@ -118,7 +118,7 @@ Features {{ bits }} - + diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index ac299a547..428752d60 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -1164,3 +1164,10 @@ app-master-page, app-liquid-master-page, app-bisq-master-page { app-global-footer { margin-top: auto; } + +.btn-xs { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 0.5; + border-radius: 0.2rem; +}