[lightning] save node features as stringified json array in db
This commit is contained in:
		
							parent
							
								
									b03f2185ce
								
							
						
					
					
						commit
						1f003cc292
					
				@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
 | 
				
			|||||||
import { RowDataPacket } from 'mysql2';
 | 
					import { RowDataPacket } from 'mysql2';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DatabaseMigration {
 | 
					class DatabaseMigration {
 | 
				
			||||||
  private static currentVersion = 63;
 | 
					  private static currentVersion = 64;
 | 
				
			||||||
  private queryTimeout = 3600_000;
 | 
					  private queryTimeout = 3600_000;
 | 
				
			||||||
  private statisticsAddedIndexed = false;
 | 
					  private statisticsAddedIndexed = false;
 | 
				
			||||||
  private uniqueLogs: string[] = [];
 | 
					  private uniqueLogs: string[] = [];
 | 
				
			||||||
@ -543,6 +543,11 @@ class DatabaseMigration {
 | 
				
			|||||||
      await this.$executeQuery('ALTER TABLE `blocks_audits` ADD fullrbf_txs JSON DEFAULT "[]"');
 | 
					      await this.$executeQuery('ALTER TABLE `blocks_audits` ADD fullrbf_txs JSON DEFAULT "[]"');
 | 
				
			||||||
      await this.updateToSchemaVersion(63);
 | 
					      await this.updateToSchemaVersion(63);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (databaseSchemaVersion < 64 && isBitcoin === true) {
 | 
				
			||||||
 | 
					      await this.$executeQuery('ALTER TABLE `nodes` ADD features text NULL');
 | 
				
			||||||
 | 
					      await this.updateToSchemaVersion(64);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
 | 
				
			|||||||
@ -656,10 +656,19 @@ class NodesApi {
 | 
				
			|||||||
          alias_search,
 | 
					          alias_search,
 | 
				
			||||||
          color,
 | 
					          color,
 | 
				
			||||||
          sockets,
 | 
					          sockets,
 | 
				
			||||||
          status
 | 
					          status,
 | 
				
			||||||
 | 
					          features
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        VALUES (?, NOW(), FROM_UNIXTIME(?), ?, ?, ?, ?, 1)
 | 
					        VALUES (?, NOW(), FROM_UNIXTIME(?), ?, ?, ?, ?, 1, ?)
 | 
				
			||||||
        ON DUPLICATE KEY UPDATE updated_at = FROM_UNIXTIME(?), alias = ?, alias_search = ?, color = ?, sockets = ?, status = 1`;
 | 
					        ON DUPLICATE KEY UPDATE
 | 
				
			||||||
 | 
					          updated_at = FROM_UNIXTIME(?),
 | 
				
			||||||
 | 
					          alias = ?,
 | 
				
			||||||
 | 
					          alias_search = ?,
 | 
				
			||||||
 | 
					          color = ?,
 | 
				
			||||||
 | 
					          sockets = ?,
 | 
				
			||||||
 | 
					          status = 1,
 | 
				
			||||||
 | 
					          features = ?
 | 
				
			||||||
 | 
					      `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await DB.query(query, [
 | 
					      await DB.query(query, [
 | 
				
			||||||
        node.pub_key,
 | 
					        node.pub_key,
 | 
				
			||||||
@ -668,11 +677,13 @@ class NodesApi {
 | 
				
			|||||||
        this.aliasToSearchText(node.alias),
 | 
					        this.aliasToSearchText(node.alias),
 | 
				
			||||||
        node.color,
 | 
					        node.color,
 | 
				
			||||||
        sockets,
 | 
					        sockets,
 | 
				
			||||||
 | 
					        JSON.stringify(node.features),
 | 
				
			||||||
        node.last_update,
 | 
					        node.last_update,
 | 
				
			||||||
        node.alias,
 | 
					        node.alias,
 | 
				
			||||||
        this.aliasToSearchText(node.alias),
 | 
					        this.aliasToSearchText(node.alias),
 | 
				
			||||||
        node.color,
 | 
					        node.color,
 | 
				
			||||||
        sockets,
 | 
					        sockets,
 | 
				
			||||||
 | 
					        JSON.stringify(node.features),
 | 
				
			||||||
      ]);
 | 
					      ]);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      logger.err('$saveNode() error: ' + (e instanceof Error ? e.message : e));
 | 
					      logger.err('$saveNode() error: ' + (e instanceof Error ? e.message : e));
 | 
				
			||||||
 | 
				
			|||||||
@ -2,8 +2,94 @@ import { ILightningApi } from '../lightning-api.interface';
 | 
				
			|||||||
import FundingTxFetcher from '../../../tasks/lightning/sync-tasks/funding-tx-fetcher';
 | 
					import FundingTxFetcher from '../../../tasks/lightning/sync-tasks/funding-tx-fetcher';
 | 
				
			||||||
import logger from '../../../logger';
 | 
					import logger from '../../../logger';
 | 
				
			||||||
import { Common } from '../../common';
 | 
					import { Common } from '../../common';
 | 
				
			||||||
 | 
					import { hex2bin } from '../../../utils/format';
 | 
				
			||||||
import config from '../../../config';
 | 
					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, string>([
 | 
				
			||||||
 | 
						[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
 | 
					 * Convert a clightning "listnode" entry to a lnd node entry
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@ -17,10 +103,34 @@ export function convertNode(clNode: any): ILightningApi.Node {
 | 
				
			|||||||
      custom_records = undefined;
 | 
					      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 {
 | 
					  return {
 | 
				
			||||||
    alias: clNode.alias ?? '',
 | 
					    alias: clNode.alias ?? '',
 | 
				
			||||||
    color: `#${clNode.color ?? ''}`,
 | 
					    color: `#${clNode.color ?? ''}`,
 | 
				
			||||||
    features: [], // TODO parse and return clNode.feature
 | 
					    features: nodeFeatures,
 | 
				
			||||||
    pub_key: clNode.nodeid,
 | 
					    pub_key: clNode.nodeid,
 | 
				
			||||||
    addresses: clNode.addresses?.map((addr) => {
 | 
					    addresses: clNode.addresses?.map((addr) => {
 | 
				
			||||||
      let address = addr.address;
 | 
					      let address = addr.address;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ import logger from '../../logger';
 | 
				
			|||||||
import channelsApi from '../../api/explorer/channels.api';
 | 
					import channelsApi from '../../api/explorer/channels.api';
 | 
				
			||||||
import bitcoinApi from '../../api/bitcoin/bitcoin-api-factory';
 | 
					import bitcoinApi from '../../api/bitcoin/bitcoin-api-factory';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import { IEsploraApi } from '../../api/bitcoin/esplora-api.interface';
 | 
					 | 
				
			||||||
import { ILightningApi } from '../../api/lightning/lightning-api.interface';
 | 
					import { ILightningApi } from '../../api/lightning/lightning-api.interface';
 | 
				
			||||||
import { $lookupNodeLocation } from './sync-tasks/node-locations';
 | 
					import { $lookupNodeLocation } from './sync-tasks/node-locations';
 | 
				
			||||||
import lightningApi from '../../api/lightning/lightning-api-factory';
 | 
					import lightningApi from '../../api/lightning/lightning-api-factory';
 | 
				
			||||||
 | 
				
			|||||||
@ -26,4 +26,37 @@ export function formatBytes(bytes: number, toUnit: string, skipUnit = false): st
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return `${bytes.toFixed(2)}${skipUnit ? '' : ' ' + byteUnits[unitIndex]}`;
 | 
					  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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user