fix u2u
This commit is contained in:
parent
229cfa7a95
commit
0b08fde708
14 changed files with 95 additions and 83 deletions
|
|
@ -4,14 +4,14 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": " tsc && node build/src/tests/testRunner.js",
|
"clean": "rimraf build",
|
||||||
"start": "tsc && node build/src/index.js",
|
"test": "npm run clean && tsc && node build/src/tests/testRunner.js",
|
||||||
|
"start": "npm run clean && tsc && node build/src/index.js",
|
||||||
"start:ci": "git reset --hard && git pull && npm run start",
|
"start:ci": "git reset --hard && git pull && npm run start",
|
||||||
"build_autogenerated": "cd proto && rimraf autogenerated && protoc -I ./service --pub_out=. service/*",
|
"build_autogenerated": "cd proto && rimraf autogenerated && protoc -I ./service --pub_out=. service/*",
|
||||||
"build_lnd_client_1": "cd proto && protoc -I ./others --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_out=./lnd --ts_proto_opt=esModuleInterop=true others/* ",
|
"build_lnd_client_1": "cd proto && protoc -I ./others --plugin=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_out=./lnd --ts_proto_opt=esModuleInterop=true others/* ",
|
||||||
"build_lnd_client": "cd proto && rimraf lnd/* && npx protoc --ts_out ./lnd --ts_opt long_type_string --proto_path others others/* ",
|
"build_lnd_client": "cd proto && rimraf lnd/* && npx protoc --ts_out ./lnd --ts_opt long_type_string --proto_path others others/* ",
|
||||||
"typeorm": "typeorm-ts-node-commonjs",
|
"typeorm": "typeorm-ts-node-commonjs"
|
||||||
"aa": "tsc && cd build && node src/services/nostr/index.js"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
||||||
|
|
@ -30,46 +30,4 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
||||||
}, event.startAtNano, event.startAtMs)
|
}, event.startAtNano, event.startAtMs)
|
||||||
})
|
})
|
||||||
return { Stop: () => nostr.Stop, Send: (...args) => nostr.Send(...args) }
|
return { Stop: () => nostr.Stop, Send: (...args) => nostr.Send(...args) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings): Nostr => {
|
|
||||||
// TODO: - move to codegen
|
|
||||||
const nostr = new Nostr(nostrSettings,
|
|
||||||
async (event) => {
|
|
||||||
if (!nostrSettings.allowedPubs.includes(event.pub)) {
|
|
||||||
console.log("nostr pub not allowed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let nostrUser = await mainHandler.storage.FindNostrUser(event.pub)
|
|
||||||
if (!nostrUser) {
|
|
||||||
nostrUser = await mainHandler.storage.AddNostrUser(event.pub)
|
|
||||||
}
|
|
||||||
let j: EventRequest
|
|
||||||
try {
|
|
||||||
j = JSON.parse(event.content)
|
|
||||||
} catch {
|
|
||||||
console.error("invalid json event received", event.content)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (handledRequests.includes(j.requestId)) {
|
|
||||||
console.log("request already handled")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handledRequests.push(j.requestId)
|
|
||||||
switch (j.method) {
|
|
||||||
case '/api/user/chain/new':
|
|
||||||
const error = Types.NewAddressRequestValidate(j.body)
|
|
||||||
if (error !== null) {
|
|
||||||
console.error("invalid request from", event.pub, j)// TODO: dont dox
|
|
||||||
return // TODO: respond
|
|
||||||
}
|
|
||||||
if (!serverMethods.NewAddress) {
|
|
||||||
throw new Error("unimplemented NewInvoice")
|
|
||||||
}
|
|
||||||
const res = await serverMethods.NewAddress({ user_id: nostrUser.user.user_id }, j.body)
|
|
||||||
nostr.Send(event.pub, JSON.stringify({ ...res, requestId: j.requestId }))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return nostr
|
|
||||||
}*/
|
|
||||||
|
|
@ -189,7 +189,6 @@ export default class {
|
||||||
this.log("paying external invoice", invoice)
|
this.log("paying external invoice", invoice)
|
||||||
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
const routingFeeLimit = this.lnd.GetFeeLimitAmount(payAmount)
|
||||||
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice)
|
await this.storage.userStorage.DecrementUserBalance(userId, totalAmountToDecrement + routingFeeLimit, invoice)
|
||||||
console.log("decremented")
|
|
||||||
const pendingPayment = await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, payAmount, linkedApplication)
|
const pendingPayment = await this.storage.paymentStorage.AddPendingExternalPayment(userId, invoice, payAmount, linkedApplication)
|
||||||
try {
|
try {
|
||||||
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit)
|
const payment = await this.lnd.PayInvoice(invoice, amountForLnd, routingFeeLimit)
|
||||||
|
|
@ -523,9 +522,8 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication: Application): Promise<number> {
|
async SendUserToUserPayment(fromUserId: string, toUserId: string, amount: number, linkedApplication: Application): Promise<{ amount: number, fees: number }> {
|
||||||
let sentAmount = 0
|
const payment = await this.storage.StartTransaction(async tx => {
|
||||||
await this.storage.StartTransaction(async tx => {
|
|
||||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
const fromUser = await this.storage.userStorage.GetUser(fromUserId, tx)
|
||||||
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
const toUser = await this.storage.userStorage.GetUser(toUserId, tx)
|
||||||
if (fromUser.locked || toUser.locked) {
|
if (fromUser.locked || toUser.locked) {
|
||||||
|
|
@ -536,21 +534,21 @@ export default class {
|
||||||
}
|
}
|
||||||
const isAppUserPayment = fromUser.user_id !== linkedApplication.owner.user_id
|
const isAppUserPayment = fromUser.user_id !== linkedApplication.owner.user_id
|
||||||
let fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isAppUserPayment)
|
let fee = this.getServiceFee(Types.UserOperationType.OUTGOING_USER_TO_USER, amount, isAppUserPayment)
|
||||||
const toIncrement = amount - fee
|
const toDecrement = amount + fee
|
||||||
const paymentEntry = await this.storage.paymentStorage.CreateUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
const paymentEntry = await this.storage.paymentStorage.AddPendingUserToUserPayment(fromUserId, toUserId, amount, fee, linkedApplication, tx)
|
||||||
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, amount, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
await this.storage.userStorage.DecrementUserBalance(fromUser.user_id, toDecrement, `${toUserId}:${paymentEntry.serial_id}`, tx)
|
||||||
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, toIncrement, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
await this.storage.userStorage.IncrementUserBalance(toUser.user_id, amount, `${fromUserId}:${paymentEntry.serial_id}`, tx)
|
||||||
await this.storage.paymentStorage.SaveUserToUserPayment(paymentEntry, tx)
|
await this.storage.paymentStorage.SetPendingUserToUserPaymentAsPaid(paymentEntry.serial_id, tx)
|
||||||
if (isAppUserPayment && fee > 0) {
|
if (isAppUserPayment && fee > 0) {
|
||||||
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee, 'fees', tx)
|
await this.storage.userStorage.IncrementUserBalance(linkedApplication.owner.user_id, fee, 'fees', tx)
|
||||||
}
|
}
|
||||||
sentAmount = toIncrement
|
return paymentEntry
|
||||||
})
|
})
|
||||||
const fromUser = await this.storage.userStorage.GetUser(fromUserId)
|
const fromUser = await this.storage.userStorage.GetUser(fromUserId)
|
||||||
const toUser = await this.storage.userStorage.GetUser(toUserId)
|
const toUser = await this.storage.userStorage.GetUser(toUserId)
|
||||||
this.storage.eventsLog.LogEvent({ type: 'u2u_sender', userId: fromUserId, appId: linkedApplication.app_id, appUserId: "", balance: fromUser.balance_sats, data: toUserId, amount: amount })
|
this.storage.eventsLog.LogEvent({ type: 'u2u_sender', userId: fromUserId, appId: linkedApplication.app_id, appUserId: "", balance: fromUser.balance_sats, data: toUserId, amount: payment.paid_amount + payment.service_fees })
|
||||||
this.storage.eventsLog.LogEvent({ type: 'u2u_receiver', userId: toUserId, appId: linkedApplication.app_id, appUserId: "", balance: toUser.balance_sats, data: fromUserId, amount: amount })
|
this.storage.eventsLog.LogEvent({ type: 'u2u_receiver', userId: toUserId, appId: linkedApplication.app_id, appUserId: "", balance: toUser.balance_sats, data: fromUserId, amount: amount })
|
||||||
return sentAmount
|
return { amount: payment.paid_amount, fees: payment.service_fees }
|
||||||
}
|
}
|
||||||
|
|
||||||
async CheckNewlyConfirmedTxs(height: number) {
|
async CheckNewlyConfirmedTxs(height: number) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Storage from '../storage/index.js'
|
||||||
import { LightningHandler } from "../lnd/index.js"
|
import { LightningHandler } from "../lnd/index.js"
|
||||||
import { LoggedEvent } from '../storage/eventsLog.js'
|
import { LoggedEvent } from '../storage/eventsLog.js'
|
||||||
import { Invoice, Payment } from '../../../proto/lnd/lightning';
|
import { Invoice, Payment } from '../../../proto/lnd/lightning';
|
||||||
|
import { getLogger } from '../helpers/logger.js';
|
||||||
const LN_INVOICE_REGEX = /^(lightning:)?(lnbc|lntb)[0-9a-zA-Z]+$/;
|
const LN_INVOICE_REGEX = /^(lightning:)?(lnbc|lntb)[0-9a-zA-Z]+$/;
|
||||||
const BITCOIN_ADDRESS_REGEX = /^(bitcoin:)?([13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/;
|
const BITCOIN_ADDRESS_REGEX = /^(bitcoin:)?([13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})$/;
|
||||||
type UniqueDecrementReasons = 'ban'
|
type UniqueDecrementReasons = 'ban'
|
||||||
|
|
@ -19,6 +20,7 @@ export default class SanityChecker {
|
||||||
incrementSources: Record<string, boolean> = {}
|
incrementSources: Record<string, boolean> = {}
|
||||||
decrementSources: Record<string, boolean> = {}
|
decrementSources: Record<string, boolean> = {}
|
||||||
decrementEvents: Record<string, { userId: string, refund: number, failure: boolean }> = {}
|
decrementEvents: Record<string, { userId: string, refund: number, failure: boolean }> = {}
|
||||||
|
log = getLogger({ appName: "SanityChecker" })
|
||||||
users: Record<string, { ts: number, updatedBalance: number }> = {}
|
users: Record<string, { ts: number, updatedBalance: number }> = {}
|
||||||
constructor(storage: Storage, lnd: LightningHandler) {
|
constructor(storage: Storage, lnd: LightningHandler) {
|
||||||
this.storage = storage
|
this.storage = storage
|
||||||
|
|
@ -201,7 +203,6 @@ export default class SanityChecker {
|
||||||
throw new Error("payment failled, should not refund routing fees " + invoice)
|
throw new Error("payment failled, should not refund routing fees " + invoice)
|
||||||
}
|
}
|
||||||
if (entry.refund !== amt) {
|
if (entry.refund !== amt) {
|
||||||
console.log(entry.refund, amt)
|
|
||||||
throw new Error("refund amount mismatch for routing fee refund " + invoice)
|
throw new Error("refund amount mismatch for routing fee refund " + invoice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,9 +252,8 @@ export default class SanityChecker {
|
||||||
|
|
||||||
checkUserEntry(e: LoggedEvent, u: { ts: number, updatedBalance: number } | undefined) {
|
checkUserEntry(e: LoggedEvent, u: { ts: number, updatedBalance: number } | undefined) {
|
||||||
const newEntry = { ts: e.timestampMs, updatedBalance: e.balance + e.amount * (e.type === 'balance_decrement' ? -1 : 1) }
|
const newEntry = { ts: e.timestampMs, updatedBalance: e.balance + e.amount * (e.type === 'balance_decrement' ? -1 : 1) }
|
||||||
console.log(e)
|
|
||||||
if (!u) {
|
if (!u) {
|
||||||
console.log(e.userId, "balance starts at", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
this.log(e.userId, "balance starts at", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||||
return newEntry
|
return newEntry
|
||||||
}
|
}
|
||||||
if (e.timestampMs < u.ts) {
|
if (e.timestampMs < u.ts) {
|
||||||
|
|
@ -262,7 +262,7 @@ export default class SanityChecker {
|
||||||
if (e.balance !== u.updatedBalance) {
|
if (e.balance !== u.updatedBalance) {
|
||||||
throw new Error("inconsistent balance update got: " + e.balance + " expected " + u.updatedBalance)
|
throw new Error("inconsistent balance update got: " + e.balance + " expected " + u.updatedBalance)
|
||||||
}
|
}
|
||||||
console.log(e.userId, "balance updates from", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
this.log(e.userId, "balance updates from", e.balance, "sats and moves by", e.amount * (e.type === 'balance_decrement' ? -1 : 1), "sats, resulting in", newEntry.updatedBalance, "sats")
|
||||||
return newEntry
|
return newEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,13 +56,13 @@ process.on("message", (message: ChildProcessRequest) => {
|
||||||
sendToNostr(message.appId, message.data, message.relays)
|
sendToNostr(message.appId, message.data, message.relays)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.error("unknown nostr request", message)
|
getLogger({ appName: "nostrMiddleware" })("ERROR", "unknown nostr request", message)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const initSubprocessHandler = (settings: NostrSettings) => {
|
const initSubprocessHandler = (settings: NostrSettings) => {
|
||||||
if (subProcessHandler) {
|
if (subProcessHandler) {
|
||||||
console.error("nostr settings ignored since handler already exists")
|
getLogger({ appName: "nostrMiddleware" })("ERROR", "nostr settings ignored since handler already exists")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subProcessHandler = new Handler(settings, event => {
|
subProcessHandler = new Handler(settings, event => {
|
||||||
|
|
@ -74,7 +74,7 @@ const initSubprocessHandler = (settings: NostrSettings) => {
|
||||||
}
|
}
|
||||||
const sendToNostr: NostrSend = (appId, data, relays) => {
|
const sendToNostr: NostrSend = (appId, data, relays) => {
|
||||||
if (!subProcessHandler) {
|
if (!subProcessHandler) {
|
||||||
console.error("nostr was not initialized")
|
getLogger({ appName: "nostrMiddleware" })("ERROR", "nostr was not initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subProcessHandler.Send(appId, data, relays)
|
subProcessHandler.Send(appId, data, relays)
|
||||||
|
|
@ -87,9 +87,10 @@ export default class Handler {
|
||||||
subs: Sub[] = []
|
subs: Sub[] = []
|
||||||
apps: Record<string, AppInfo> = {}
|
apps: Record<string, AppInfo> = {}
|
||||||
eventCallback: (event: NostrEvent) => void
|
eventCallback: (event: NostrEvent) => void
|
||||||
|
log = getLogger({ appName: "nostrMiddleware" })
|
||||||
constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) {
|
constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) {
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
console.log(
|
this.log(
|
||||||
{
|
{
|
||||||
...settings,
|
...settings,
|
||||||
apps: settings.apps.map(app => {
|
apps: settings.apps.map(app => {
|
||||||
|
|
@ -151,7 +152,7 @@ export default class Handler {
|
||||||
}
|
}
|
||||||
const eventId = e.id
|
const eventId = e.id
|
||||||
if (handledEvents.includes(eventId)) {
|
if (handledEvents.includes(eventId)) {
|
||||||
console.log("event already handled")
|
this.log("event already handled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handledEvents.push(eventId)
|
handledEvents.push(eventId)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export default class {
|
||||||
return { executedMigrations, executedMetricsMigrations };
|
return { executedMigrations, executedMetricsMigrations };
|
||||||
}
|
}
|
||||||
|
|
||||||
StartTransaction(exec: TX<void>, description?: string) {
|
StartTransaction<T>(exec: TX<T>, description?: string) {
|
||||||
return this.txQueue.PushToQueue({ exec, dbTx: true, description })
|
return this.txQueue.PushToQueue({ exec, dbTx: true, description })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,18 +283,19 @@ export default class {
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
async CreateUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, linkedApplication: Application, dbTx: DataSource | EntityManager) {
|
async AddPendingUserToUserPayment(fromUserId: string, toUserId: string, amount: number, fee: number, linkedApplication: Application, dbTx: DataSource | EntityManager) {
|
||||||
return dbTx.getRepository(UserToUserPayment).create({
|
const entry = dbTx.getRepository(UserToUserPayment).create({
|
||||||
from_user: await this.userStorage.GetUser(fromUserId, dbTx),
|
from_user: await this.userStorage.GetUser(fromUserId, dbTx),
|
||||||
to_user: await this.userStorage.GetUser(toUserId, dbTx),
|
to_user: await this.userStorage.GetUser(toUserId, dbTx),
|
||||||
paid_at_unix: Math.floor(Date.now() / 1000),
|
paid_at_unix: 0,
|
||||||
paid_amount: amount,
|
paid_amount: amount,
|
||||||
service_fees: fee,
|
service_fees: fee,
|
||||||
linkedApplication
|
linkedApplication
|
||||||
})
|
})
|
||||||
|
return dbTx.getRepository(UserToUserPayment).save(entry)
|
||||||
}
|
}
|
||||||
async SaveUserToUserPayment(payment: UserToUserPayment, dbTx: DataSource | EntityManager) {
|
async SetPendingUserToUserPaymentAsPaid(serialId: number, dbTx: DataSource | EntityManager) {
|
||||||
return dbTx.getRepository(UserToUserPayment).save(payment)
|
dbTx.getRepository(UserToUserPayment).update(serialId, { paid_at_unix: Math.floor(Date.now() / 1000) })
|
||||||
}
|
}
|
||||||
|
|
||||||
GetUserToUserReceivedPayments(userId: string, fromIndex: number, take = 50, entityManager = this.DB) {
|
GetUserToUserReceivedPayments(userId: string, fromIndex: number, take = 50, entityManager = this.DB) {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||||
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
|
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
|
||||||
export const ignore = false
|
export const ignore = false
|
||||||
|
export const dev = false
|
||||||
export default async (T: TestBase) => {
|
export default async (T: TestBase) => {
|
||||||
await safelySetUserBalance(T, T.user1, 2000)
|
await safelySetUserBalance(T, T.user1, 2000)
|
||||||
await testSuccessfulExternalPayment(T)
|
await testSuccessfulExternalPayment(T)
|
||||||
|
await testFailedExternalPayment(T)
|
||||||
await runSanityCheck(T)
|
await runSanityCheck(T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const testSuccessfulExternalPayment = async (T: TestBase) => {
|
const testSuccessfulExternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testSuccessfulExternalPayment")
|
||||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
const invoice = await T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)
|
const invoice = await T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)
|
||||||
expect(invoice.payRequest).to.startWith("lnbcrt5u")
|
expect(invoice.payRequest).to.startWith("lnbcrt5u")
|
||||||
|
|
@ -27,3 +29,19 @@ const testSuccessfulExternalPayment = async (T: TestBase) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const testFailedExternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testFailedExternalPayment")
|
||||||
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
|
const invoice = await T.externalAccessToOtherLnd.NewInvoice(1500, "test", defaultInvoiceExpiry)
|
||||||
|
expect(invoice.payRequest).to.startWith("lnbcrt15u")
|
||||||
|
T.d("generated 1500 sats invoice for external node")
|
||||||
|
|
||||||
|
await expectThrowsAsync(T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice.payRequest, amount: 0 }, application), "not enough balance to decrement")
|
||||||
|
T.d("payment failed as expected, with the expected error message")
|
||||||
|
const u1 = await T.main.storage.userStorage.GetUser(T.user1.userId)
|
||||||
|
expect(u1.balance_sats).to.be.equal(1496)
|
||||||
|
T.d("user1 balance is still 1496")
|
||||||
|
const owner = await T.main.storage.userStorage.GetUser(application.owner.user_id)
|
||||||
|
expect(owner.balance_sats).to.be.equal(3)
|
||||||
|
T.d("app balance is still 3 sats")
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ export default async (T: TestBase) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testSuccessfulInternalPayment = async (T: TestBase) => {
|
const testSuccessfulInternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testSuccessfulInternalPayment")
|
||||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
||||||
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
||||||
|
|
@ -29,6 +30,7 @@ const testSuccessfulInternalPayment = async (T: TestBase) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testFailedInternalPayment = async (T: TestBase) => {
|
const testFailedInternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testFailedInternalPayment")
|
||||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
const invoice = await T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 1000, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })
|
||||||
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
expect(invoice.invoice).to.startWith("lnbcrt10u")
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,15 @@ export default async (T: TestBase) => {
|
||||||
|
|
||||||
|
|
||||||
const testSpamExternalPayment = async (T: TestBase) => {
|
const testSpamExternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testSpamExternalPayment")
|
||||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
const invoices = await Promise.all(new Array(10).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
const invoices = await Promise.all(new Array(10).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
||||||
T.d("generated 10 500 sats invoices for external node")
|
T.d("generated 10 500 sats invoices for external node")
|
||||||
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
||||||
try {
|
try {
|
||||||
T.d("trying to pay invoice " + i)
|
|
||||||
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice.payRequest, amount: 0 }, application)
|
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice.payRequest, amount: 0 }, application)
|
||||||
T.d("payment succeeded " + i)
|
|
||||||
return { success: true, result }
|
return { success: true, result }
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
T.d("payment failed " + i)
|
|
||||||
console.log(e, i)
|
|
||||||
return { success: false, err: e }
|
return { success: false, err: e }
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export default async (T: TestBase) => {
|
||||||
|
|
||||||
|
|
||||||
const testSpamExternalPayment = async (T: TestBase) => {
|
const testSpamExternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testSpamExternalPayment")
|
||||||
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
const invoicesForExternal = await Promise.all(new Array(5).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
const invoicesForExternal = await Promise.all(new Array(5).fill(0).map(() => T.externalAccessToOtherLnd.NewInvoice(500, "test", defaultInvoiceExpiry)))
|
||||||
const invoicesForUser2 = await Promise.all(new Array(5).fill(0).map(() => T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 500, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })))
|
const invoicesForUser2 = await Promise.all(new Array(5).fill(0).map(() => T.main.paymentManager.NewInvoice(T.user2.userId, { amountSats: 500, memo: "test" }, { linkedApplication: application, expiry: defaultInvoiceExpiry })))
|
||||||
|
|
@ -20,13 +21,9 @@ const testSpamExternalPayment = async (T: TestBase) => {
|
||||||
T.d("generated 10 500 sats mixed invoices between external node and user 2")
|
T.d("generated 10 500 sats mixed invoices between external node and user 2")
|
||||||
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
const res = await Promise.all(invoices.map(async (invoice, i) => {
|
||||||
try {
|
try {
|
||||||
T.d("trying to pay invoice " + i)
|
|
||||||
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice, amount: 0 }, application)
|
const result = await T.main.paymentManager.PayInvoice(T.user1.userId, { invoice: invoice, amount: 0 }, application)
|
||||||
T.d("payment succeeded " + i)
|
|
||||||
return { success: true, result }
|
return { success: true, result }
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
T.d("payment failed " + i)
|
|
||||||
console.log(e, i)
|
|
||||||
return { success: false, err: e }
|
return { success: false, err: e }
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export const teardown = async (T: TestBase) => {
|
||||||
T.main.lnd.Stop()
|
T.main.lnd.Stop()
|
||||||
T.externalAccessToMainLnd.Stop()
|
T.externalAccessToMainLnd.Stop()
|
||||||
T.externalAccessToOtherLnd.Stop()
|
T.externalAccessToOtherLnd.Stop()
|
||||||
|
T.externalAccessToThirdLnd.Stop()
|
||||||
console.log("teardown")
|
console.log("teardown")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ const start = async () => {
|
||||||
const module = await import(`./${file.slice("build/src/tests/".length)}`) as TestModule
|
const module = await import(`./${file.slice("build/src/tests/".length)}`) as TestModule
|
||||||
modules.push({ module, file })
|
modules.push({ module, file })
|
||||||
if (module.dev) {
|
if (module.dev) {
|
||||||
|
console.log("dev module found", file)
|
||||||
if (devModule !== -1) {
|
if (devModule !== -1) {
|
||||||
console.error(redConsole, "there are multiple dev modules", resetConsole)
|
console.error(redConsole, "there are multiple dev modules", resetConsole)
|
||||||
return
|
return
|
||||||
|
|
@ -63,6 +64,9 @@ const runTestFile = async (fileName: string, mod: TestModule) => {
|
||||||
d(e, true)
|
d(e, true)
|
||||||
await teardown(T)
|
await teardown(T)
|
||||||
}
|
}
|
||||||
|
if (mod.dev) {
|
||||||
|
d("dev mod is not allowed to in CI, failing for precaution", true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDescribe = (fileName: string): Describe => {
|
const getDescribe = (fileName: string): Describe => {
|
||||||
|
|
|
||||||
35
src/tests/userToUserPayment.spec.ts
Normal file
35
src/tests/userToUserPayment.spec.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { defaultInvoiceExpiry } from '../services/storage/paymentStorage.js'
|
||||||
|
import { Describe, expect, expectThrowsAsync, runSanityCheck, safelySetUserBalance, SetupTest, TestBase } from './testBase.js'
|
||||||
|
export const ignore = false
|
||||||
|
export const dev = true
|
||||||
|
export default async (T: TestBase) => {
|
||||||
|
await safelySetUserBalance(T, T.user1, 2000)
|
||||||
|
await testSuccessfulU2UPayment(T)
|
||||||
|
await testFailedInternalPayment(T)
|
||||||
|
await runSanityCheck(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
const testSuccessfulU2UPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testSuccessfulU2UPayment")
|
||||||
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
|
const sentAmt = await T.main.paymentManager.SendUserToUserPayment(T.user1.userId, T.user2.userId, 1000, application)
|
||||||
|
expect(sentAmt.amount).to.be.equal(1000)
|
||||||
|
T.d("paid 1000 sats u2u from user1 to user2")
|
||||||
|
const u1 = await T.main.storage.userStorage.GetUser(T.user1.userId)
|
||||||
|
const u2 = await T.main.storage.userStorage.GetUser(T.user2.userId)
|
||||||
|
const owner = await T.main.storage.userStorage.GetUser(application.owner.user_id)
|
||||||
|
expect(u2.balance_sats).to.be.equal(1000)
|
||||||
|
T.d("user2 balance is 1000")
|
||||||
|
expect(u1.balance_sats).to.be.equal(994)
|
||||||
|
T.d("user1 balance is 994 cuz he paid 6 sats fee")
|
||||||
|
expect(owner.balance_sats).to.be.equal(6)
|
||||||
|
T.d("app balance is 6 sats")
|
||||||
|
}
|
||||||
|
|
||||||
|
const testFailedInternalPayment = async (T: TestBase) => {
|
||||||
|
T.d("starting testFailedInternalPayment")
|
||||||
|
const application = await T.main.storage.applicationStorage.GetApplication(T.app.appId)
|
||||||
|
await expectThrowsAsync(T.main.paymentManager.SendUserToUserPayment(T.user1.userId, T.user2.userId, 1000, application), "not enough balance to send payment")
|
||||||
|
T.d("payment failed as expected, with the expected error message")
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue