mirror of
https://github.com/IT4Change/gradido.git
synced 2026-04-06 01:25:28 +00:00
more detailed error handling
This commit is contained in:
parent
02483a5993
commit
e1a94e3c93
@ -1,6 +1,7 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { AccountBalance, GradidoUnit, MemoryBlockPtr } from 'gradido-blockchain-js'
|
||||
import { legacyCalculateDecay } from '../utils'
|
||||
import { NegativeBalanceError } from '../errors'
|
||||
|
||||
export class Balance {
|
||||
private balance: GradidoUnit
|
||||
@ -25,8 +26,15 @@ export class Balance {
|
||||
return this.balance
|
||||
}
|
||||
|
||||
getDate(): Date {
|
||||
return this.date
|
||||
}
|
||||
|
||||
updateLegacyDecay(amount: GradidoUnit, date: Date) {
|
||||
// make sure to copy instead of referencing
|
||||
const previousBalanceString = this.balance.toString()
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
@ -37,12 +45,20 @@ export class Balance {
|
||||
this.date = date
|
||||
}
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
throw new Error(`negative Gradido amount detected in Balance.updateLegacyDecay, previous balance: ${previousBalanceString}, amount: ${amount.toString()}`)
|
||||
const previousDecayedBalance = legacyCalculateDecay(new Decimal(previousBalanceString), previousDate, date)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.updateLegacyDecay`,
|
||||
previousBalanceString,
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
update(amount: GradidoUnit, date: Date) {
|
||||
const previousBalanceString = this.balance.toString()
|
||||
const previousBalance = new GradidoUnit(this.balance.toString())
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
@ -53,7 +69,13 @@ export class Balance {
|
||||
this.date = date
|
||||
}
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
throw new Error(`negative Gradido amount detected in Balance.update, previous balance: ${previousBalanceString}, amount: ${amount.toString()}`)
|
||||
const previousDecayedBalance = this.balance.calculateDecay(previousDate, date)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.update`,
|
||||
previousBalance.toString(),
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import * as v from 'valibot'
|
||||
export class NotEnoughGradidoBalanceError extends Error {
|
||||
constructor(public needed: number, public exist: number) {
|
||||
super(`Not enough Gradido Balance for send coins, needed: ${needed} Gradido, exist: ${exist} Gradido`)
|
||||
this.name = 'NotEnoughGradidoBalanceError'
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,3 +43,14 @@ export class BlockchainError extends Error {
|
||||
this.stack = originalError.stack
|
||||
}
|
||||
}
|
||||
|
||||
export class NegativeBalanceError extends Error {
|
||||
constructor(message: string, previousBalanceString: string, amount: string, previousDecayedBalance: string) {
|
||||
const parts: string[] = [`NegativeBalanceError in ${message}`]
|
||||
parts.push(`Previous balance: ${previousBalanceString}`)
|
||||
parts.push(`Amount: ${amount}`)
|
||||
parts.push(`Previous decayed balance: ${previousDecayedBalance}`)
|
||||
super(parts.join('\n'))
|
||||
this.name = 'NegativeBalanceError'
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,21 @@ export abstract class AbstractSyncRole<T> {
|
||||
return Balance.fromAccountBalance(senderLastAccountBalance, lastConfirmedTransaction.getConfirmedAt().getDate())
|
||||
}
|
||||
|
||||
logLastBalanceChangingTransactions(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, transactionCount: number = 5) {
|
||||
if (!this.context.logger.isDebugEnabled()) {
|
||||
return
|
||||
}
|
||||
const f = new Filter()
|
||||
f.updatedBalancePublicKey = publicKey
|
||||
f.searchDirection = SearchDirection_DESC
|
||||
f.pagination.size = transactionCount
|
||||
const lastTransactions = blockchain.findAll(f)
|
||||
for (let i = lastTransactions.size() - 1; i >= 0; i--) {
|
||||
const tx = lastTransactions.get(i)
|
||||
this.context.logger.debug(`${tx?.getConfirmedTransaction()!.toJson(true)}`)
|
||||
}
|
||||
}
|
||||
|
||||
abstract getDate(): Date
|
||||
abstract loadFromDb(offset: number, count: number): Promise<T[]>
|
||||
abstract pushToBlockchain(item: T): void
|
||||
|
||||
@ -15,7 +15,7 @@ import * as v from 'valibot'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { TransactionTypeId } from '../../data/TransactionTypeId'
|
||||
import { transactionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { BlockchainError, DatabaseError, NegativeBalanceError, NotEnoughGradidoBalanceError } from '../../errors'
|
||||
import { CommunityContext, TransactionDb, transactionDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole } from './AbstractSync.role'
|
||||
|
||||
@ -99,20 +99,14 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain)
|
||||
const recipientLastBalance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain)
|
||||
|
||||
if (senderLastBalance.getAccountBalance().getBalance().lt(item.amount)) {
|
||||
const f = new Filter()
|
||||
f.updatedBalancePublicKey = senderPublicKey
|
||||
f.searchDirection = SearchDirection_DESC
|
||||
f.pagination.size = 5
|
||||
const lastTransactions = communityContext.blockchain.findAll(f)
|
||||
for (let i = lastTransactions.size() - 1; i >= 0; i--) {
|
||||
const tx = lastTransactions.get(i)
|
||||
this.context.logger.error(`${tx?.getConfirmedTransaction()!.toJson(true)}`)
|
||||
try {
|
||||
senderLastBalance.updateLegacyDecay(item.amount.negated(), item.balanceDate)
|
||||
} catch(e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, communityContext.blockchain)
|
||||
throw e
|
||||
}
|
||||
throw new Error(`sender has not enough balance (${senderLastBalance.getAccountBalance().getBalance().toString()}) to send ${item.amount.toString()} to ${recipientPublicKey.convertToHex()}`)
|
||||
}
|
||||
|
||||
senderLastBalance.updateLegacyDecay(item.amount.negated(), item.balanceDate)
|
||||
recipientLastBalance.updateLegacyDecay(item.amount, item.balanceDate)
|
||||
|
||||
accountBalances.add(senderLastBalance.getAccountBalance())
|
||||
@ -145,6 +139,9 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
this.calculateBalances(item, communityContext, senderPublicKey, recipientPublicKey),
|
||||
)
|
||||
} catch(e) {
|
||||
if (e instanceof NotEnoughGradidoBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, blockchain)
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,10 +17,12 @@ import {
|
||||
import * as v from 'valibot'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { transactionLinksTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { BlockchainError, DatabaseError, NegativeBalanceError } from '../../errors'
|
||||
import { CommunityContext, TransactionLinkDb, transactionLinkDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole } from './AbstractSync.role'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { legacyCalculateDecay } from '../../utils'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<TransactionLinkDb> {
|
||||
getDate(): Date {
|
||||
@ -87,22 +89,9 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
const accountBalances = new AccountBalances()
|
||||
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain)
|
||||
if (senderLastBalance.getBalance().lt(blockedAmount)) {
|
||||
const f = new Filter()
|
||||
f.updatedBalancePublicKey = senderPublicKey
|
||||
f.pagination.size = 4
|
||||
f.searchDirection = SearchDirection_DESC
|
||||
const lastSenderTransactions = communityContext.blockchain.findAll(f)
|
||||
this.context.logger.error(`sender hasn't enough balance: ${senderPublicKey.convertToHex()}, last ${lastSenderTransactions.size()} balance changing transactions:`)
|
||||
for(let i = lastSenderTransactions.size() - 1; i >= 0; i--) {
|
||||
const lastSenderTransaction = lastSenderTransactions.get(i)
|
||||
this.context.logger.error(`${lastSenderTransaction?.getConfirmedTransaction()?.toJson(true)}`)
|
||||
}
|
||||
}
|
||||
let senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain)
|
||||
senderLastBalance.updateLegacyDecay(blockedAmount.negated(), item.createdAt)
|
||||
|
||||
|
||||
accountBalances.add(senderLastBalance.getAccountBalance())
|
||||
accountBalances.add(new AccountBalance(recipientPublicKey, blockedAmount, ''))
|
||||
return accountBalances
|
||||
@ -122,16 +111,39 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
|
||||
}
|
||||
|
||||
const duration = new DurationSeconds((item.validUntil.getTime() - item.createdAt.getTime()) / 1000)
|
||||
const blockedAmount = item.amount.calculateCompoundInterest(duration.getSeconds())
|
||||
|
||||
let blockedAmount = item.amount.calculateCompoundInterest(duration.getSeconds())
|
||||
let accountBalances: AccountBalances
|
||||
try {
|
||||
accountBalances = this.calculateBalances(item, blockedAmount, communityContext, senderPublicKey, recipientPublicKey)
|
||||
} catch(e) {
|
||||
if (item.deletedAt && e instanceof NegativeBalanceError) {
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain)
|
||||
senderLastBalance.updateLegacyDecay(GradidoUnit.zero(), item.createdAt)
|
||||
blockedAmount = senderLastBalance.getBalance()
|
||||
accountBalances = this.calculateBalances(item, blockedAmount, communityContext, senderPublicKey, recipientPublicKey)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
/*
|
||||
const decayedAmount = GradidoUnit.fromString(legacyCalculateDecay(new Decimal(item.amount.toString()), item.createdAt, item.validUntil).toString())
|
||||
const blockedAmount = item.amount.add(item.amount.minus(decayedAmount))
|
||||
*/
|
||||
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(item, blockedAmount, duration, senderKeyPair, recipientKeyPair),
|
||||
blockchain,
|
||||
item.id,
|
||||
this.calculateBalances(item, blockedAmount, communityContext, senderPublicKey, recipientPublicKey),
|
||||
accountBalances,
|
||||
)
|
||||
} catch(e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
if (!item.deletedAt && !item.redeemedAt && item.validUntil.getTime() < new Date().getTime()) {
|
||||
this.context.logger.warn(`TransactionLinks: ${item.id} skipped, because else it lead to negative balance error, but it wasn't used.`)
|
||||
return
|
||||
}
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +77,8 @@ export const transactionLinkDbSchema = v.object({
|
||||
code: identifierSeedSchema,
|
||||
createdAt: dateSchema,
|
||||
validUntil: dateSchema,
|
||||
redeemedAt: v.nullish(dateSchema),
|
||||
deletedAt: v.nullish(dateSchema),
|
||||
})
|
||||
|
||||
export const redeemedTransactionLinkDbSchema = v.object({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user