Merge branch 'master' into admin-swaps
This commit is contained in:
commit
e71f631993
12 changed files with 169 additions and 46 deletions
46
DOCKER.md
46
DOCKER.md
|
|
@ -14,9 +14,55 @@ docker run -d \
|
|||
--network host \
|
||||
-p 1776:1776 \
|
||||
-p 1777:1777 \
|
||||
-e DATA_DIR=/app/data \
|
||||
-v /path/to/local/data:/app/data \
|
||||
-v $HOME/.lnd:/root/.lnd \
|
||||
ghcr.io/shocknet/lightning-pub:latest
|
||||
```
|
||||
|
||||
Network host is used so the service can reach a local LND via localhost. LND is assumed to be under the users home folder, update these resources as needed.
|
||||
|
||||
## Getting the Connection String
|
||||
|
||||
After starting the container, you can get the connection string in several ways:
|
||||
|
||||
### Option 1: From the mounted data directory
|
||||
|
||||
If `DATA_DIR=/app/data` is set and the volume is mounted correctly, the connection string will be in:
|
||||
|
||||
```ssh
|
||||
cat /path/to/local/data/admin.connect
|
||||
```
|
||||
|
||||
The connection string format is: `nprofile1...:token`
|
||||
|
||||
### Option 2: Via API endpoint
|
||||
|
||||
Access the wizard endpoint to get connection info:
|
||||
|
||||
```ssh
|
||||
curl http://localhost:1777/wizard/admin_connect_info
|
||||
```
|
||||
|
||||
The response will include the `nprofile` and `admin_token`. Combine them as `nprofile:admin_token` to form the connection string.
|
||||
|
||||
### Option 3: From inside the container
|
||||
|
||||
If the files aren't in the mounted volume, check inside the container:
|
||||
|
||||
```ssh
|
||||
docker exec lightning-pub cat /app/data/admin.connect
|
||||
```
|
||||
|
||||
Or if `DATA_DIR` wasn't set, check the working directory:
|
||||
|
||||
```ssh
|
||||
docker exec lightning-pub cat /app/admin.connect
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If `/data` folder is empty:
|
||||
- Ensure `DATA_DIR=/app/data` environment variable is set in the docker run command
|
||||
- Check container logs: `docker logs lightning-pub` to see where files are being written
|
||||
- The logs will show the configured data directory path at startup
|
||||
|
|
|
|||
|
|
@ -14,11 +14,18 @@ export type ClientParams = {
|
|||
checkResult?: true
|
||||
}
|
||||
export default (params: ClientParams) => ({
|
||||
EnrollServicePub: async (request: Types.ServiceNpub): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
EnrollServicePub: async (query: Types.EnrollServicePub_Query): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
let finalRoute = '/api/admin/service/enroll'
|
||||
const initialQuery = query as Record<string, string | string[]>
|
||||
const finalQuery: Record<string, string> = {}
|
||||
for (const key in initialQuery)
|
||||
if (Array.isArray(initialQuery[key]))
|
||||
finalQuery[key] = initialQuery[key].join(',')
|
||||
const q = (new URLSearchParams(finalQuery)).toString()
|
||||
finalRoute = finalRoute + (q === '' ? '' : '?' + q)
|
||||
const auth = await params.retrieveAdminAuth()
|
||||
if (auth === null) throw new Error('retrieveAdminAuth() returned null')
|
||||
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||
const { data } = await axios.get(params.baseUrl + finalRoute, { headers: { 'authorization': auth } })
|
||||
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||
if (data.status === 'OK') {
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ export type NostrAppMethodInputs = SendNotification_Input
|
|||
export type NostrAppMethodOutputs = SendNotification_Output
|
||||
export type AuthContext = AdminContext | GuestContext | NostrAppContext
|
||||
|
||||
export type EnrollServicePub_Input = { rpcName: 'EnrollServicePub', req: ServiceNpub }
|
||||
export type EnrollServicePub_Query = {
|
||||
pubkey_hex?: string[] | string
|
||||
}
|
||||
export type EnrollServicePub_Input = { rpcName: 'EnrollServicePub', query: EnrollServicePub_Query }
|
||||
export type EnrollServicePub_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type Health_Input = { rpcName: 'Health' }
|
||||
|
|
@ -59,19 +62,27 @@ export const EmptyValidate = (o?: Empty, opts: EmptyOptions = {}, path: string =
|
|||
}
|
||||
|
||||
export type Notification = {
|
||||
body?: string
|
||||
data: string
|
||||
recipient_registration_tokens: string[]
|
||||
title?: string
|
||||
}
|
||||
export const NotificationOptionalFields: [] = []
|
||||
export type NotificationOptionalField = 'body' | 'title'
|
||||
export const NotificationOptionalFields: NotificationOptionalField[] = ['body', 'title']
|
||||
export type NotificationOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
checkOptionalsAreSet?: NotificationOptionalField[]
|
||||
body_CustomCheck?: (v?: string) => boolean
|
||||
data_CustomCheck?: (v: string) => boolean
|
||||
recipient_registration_tokens_CustomCheck?: (v: string[]) => boolean
|
||||
title_CustomCheck?: (v?: string) => boolean
|
||||
}
|
||||
export const NotificationValidate = (o?: Notification, opts: NotificationOptions = {}, path: string = 'Notification::root.'): Error | null => {
|
||||
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
|
||||
if ((o.body || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('body')) && typeof o.body !== 'string') return new Error(`${path}.body: is not a string`)
|
||||
if (opts.body_CustomCheck && !opts.body_CustomCheck(o.body)) return new Error(`${path}.body: custom check failed`)
|
||||
|
||||
if (typeof o.data !== 'string') return new Error(`${path}.data: is not a string`)
|
||||
if (opts.data_CustomCheck && !opts.data_CustomCheck(o.data)) return new Error(`${path}.data: custom check failed`)
|
||||
|
||||
|
|
@ -81,6 +92,9 @@ export const NotificationValidate = (o?: Notification, opts: NotificationOptions
|
|||
}
|
||||
if (opts.recipient_registration_tokens_CustomCheck && !opts.recipient_registration_tokens_CustomCheck(o.recipient_registration_tokens)) return new Error(`${path}.recipient_registration_tokens: custom check failed`)
|
||||
|
||||
if ((o.title || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('title')) && typeof o.title !== 'string') return new Error(`${path}.title: is not a string`)
|
||||
if (opts.title_CustomCheck && !opts.title_CustomCheck(o.title)) return new Error(`${path}.title: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import { bytesToHex } from '@noble/hashes/utils'
|
|||
import { sha256 } from '@noble/hashes/sha256'
|
||||
import { base64 } from '@scure/base';
|
||||
import NewClient, { ClientParams } from './autogenerated/http_client.js'
|
||||
import * as Types from './autogenerated/types.js'
|
||||
import { ERROR, getLogger } from '../helpers/logger.js'
|
||||
const utf8Encoder = new TextEncoder()
|
||||
export type PushPair = { pubkey: string, privateKey: string }
|
||||
const nip98Kind = 27235
|
||||
export type ShockPushNotification = { message: string, body: string, title: string }
|
||||
export class ShockPush {
|
||||
private client: ReturnType<typeof NewClient>
|
||||
private logger: ReturnType<typeof getLogger>
|
||||
|
|
@ -52,8 +54,11 @@ export class ShockPush {
|
|||
return nip98Header
|
||||
}
|
||||
|
||||
SendNotification = async (message: string, messagingTokens: string[]) => {
|
||||
const res = await this.client.SendNotification({ recipient_registration_tokens: messagingTokens, data: message })
|
||||
SendNotification = async ({ body, message, title }: ShockPushNotification, messagingTokens: string[]) => {
|
||||
const res = await this.client.SendNotification({
|
||||
recipient_registration_tokens: messagingTokens, data: message,
|
||||
body, title
|
||||
})
|
||||
if (res.status !== 'OK') {
|
||||
this.logger(ERROR, `failed to send notification: ${res.status}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ export type BalanceInfo = {
|
|||
channelsBalance: ChannelBalance[];
|
||||
}
|
||||
|
||||
export type AddressPaidCb = (txOutput: TxOutput, address: string, amount: number, used: 'lnd' | 'provider' | 'internal') => Promise<void>
|
||||
export type AddressPaidCb = (txOutput: TxOutput, address: string, amount: number, used: 'lnd' | 'provider' | 'internal', broadcastHeight?: number) => Promise<void>
|
||||
export type InvoicePaidCb = (paymentRequest: string, amount: number, used: 'lnd' | 'provider' | 'internal') => Promise<void>
|
||||
export type NewBlockCb = (height: number) => void
|
||||
export type NewBlockCb = (height: number, skipMetrics?: boolean) => Promise<void>
|
||||
export type HtlcCb = (event: HtlcEvent) => void
|
||||
export type ChannelEventCb = (event: ChannelEventUpdate, channels: Channel[]) => void
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export default class {
|
|||
const appUser = await this.storage.applicationStorage.GetAppUserFromUser(app, user.user_id)
|
||||
|
||||
if (!appUser) {
|
||||
throw new Error(`app user ${ctx.user_id} not found`) // TODO: fix logs doxing
|
||||
throw new Error("app user not found")
|
||||
}
|
||||
const nostrSettings = this.settings.getSettings().nostrRelaySettings
|
||||
const { max, serviceFeeFloor, serviceFeeBps } = this.applicationManager.paymentManager.GetMaxPayableInvoice(user.balance_sats)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { NotificationsManager } from "./notificationsManager.js"
|
|||
import { ApplicationUser } from '../storage/entity/ApplicationUser.js'
|
||||
import SettingsManager from './settingsManager.js'
|
||||
import { NostrSettings, AppInfo } from '../nostr/nostrPool.js'
|
||||
import { ShockPushNotification } from '../ShockPush/index.js'
|
||||
type UserOperationsSub = {
|
||||
id: string
|
||||
newIncomingInvoice: (operation: Types.UserOperation) => void
|
||||
|
|
@ -80,7 +81,7 @@ export default class {
|
|||
this.liquidityManager = new LiquidityManager(this.settings, this.storage, this.utils, this.liquidityProvider, this.lnd, this.rugPullTracker)
|
||||
this.metricsManager = new MetricsManager(this.storage, this.lnd)
|
||||
|
||||
this.paymentManager = new PaymentManager(this.storage, this.metricsManager, this.lnd, adminManager.swaps, this.settings, this.liquidityManager, this.utils, this.addressPaidCb, this.invoicePaidCb)
|
||||
this.paymentManager = new PaymentManager(this.storage, this.metricsManager, this.lnd, adminManager.swaps, this.settings, this.liquidityManager, this.utils, this.addressPaidCb, this.invoicePaidCb, this.newBlockCb)
|
||||
this.productManager = new ProductManager(this.storage, this.paymentManager, this.settings)
|
||||
this.applicationManager = new ApplicationManager(this.storage, this.settings, this.paymentManager)
|
||||
this.appUserManager = new AppUserManager(this.storage, this.settings, this.applicationManager)
|
||||
|
|
@ -153,18 +154,20 @@ export default class {
|
|||
this.metricsManager.HtlcCb(e)
|
||||
}
|
||||
|
||||
newBlockCb: NewBlockCb = (height) => {
|
||||
this.NewBlockHandler(height)
|
||||
newBlockCb: NewBlockCb = (height, skipMetrics) => {
|
||||
return this.NewBlockHandler(height, skipMetrics)
|
||||
}
|
||||
|
||||
NewBlockHandler = async (height: number) => {
|
||||
NewBlockHandler = async (height: number, skipMetrics?: boolean) => {
|
||||
let confirmed: (PendingTx & { confs: number; })[]
|
||||
let log = getLogger({})
|
||||
this.storage.paymentStorage.DeleteExpiredTransactionSwaps(height)
|
||||
.catch(err => log(ERROR, "failed to delete expired transaction swaps", err.message || err))
|
||||
try {
|
||||
const balanceEvents = await this.paymentManager.GetLndBalance()
|
||||
if (!skipMetrics) {
|
||||
await this.metricsManager.NewBlockCb(height, balanceEvents)
|
||||
}
|
||||
confirmed = await this.paymentManager.CheckNewlyConfirmedTxs(height)
|
||||
await this.liquidityManager.onNewBlock()
|
||||
} catch (err: any) {
|
||||
|
|
@ -203,7 +206,7 @@ export default class {
|
|||
}))
|
||||
}
|
||||
|
||||
addressPaidCb: AddressPaidCb = (txOutput, address, amount, used) => {
|
||||
addressPaidCb: AddressPaidCb = (txOutput, address, amount, used, broadcastHeight) => {
|
||||
return this.storage.StartTransaction(async tx => {
|
||||
// On-chain payments not supported when bypass is enabled
|
||||
if (this.liquidityProvider.getSettings().useOnlyLiquidityProvider) {
|
||||
|
|
@ -231,7 +234,8 @@ export default class {
|
|||
const fee = this.paymentManager.getReceiveServiceFee(Types.UserOperationType.INCOMING_TX, amount, isManagedUser)
|
||||
try {
|
||||
// This call will fail if the transaction is already registered
|
||||
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, internal, blockHeight, tx)
|
||||
const txBroadcastHeight = broadcastHeight ? broadcastHeight : blockHeight
|
||||
const addedTx = await this.storage.paymentStorage.AddAddressReceivingTransaction(userAddress, txOutput.hash, txOutput.index, amount, fee, internal, txBroadcastHeight, tx)
|
||||
if (internal) {
|
||||
const addressData = `${address}:${txOutput.hash}`
|
||||
this.storage.eventsLog.LogEvent({ type: 'address_paid', userId: userAddress.user.user_id, appId: userAddress.linkedApplication.app_id, appUserId: "", balance: userAddress.user.balance_sats, data: addressData, amount })
|
||||
|
|
@ -381,10 +385,36 @@ export default class {
|
|||
{ operation: op, requestId: "GetLiveUserOperations", status: 'OK', latest_balance: balance }
|
||||
const j = JSON.stringify(message)
|
||||
this.utils.nostrSender.Send({ type: 'app', appId: app.app_id }, { type: 'content', content: j, pub: user.nostr_public_key })
|
||||
this.SendEncryptedNotification(app, user, op)
|
||||
|
||||
this.SendEncryptedNotification(app, user, op, this.getOperationMessage(op))
|
||||
}
|
||||
|
||||
async SendEncryptedNotification(app: Application, appUser: ApplicationUser, op: Types.UserOperation) {
|
||||
getOperationMessage = (op: Types.UserOperation) => {
|
||||
switch (op.type) {
|
||||
case Types.UserOperationType.INCOMING_TX:
|
||||
case Types.UserOperationType.INCOMING_INVOICE:
|
||||
case Types.UserOperationType.INCOMING_USER_TO_USER:
|
||||
return {
|
||||
body: "You received a new payment",
|
||||
title: "Payment Received"
|
||||
}
|
||||
case Types.UserOperationType.OUTGOING_TX:
|
||||
case Types.UserOperationType.OUTGOING_INVOICE:
|
||||
case Types.UserOperationType.OUTGOING_USER_TO_USER:
|
||||
return {
|
||||
body: "You sent a new payment",
|
||||
title: "Payment Sent"
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
body: "Unknown operation",
|
||||
title: "Unknown Operation"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async SendEncryptedNotification(app: Application, appUser: ApplicationUser, op: Types.UserOperation, { body, title }: { body: string, title: string }) {
|
||||
const devices = await this.storage.applicationStorage.GetAppUserDevices(appUser.identifier)
|
||||
if (devices.length === 0 || !app.nostr_public_key || !app.nostr_private_key || !appUser.nostr_public_key) {
|
||||
return
|
||||
|
|
@ -394,8 +424,12 @@ export default class {
|
|||
const j = JSON.stringify(op)
|
||||
const encrypted = nip44.encrypt(j, ck)
|
||||
const encryptedData: { encrypted: string, app_npub_hex: string } = { encrypted, app_npub_hex: app.nostr_public_key }
|
||||
|
||||
this.notificationsManager.SendNotification(JSON.stringify(encryptedData), tokens, {
|
||||
const notification: ShockPushNotification = {
|
||||
message: JSON.stringify(encryptedData),
|
||||
body,
|
||||
title
|
||||
}
|
||||
await this.notificationsManager.SendNotification(notification, tokens, {
|
||||
pubkey: app.nostr_public_key!,
|
||||
privateKey: app.nostr_private_key!
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { PushPair, ShockPush } from "../ShockPush/index.js"
|
||||
import { PushPair, ShockPush, ShockPushNotification } from "../ShockPush/index.js"
|
||||
import { getLogger, PubLogger } from "../helpers/logger.js"
|
||||
import SettingsManager from "./settingsManager.js"
|
||||
|
||||
|
|
@ -21,12 +21,12 @@ export class NotificationsManager {
|
|||
return newClient
|
||||
}
|
||||
|
||||
SendNotification = async (message: string, messagingTokens: string[], pair: PushPair) => {
|
||||
SendNotification = async (notification: ShockPushNotification, messagingTokens: string[], pair: PushPair) => {
|
||||
if (!this.settings.getSettings().serviceSettings.shockPushBaseUrl) {
|
||||
this.logger("ShockPush is not configured, skipping notification")
|
||||
return
|
||||
}
|
||||
const client = this.getClient(pair)
|
||||
await client.SendNotification(message, messagingTokens)
|
||||
await client.SendNotification(notification, messagingTokens)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import { InboundOptionals, defaultInvoiceExpiry } from '../storage/paymentStorag
|
|||
import LND from '../lnd/lnd.js'
|
||||
import { Application } from '../storage/entity/Application.js'
|
||||
import { ERROR, getLogger, PubLogger } from '../helpers/logger.js'
|
||||
import { AddressPaidCb, InvoicePaidCb } from '../lnd/settings.js'
|
||||
import { AddressPaidCb, InvoicePaidCb, NewBlockCb } from '../lnd/settings.js'
|
||||
import { UserReceivingInvoice, ZapInfo } from '../storage/entity/UserReceivingInvoice.js'
|
||||
import { Payment_PaymentStatus } from '../../../proto/lnd/lightning.js'
|
||||
import { Event, verifiedSymbol, verifyEvent } from 'nostr-tools'
|
||||
|
|
@ -54,6 +54,7 @@ export default class {
|
|||
lnd: LND
|
||||
addressPaidCb: AddressPaidCb
|
||||
invoicePaidCb: InvoicePaidCb
|
||||
newBlockCb: NewBlockCb
|
||||
log = getLogger({ component: "PaymentManager" })
|
||||
watchDog: Watchdog
|
||||
liquidityManager: LiquidityManager
|
||||
|
|
@ -61,7 +62,7 @@ export default class {
|
|||
swaps: Swaps
|
||||
invoiceLock: InvoiceLock
|
||||
metrics: Metrics
|
||||
constructor(storage: Storage, metrics: Metrics, lnd: LND, swaps: Swaps, settings: SettingsManager, liquidityManager: LiquidityManager, utils: Utils, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb) {
|
||||
constructor(storage: Storage, metrics: Metrics, lnd: LND, swaps: Swaps, settings: SettingsManager, liquidityManager: LiquidityManager, utils: Utils, addressPaidCb: AddressPaidCb, invoicePaidCb: InvoicePaidCb, newBlockCb: NewBlockCb) {
|
||||
this.storage = storage
|
||||
this.metrics = metrics
|
||||
this.settings = settings
|
||||
|
|
@ -72,6 +73,7 @@ export default class {
|
|||
this.swaps = swaps
|
||||
this.addressPaidCb = addressPaidCb
|
||||
this.invoicePaidCb = invoicePaidCb
|
||||
this.newBlockCb = newBlockCb
|
||||
this.invoiceLock = new InvoiceLock()
|
||||
}
|
||||
|
||||
|
|
@ -185,24 +187,39 @@ export default class {
|
|||
}
|
||||
|
||||
try {
|
||||
const { txs, currentHeight, lndPubkey } = await this.getLatestTransactions(log)
|
||||
const { txs, currentHeight, lndPubkey, startHeight } = await this.getLatestTransactions(log)
|
||||
|
||||
const recoveredCount = await this.processMissedChainTransactions(txs, log)
|
||||
const recoveredCount = await this.processMissedChainTransactions(txs, log, startHeight)
|
||||
|
||||
// Update latest checked height to current block height
|
||||
await this.storage.liquidityStorage.UpdateLatestCheckedHeight('lnd', lndPubkey, currentHeight)
|
||||
|
||||
if (recoveredCount > 0) {
|
||||
log(`processed ${recoveredCount} missed chain tx(s)`)
|
||||
log(`processed ${recoveredCount} missed chain tx(s) triggering new block callback`)
|
||||
// Call new block callback to process any new transaction that was just added to the database as pending
|
||||
await this.newBlockCb(currentHeight, true)
|
||||
} else {
|
||||
log("no missed chain transactions found")
|
||||
}
|
||||
await this.reprocessStuckPendingTx(log, currentHeight)
|
||||
} catch (err: any) {
|
||||
log(ERROR, "failed to check for missed chain transactions:", err.message || err)
|
||||
}
|
||||
}
|
||||
|
||||
private async getLatestTransactions(log: PubLogger): Promise<{ txs: Transaction[], currentHeight: number, lndPubkey: string }> {
|
||||
reprocessStuckPendingTx = async (log: PubLogger, currentHeight: number) => {
|
||||
const { incoming } = await this.storage.paymentStorage.GetPendingTransactions()
|
||||
const found = incoming.find(t => t.broadcast_height < currentHeight - 100)
|
||||
if (found) {
|
||||
log("found a possibly stuck pending transaction, reprocessing with full transaction history")
|
||||
// There is a pending transaction more than 100 blocks old, this is likely a transaction
|
||||
// that has a broadcast height higher than it actually is, so its not getting picked up when being processed
|
||||
// by calling new block cb with height of 1, we make sure that even if the transaction has a newer height, it will still be processed
|
||||
await this.newBlockCb(1, true)
|
||||
}
|
||||
}
|
||||
|
||||
private async getLatestTransactions(log: PubLogger): Promise<{ txs: Transaction[], currentHeight: number, lndPubkey: string, startHeight: number }> {
|
||||
const lndInfo = await this.lnd.GetInfo()
|
||||
const lndPubkey = lndInfo.identityPubkey
|
||||
|
||||
|
|
@ -212,10 +229,10 @@ export default class {
|
|||
const { transactions } = await this.lnd.GetTransactions(startHeight)
|
||||
log(`retrieved ${transactions.length} transactions from LND`)
|
||||
|
||||
return { txs: transactions, currentHeight: lndInfo.blockHeight, lndPubkey }
|
||||
return { txs: transactions, currentHeight: lndInfo.blockHeight, lndPubkey, startHeight }
|
||||
}
|
||||
|
||||
private async processMissedChainTransactions(transactions: Transaction[], log: PubLogger): Promise<number> {
|
||||
private async processMissedChainTransactions(transactions: Transaction[], log: PubLogger, startHeight: number): Promise<number> {
|
||||
let recoveredCount = 0
|
||||
const addresses = await this.lnd.ListAddresses()
|
||||
for (const tx of transactions) {
|
||||
|
|
@ -231,7 +248,7 @@ export default class {
|
|||
continue
|
||||
}
|
||||
|
||||
const processed = await this.processUserAddressOutput(output, tx, log)
|
||||
const processed = await this.processUserAddressOutput(output, tx, log, startHeight)
|
||||
if (processed) {
|
||||
recoveredCount++
|
||||
}
|
||||
|
|
@ -272,7 +289,7 @@ export default class {
|
|||
return true
|
||||
}
|
||||
|
||||
private async processUserAddressOutput(output: OutputDetail, tx: Transaction, log: PubLogger) {
|
||||
private async processUserAddressOutput(output: OutputDetail, tx: Transaction, log: PubLogger, startHeight: number) {
|
||||
const existingTx = await this.storage.paymentStorage.GetAddressReceivingTransactionOwner(
|
||||
output.address,
|
||||
tx.txHash
|
||||
|
|
@ -285,7 +302,7 @@ export default class {
|
|||
const amount = Number(output.amount)
|
||||
const outputIndex = Number(output.outputIndex)
|
||||
log(`processing missed chain tx: address=${output.address}, txHash=${tx.txHash}, amount=${amount}, outputIndex=${outputIndex}`)
|
||||
this.addressPaidCb({ hash: tx.txHash, index: outputIndex }, output.address, amount, 'lnd')
|
||||
this.addressPaidCb({ hash: tx.txHash, index: outputIndex }, output.address, amount, 'lnd', startHeight)
|
||||
.catch(err => log(ERROR, "failed to process user address output:", err.message || err))
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default class {
|
|||
async GetUser(userId: string, txId?: string): Promise<User> {
|
||||
const user = await this.FindUser(userId, txId)
|
||||
if (!user) {
|
||||
throw new Error(`user ${userId} not found`) // TODO: fix logs doxing
|
||||
throw new Error(`user not found`)
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ export default class {
|
|||
async UnbanUser(userId: string, txId?: string) {
|
||||
const affected = await this.dbs.Update<User>('User', { user_id: userId }, { locked: false }, txId)
|
||||
if (!affected) {
|
||||
throw new Error("unaffected user unlock for " + userId) // TODO: fix logs doxing
|
||||
throw new Error("unaffected user unlock")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ export default class {
|
|||
const user = await this.GetUser(userId, txId)
|
||||
const affected = await this.dbs.Update<User>('User', { user_id: userId }, { balance_sats: 0, locked: true }, txId)
|
||||
if (!affected) {
|
||||
throw new Error("unaffected ban user for " + userId) // TODO: fix logs doxing
|
||||
throw new Error("unaffected ban user")
|
||||
}
|
||||
if (user.balance_sats > 0) {
|
||||
this.eventsLog.LogEvent({ type: 'balance_decrement', userId, appId: "", appUserId: "", balance: user.balance_sats, data: 'ban', amount: user.balance_sats })
|
||||
|
|
@ -80,7 +80,7 @@ export default class {
|
|||
const affected = await this.dbs.Increment<User>('User', { user_id: userId }, "balance_sats", increment, txId)
|
||||
if (!affected) {
|
||||
getLogger({ userId: userId, component: "balanceUpdates" })("user unaffected by increment")
|
||||
throw new Error("unaffected balance increment for " + userId) // TODO: fix logs doxing
|
||||
throw new Error("unaffected balance increment")
|
||||
}
|
||||
getLogger({ userId: userId, component: "balanceUpdates" })("incremented balance from", user.balance_sats, "sats, by", increment, "sats")
|
||||
this.eventsLog.LogEvent({ type: 'balance_increment', userId, appId: "", appUserId: "", balance: user.balance_sats, data: reason, amount: increment })
|
||||
|
|
@ -105,7 +105,7 @@ export default class {
|
|||
const affected = await this.dbs.Decrement<User>('User', { user_id: userId }, "balance_sats", decrement, txId)
|
||||
if (!affected) {
|
||||
getLogger({ userId: userId, component: "balanceUpdates" })("user unaffected by decrement")
|
||||
throw new Error("unaffected balance decrement for " + userId) // TODO: fix logs doxing
|
||||
throw new Error("unaffected balance decrement")
|
||||
}
|
||||
getLogger({ userId: userId, component: "balanceUpdates" })("decremented balance from", user.balance_sats, "sats, by", decrement, "sats")
|
||||
this.eventsLog.LogEvent({ type: 'balance_decrement', userId, appId: "", appUserId: "", balance: user.balance_sats, data: reason, amount: decrement })
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ export const setupNetwork = async (): Promise<ChainTools> => {
|
|||
const lndNodeSettings = LoadLndNodeSettingsFromEnv({})
|
||||
const secondLndNodeSettings = LoadSecondLndSettingsFromEnv()
|
||||
const liquiditySettings: LiquiditySettings = { disableLiquidityProvider: true, liquidityProviderPub: "", useOnlyLiquidityProvider: false, providerRelayUrl: "" }
|
||||
const alice = new LND(() => ({ lndSettings, lndNodeSettings }), new LiquidityProvider(() => liquiditySettings, setupUtils, async () => { }, async () => { }), async () => { }, setupUtils, async () => { }, async () => { }, () => { }, () => { }, () => { })
|
||||
const bob = new LND(() => ({ lndSettings, lndNodeSettings: secondLndNodeSettings }), new LiquidityProvider(() => liquiditySettings, setupUtils, async () => { }, async () => { }), async () => { }, setupUtils, async () => { }, async () => { }, () => { }, () => { }, () => { })
|
||||
const alice = new LND(() => ({ lndSettings, lndNodeSettings }), new LiquidityProvider(() => liquiditySettings, setupUtils, async () => { }, async () => { }), async () => { }, setupUtils, async () => { }, async () => { }, async () => { }, () => { }, () => { })
|
||||
const bob = new LND(() => ({ lndSettings, lndNodeSettings: secondLndNodeSettings }), new LiquidityProvider(() => liquiditySettings, setupUtils, async () => { }, async () => { }), async () => { }, setupUtils, async () => { }, async () => { }, async () => { }, () => { }, () => { })
|
||||
await tryUntil<void>(async i => {
|
||||
const peers = await alice.ListPeers()
|
||||
if (peers.peers.length > 0) {
|
||||
|
|
|
|||
|
|
@ -89,12 +89,12 @@ export const SetupTest = async (d: Describe, chainTools: ChainTools): Promise<Te
|
|||
const lndSettings = LoadLndSettingsFromEnv({})
|
||||
const secondLndNodeSettings = LoadSecondLndSettingsFromEnv()
|
||||
const otherLndSetting = () => ({ lndSettings, lndNodeSettings: secondLndNodeSettings })
|
||||
const externalAccessToOtherLnd = new LND(otherLndSetting, new LiquidityProvider(() => liquiditySettings, extermnalUtils, async () => { }, async () => { }), async () => { }, extermnalUtils, async () => { }, async () => { }, () => { }, () => { }, () => { })
|
||||
const externalAccessToOtherLnd = new LND(otherLndSetting, new LiquidityProvider(() => liquiditySettings, extermnalUtils, async () => { }, async () => { }), async () => { }, extermnalUtils, async () => { }, async () => { }, async () => { }, () => { }, () => { })
|
||||
await externalAccessToOtherLnd.Warmup()
|
||||
|
||||
const thirdLndNodeSettings = LoadThirdLndSettingsFromEnv()
|
||||
const thirdLndSetting = () => ({ lndSettings, lndNodeSettings: thirdLndNodeSettings })
|
||||
const externalAccessToThirdLnd = new LND(thirdLndSetting, new LiquidityProvider(() => liquiditySettings, extermnalUtils, async () => { }, async () => { }), async () => { }, extermnalUtils, async () => { }, async () => { }, () => { }, () => { }, () => { })
|
||||
const externalAccessToThirdLnd = new LND(thirdLndSetting, new LiquidityProvider(() => liquiditySettings, extermnalUtils, async () => { }, async () => { }), async () => { }, extermnalUtils, async () => { }, async () => { }, async () => { }, () => { }, () => { })
|
||||
await externalAccessToThirdLnd.Warmup()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue