diff --git a/metricsDatasource.js b/metricsDatasource.js index fc8e901a..1f897c5d 100644 --- a/metricsDatasource.js +++ b/metricsDatasource.js @@ -1,10 +1,10 @@ import { DataSource } from "typeorm" -import { ChannelRouting } from "./build/src/services/storage/entity/ChannelRouting.js" +import { LspOrder } from "./build/src/services/storage/entity/LspOrder.js" export default new DataSource({ type: "sqlite", - database: "metrics.sqlite", - entities: [ChannelRouting], + database: "db.sqlite", + entities: [LspOrder], }); \ No newline at end of file diff --git a/src/services/lnd/index.ts b/src/services/lnd/index.ts index 5cc46931..765db0e6 100644 --- a/src/services/lnd/index.ts +++ b/src/services/lnd/index.ts @@ -1,5 +1,4 @@ import { EnvMustBeNonEmptyString, EnvMustBeInteger, EnvCanBeBoolean, EnvCanBeInteger } from '../helpers/envParser.js' -import { LoadLiquiditySettingsFromEnv } from './liquidityManager.js' import { LndSettings } from './settings.js' export const LoadLndSettingsFromEnv = (): LndSettings => { const lndAddr = process.env.LND_ADDRESS || "127.0.0.1:10009" @@ -8,6 +7,5 @@ export const LoadLndSettingsFromEnv = (): LndSettings => { const feeRateLimit = EnvCanBeInteger("OUTBOUND_MAX_FEE_BPS", 60) / 10000 const feeFixedLimit = EnvCanBeInteger("OUTBOUND_MAX_FEE_EXTRA_SATS", 100) const mockLnd = EnvCanBeBoolean("MOCK_LND") - const liquiditySettings = LoadLiquiditySettingsFromEnv() - return { mainNode: { lndAddr, lndCertPath, lndMacaroonPath }, feeRateLimit, feeFixedLimit, mockLnd, liquiditySettings } + return { mainNode: { lndAddr, lndCertPath, lndMacaroonPath }, feeRateLimit, feeFixedLimit, mockLnd } } diff --git a/src/services/lnd/lnd.ts b/src/services/lnd/lnd.ts index 54c3db66..2930ae3a 100644 --- a/src/services/lnd/lnd.ts +++ b/src/services/lnd/lnd.ts @@ -36,7 +36,8 @@ export default class { log = getLogger({ component: 'lndManager' }) outgoingOpsLocked = false liquidProvider: LiquidityProvider - constructor(settings: LndSettings, liquidProvider: LiquidityProvider, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb, newBlockCb: NewBlockCb, htlcCb: HtlcCb) { + useOnlyLiquidityProvider = false + constructor(settings: LndSettings, provider: { liquidProvider: LiquidityProvider, useOnly?: boolean }, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb, newBlockCb: NewBlockCb, htlcCb: HtlcCb) { this.settings = settings this.addressPaidCb = addressPaidCb this.invoicePaidCb = invoicePaidCb @@ -62,7 +63,8 @@ export default class { this.invoices = new InvoicesClient(transport) this.router = new RouterClient(transport) this.chainNotifier = new ChainNotifierClient(transport) - this.liquidProvider = liquidProvider + this.liquidProvider = provider.liquidProvider + this.useOnlyLiquidityProvider = !!provider.useOnly } LockOutgoingOperations(): void { @@ -81,7 +83,7 @@ export default class { } async ShouldUseLiquidityProvider(req: LiquidityRequest): Promise { - if (this.settings.liquiditySettings.useOnlyLiquidityProvider) { + if (this.useOnlyLiquidityProvider) { return true } if (!this.liquidProvider.CanProviderHandle(req)) { diff --git a/src/services/lnd/lsp.ts b/src/services/lnd/lsp.ts index 5011608c..9f9025c2 100644 --- a/src/services/lnd/lsp.ts +++ b/src/services/lnd/lsp.ts @@ -42,6 +42,13 @@ type FlashsatsOrder = { "token": string } +type OrderResponse = { + orderId: string + invoice: string + totalSats: number + fees: number +} + class LSP { settings: LSPSettings liquidityProvider: LiquidityProvider @@ -71,7 +78,7 @@ class LSP { } const userState = await this.liquidityProvider.CheckUserState() if (!userState || userState.max_withdrawable < this.settings.channelThreshold) { - this.log("user balance too low to trigger channel request") + this.log("balance of", userState?.max_withdrawable || 0, "is lower than channel threshold of", this.settings.channelThreshold) return { shouldOpen: false } } return { shouldOpen: true, maxSpendable: userState.max_withdrawable } @@ -91,50 +98,50 @@ export class FlashsatsLSP extends LSP { super("FlashsatsLSP", settings, lnd, liquidityProvider) } - openChannelIfReady = async (): Promise => { + openChannelIfReady = async (): Promise => { const shouldOpen = await this.shouldOpenChannel() if (!shouldOpen.shouldOpen) { - return false + return null } if (!this.settings.flashsatsServiceUrl) { this.log("no flashsats service url provided") - return false + return null } const serviceInfo = await this.getInfo() if (+serviceInfo.options.min_initial_client_balance_sat > shouldOpen.maxSpendable) { - this.log("user balance too low for service minimum") - return false + this.log("balance of", shouldOpen.maxSpendable, "is lower than service minimum of", serviceInfo.options.min_initial_client_balance_sat) + return null } const lndInfo = await this.lnd.GetInfo() const myUri = lndInfo.uris.length > 0 ? lndInfo.uris[0] : "" if (!myUri) { this.log("no uri found for this node,uri is required to use flashsats") - return false + return null } const lspBalance = (this.settings.channelThreshold * 2).toString() const chanExpiryBlocks = serviceInfo.options.max_channel_expiry_blocks const order = await this.createOrder({ nodeUri: myUri, lspBalance, clientBalance: "0", chanExpiryBlocks }) if (order.payment.state !== 'EXPECT_PAYMENT') { this.log("order not in expect payment state") - return false + return null } const decoded = await this.lnd.DecodeInvoice(order.payment.bolt11_invoice) if (decoded.numSatoshis !== +order.payment.order_total_sat) { - this.log("invoice amount does not match order total") - return false + this.log("invoice of amount", decoded.numSatoshis, "does not match order total of", order.payment.order_total_sat) + return null } if (decoded.numSatoshis > shouldOpen.maxSpendable) { - this.log("invoice amount exceeds user balance") - return false + this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", shouldOpen.maxSpendable) + return null } const relativeFee = +order.payment.fee_total_sat / this.settings.channelThreshold if (relativeFee > this.settings.maxRelativeFee) { - this.log("invoice fee exceeds max fee percent") - return false + this.log("invoice relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee) + return null } - await this.liquidityProvider.PayInvoice(order.payment.bolt11_invoice) - this.log("paid invoice to open channel") - return true + const res = await this.liquidityProvider.PayInvoice(order.payment.bolt11_invoice) + this.log("paid", res.amount_paid, "to open channel") + return { orderId: order.order_id, invoice: order.payment.bolt11_invoice, totalSats: +order.payment.order_total_sat, fees: +order.payment.fee_total_sat } } getInfo = async () => { @@ -167,20 +174,19 @@ export class OlympusLSP extends LSP { super("OlympusLSP", settings, lnd, liquidityProvider) } - openChannelIfReady = async (): Promise => { - this.log("checking if channel should be opened") + openChannelIfReady = async (): Promise => { const shouldOpen = await this.shouldOpenChannel() if (!shouldOpen.shouldOpen) { - return false + return null } if (!this.settings.olympusServiceUrl) { this.log("no olympus service url provided") - return false + return null } const serviceInfo = await this.getInfo() if (+serviceInfo.options.min_initial_client_balance_sat > shouldOpen.maxSpendable) { - this.log("user balance too low for service minimum") - return false + this.log("balance of", shouldOpen.maxSpendable, "is lower than service minimum of", serviceInfo.options.min_initial_client_balance_sat) + return null } const [servicePub, host] = serviceInfo.uris[0].split('@') await this.addPeer(servicePub, host) @@ -192,25 +198,25 @@ export class OlympusLSP extends LSP { const order = await this.createOrder({ pubKey: myPub, refundAddr: refundAddr.address, lspBalance, clientBalance: "0", chanExpiryBlocks }) if (order.payment.state !== 'EXPECT_PAYMENT') { this.log("order not in expect payment state") - return false + return null } const decoded = await this.lnd.DecodeInvoice(order.payment.bolt11_invoice) if (decoded.numSatoshis !== +order.payment.order_total_sat) { - this.log("invoice amount does not match order total") - return false + this.log("invoice of amount", decoded.numSatoshis, "does not match order total of", order.payment.order_total_sat) + return null } if (decoded.numSatoshis > shouldOpen.maxSpendable) { - this.log("invoice amount exceeds user balance") - return false + this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", shouldOpen.maxSpendable) + return null } const relativeFee = +order.payment.fee_total_sat / this.settings.channelThreshold if (relativeFee > this.settings.maxRelativeFee) { - this.log("invoice fee exceeds max fee percent") - return false + this.log("invoice relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee) + return null } - await this.liquidityProvider.PayInvoice(order.payment.bolt11_invoice) - this.log("paid invoice to open channel") - return true + const res = await this.liquidityProvider.PayInvoice(order.payment.bolt11_invoice) + this.log("paid", res.amount_paid, "to open channel") + return { orderId: order.order_id, invoice: order.payment.bolt11_invoice, totalSats: +order.payment.order_total_sat, fees: +order.payment.fee_total_sat } } getInfo = async () => { @@ -267,15 +273,15 @@ export class VoltageLSP extends LSP { return json } - openChannelIfReady = async (): Promise => { + openChannelIfReady = async (): Promise => { const shouldOpen = await this.shouldOpenChannel() if (!shouldOpen.shouldOpen) { - return false + return null } if (!this.settings.voltageServiceUrl) { this.log("no voltage service url provided") - return false + return null } const lndInfo = await this.lnd.GetInfo() @@ -286,15 +292,15 @@ export class VoltageLSP extends LSP { const relativeFee = feeSats / this.settings.channelThreshold if (relativeFee > this.settings.maxRelativeFee) { - this.log("fee percent exceeds max fee percent") - return false + this.log("relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee) + return null } const info = await this.getInfo() const ipv4 = info.connection_methods.find(c => c.type === 'ipv4') if (!ipv4) { this.log("no ipv4 address found") - return false + return null } await this.addPeer(info.pubkey, `${ipv4.address}:${ipv4.port}`) @@ -302,13 +308,13 @@ export class VoltageLSP extends LSP { const res = await this.proposal(invoice.payRequest, fee.id) const decoded = await this.lnd.DecodeInvoice(res.jit_bolt11) if (decoded.numSatoshis !== this.settings.channelThreshold + feeSats) { - this.log("invoice amount does not math requested amount") - return false + this.log("invoice of amount", decoded.numSatoshis, "does not match expected amount of", this.settings.channelThreshold + feeSats) + return null } - await this.liquidityProvider.PayInvoice(res.jit_bolt11) - this.log("paid invoice to open channel") - return true + const invoiceRes = await this.liquidityProvider.PayInvoice(res.jit_bolt11) + this.log("paid", invoiceRes.amount_paid, "to open channel") + return { orderId: fee.id, invoice: res.jit_bolt11, totalSats: decoded.numSatoshis, fees: feeSats } } proposal = async (bolt11: string, feeId: string) => { diff --git a/src/services/lnd/settings.ts b/src/services/lnd/settings.ts index e9ea5aa1..3d4c97f1 100644 --- a/src/services/lnd/settings.ts +++ b/src/services/lnd/settings.ts @@ -1,5 +1,4 @@ import { HtlcEvent } from "../../../proto/lnd/router" -import { LiquiditySettings } from "./liquidityManager" export type NodeSettings = { lndAddr: string lndCertPath: string @@ -13,8 +12,6 @@ export type LndSettings = { otherNode?: NodeSettings thirdNode?: NodeSettings - - liquiditySettings: LiquiditySettings } type TxOutput = { hash: string diff --git a/src/services/main/index.ts b/src/services/main/index.ts index 80c9c0cf..b34420bc 100644 --- a/src/services/main/index.ts +++ b/src/services/main/index.ts @@ -16,7 +16,7 @@ import { NostrSend } from '../nostr/handler.js' import MetricsManager from '../metrics/index.js' import { LoggedEvent } from '../storage/eventsLog.js' import { LiquidityProvider } from "../lnd/liquidityProvider.js" -import { LiquidityManager } from "../lnd/liquidityManager.js" +import { LiquidityManager } from "./liquidityManager.js" type UserOperationsSub = { id: string @@ -43,9 +43,10 @@ export default class { constructor(settings: MainSettings, storage: Storage) { this.settings = settings this.storage = storage - this.liquidProvider = new LiquidityProvider(settings.lndSettings.liquiditySettings.liquidityProviderPub, this.invoicePaidCb) - this.lnd = new LND(settings.lndSettings, this.liquidProvider, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb, this.htlcCb) - this.liquidityManager = new LiquidityManager(this.settings.lndSettings.liquiditySettings, this.liquidProvider, this.lnd) + this.liquidProvider = new LiquidityProvider(settings.liquiditySettings.liquidityProviderPub, this.invoicePaidCb) + const provider = { liquidProvider: this.liquidProvider, useOnly: settings.liquiditySettings.useOnlyLiquidityProvider } + this.lnd = new LND(settings.lndSettings, provider, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb, this.htlcCb) + this.liquidityManager = new LiquidityManager(this.settings.liquiditySettings, this.liquidProvider, this.lnd) this.metricsManager = new MetricsManager(this.storage, this.lnd) this.paymentManager = new PaymentManager(this.storage, this.lnd, this.settings, this.addressPaidCb, this.invoicePaidCb) diff --git a/src/services/lnd/liquidityManager.ts b/src/services/main/liquidityManager.ts similarity index 68% rename from src/services/lnd/liquidityManager.ts rename to src/services/main/liquidityManager.ts index 2ab2abc6..b3e8c7c0 100644 --- a/src/services/lnd/liquidityManager.ts +++ b/src/services/main/liquidityManager.ts @@ -1,7 +1,8 @@ import { getLogger } from "../helpers/logger.js" -import { LiquidityProvider } from "./liquidityProvider.js" -import LND from "./lnd.js" -import { FlashsatsLSP, LoadLSPSettingsFromEnv, LSPSettings, OlympusLSP, VoltageLSP } from "./lsp.js" +import { LiquidityProvider } from "../lnd/liquidityProvider.js" +import LND from "../lnd/lnd.js" +import { FlashsatsLSP, LoadLSPSettingsFromEnv, LSPSettings, OlympusLSP, VoltageLSP } from "../lnd/lsp.js" +import Storage from '../storage/index.js' export type LiquiditySettings = { lspSettings: LSPSettings liquidityProviderPub: string @@ -14,6 +15,7 @@ export const LoadLiquiditySettingsFromEnv = (): LiquiditySettings => { } export class LiquidityManager { settings: LiquiditySettings + storage: Storage liquidityProvider: LiquidityProvider lnd: LND olympusLSP: OlympusLSP @@ -31,19 +33,26 @@ export class LiquidityManager { } beforeInvoiceCreation = async () => { } afterInInvoicePaid = async () => { + const existingOrder = await this.storage.liquidityStorage.GetLatestLspOrder() + if (existingOrder) { + return + } if (this.channelRequested) { return } + this.log("checking if channel should be requested") const olympusOk = await this.olympusLSP.openChannelIfReady() if (olympusOk) { this.log("requested channel from olympus") this.channelRequested = true + await this.storage.liquidityStorage.SaveLspOrder({ service_name: 'olympus', invoice: olympusOk.invoice, total_paid: olympusOk.totalSats, order_id: olympusOk.orderId, fees: olympusOk.fees }) return } const voltageOk = await this.voltageLSP.openChannelIfReady() if (voltageOk) { this.log("requested channel from voltage") this.channelRequested = true + await this.storage.liquidityStorage.SaveLspOrder({ service_name: 'voltage', invoice: voltageOk.invoice, total_paid: voltageOk.totalSats, order_id: voltageOk.orderId, fees: voltageOk.fees }) return } @@ -51,6 +60,7 @@ export class LiquidityManager { if (flashsatsOk) { this.log("requested channel from flashsats") this.channelRequested = true + await this.storage.liquidityStorage.SaveLspOrder({ service_name: 'flashsats', invoice: flashsatsOk.invoice, total_paid: flashsatsOk.totalSats, order_id: flashsatsOk.orderId, fees: flashsatsOk.fees }) return } this.log("no channel requested") diff --git a/src/services/main/settings.ts b/src/services/main/settings.ts index a4337274..5ae32474 100644 --- a/src/services/main/settings.ts +++ b/src/services/main/settings.ts @@ -6,10 +6,12 @@ import { EnvCanBeInteger, EnvMustBeInteger, EnvMustBeNonEmptyString } from '../h import { getLogger } from '../helpers/logger.js' import fs from 'fs' import crypto from 'crypto'; +import { LiquiditySettings, LoadLiquiditySettingsFromEnv } from './liquidityManager.js' export type MainSettings = { storageSettings: StorageSettings, lndSettings: LndSettings, watchDogSettings: WatchdogSettings, + liquiditySettings: LiquiditySettings, jwtSecret: string incomingTxFee: number outgoingTxFee: number @@ -32,11 +34,13 @@ export type BitcoinCoreSettings = { } export type TestSettings = MainSettings & { lndSettings: { otherNode: NodeSettings, thirdNode: NodeSettings, fourthNode: NodeSettings }, bitcoinCoreSettings: BitcoinCoreSettings } export const LoadMainSettingsFromEnv = (): MainSettings => { + const storageSettings = LoadStorageSettingsFromEnv() return { watchDogSettings: LoadWatchdogSettingsFromEnv(), lndSettings: LoadLndSettingsFromEnv(), - storageSettings: LoadStorageSettingsFromEnv(), - jwtSecret: loadJwtSecret(), + storageSettings: storageSettings, + liquiditySettings: LoadLiquiditySettingsFromEnv(), + jwtSecret: loadJwtSecret(storageSettings.dataDir), incomingTxFee: EnvCanBeInteger("INCOMING_CHAIN_FEE_ROOT_BPS", 0) / 10000, outgoingTxFee: EnvCanBeInteger("OUTGOING_CHAIN_FEE_ROOT_BPS", 60) / 10000, incomingAppInvoiceFee: EnvCanBeInteger("INCOMING_INVOICE_FEE_ROOT_BPS", 0) / 10000, @@ -58,7 +62,7 @@ export const LoadTestSettingsFromEnv = (): TestSettings => { const settings = LoadMainSettingsFromEnv() return { ...settings, - storageSettings: { dbSettings: { ...settings.storageSettings.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" }, eventLogPath }, + storageSettings: { dbSettings: { ...settings.storageSettings.dbSettings, databaseFile: ":memory:", metricsDatabaseFile: ":memory:" }, eventLogPath, dataDir: "data" }, lndSettings: { ...settings.lndSettings, otherNode: { @@ -76,10 +80,10 @@ export const LoadTestSettingsFromEnv = (): TestSettings => { lndCertPath: EnvMustBeNonEmptyString("LND_FOURTH_CERT_PATH"), lndMacaroonPath: EnvMustBeNonEmptyString("LND_FOURTH_MACAROON_PATH") }, - liquiditySettings: { - ...settings.lndSettings.liquiditySettings, - liquidityProviderPub: "", - } + }, + liquiditySettings: { + ...settings.liquiditySettings, + liquidityProviderPub: "", }, skipSanityCheck: true, bitcoinCoreSettings: { @@ -90,20 +94,21 @@ export const LoadTestSettingsFromEnv = (): TestSettings => { } } -export const loadJwtSecret = (): string => { +export const loadJwtSecret = (dataDir: string): string => { const secret = process.env["JWT_SECRET"] const log = getLogger({}) if (secret) { return secret } log("JWT_SECRET not set in env, checking .jwt_secret file") + const secretPath = dataDir !== "" ? `${dataDir}/.jwt_secret` : ".jwt_secret" try { - const fileContent = fs.readFileSync(".jwt_secret", "utf-8") + const fileContent = fs.readFileSync(secretPath, "utf-8") return fileContent.trim() } catch (e) { log(".jwt_secret file not found, generating random secret") const secret = crypto.randomBytes(32).toString('hex') - fs.writeFileSync(".jwt_secret", secret) + fs.writeFileSync(secretPath, secret) return secret } } \ No newline at end of file diff --git a/src/services/storage/db.ts b/src/services/storage/db.ts index be18d448..657e4724 100644 --- a/src/services/storage/db.ts +++ b/src/services/storage/db.ts @@ -17,6 +17,7 @@ import { BalanceEvent } from "./entity/BalanceEvent.js" import { ChannelBalanceEvent } from "./entity/ChannelsBalanceEvent.js" import { getLogger } from "../helpers/logger.js" import { ChannelRouting } from "./entity/ChannelRouting.js" +import { LspOrder } from "./entity/LspOrder.js" export type DbSettings = { @@ -56,7 +57,7 @@ export default async (settings: DbSettings, migrations: Function[]): Promise<{ s database: settings.databaseFile, // logging: true, entities: [User, UserReceivingInvoice, UserReceivingAddress, AddressReceivingTransaction, UserInvoicePayment, UserTransactionPayment, - UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment], + UserBasicAuth, UserEphemeralKey, Product, UserToUserPayment, Application, ApplicationUser, UserToUserPayment, LspOrder], //synchronize: true, migrations }).initialize() diff --git a/src/services/storage/entity/LspOrder.ts b/src/services/storage/entity/LspOrder.ts new file mode 100644 index 00000000..ca4acee9 --- /dev/null +++ b/src/services/storage/entity/LspOrder.ts @@ -0,0 +1,28 @@ +import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm" + +@Entity() +export class LspOrder { + @PrimaryGeneratedColumn() + serial_id: number + + @Column() + service_name: string + + @Column() + invoice: string + + @Column() + order_id: string + + @Column() + total_paid: number + + @Column() + fees: number + + @CreateDateColumn() + created_at: Date + + @UpdateDateColumn() + updated_at: Date +} diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index 1510dc9e..169691be 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -7,12 +7,14 @@ import PaymentStorage from "./paymentStorage.js"; import MetricsStorage from "./metricsStorage.js"; import TransactionsQueue, { TX } from "./transactionsQueue.js"; import EventsLogManager from "./eventsLog.js"; +import { LiquidityStorage } from "./liquidityStorage.js"; export type StorageSettings = { dbSettings: DbSettings eventLogPath: string + dataDir: string } export const LoadStorageSettingsFromEnv = (): StorageSettings => { - return { dbSettings: LoadDbSettingsFromEnv(), eventLogPath: "logs/eventLogV2.csv" } + return { dbSettings: LoadDbSettingsFromEnv(), eventLogPath: "logs/eventLogV2.csv", dataDir: process.env.DATA_DIR || "" } } export default class { DB: DataSource | EntityManager @@ -23,6 +25,7 @@ export default class { userStorage: UserStorage paymentStorage: PaymentStorage metricsStorage: MetricsStorage + liquidityStorage: LiquidityStorage eventsLog: EventsLogManager constructor(settings: StorageSettings) { this.settings = settings @@ -37,6 +40,7 @@ export default class { this.applicationStorage = new ApplicationStorage(this.DB, this.userStorage, this.txQueue) this.paymentStorage = new PaymentStorage(this.DB, this.userStorage, this.txQueue) this.metricsStorage = new MetricsStorage(this.settings) + this.liquidityStorage = new LiquidityStorage(this.DB, this.txQueue) const executedMetricsMigrations = await this.metricsStorage.Connect(metricsMigrations) return { executedMigrations, executedMetricsMigrations }; } diff --git a/src/services/storage/liquidityStorage.ts b/src/services/storage/liquidityStorage.ts new file mode 100644 index 00000000..1ae0e388 --- /dev/null +++ b/src/services/storage/liquidityStorage.ts @@ -0,0 +1,20 @@ +import { DataSource, EntityManager } from "typeorm" +import { LspOrder } from "./entity/LspOrder.js"; +import TransactionsQueue, { TX } from "./transactionsQueue.js"; +export class LiquidityStorage { + DB: DataSource | EntityManager + txQueue: TransactionsQueue + constructor(DB: DataSource | EntityManager, txQueue: TransactionsQueue) { + this.DB = DB + this.txQueue = txQueue + } + + GetLatestLspOrder() { + return this.DB.getRepository(LspOrder).findOne({ order: { serial_id: "DESC" } }) + } + + SaveLspOrder(order: Partial) { + const entry = this.DB.getRepository(LspOrder).create(order) + return this.txQueue.PushToQueue({ exec: async db => db.getRepository(LspOrder).save(entry), dbTx: false }) + } +} \ No newline at end of file diff --git a/src/services/storage/migrations/1718387847693-lsp_order.ts b/src/services/storage/migrations/1718387847693-lsp_order.ts new file mode 100644 index 00000000..8972d1c8 --- /dev/null +++ b/src/services/storage/migrations/1718387847693-lsp_order.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class LspOrder1718387847693 implements MigrationInterface { + name = 'LspOrder1718387847693' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "lsp_order" ("serial_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "service_name" varchar NOT NULL, "invoice" varchar NOT NULL, "order_id" varchar NOT NULL, "total_paid" integer NOT NULL, "fees" integer NOT NULL, "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "lsp_order"`); + } + +} diff --git a/src/services/storage/migrations/runner.ts b/src/services/storage/migrations/runner.ts index 3c81aa2f..479264e4 100644 --- a/src/services/storage/migrations/runner.ts +++ b/src/services/storage/migrations/runner.ts @@ -4,7 +4,8 @@ import Storage, { StorageSettings } from '../index.js' import { Initial1703170309875 } from './1703170309875-initial.js' import { LndMetrics1703170330183 } from './1703170330183-lnd_metrics.js' import { ChannelRouting1709316653538 } from './1709316653538-channel_routing.js' -const allMigrations = [Initial1703170309875] +import { LspOrder1718387847693 } from './1718387847693-lsp_order.js' +const allMigrations = [Initial1703170309875, LspOrder1718387847693] const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538] export const TypeOrmMigrationRunner = async (log: PubLogger, storageManager: Storage, settings: DbSettings, arg: string | undefined): Promise => { if (arg === 'fake_initial_migration') { diff --git a/src/tests/networkSetup.ts b/src/tests/networkSetup.ts index 91af289a..2b83c711 100644 --- a/src/tests/networkSetup.ts +++ b/src/tests/networkSetup.ts @@ -8,8 +8,8 @@ export const setupNetwork = async () => { const core = new BitcoinCoreWrapper(settings) await core.InitAddress() await core.Mine(1) - const alice = new LND(settings.lndSettings, new LiquidityProvider("", () => { }), () => { }, () => { }, () => { }, () => { }) - const bob = new LND({ ...settings.lndSettings, mainNode: settings.lndSettings.otherNode }, new LiquidityProvider("", () => { }), () => { }, () => { }, () => { }, () => { }) + const alice = new LND(settings.lndSettings, { liquidProvider: new LiquidityProvider("", () => { }) }, () => { }, () => { }, () => { }, () => { }) + const bob = new LND({ ...settings.lndSettings, mainNode: settings.lndSettings.otherNode }, { liquidProvider: new LiquidityProvider("", () => { }) }, () => { }, () => { }, () => { }, () => { }) await tryUntil(async i => { const peers = await alice.ListPeers() if (peers.peers.length > 0) { diff --git a/src/tests/setupBootstrapped.ts b/src/tests/setupBootstrapped.ts index 92e0fa70..a25c2367 100644 --- a/src/tests/setupBootstrapped.ts +++ b/src/tests/setupBootstrapped.ts @@ -7,8 +7,8 @@ import * as Types from '../../proto/autogenerated/ts/types.js' export const initBootstrappedInstance = async (T: TestBase) => { const settings = LoadTestSettingsFromEnv() - settings.lndSettings.liquiditySettings.useOnlyLiquidityProvider = true - settings.lndSettings.liquiditySettings.liquidityProviderPub = T.app.publicKey + settings.liquiditySettings.useOnlyLiquidityProvider = true + settings.liquiditySettings.liquidityProviderPub = T.app.publicKey settings.lndSettings.mainNode = settings.lndSettings.thirdNode const initialized = await initMainHandler(getLogger({ component: "bootstrapped" }), settings) if (!initialized) { diff --git a/src/tests/testBase.ts b/src/tests/testBase.ts index a130703f..e2ee7902 100644 --- a/src/tests/testBase.ts +++ b/src/tests/testBase.ts @@ -46,15 +46,15 @@ export const SetupTest = async (d: Describe): Promise => { const user2 = { userId: u2.info.userId, appUserIdentifier: u2.identifier, appId: app.appId } - const externalAccessToMainLnd = new LND(settings.lndSettings, new LiquidityProvider("", () => { }), console.log, console.log, () => { }, () => { }) + const externalAccessToMainLnd = new LND(settings.lndSettings, { liquidProvider: new LiquidityProvider("", () => { }) }, console.log, console.log, () => { }, () => { }) await externalAccessToMainLnd.Warmup() const otherLndSetting = { ...settings.lndSettings, mainNode: settings.lndSettings.otherNode } - const externalAccessToOtherLnd = new LND(otherLndSetting, new LiquidityProvider("", () => { }), console.log, console.log, () => { }, () => { }) + const externalAccessToOtherLnd = new LND(otherLndSetting, { liquidProvider: new LiquidityProvider("", () => { }) }, console.log, console.log, () => { }, () => { }) await externalAccessToOtherLnd.Warmup() const thirdLndSetting = { ...settings.lndSettings, mainNode: settings.lndSettings.thirdNode } - const externalAccessToThirdLnd = new LND(thirdLndSetting, new LiquidityProvider("", () => { }), console.log, console.log, () => { }, () => { }) + const externalAccessToThirdLnd = new LND(thirdLndSetting, { liquidProvider: new LiquidityProvider("", () => { }) }, console.log, console.log, () => { }, () => { }) await externalAccessToThirdLnd.Warmup()