Compare commits
5 commits
master
...
fix/watchd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6513b4797 | ||
|
|
b1fd18d45c | ||
|
|
7973fa83cb | ||
|
|
748a2d3ed6 | ||
|
|
30d818c4d4 |
9 changed files with 58 additions and 35 deletions
|
|
@ -105,7 +105,7 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Stop: () => { mainHandler.adminManager.setNostrConnected(false); return nostr.Stop },
|
Stop: () => { mainHandler.adminManager.setNostrConnected(false); return nostr.Stop },
|
||||||
Send: (...args) => nostr.Send(...args),
|
Send: async (...args) => nostr.Send(...args),
|
||||||
Ping: () => nostr.Ping(),
|
Ping: () => nostr.Ping(),
|
||||||
Reset: (settings: NostrSettings) => nostr.Reset(settings)
|
Reset: (settings: NostrSettings) => nostr.Reset(settings)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,15 +142,20 @@ export default class {
|
||||||
return new Promise<void>((res, rej) => {
|
return new Promise<void>((res, rej) => {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
await this.GetInfo()
|
const info = await this.GetInfo()
|
||||||
|
if (!info.syncedToChain || !info.syncedToGraph) {
|
||||||
|
this.log("LND responding but not synced yet, waiting...")
|
||||||
|
return
|
||||||
|
}
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
this.ready = true
|
this.ready = true
|
||||||
res()
|
res()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.log("LND is not ready yet, will try again in 1 second")
|
this.log("LND is not ready yet, will try again in 1 second")
|
||||||
if (Date.now() - now > 1000 * 60) {
|
}
|
||||||
rej(new Error("LND not ready after 1 minute"))
|
if (Date.now() - now > 1000 * 60 * 10) {
|
||||||
}
|
clearInterval(interval)
|
||||||
|
rej(new Error("LND not synced after 10 minutes"))
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -153,13 +153,14 @@ export class DebitManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyPaymentSuccess = (debitRes: NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
|
notifyPaymentSuccess = (debitRes: NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
|
||||||
|
this.logger("✅ [DEBIT REQUEST] Payment successful, sending OK response to", event.pub.slice(0, 16) + "...", "for event", event.id.slice(0, 16) + "...")
|
||||||
this.sendDebitResponse(debitRes, event)
|
this.sendDebitResponse(debitRes, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendDebitResponse = (debitRes: NdebitFailure | NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
|
sendDebitResponse = (debitRes: NdebitFailure | NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
|
||||||
|
this.logger("📤 [DEBIT RESPONSE] Sending Kind 21002 response:", JSON.stringify(debitRes), "to", event.pub.slice(0, 16) + "...")
|
||||||
const e = newNdebitResponse(JSON.stringify(debitRes), event)
|
const e = newNdebitResponse(JSON.stringify(debitRes), event)
|
||||||
this.storage.NostrSender().Send({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
this.storage.NostrSender().Send({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payNdebitInvoice = async (event: NostrEvent, pointerdata: NdebitData): Promise<HandleNdebitRes> => {
|
payNdebitInvoice = async (event: NostrEvent, pointerdata: NdebitData): Promise<HandleNdebitRes> => {
|
||||||
|
|
|
||||||
|
|
@ -196,15 +196,19 @@ export class Watchdog {
|
||||||
const knownMaxIndex = Math.max(maxFromDb, this.latestPaymentIndexOffset)
|
const knownMaxIndex = Math.max(maxFromDb, this.latestPaymentIndexOffset)
|
||||||
const newLatest = await this.lnd.GetLatestPaymentIndex(knownMaxIndex)
|
const newLatest = await this.lnd.GetLatestPaymentIndex(knownMaxIndex)
|
||||||
const historyMismatch = newLatest > knownMaxIndex
|
const historyMismatch = newLatest > knownMaxIndex
|
||||||
|
if (historyMismatch) {
|
||||||
|
this.log("Payment index advanced from", knownMaxIndex, "to", newLatest, "- updating offset (likely LND restart or external payment)")
|
||||||
|
this.latestPaymentIndexOffset = newLatest
|
||||||
|
}
|
||||||
const other = { ilnd: this.initialLndBalance, hf: this.accumulatedHtlcFees, iu: this.initialUsersBalance, tu: totalUsersBalance, km: knownMaxIndex, nl: newLatest, oext: otherExternal }
|
const other = { ilnd: this.initialLndBalance, hf: this.accumulatedHtlcFees, iu: this.initialUsersBalance, tu: totalUsersBalance, km: knownMaxIndex, nl: newLatest, oext: otherExternal }
|
||||||
//getLogger({ component: 'watchdog_debug2' })(JSON.stringify({ deltaLnd, deltaUsers, totalExternal, other }))
|
//getLogger({ component: 'watchdog_debug2' })(JSON.stringify({ deltaLnd, deltaUsers, totalExternal, other }))
|
||||||
const deny = await this.checkBalanceUpdate(deltaLnd, deltaUsers)
|
const deny = await this.checkBalanceUpdate(deltaLnd, deltaUsers)
|
||||||
if (historyMismatch) {
|
|
||||||
getLogger({ component: 'bark' })("History mismatch detected in absolute update, locking outgoing operations")
|
|
||||||
this.lnd.LockOutgoingOperations()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (deny) {
|
if (deny) {
|
||||||
|
if (historyMismatch) {
|
||||||
|
getLogger({ component: 'bark' })("Balance mismatch with unexpected payment history, locking outgoing operations")
|
||||||
|
this.lnd.LockOutgoingOperations()
|
||||||
|
return
|
||||||
|
}
|
||||||
this.log("Balance mismatch detected in absolute update, but history is ok")
|
this.log("Balance mismatch detected in absolute update, but history is ok")
|
||||||
}
|
}
|
||||||
this.lnd.UnlockOutgoingOperations()
|
this.lnd.UnlockOutgoingOperations()
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,12 @@ const handleNostrSettings = (settings: NostrSettings) => {
|
||||||
send(event)
|
send(event)
|
||||||
})
|
})
|
||||||
} */
|
} */
|
||||||
const sendToNostr: NostrSend = (initiator, data, relays) => {
|
const sendToNostr: NostrSend = async (initiator, data, relays) => {
|
||||||
if (!subProcessHandler) {
|
if (!subProcessHandler) {
|
||||||
getLogger({ component: "nostrMiddleware" })(ERROR, "nostr was not initialized")
|
getLogger({ component: "nostrMiddleware" })(ERROR, "nostr was not initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subProcessHandler.Send(initiator, data, relays)
|
await subProcessHandler.Send(initiator, data, relays)
|
||||||
}
|
}
|
||||||
|
|
||||||
send({ type: 'ready' })
|
send({ type: 'ready' })
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export type SendDataContent = { type: "content", content: string, pub: string }
|
||||||
export type SendDataEvent = { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } }
|
export type SendDataEvent = { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } }
|
||||||
export type SendData = SendDataContent | SendDataEvent
|
export type SendData = SendDataContent | SendDataEvent
|
||||||
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
|
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
|
||||||
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void
|
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => Promise<void>
|
||||||
|
|
||||||
export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string }
|
export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string }
|
||||||
export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo }
|
export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo }
|
||||||
|
|
@ -203,21 +203,26 @@ export class NostrPool {
|
||||||
const signed = finalizeEvent(event, Buffer.from(keys.privateKey, 'hex'))
|
const signed = finalizeEvent(event, Buffer.from(keys.privateKey, 'hex'))
|
||||||
let sent = false
|
let sent = false
|
||||||
const log = getLogger({ appName: keys.name })
|
const log = getLogger({ appName: keys.name })
|
||||||
// const r = relays ? relays : this.getServiceRelays()
|
this.log(`📤 Publishing Kind ${event.kind} event to ${relays.length} relay(s): ${relays.join(', ')}`)
|
||||||
const pool = new SimplePool()
|
const pool = new SimplePool()
|
||||||
await Promise.all(pool.publish(relays, signed).map(async p => {
|
try {
|
||||||
try {
|
await Promise.all(pool.publish(relays, signed).map(async p => {
|
||||||
await p
|
try {
|
||||||
sent = true
|
await p
|
||||||
} catch (e: any) {
|
sent = true
|
||||||
console.log(e)
|
} catch (e: any) {
|
||||||
log(e)
|
this.log(ERROR, `Failed to publish Kind ${event.kind} event:`, e.message || e)
|
||||||
|
log(e)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
if (!sent) {
|
||||||
|
this.log(ERROR, `Failed to send Kind ${event.kind} event to any relay`)
|
||||||
|
log("failed to send event")
|
||||||
|
} else {
|
||||||
|
this.log(`✅ Kind ${event.kind} event published successfully (id: ${signed.id.slice(0, 16)}...)`)
|
||||||
}
|
}
|
||||||
}))
|
} finally {
|
||||||
if (!sent) {
|
pool.close(relays)
|
||||||
log("failed to send event")
|
|
||||||
} else {
|
|
||||||
//log("sent event")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { NostrSend, SendData, SendInitiator } from "./nostrPool.js"
|
import { NostrSend, SendData, SendInitiator } from "./nostrPool.js"
|
||||||
import { getLogger } from "../helpers/logger.js"
|
import { ERROR, getLogger } from "../helpers/logger.js"
|
||||||
export class NostrSender {
|
export class NostrSender {
|
||||||
private _nostrSend: NostrSend = () => { throw new Error('nostr send not initialized yet') }
|
private _nostrSend: NostrSend = async () => { throw new Error('nostr send not initialized yet') }
|
||||||
private isReady: boolean = false
|
private isReady: boolean = false
|
||||||
private onReadyCallbacks: (() => void)[] = []
|
private onReadyCallbacks: (() => void)[] = []
|
||||||
private pendingSends: { initiator: SendInitiator, data: SendData, relays?: string[] | undefined }[] = []
|
private pendingSends: { initiator: SendInitiator, data: SendData, relays?: string[] | undefined }[] = []
|
||||||
|
|
@ -12,7 +12,12 @@ export class NostrSender {
|
||||||
this.isReady = true
|
this.isReady = true
|
||||||
this.onReadyCallbacks.forEach(cb => cb())
|
this.onReadyCallbacks.forEach(cb => cb())
|
||||||
this.onReadyCallbacks = []
|
this.onReadyCallbacks = []
|
||||||
this.pendingSends.forEach(send => this._nostrSend(send.initiator, send.data, send.relays))
|
// Process pending sends with proper error handling
|
||||||
|
this.pendingSends.forEach(send => {
|
||||||
|
this._nostrSend(send.initiator, send.data, send.relays).catch(e => {
|
||||||
|
this.log(ERROR, "failed to send pending event", e.message || e)
|
||||||
|
})
|
||||||
|
})
|
||||||
this.pendingSends = []
|
this.pendingSends = []
|
||||||
}
|
}
|
||||||
OnReady(callback: () => void) {
|
OnReady(callback: () => void) {
|
||||||
|
|
@ -22,13 +27,16 @@ export class NostrSender {
|
||||||
this.onReadyCallbacks.push(callback)
|
this.onReadyCallbacks.push(callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined) {
|
Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined): void {
|
||||||
if (!this.isReady) {
|
if (!this.isReady) {
|
||||||
this.log("tried to send before nostr was ready, caching request")
|
this.log("tried to send before nostr was ready, caching request")
|
||||||
this.pendingSends.push({ initiator, data, relays })
|
this.pendingSends.push({ initiator, data, relays })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this._nostrSend(initiator, data, relays)
|
// Fire and forget but log errors
|
||||||
|
this._nostrSend(initiator, data, relays).catch(e => {
|
||||||
|
this.log(ERROR, "failed to send event", e.message || e)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
IsReady() {
|
IsReady() {
|
||||||
return this.isReady
|
return this.isReady
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ class TlvFilesStorageProcessor {
|
||||||
throw new Error('Unknown metric type: ' + t)
|
throw new Error('Unknown metric type: ' + t)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.wrtc.attachNostrSend((initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
|
this.wrtc.attachNostrSend(async (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
|
||||||
this.sendResponse({
|
this.sendResponse({
|
||||||
success: true,
|
success: true,
|
||||||
type: 'nostrSend',
|
type: 'nostrSend',
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ export default class webRTC {
|
||||||
attachNostrSend(f: NostrSend) {
|
attachNostrSend(f: NostrSend) {
|
||||||
this._nostrSend = f
|
this._nostrSend = f
|
||||||
}
|
}
|
||||||
private nostrSend: NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
|
private nostrSend: NostrSend = async (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
|
||||||
if (!this._nostrSend) {
|
if (!this._nostrSend) {
|
||||||
throw new Error("No nostrSend attached")
|
throw new Error("No nostrSend attached")
|
||||||
}
|
}
|
||||||
this._nostrSend(initiator, data, relays)
|
await this._nostrSend(initiator, data, relays)
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendCandidate = (u: WebRtcUserInfo, candidate: string) => {
|
private sendCandidate = (u: WebRtcUserInfo, candidate: string) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue