open more channels

This commit is contained in:
boufni95 2024-07-02 19:25:22 +02:00
parent 20d2afa464
commit e7c2511fc8
4 changed files with 67 additions and 64 deletions

View file

@ -290,7 +290,7 @@ export default class {
throw new Error("lnd node is currently out of sync")
}
await this.Health()
this.log("paying invoice", invoice, "for", amount, "sats")
this.log("paying invoice", invoice, "for", amount, "sats with", useProvider ? 'provider' : 'lnd')
if (useProvider) {
const res = await this.liquidProvider.PayInvoice(invoice)
const providerDst = this.liquidProvider.GetProviderDestination()
@ -314,6 +314,9 @@ export default class {
case Payment_PaymentStatus.SUCCEEDED:
this.log("invoice payment succeded", Number(payment.valueSat))
res({ feeSat: Math.ceil(Number(payment.feeMsat) / 1000), valueSat: Number(payment.valueSat), paymentPreimage: payment.paymentPreimage })
return
default:
this.log("inflight payment update index", Number(payment.paymentIndex), Payment_PaymentStatus[payment.status])
}
})
})

View file

@ -61,29 +61,6 @@ class LSP {
this.log = getLogger({ component: serviceName })
}
shouldOpenChannel = async (): Promise<{ shouldOpen: false } | { shouldOpen: true, maxSpendable: number }> => {
if (this.settings.channelThreshold === 0) {
this.log("channel threshold is 0")
return { shouldOpen: false }
}
const channels = await this.lnd.ListChannels()
if (channels.channels.length > 0) {
this.log("this node already has open channels")
return { shouldOpen: false }
}
const pendingChannels = await this.lnd.ListPendingChannels()
if (pendingChannels.pendingOpenChannels.length > 0) {
this.log("this node already has pending channels")
return { shouldOpen: false }
}
const userState = await this.liquidityProvider.CheckUserState()
if (!userState || userState.max_withdrawable < this.settings.channelThreshold) {
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 }
}
addPeer = async (pubKey: string, host: string) => {
const { peers } = await this.lnd.ListPeers()
if (!peers.find(p => p.pubKey === pubKey)) {
@ -98,18 +75,14 @@ export class FlashsatsLSP extends LSP {
super("FlashsatsLSP", settings, lnd, liquidityProvider)
}
openChannelIfReady = async (): Promise<OrderResponse | null> => {
const shouldOpen = await this.shouldOpenChannel()
if (!shouldOpen.shouldOpen) {
return null
}
requestChannel = async (maxSpendable: number): Promise<OrderResponse | null> => {
if (!this.settings.flashsatsServiceUrl) {
this.log("no flashsats service url provided")
return null
}
const serviceInfo = await this.getInfo()
if (+serviceInfo.options.min_initial_client_balance_sat > shouldOpen.maxSpendable) {
this.log("balance of", shouldOpen.maxSpendable, "is lower than service minimum of", serviceInfo.options.min_initial_client_balance_sat)
if (+serviceInfo.options.min_initial_client_balance_sat > maxSpendable) {
this.log("balance of", maxSpendable, "is lower than service minimum of", serviceInfo.options.min_initial_client_balance_sat)
return null
}
const lndInfo = await this.lnd.GetInfo()
@ -118,7 +91,8 @@ export class FlashsatsLSP extends LSP {
this.log("no uri found for this node,uri is required to use flashsats")
return null
}
const lspBalance = (this.settings.channelThreshold * 2).toString()
const channelSize = Math.floor(maxSpendable * (1 - this.settings.maxRelativeFee)) * 2
const lspBalance = channelSize.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') {
@ -130,11 +104,11 @@ export class FlashsatsLSP extends LSP {
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 of amount", decoded.numSatoshis, "exceeds user balance of", shouldOpen.maxSpendable)
if (decoded.numSatoshis > maxSpendable) {
this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", maxSpendable)
return null
}
const relativeFee = +order.payment.fee_total_sat / this.settings.channelThreshold
const relativeFee = +order.payment.fee_total_sat / channelSize
if (relativeFee > this.settings.maxRelativeFee) {
this.log("invoice relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee)
return null
@ -175,18 +149,14 @@ export class OlympusLSP extends LSP {
super("OlympusLSP", settings, lnd, liquidityProvider)
}
openChannelIfReady = async (): Promise<OrderResponse | null> => {
const shouldOpen = await this.shouldOpenChannel()
if (!shouldOpen.shouldOpen) {
return null
}
requestChannel = async (maxSpendable: number): Promise<OrderResponse | null> => {
if (!this.settings.olympusServiceUrl) {
this.log("no olympus service url provided")
return null
}
const serviceInfo = await this.getInfo()
if (+serviceInfo.min_initial_client_balance_sat > shouldOpen.maxSpendable) {
this.log("balance of", shouldOpen.maxSpendable, "is lower than service minimum of", serviceInfo.min_initial_client_balance_sat)
if (+serviceInfo.min_initial_client_balance_sat > maxSpendable) {
this.log("balance of", maxSpendable, "is lower than service minimum of", serviceInfo.min_initial_client_balance_sat)
return null
}
const [servicePub, host] = serviceInfo.uris[0].split('@')
@ -194,7 +164,8 @@ export class OlympusLSP extends LSP {
const lndInfo = await this.lnd.GetInfo()
const myPub = lndInfo.identityPubkey
const refundAddr = await this.lnd.NewAddress(AddressType.WITNESS_PUBKEY_HASH)
const lspBalance = (this.settings.channelThreshold * 2).toString()
const channelSize = Math.floor(maxSpendable * (1 - this.settings.maxRelativeFee)) * 2
const lspBalance = channelSize.toString()
const chanExpiryBlocks = serviceInfo.max_channel_expiry_blocks
const order = await this.createOrder({ pubKey: myPub, refundAddr: refundAddr.address, lspBalance, clientBalance: "0", chanExpiryBlocks })
if (order.payment.bolt11.state !== 'EXPECT_PAYMENT') {
@ -206,11 +177,11 @@ export class OlympusLSP extends LSP {
this.log("invoice of amount", decoded.numSatoshis, "does not match order total of", order.payment.bolt11.order_total_sat)
return null
}
if (decoded.numSatoshis > shouldOpen.maxSpendable) {
this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", shouldOpen.maxSpendable)
if (decoded.numSatoshis > maxSpendable) {
this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", maxSpendable)
return null
}
const relativeFee = +order.payment.bolt11.fee_total_sat / this.settings.channelThreshold
const relativeFee = +order.payment.bolt11.fee_total_sat / channelSize
if (relativeFee > this.settings.maxRelativeFee) {
this.log("invoice relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee)
return null
@ -277,12 +248,7 @@ export class VoltageLSP extends LSP {
return json
}
openChannelIfReady = async (): Promise<OrderResponse | null> => {
const shouldOpen = await this.shouldOpenChannel()
if (!shouldOpen.shouldOpen) {
return null
}
requestChannel = async (maxSpendable: number): Promise<OrderResponse | null> => {
if (!this.settings.voltageServiceUrl) {
this.log("no voltage service url provided")
return null
@ -290,10 +256,11 @@ export class VoltageLSP extends LSP {
const lndInfo = await this.lnd.GetInfo()
const myPub = lndInfo.identityPubkey
const amtMsats = this.settings.channelThreshold * 1000
const amtSats = Math.floor(maxSpendable * 0.9)
const amtMsats = Math.floor(maxSpendable * 0.9) * 1000
const fee = await this.getFees(amtMsats, myPub)
const feeSats = fee.fee_amount_msat / 1000
const relativeFee = feeSats / this.settings.channelThreshold
const relativeFee = feeSats / (amtSats * 1.1)
if (relativeFee > this.settings.maxRelativeFee) {
this.log("relative fee of", relativeFee, "exceeds max relative fee of", this.settings.maxRelativeFee)
@ -308,15 +275,18 @@ export class VoltageLSP extends LSP {
}
await this.addPeer(info.pubkey, `${ipv4.address}:${ipv4.port}`)
const invoice = await this.lnd.NewInvoice(this.settings.channelThreshold, "open channel", 60 * 60)
const invoice = await this.lnd.NewInvoice(amtSats, "open channel", 60 * 60)
const proposalRes = await this.proposal(invoice.payRequest, fee.id)
this.log("proposal res", proposalRes, fee.id)
const decoded = await this.lnd.DecodeInvoice(proposalRes.jit_bolt11)
if (decoded.numSatoshis !== this.settings.channelThreshold + feeSats) {
this.log("invoice of amount", decoded.numSatoshis, "does not match expected amount of", this.settings.channelThreshold + feeSats)
if (decoded.numSatoshis !== amtSats + feeSats) {
this.log("invoice of amount", decoded.numSatoshis, "does not match expected amount of", amtSats + feeSats)
return null
}
if (decoded.numSatoshis > maxSpendable) {
this.log("invoice of amount", decoded.numSatoshis, "exceeds user balance of", maxSpendable)
return null
}
const res = await this.liquidityProvider.PayInvoice(proposalRes.jit_bolt11)
const fees = feeSats + res.network_fee + res.service_fee
this.log("paid", res.amount_paid, "to open channel, and a fee of", fees)

View file

@ -4,7 +4,7 @@ import { SendPaymentRequest } from "../../../proto/lnd/router";
export const PayInvoiceReq = (invoice: string, amount: number, feeLimit: number): SendPaymentRequest => ({
amt: BigInt(amount),
feeLimitSat: BigInt(feeLimit),
noInflightUpdates: true,
noInflightUpdates: false,
paymentRequest: invoice,
maxParts: 3,
timeoutSeconds: 50,
@ -25,6 +25,5 @@ export const PayInvoiceReq = (invoice: string, amount: number, feeLimit: number)
paymentHash: Buffer.alloc(0),
routeHints: [],
timePref: 0,
outgoingChanId: '0'
})

View file

@ -71,6 +71,7 @@ export class LiquidityManager {
this.log("channel does not have enough balance for invoice,suggesting provider")
return 'provider'
}
afterInInvoicePaid = async () => {
try {
await this.orderChannelIfNeeded()
@ -78,9 +79,39 @@ export class LiquidityManager {
this.log("error ordering channel", e)
}
}
shouldOpenChannel = async (): Promise<{ shouldOpen: false } | { shouldOpen: true, maxSpendable: number }> => {
const threshold = this.settings.lspSettings.channelThreshold
if (threshold === 0) {
this.log("channel threshold is 0")
return { shouldOpen: false }
}
const { remote } = await this.lnd.ChannelBalance()
if (remote > threshold) {
this.log("remote channel balance is already more than threshold")
return { shouldOpen: false }
}
const pendingChannels = await this.lnd.ListPendingChannels()
if (pendingChannels.pendingOpenChannels.length > 0) {
this.log("pending open channels detected, liquidiity might be on the way")
return { shouldOpen: false }
}
const userState = await this.liquidityProvider.CheckUserState()
if (!userState || userState.max_withdrawable < threshold) {
this.log("balance of", userState?.max_withdrawable || 0, "is lower than channel threshold of", threshold)
return { shouldOpen: false }
}
return { shouldOpen: true, maxSpendable: userState.max_withdrawable }
}
orderChannelIfNeeded = async () => {
const existingOrder = await this.storage.liquidityStorage.GetLatestLspOrder()
if (existingOrder) {
if (existingOrder && existingOrder.created_at > new Date(Date.now() - 20 * 60 * 1000)) {
this.log("most recent lsp order is less than 20 minutes old")
return
}
const shouldOpen = await this.shouldOpenChannel()
if (!shouldOpen.shouldOpen) {
return
}
if (this.channelRequested || this.channelRequesting) {
@ -88,7 +119,7 @@ export class LiquidityManager {
}
this.channelRequesting = true
this.log("checking if channel should be requested")
const olympusOk = await this.olympusLSP.openChannelIfReady()
const olympusOk = await this.olympusLSP.requestChannel(shouldOpen.maxSpendable)
if (olympusOk) {
this.log("requested channel from olympus")
this.channelRequested = true
@ -97,7 +128,7 @@ export class LiquidityManager {
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()
const voltageOk = await this.voltageLSP.requestChannel(shouldOpen.maxSpendable)
if (voltageOk) {
this.log("requested channel from voltage")
this.channelRequested = true
@ -107,7 +138,7 @@ export class LiquidityManager {
return
}
const flashsatsOk = await this.flashsatsLSP.openChannelIfReady()
const flashsatsOk = await this.flashsatsLSP.requestChannel(shouldOpen.maxSpendable)
if (flashsatsOk) {
this.log("requested channel from flashsats")
this.channelRequested = true