Merge pull request #677 from shocknet/cleanup-ln-interface

cleanup
This commit is contained in:
boufni95 2024-04-26 17:12:13 +02:00 committed by GitHub
commit 1debe0baf4
8 changed files with 16 additions and 206 deletions

View file

@ -1,10 +1,5 @@
import * as Types from '../../../proto/autogenerated/ts/types.js'
import { GetInfoResponse, NewAddressResponse, AddInvoiceResponse, PayReq, Payment, SendCoinsResponse, EstimateFeeResponse, TransactionDetails, ClosedChannelsResponse, ListChannelsResponse, PendingChannelsResponse, ListInvoiceResponse, ListPaymentsResponse, ChannelBalanceResponse, WalletBalanceResponse } from '../../../proto/lnd/lightning.js'
import { EnvMustBeNonEmptyString, EnvMustBeInteger, EnvCanBeBoolean } from '../helpers/envParser.js' import { EnvMustBeNonEmptyString, EnvMustBeInteger, EnvCanBeBoolean } from '../helpers/envParser.js'
import { AddressPaidCb, BalanceInfo, DecodedInvoice, HtlcCb, Invoice, InvoicePaidCb, LndSettings, NewBlockCb, NodeInfo, PaidInvoice } from './settings.js' import { LndSettings } from './settings.js'
import LND from './lnd.js'
import MockLnd from './mock.js'
import { getLogger } from '../helpers/logger.js'
export const LoadLndSettingsFromEnv = (): LndSettings => { export const LoadLndSettingsFromEnv = (): LndSettings => {
const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS") const lndAddr = EnvMustBeNonEmptyString("LND_ADDRESS")
const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH") const lndCertPath = EnvMustBeNonEmptyString("LND_CERT_PATH")
@ -14,42 +9,3 @@ export const LoadLndSettingsFromEnv = (): LndSettings => {
const mockLnd = EnvCanBeBoolean("MOCK_LND") const mockLnd = EnvCanBeBoolean("MOCK_LND")
return { mainNode: { lndAddr, lndCertPath, lndMacaroonPath }, feeRateLimit, feeFixedLimit, mockLnd } return { mainNode: { lndAddr, lndCertPath, lndMacaroonPath }, feeRateLimit, feeFixedLimit, mockLnd }
} }
export interface LightningHandler {
Stop(): void
Warmup(): Promise<void>
GetInfo(): Promise<NodeInfo>
Health(): Promise<void>
NewAddress(addressType: Types.AddressType): Promise<NewAddressResponse>
NewInvoice(value: number, memo: string, expiry: number): Promise<Invoice>
DecodeInvoice(paymentRequest: string): Promise<DecodedInvoice>
GetFeeLimitAmount(amount: number): number
GetMaxWithinLimit(amount: number): number
PayInvoice(invoice: string, amount: number, feeLimit: number): Promise<PaidInvoice>
EstimateChainFees(address: string, amount: number, targetConf: number): Promise<EstimateFeeResponse>
PayAddress(address: string, amount: number, satPerVByte: number, label?: string): Promise<SendCoinsResponse>
//OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise<string>
SetMockInvoiceAsPaid(invoice: string, amount: number): Promise<void>
ChannelBalance(): Promise<{ local: number, remote: number }>
GetTransactions(startHeight: number): Promise<TransactionDetails>
GetBalance(): Promise<BalanceInfo>
GetWalletBalance(): Promise<WalletBalanceResponse>
GetChannelBalance(): Promise<ChannelBalanceResponse>
ListClosedChannels(): Promise<ClosedChannelsResponse>
ListChannels(): Promise<ListChannelsResponse>
ListPendingChannels(): Promise<PendingChannelsResponse>
GetForwardingHistory(indexOffset: number): Promise<{ fee: number, chanIdIn: string, chanIdOut: string, timestampNs: number, offset: number }[]>
GetAllPaidInvoices(max: number): Promise<ListInvoiceResponse>
GetAllPayments(max: number): Promise<ListPaymentsResponse>
LockOutgoingOperations(): void
UnlockOutgoingOperations(): void
}
export default (settings: LndSettings, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb, newBlockCb: NewBlockCb, htlcCb: HtlcCb): LightningHandler => {
if (settings.mockLnd) {
getLogger({})("registering mock lnd handler")
return new MockLnd(settings, addressPaidCb, invoicePaidCb, newBlockCb)
} else {
getLogger({})("registering prod lnd handler")
return new LND(settings, addressPaidCb, invoicePaidCb, newBlockCb, htlcCb)
}
}

View file

@ -1,145 +0,0 @@
//const grpc = require('@grpc/grpc-js');
import { credentials, Metadata } from '@grpc/grpc-js'
import { GrpcTransport } from "@protobuf-ts/grpc-transport";
import fs from 'fs'
import crypto from 'crypto'
import * as Types from '../../../proto/autogenerated/ts/types.js'
import { LightningClient } from '../../../proto/lnd/lightning.client.js'
import { InvoicesClient } from '../../../proto/lnd/invoices.client.js'
import { RouterClient } from '../../../proto/lnd/router.client.js'
import { GetInfoResponse, AddressType, NewAddressResponse, AddInvoiceResponse, Invoice_InvoiceState, PayReq, Payment_PaymentStatus, Payment, PaymentFailureReason, SendCoinsResponse, EstimateFeeResponse, TransactionDetails, ClosedChannelsResponse, ListChannelsResponse, PendingChannelsResponse, ListInvoiceResponse, ListPaymentsResponse, ChannelBalanceResponse, WalletBalanceResponse } from '../../../proto/lnd/lightning.js'
import { OpenChannelReq } from './openChannelReq.js';
import { AddInvoiceReq } from './addInvoiceReq.js';
import { PayInvoiceReq } from './payInvoiceReq.js';
import { SendCoinsReq } from './sendCoinsReq.js';
import { LndSettings, AddressPaidCb, InvoicePaidCb, NodeInfo, Invoice, DecodedInvoice, PaidInvoice, NewBlockCb, BalanceInfo } from './settings.js';
import { getLogger } from '../helpers/logger.js';
export default class {
invoicesAwaiting: Record<string /* invoice */, { value: number, memo: string, expiryUnix: number }> = {}
settings: LndSettings
abortController = new AbortController()
addressPaidCb: AddressPaidCb
invoicePaidCb: InvoicePaidCb
constructor(settings: LndSettings, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb, newBlockCb: NewBlockCb) {
this.settings = settings
this.addressPaidCb = addressPaidCb
this.invoicePaidCb = invoicePaidCb
}
async SetMockInvoiceAsPaid(invoice: string, amount: number): Promise<void> {
const decoded = await this.DecodeInvoice(invoice)
if (decoded.numSatoshis && amount) {
throw new Error("non zero amount provided to pay invoice but invoice has value already")
}
this.invoicePaidCb(invoice, decoded.numSatoshis || amount, false)
delete this.invoicesAwaiting[invoice]
}
GetChannelBalance(): Promise<ChannelBalanceResponse> {
throw new Error("Method not implemented.");
}
Stop() { }
async Warmup() { }
async GetWalletBalance(): Promise<WalletBalanceResponse> { throw new Error("ListClosedChannels disabled in mock mode") }
async ListClosedChannels(): Promise<ClosedChannelsResponse> { throw new Error("ListClosedChannels disabled in mock mode") }
async ListChannels(): Promise<ListChannelsResponse> { throw new Error("ListChannels disabled in mock mode") }
async ListPendingChannels(): Promise<PendingChannelsResponse> { throw new Error("ListPendingChannels disabled in mock mode") }
async GetForwardingHistory(indexOffset: number): Promise<{ fee: number, chanIdIn: string, chanIdOut: string, timestampNs: number, offset: number }[]> { throw new Error("GetForwardingHistory disabled in mock mode") }
async GetInfo(): Promise<NodeInfo> {
return { alias: "mock", syncedToChain: true, syncedToGraph: true, blockHeight: 1, blockHash: "", identityPubkey: "mock", uris: [] }
}
async Health(): Promise<void> { }
async NewAddress(addressType: Types.AddressType): Promise<NewAddressResponse> {
throw new Error("NewAddress disabled in mock mode")
}
async NewInvoice(value: number, memo: string, expiry: number): Promise<Invoice> {
const mockInvoice = "lnbcrtmockin" + crypto.randomBytes(32).toString('hex')
this.invoicesAwaiting[mockInvoice] = { value, memo, expiryUnix: expiry + Date.now() / 1000 }
return { payRequest: mockInvoice }
}
async DecodeInvoice(paymentRequest: string): Promise<DecodedInvoice> {
if (paymentRequest.startsWith('lnbcrtmockout')) {
const amt = this.decodeOutboundInvoice(paymentRequest)
return { numSatoshis: amt, paymentHash: paymentRequest }
}
const i = this.invoicesAwaiting[paymentRequest]
if (!i) {
throw new Error("invoice not found")
}
return { numSatoshis: i.value, paymentHash: paymentRequest }
}
GetFeeLimitAmount(amount: number): number {
return Math.ceil(amount * this.settings.feeRateLimit + this.settings.feeFixedLimit);
}
GetMaxWithinLimit(amount: number): number {
return Math.max(0, Math.floor(amount * (1 - this.settings.feeRateLimit) - this.settings.feeFixedLimit))
}
decodeOutboundInvoice(invoice: string): number {
if (!invoice.startsWith('lnbcrtmockout')) {
throw new Error("invalid mock invoice provided for payment")
}
const amt = invoice.substring('lnbcrtmockout'.length).split("__")[0]
if (isNaN(+amt)) {
throw new Error("invalid mock invoice provided for payment")
}
return +amt
}
async PayInvoice(invoice: string, amount: number, feeLimit: number): Promise<PaidInvoice> {
const log = getLogger({})
log('payng', invoice)
await new Promise(res => setTimeout(res, 200))
const amt = this.decodeOutboundInvoice(invoice)
log('paid', invoice)
return { feeSat: 1, paymentPreimage: "all_good", valueSat: amt || amount }
}
async ChannelBalance(): Promise<{ local: number, remote: number }> {
return { local: 100 * 1000 * 1000, remote: 100 * 1000 * 1000 }
}
async EstimateChainFees(address: string, amount: number, targetConf: number): Promise<EstimateFeeResponse> {
throw new Error("EstimateChainFees disabled in mock mode")
}
async PayAddress(address: string, amount: number, satPerVByte: number, label = ""): Promise<SendCoinsResponse> {
throw new Error("PayAddress disabled in mock mode")
}
async OpenChannel(destination: string, closeAddress: string, fundingAmount: number, pushSats: number): Promise<string> {
throw new Error("OpenChannel disabled in mock mode")
}
async GetTransactions(startHeight: number): Promise<TransactionDetails> {
throw new Error("GetTransactions disabled in mock mode")
}
GetBalance(): Promise<BalanceInfo> {
throw new Error("GetBalance disabled in mock mode")
}
async GetAllPaidInvoices(max: number): Promise<ListInvoiceResponse> {
throw new Error("not implemented")
}
async GetAllPayments(max: number): Promise<ListPaymentsResponse> {
throw new Error("not implemented")
}
LockOutgoingOperations() {
throw new Error("not implemented")
}
UnlockOutgoingOperations() {
throw new Error("not implemented")
}
}

View file

@ -5,7 +5,7 @@ import ProductManager from './productManager.js'
import ApplicationManager from './applicationManager.js' import ApplicationManager from './applicationManager.js'
import PaymentManager, { PendingTx } from './paymentManager.js' import PaymentManager, { PendingTx } from './paymentManager.js'
import { MainSettings } from './settings.js' import { MainSettings } from './settings.js'
import NewLightningHandler, { LightningHandler } from "../lnd/index.js" import LND from "../lnd/lnd.js"
import { AddressPaidCb, HtlcCb, InvoicePaidCb, NewBlockCb } from "../lnd/settings.js" import { AddressPaidCb, HtlcCb, InvoicePaidCb, NewBlockCb } from "../lnd/settings.js"
import { getLogger, PubLogger } from "../helpers/logger.js" import { getLogger, PubLogger } from "../helpers/logger.js"
import AppUserManager from "./appUserManager.js" import AppUserManager from "./appUserManager.js"
@ -26,7 +26,7 @@ type UserOperationsSub = {
export default class { export default class {
storage: Storage storage: Storage
lnd: LightningHandler lnd: LND
settings: MainSettings settings: MainSettings
userOperationsSub: UserOperationsSub | null = null userOperationsSub: UserOperationsSub | null = null
productManager: ProductManager productManager: ProductManager
@ -41,7 +41,7 @@ export default class {
this.settings = settings this.settings = settings
this.storage = storage this.storage = storage
this.lnd = NewLightningHandler(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb, this.htlcCb) this.lnd = new LND(settings.lndSettings, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb, this.htlcCb)
this.metricsManager = new MetricsManager(this.storage, this.lnd) this.metricsManager = new MetricsManager(this.storage, this.lnd)
this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.addressPaidCb, this.invoicePaidCb) this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.addressPaidCb, this.invoicePaidCb)

View file

@ -4,7 +4,7 @@ import Storage from '../storage/index.js'
import * as Types from '../../../proto/autogenerated/ts/types.js' import * as Types from '../../../proto/autogenerated/ts/types.js'
import { MainSettings } from './settings.js' import { MainSettings } from './settings.js'
import { InboundOptionals, defaultInvoiceExpiry } from '../storage/paymentStorage.js' import { InboundOptionals, defaultInvoiceExpiry } from '../storage/paymentStorage.js'
import { LightningHandler } from '../lnd/index.js' import LND from '../lnd/lnd.js'
import { Application } from '../storage/entity/Application.js' import { Application } from '../storage/entity/Application.js'
import { getLogger } from '../helpers/logger.js' import { getLogger } from '../helpers/logger.js'
import { UserReceivingAddress } from '../storage/entity/UserReceivingAddress.js' import { UserReceivingAddress } from '../storage/entity/UserReceivingAddress.js'
@ -42,12 +42,12 @@ export default class {
storage: Storage storage: Storage
settings: MainSettings settings: MainSettings
lnd: LightningHandler lnd: LND
addressPaidCb: AddressPaidCb addressPaidCb: AddressPaidCb
invoicePaidCb: InvoicePaidCb invoicePaidCb: InvoicePaidCb
log = getLogger({ appName: "PaymentManager" }) log = getLogger({ appName: "PaymentManager" })
watchDog: Watchdog watchDog: Watchdog
constructor(storage: Storage, lnd: LightningHandler, settings: MainSettings, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) { constructor(storage: Storage, lnd: LND, settings: MainSettings, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) {
this.storage = storage this.storage = storage
this.settings = settings this.settings = settings
this.lnd = lnd this.lnd = lnd

View file

@ -1,5 +1,5 @@
import Storage from '../storage/index.js' import Storage from '../storage/index.js'
import { LightningHandler } from "../lnd/index.js" import LND from "../lnd/lnd.js"
import { LoggedEvent } from '../storage/eventsLog.js' import { LoggedEvent } from '../storage/eventsLog.js'
import { Invoice, Payment } from '../../../proto/lnd/lightning'; import { Invoice, Payment } from '../../../proto/lnd/lightning';
import { getLogger } from '../helpers/logger.js'; import { getLogger } from '../helpers/logger.js';
@ -12,7 +12,7 @@ type Reason = UniqueDecrementReasons | UniqueIncrementReasons | CommonReasons
const incrementTwiceAllowed = ['fees', 'ban'] const incrementTwiceAllowed = ['fees', 'ban']
export default class SanityChecker { export default class SanityChecker {
storage: Storage storage: Storage
lnd: LightningHandler lnd: LND
events: LoggedEvent[] = [] events: LoggedEvent[] = []
invoices: Invoice[] = [] invoices: Invoice[] = []
@ -22,7 +22,7 @@ export default class SanityChecker {
decrementEvents: Record<string, { userId: string, refund: number, failure: boolean }> = {} decrementEvents: Record<string, { userId: string, refund: number, failure: boolean }> = {}
log = getLogger({ appName: "SanityChecker" }) log = getLogger({ appName: "SanityChecker" })
users: Record<string, { ts: number, updatedBalance: number }> = {} users: Record<string, { ts: number, updatedBalance: number }> = {}
constructor(storage: Storage, lnd: LightningHandler) { constructor(storage: Storage, lnd: LND) {
this.storage = storage this.storage = storage
this.lnd = lnd this.lnd = lnd
} }

View file

@ -1,6 +1,6 @@
import { EnvCanBeInteger } from "../helpers/envParser.js"; import { EnvCanBeInteger } from "../helpers/envParser.js";
import { getLogger } from "../helpers/logger.js"; import { getLogger } from "../helpers/logger.js";
import { LightningHandler } from "../lnd/index.js"; import LND from "../lnd/lnd.js";
import { ChannelBalance } from "../lnd/settings.js"; import { ChannelBalance } from "../lnd/settings.js";
import Storage from '../storage/index.js' import Storage from '../storage/index.js'
export type WatchdogSettings = { export type WatchdogSettings = {
@ -15,14 +15,14 @@ export class Watchdog {
initialLndBalance: number; initialLndBalance: number;
initialUsersBalance: number; initialUsersBalance: number;
lnd: LightningHandler; lnd: LND;
settings: WatchdogSettings; settings: WatchdogSettings;
storage: Storage; storage: Storage;
latestCheckStart = 0 latestCheckStart = 0
log = getLogger({ appName: "watchdog" }) log = getLogger({ appName: "watchdog" })
enabled = false enabled = false
interval: NodeJS.Timer; interval: NodeJS.Timer;
constructor(settings: WatchdogSettings, lnd: LightningHandler, storage: Storage) { constructor(settings: WatchdogSettings, lnd: LND, storage: Storage) {
this.lnd = lnd; this.lnd = lnd;
this.settings = settings; this.settings = settings;
this.storage = storage; this.storage = storage;

View file

@ -5,15 +5,15 @@ import { HtlcEvent, HtlcEvent_EventType } from '../../../proto/lnd/router.js'
import { BalanceInfo } from '../lnd/settings.js' import { BalanceInfo } from '../lnd/settings.js'
import { BalanceEvent } from '../storage/entity/BalanceEvent.js' import { BalanceEvent } from '../storage/entity/BalanceEvent.js'
import { ChannelBalanceEvent } from '../storage/entity/ChannelsBalanceEvent.js' import { ChannelBalanceEvent } from '../storage/entity/ChannelsBalanceEvent.js'
import { LightningHandler } from '../lnd/index.js' import LND from '../lnd/lnd.js'
import HtlcTracker from './htlcTracker.js' import HtlcTracker from './htlcTracker.js'
const maxEvents = 100_000 const maxEvents = 100_000
export default class Handler { export default class Handler {
storage: Storage storage: Storage
lnd: LightningHandler lnd: LND
htlcTracker: HtlcTracker htlcTracker: HtlcTracker
metrics: Types.UsageMetric[] = [] metrics: Types.UsageMetric[] = []
constructor(storage: Storage, lnd: LightningHandler) { constructor(storage: Storage, lnd: LND) {
this.storage = storage this.storage = storage
this.lnd = lnd this.lnd = lnd
this.htlcTracker = new HtlcTracker(this.storage) this.htlcTracker = new HtlcTracker(this.storage)

View file

@ -9,7 +9,6 @@ import chaiString from 'chai-string'
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js' import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
import SanityChecker from '../services/main/sanityChecker.js' import SanityChecker from '../services/main/sanityChecker.js'
import LND from '../services/lnd/lnd.js' import LND from '../services/lnd/lnd.js'
import { LightningHandler } from '../services/lnd/index.js'
chai.use(chaiString) chai.use(chaiString)
export const expect = chai.expect export const expect = chai.expect
export type Describe = (message: string, failure?: boolean) => void export type Describe = (message: string, failure?: boolean) => void