liquidity provider tested
This commit is contained in:
parent
f31551a2b5
commit
72683a3e88
9 changed files with 186 additions and 42 deletions
|
|
@ -21,6 +21,7 @@ export class LiquidityProvider {
|
|||
pubDestination: string
|
||||
latestMaxWithdrawable: number | null = null
|
||||
invoicePaidCb: InvoicePaidCb
|
||||
connecting = false
|
||||
// make the sub process accept client
|
||||
constructor(pubDestination: string, invoicePaidCb: InvoicePaidCb) {
|
||||
if (!pubDestination) {
|
||||
|
|
@ -44,11 +45,13 @@ export class LiquidityProvider {
|
|||
Connect = async () => {
|
||||
await new Promise(res => setTimeout(res, 2000))
|
||||
this.log("ready")
|
||||
this.CheckUSerState()
|
||||
await this.CheckUserState()
|
||||
if (this.latestMaxWithdrawable === null) {
|
||||
return
|
||||
}
|
||||
this.log("subbing to user operations")
|
||||
this.client.GetLiveUserOperations(res => {
|
||||
console.log("got user operation", res)
|
||||
if (res.status === 'ERROR') {
|
||||
this.log("error getting user operations", res.reason)
|
||||
return
|
||||
|
|
@ -61,7 +64,7 @@ export class LiquidityProvider {
|
|||
})
|
||||
}
|
||||
|
||||
CheckUSerState = async () => {
|
||||
CheckUserState = async () => {
|
||||
const res = await this.client.GetUserInfo()
|
||||
if (res.status === 'ERROR') {
|
||||
this.log("error getting user info", res)
|
||||
|
|
@ -69,6 +72,7 @@ export class LiquidityProvider {
|
|||
}
|
||||
this.latestMaxWithdrawable = res.max_withdrawable
|
||||
this.log("latest provider balance:", res.max_withdrawable)
|
||||
return res
|
||||
}
|
||||
|
||||
CanProviderHandle = (req: LiquidityRequest) => {
|
||||
|
|
@ -88,7 +92,7 @@ export class LiquidityProvider {
|
|||
throw new Error(res.reason)
|
||||
}
|
||||
this.log("new invoice", res.invoice)
|
||||
this.CheckUSerState()
|
||||
this.CheckUserState()
|
||||
return res.invoice
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +103,7 @@ export class LiquidityProvider {
|
|||
throw new Error(res.reason)
|
||||
}
|
||||
this.log("paid invoice", res)
|
||||
this.CheckUSerState()
|
||||
this.CheckUserState()
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
@ -123,18 +127,17 @@ export class LiquidityProvider {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
onEvent = async (res: { requestId: string }, fromPub: string) => {
|
||||
if (fromPub !== this.pubDestination) {
|
||||
this.log("got event from invalid pub", fromPub, this.pubDestination)
|
||||
return false
|
||||
}
|
||||
if (this.clientCbs[res.requestId]) {
|
||||
const cb = this.clientCbs[res.requestId]
|
||||
cb.f(res)
|
||||
if (cb.type === 'single') {
|
||||
const deleteOk = (delete this.clientCbs[res.requestId])
|
||||
console.log(this.getSingleSubs(), "single subs left", deleteOk)
|
||||
delete this.clientCbs[res.requestId]
|
||||
this.log(this.getSingleSubs(), "single subs left")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -160,7 +163,7 @@ export class LiquidityProvider {
|
|||
|
||||
//this.nostrSend(this.relays, to, JSON.stringify(message), this.settings)
|
||||
|
||||
console.log("subbing to single send", reqId, message.rpcName)
|
||||
this.log("subbing to single send", reqId, message.rpcName || 'no rpc name')
|
||||
return new Promise(res => {
|
||||
this.clientCbs[reqId] = {
|
||||
startedAtMillis: Date.now(),
|
||||
|
|
@ -187,7 +190,7 @@ export class LiquidityProvider {
|
|||
type: 'stream',
|
||||
f: (response: any) => { cb(response) },
|
||||
}
|
||||
console.log("sub for", reqId, "was already registered, overriding")
|
||||
this.log("sub for", reqId, "was already registered, overriding")
|
||||
return
|
||||
}
|
||||
this.nostrSend({ type: 'client', clientId: this.clientId }, {
|
||||
|
|
@ -195,7 +198,7 @@ export class LiquidityProvider {
|
|||
pub: to,
|
||||
content: JSON.stringify(message)
|
||||
})
|
||||
console.log("subbing to stream", reqId)
|
||||
this.log("subbing to stream", reqId)
|
||||
this.clientCbs[reqId] = {
|
||||
startedAtMillis: Date.now(),
|
||||
type: 'stream',
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ export default class {
|
|||
}, { abort: this.abortController.signal })
|
||||
stream.responses.onMessage(invoice => {
|
||||
if (invoice.state === Invoice_InvoiceState.SETTLED) {
|
||||
this.log("An invoice was paid for", Number(invoice.amtPaidSat), "sats")
|
||||
this.log("An invoice was paid for", Number(invoice.amtPaidSat), "sats", invoice.paymentRequest)
|
||||
this.latestKnownSettleIndex = Number(invoice.settleIndex)
|
||||
this.invoicePaidCb(invoice.paymentRequest, Number(invoice.amtPaidSat), false)
|
||||
}
|
||||
|
|
|
|||
29
src/services/lnd/lsp.ts
Normal file
29
src/services/lnd/lsp.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import fetch from "node-fetch"
|
||||
|
||||
export class LSP {
|
||||
serviceUrl: string
|
||||
constructor(serviceUrl: string) {
|
||||
this.serviceUrl = serviceUrl
|
||||
}
|
||||
|
||||
getInfo = async () => {
|
||||
const res = await fetch(`${this.serviceUrl}/getinfo`)
|
||||
const json = await res.json() as { options: {}, uris: string[] }
|
||||
}
|
||||
|
||||
createOrder = async (req: { public_key: string }) => {
|
||||
const res = await fetch(`${this.serviceUrl}/create_order`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(req),
|
||||
headers: { "Content-Type": "application/json" }
|
||||
})
|
||||
const json = await res.json() as {}
|
||||
return json
|
||||
}
|
||||
|
||||
getOrder = async (orderId: string) => {
|
||||
const res = await fetch(`${this.serviceUrl}/get_order&order_id=${orderId}`)
|
||||
const json = await res.json() as {}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ export const initMainHandler = async (log: PubLogger, mainSettings: MainSettings
|
|||
if (stop) {
|
||||
return
|
||||
}
|
||||
return { mainHandler, apps, liquidityProviderInfo }
|
||||
return { mainHandler, apps, liquidityProviderInfo, liquidityProviderApp }
|
||||
}
|
||||
|
||||
const processArgs = async (mainHandler: Main) => {
|
||||
|
|
|
|||
|
|
@ -75,14 +75,15 @@ export const LoadTestSettingsFromEnv = (): TestSettings => {
|
|||
lndAddr: EnvMustBeNonEmptyString("LND_FOURTH_ADDR"),
|
||||
lndCertPath: EnvMustBeNonEmptyString("LND_FOURTH_CERT_PATH"),
|
||||
lndMacaroonPath: EnvMustBeNonEmptyString("LND_FOURTH_MACAROON_PATH")
|
||||
}
|
||||
},
|
||||
liquidityProviderPub: ""
|
||||
},
|
||||
skipSanityCheck: true,
|
||||
bitcoinCoreSettings: {
|
||||
port: EnvMustBeInteger("BITCOIN_CORE_PORT"),
|
||||
user: EnvMustBeNonEmptyString("BITCOIN_CORE_USER"),
|
||||
pass: EnvMustBeNonEmptyString("BITCOIN_CORE_PASS")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,53 @@
|
|||
import { initMainHandler } from '../services/main/init.js'
|
||||
import { LoadTestSettingsFromEnv } from '../services/main/settings.js'
|
||||
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||
import { runSanityCheck, safelySetUserBalance, TestBase } from './testBase.js'
|
||||
import { disableLoggers } from '../services/helpers/logger.js'
|
||||
import { runSanityCheck, safelySetUserBalance, TestBase, TestUserData } from './testBase.js'
|
||||
import { initBootstrappedInstance } from './setupBootstrapped.js'
|
||||
import Main from '../services/main/index.js'
|
||||
import { AppData } from '../services/main/init.js'
|
||||
export const ignore = false
|
||||
export const dev = true
|
||||
export const dev = false
|
||||
|
||||
|
||||
|
||||
export default async (T: TestBase) => {
|
||||
disableLoggers([], ["EventsLogManager", "watchdog", "htlcTracker", "debugHtlcs", "debugLndBalancev3", "metrics", "mainForTest", "main"])
|
||||
await safelySetUserBalance(T, T.user1, 2000)
|
||||
const bootstrapped = await initBootstrappedInstance(T)
|
||||
bootstrapped.appUserManager.NewInvoice({ app_id: T.user1.appId, user_id: T.user1.userId, app_user_id: T.user1.appUserIdentifier }, { amountSats: 2000, memo: "liquidityTest" })
|
||||
T.d("starting liquidityProvider tests...")
|
||||
const { bootstrapped, bootstrappedUser } = await initBootstrappedInstance(T)
|
||||
await testInboundPaymentFromProvider(T, bootstrapped, bootstrappedUser)
|
||||
await testOutboundPaymentFromProvider(T, bootstrapped, bootstrappedUser)
|
||||
await runSanityCheck(T)
|
||||
}
|
||||
|
||||
const initBootstrappedInstance = async (T: TestBase) => {
|
||||
const settings = LoadTestSettingsFromEnv()
|
||||
settings.lndSettings.useOnlyLiquidityProvider = true
|
||||
const initialized = await initMainHandler(console.log, settings)
|
||||
if (!initialized) {
|
||||
throw new Error("failed to initialize bootstrapped main handler")
|
||||
}
|
||||
const { mainHandler: bootstrapped, liquidityProviderInfo } = initialized
|
||||
const testInboundPaymentFromProvider = async (T: TestBase, bootstrapped: Main, bUser: TestUserData) => {
|
||||
T.d("starting testInboundPaymentFromProvider")
|
||||
const invoiceRes = await bootstrapped.appUserManager.NewInvoice({ app_id: bUser.appId, user_id: bUser.userId, app_user_id: bUser.appUserIdentifier }, { amountSats: 2000, memo: "liquidityTest" })
|
||||
|
||||
bootstrapped.liquidProvider.attachNostrSend((identifier, data, r) => {
|
||||
console.log(identifier, data)
|
||||
})
|
||||
bootstrapped.liquidProvider.setNostrInfo({ clientId: liquidityProviderInfo.clientId, myPub: liquidityProviderInfo.publicKey })
|
||||
return bootstrapped
|
||||
await T.externalAccessToOtherLnd.PayInvoice(invoiceRes.invoice, 0, 100)
|
||||
const userBalance = await bootstrapped.appUserManager.GetUserInfo({ app_id: bUser.appId, user_id: bUser.userId, app_user_id: bUser.appUserIdentifier })
|
||||
T.expect(userBalance.balance).to.equal(2000)
|
||||
|
||||
const providerBalance = await bootstrapped.liquidProvider.CheckUserState()
|
||||
if (!providerBalance) {
|
||||
throw new Error("provider balance not found")
|
||||
}
|
||||
T.expect(providerBalance.balance).to.equal(2000)
|
||||
T.d("testInboundPaymentFromProvider done")
|
||||
}
|
||||
|
||||
const testOutboundPaymentFromProvider = async (T: TestBase, bootstrapped: Main, bootstrappedUser: TestUserData) => {
|
||||
T.d("starting testOutboundPaymentFromProvider")
|
||||
|
||||
const invoice = await T.externalAccessToOtherLnd.NewInvoice(1000, "", 60 * 60)
|
||||
const ctx = { app_id: bootstrappedUser.appId, user_id: bootstrappedUser.userId, app_user_id: bootstrappedUser.appUserIdentifier }
|
||||
const res = await bootstrapped.appUserManager.PayInvoice(ctx, { invoice: invoice.payRequest, amount: 0 })
|
||||
|
||||
const userBalance = await bootstrapped.appUserManager.GetUserInfo(ctx)
|
||||
T.expect(userBalance.balance).to.equal(986) // 2000 - (1000 + 6(x2) + 2)
|
||||
|
||||
const providerBalance = await bootstrapped.liquidProvider.CheckUserState()
|
||||
if (!providerBalance) {
|
||||
throw new Error("provider balance not found")
|
||||
}
|
||||
T.expect(providerBalance.balance).to.equal(992) // 2000 - (1000 + 6 +2)
|
||||
T.d("testOutboundPaymentFromProvider done")
|
||||
}
|
||||
|
|
@ -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, new LiquidityProvider("", () => { }), () => { }, () => { }, () => { }, () => { })
|
||||
const bob = new LND({ ...settings.lndSettings, mainNode: settings.lndSettings.otherNode }, new LiquidityProvider("", () => { }), () => { }, () => { }, () => { }, () => { })
|
||||
await tryUntil<void>(async i => {
|
||||
const peers = await alice.ListPeers()
|
||||
if (peers.peers.length > 0) {
|
||||
|
|
|
|||
89
src/tests/setupBootstrapped.ts
Normal file
89
src/tests/setupBootstrapped.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { getLogger } from '../services/helpers/logger.js'
|
||||
import { initMainHandler } from '../services/main/init.js'
|
||||
import { LoadTestSettingsFromEnv } from '../services/main/settings.js'
|
||||
import { SendData } from '../services/nostr/handler.js'
|
||||
import { TestBase, TestUserData } from './testBase.js'
|
||||
import * as Types from '../../proto/autogenerated/ts/types.js'
|
||||
|
||||
export const initBootstrappedInstance = async (T: TestBase) => {
|
||||
const requests = {}
|
||||
const settings = LoadTestSettingsFromEnv()
|
||||
settings.lndSettings.useOnlyLiquidityProvider = true
|
||||
settings.lndSettings.liquidityProviderPub = T.app.publicKey
|
||||
settings.lndSettings.mainNode = settings.lndSettings.thirdNode
|
||||
const initialized = await initMainHandler(getLogger({ component: "bootstrapped" }), settings)
|
||||
if (!initialized) {
|
||||
throw new Error("failed to initialize bootstrapped main handler")
|
||||
}
|
||||
const { mainHandler: bootstrapped, liquidityProviderInfo, liquidityProviderApp, apps } = initialized
|
||||
T.main.attachNostrSend(async (initiator, data, r) => {
|
||||
if (data.type === 'event') {
|
||||
throw new Error("unsupported event type")
|
||||
}
|
||||
if (data.pub !== liquidityProviderInfo.publicKey) {
|
||||
throw new Error("invalid pub " + data.pub + " expected " + liquidityProviderInfo.publicKey)
|
||||
}
|
||||
const j = JSON.parse(data.content) as { requestId: string }
|
||||
console.log("sending new operation to provider")
|
||||
bootstrapped.liquidProvider.onEvent(j, T.app.publicKey)
|
||||
})
|
||||
bootstrapped.liquidProvider.attachNostrSend(async (initiator, data, r) => {
|
||||
const res = await handleSend(T, data)
|
||||
if (data.type === 'event') {
|
||||
throw new Error("unsupported event type")
|
||||
}
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
bootstrapped.liquidProvider.onEvent(res, data.pub)
|
||||
})
|
||||
bootstrapped.liquidProvider.setNostrInfo({ clientId: liquidityProviderInfo.clientId, myPub: liquidityProviderInfo.publicKey })
|
||||
await new Promise<void>(res => {
|
||||
const interval = setInterval(() => {
|
||||
if (bootstrapped.liquidProvider.CanProviderHandle({ action: 'receive', amount: 2000 })) {
|
||||
clearInterval(interval)
|
||||
res()
|
||||
} else {
|
||||
console.log("waiting for provider to be able to handle the request")
|
||||
}
|
||||
}, 500)
|
||||
})
|
||||
const bUser = await bootstrapped.applicationManager.AddAppUser(liquidityProviderApp.appId, { identifier: "user1_bootstrapped", balance: 0, fail_if_exists: true })
|
||||
const bootstrappedUser: TestUserData = { userId: bUser.info.userId, appUserIdentifier: bUser.identifier, appId: liquidityProviderApp.appId }
|
||||
return { bootstrapped, liquidityProviderInfo, liquidityProviderApp, bootstrappedUser }
|
||||
}
|
||||
type TransportRequest = { requestId: string, authIdentifier: string } & (
|
||||
{ rpcName: 'GetUserInfo' } |
|
||||
{ rpcName: 'NewInvoice', body: Types.NewInvoiceRequest } |
|
||||
{ rpcName: 'PayInvoice', body: Types.PayInvoiceRequest } |
|
||||
{ rpcName: 'GetLiveUserOperations' } |
|
||||
{ rpcName: "" }
|
||||
)
|
||||
const handleSend = async (T: TestBase, data: SendData) => {
|
||||
if (data.type === 'event') {
|
||||
throw new Error("unsupported event type")
|
||||
}
|
||||
if (data.pub !== T.app.publicKey) {
|
||||
throw new Error("invalid pub")
|
||||
}
|
||||
const j = JSON.parse(data.content) as TransportRequest
|
||||
const app = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||
const nostrUser = await T.main.storage.applicationStorage.GetOrCreateNostrAppUser(app, j.authIdentifier)
|
||||
const userCtx = { app_id: app.app_id, user_id: nostrUser.user.user_id, app_user_id: nostrUser.identifier }
|
||||
switch (j.rpcName) {
|
||||
case 'GetUserInfo':
|
||||
const infoRes = await T.main.appUserManager.GetUserInfo(userCtx)
|
||||
return { ...infoRes, status: "OK", requestId: j.requestId }
|
||||
case 'NewInvoice':
|
||||
const genInvoiceRes = await T.main.appUserManager.NewInvoice(userCtx, j.body)
|
||||
return { ...genInvoiceRes, status: "OK", requestId: j.requestId }
|
||||
case 'PayInvoice':
|
||||
const payRes = await T.main.appUserManager.PayInvoice(userCtx, j.body)
|
||||
return { ...payRes, status: "OK", requestId: j.requestId }
|
||||
case 'GetLiveUserOperations':
|
||||
return
|
||||
default:
|
||||
console.log(data)
|
||||
throw new Error("unsupported rpcName " + j.rpcName)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import chaiString from 'chai-string'
|
|||
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||
import SanityChecker from '../services/main/sanityChecker.js'
|
||||
import LND from '../services/lnd/lnd.js'
|
||||
import { resetDisabledLoggers } from '../services/helpers/logger.js'
|
||||
import { getLogger, resetDisabledLoggers } from '../services/helpers/logger.js'
|
||||
import { LiquidityProvider } from '../services/lnd/liquidityProvider.js'
|
||||
chai.use(chaiString)
|
||||
export const expect = chai.expect
|
||||
|
|
@ -34,7 +34,7 @@ export type TestBase = {
|
|||
|
||||
export const SetupTest = async (d: Describe): Promise<TestBase> => {
|
||||
const settings = LoadTestSettingsFromEnv()
|
||||
const initialized = await initMainHandler(console.log, settings)
|
||||
const initialized = await initMainHandler(getLogger({ component: "mainForTest" }), settings)
|
||||
if (!initialized) {
|
||||
throw new Error("failed to initialize main handler")
|
||||
}
|
||||
|
|
@ -46,15 +46,15 @@ export const SetupTest = async (d: Describe): Promise<TestBase> => {
|
|||
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, 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, 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, new LiquidityProvider("", () => { }), console.log, console.log, () => { }, () => { })
|
||||
await externalAccessToThirdLnd.Warmup()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue