diff --git a/src/server/modules/api/lnmarkets/lnmarkets.module.ts b/src/server/modules/api/lnmarkets/lnmarkets.module.ts index bfa0e8ae..196479c2 100644 --- a/src/server/modules/api/lnmarkets/lnmarkets.module.ts +++ b/src/server/modules/api/lnmarkets/lnmarkets.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { FetchModule } from '../../fetch/fetch.module'; import { NodeModule } from '../../node/node.module'; +import { LnUrlModule } from '../lnurl/lnurl.module'; import { LnMarketsResolver } from './lnmarkets.resolver'; import { LnMarketsService } from './lnmarkets.service'; @Module({ - imports: [NodeModule, FetchModule], + imports: [LnUrlModule, NodeModule, FetchModule], providers: [LnMarketsService, LnMarketsResolver], }) export class LnMarketsModule {} diff --git a/src/server/modules/api/lnmarkets/lnmarkets.service.ts b/src/server/modules/api/lnmarkets/lnmarkets.service.ts index 4312b152..e305b410 100644 --- a/src/server/modules/api/lnmarkets/lnmarkets.service.ts +++ b/src/server/modules/api/lnmarkets/lnmarkets.service.ts @@ -1,26 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; -import { NodeService } from '../../node/node.service'; -import hmacSHA256 from 'crypto-js/hmac-sha256'; -import bip39 from 'bip39'; -import BIP32Factory, { BIP32Interface } from 'bip32'; -import secp256k1 from 'secp256k1'; -import * as ecc from 'tiny-secp256k1'; -import { enc } from 'crypto-js'; import { FetchService } from '../../fetch/fetch.service'; import { bech32 } from 'bech32'; import { ConfigService } from '@nestjs/config'; - -const bip32 = BIP32Factory(ecc); - -const fromHexString = (hexString: string) => - new Uint8Array( - hexString.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || [] - ); - -const toHexString = (bytes: Uint8Array) => - bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); +import { LnUrlService } from '../lnurl/lnurl.service'; const decodeLnUrl = (url: string): string => { const cleanUrl = url.toLowerCase().replace('lightning:', ''); @@ -34,7 +18,7 @@ export class LnMarketsService { constructor( private configService: ConfigService, private fetchService: FetchService, - private nodeService: NodeService, + private lnUrlService: LnUrlService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger ) {} @@ -105,65 +89,6 @@ export class LnMarketsService { } } - async lnAuthUrlGenerator(url: string, id: string) { - const domainUrl = new URL(url); - const host = domainUrl.host; - - const k1 = domainUrl.searchParams.get('k1'); - - if (!host || !k1) { - this.logger.error('Missing host or k1 in url', { url }); - throw new Error('WrongUrlFormat'); - } - - const wallet = await this.nodeService.getWalletInfo(id); - - // Generate entropy - const secret = await this.nodeService.diffieHellmanComputeSecret(id, { - key_family: 138, - key_index: 0, - partner_public_key: wallet?.public_key, - }); - - // Generate hash from host and entropy - const hashed = hmacSHA256(host, secret.secret).toString(enc.Hex); - - const indexes = - hashed.match(/.{1,4}/g)?.map(index => parseInt(index, 16)) || []; - - // Generate private seed from entropy - const secretKey = bip39.entropyToMnemonic(hashed); - const base58 = bip39.mnemonicToSeedSync(secretKey); - - // Derive private seed from previous private seed and path - const node: BIP32Interface = bip32.fromSeed(base58); - const derived = node.derivePath( - `m/138/${indexes[0]}/${indexes[1]}/${indexes[2]}/${indexes[3]}` - ); - - // Get private and public key from derived private seed - const privateKey = derived.privateKey?.toString('hex'); - const linkingKey = derived.publicKey.toString('hex'); - - if (!privateKey || !linkingKey) { - this.logger.error('Error deriving private or public key', { url }); - throw new Error('ErrorDerivingPrivateKey'); - } - - // Sign k1 with derived private seed - const sigObj = secp256k1.ecdsaSign( - fromHexString(k1), - fromHexString(privateKey) - ); - - // Get signature - const signature = secp256k1.signatureExport(sigObj.signature); - const encodedSignature = toHexString(signature); - - // Build and return final url with signature and public key - return `${url}&sig=${encodedSignature}&key=${linkingKey}`; - } - async getLnMarketsAuth( id: string, cookie?: string | null @@ -212,7 +137,7 @@ export class LnMarketsService { // Decode the LnUrl and authenticate with it const decoded = decodeLnUrl(lnUrl); - const finalUrl = await this.lnAuthUrlGenerator(decoded, id); + const finalUrl = await this.lnUrlService.lnAuthUrlGenerator(id, decoded); // Try to authenticate with lnMarkets try { diff --git a/src/server/modules/api/lnurl/lnurl.service.ts b/src/server/modules/api/lnurl/lnurl.service.ts index 52dd59d2..03985dc5 100644 --- a/src/server/modules/api/lnurl/lnurl.service.ts +++ b/src/server/modules/api/lnurl/lnurl.service.ts @@ -5,7 +5,7 @@ import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import { Logger } from 'winston'; import { enc } from 'crypto-js'; import hmacSHA256 from 'crypto-js/hmac-sha256'; -import bip39 from 'bip39'; +import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'; import BIP32Factory, { BIP32Interface } from 'bip32'; import secp256k1 from 'secp256k1'; import * as ecc from 'tiny-secp256k1'; @@ -54,8 +54,8 @@ export class LnUrlService { hashed.match(/.{1,4}/g)?.map(index => parseInt(index, 16)) || []; // Generate private seed from entropy - const secretKey = bip39.entropyToMnemonic(hashed); - const base58 = bip39.mnemonicToSeedSync(secretKey); + const secretKey = entropyToMnemonic(hashed); + const base58 = mnemonicToSeedSync(secretKey); // Derive private seed from previous private seed and path const node: BIP32Interface = bip32.fromSeed(base58);