From 0b39620f9968b474ba7d886a420b9f498cffae80 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:44:10 +0100 Subject: [PATCH 01/88] updated database requirement --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 8961fc358..3dbf052b1 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0024-combine_transaction_tables', + DB_VERSION: '0025-combine_transaction_tables2', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } From 0a1bcac45064244c07c1cb2da187c9c183b95ee7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:58:22 +0100 Subject: [PATCH 02/88] remove more unused code --- .../graphql/resolver/TransactionResolver.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 489034152..79692aa8c 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -133,9 +133,7 @@ export class TransactionResolver { const finalTransactions: Transaction[] = [] const involvedUserIds: number[] = [] - const transactionIndiced: dbTransaction[] = [] userTransactions.forEach((transaction: dbTransaction) => { - transactionIndiced[transaction.id] = transaction involvedUserIds.push(transaction.userId) if ( transaction.transactionTypeId === TransactionTypeId.SEND || @@ -153,11 +151,10 @@ export class TransactionResolver { for (let i = 0; i < userTransactions.length; i++) { const userTransaction = userTransactions[i] - const transaction = transactionIndiced[userTransaction.transactionId] const finalTransaction = new Transaction() - finalTransaction.transactionId = transaction.id - finalTransaction.date = transaction.received.toISOString() - finalTransaction.memo = transaction.memo + finalTransaction.transactionId = userTransaction.id + finalTransaction.date = userTransaction.received.toISOString() + finalTransaction.memo = userTransaction.memo finalTransaction.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) const previousTransaction = i > 0 ? userTransactions[i - 1] : null @@ -184,18 +181,15 @@ export class TransactionResolver { } } - const otherUser = userIndiced.find((u) => u.id === transaction.linkedUserId) + finalTransaction.balance = roundFloorFrom4(Number(userTransaction.amount)) // Todo unsafe conversion + + const otherUser = userIndiced.find((u) => u.id === userTransaction.linkedUserId) switch (userTransaction.transactionTypeId) { case TransactionTypeId.CREATION: - // creation finalTransaction.name = 'Gradido Akademie' finalTransaction.type = TransactionType.CREATION - // finalTransaction.targetDate = creation.targetDate - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion break case TransactionTypeId.SEND: - // send coin - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion finalTransaction.type = TransactionType.SEND if (otherUser) { finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName @@ -203,7 +197,6 @@ export class TransactionResolver { } break case TransactionTypeId.RECEIVE: - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion finalTransaction.type = TransactionType.RECIEVE if (otherUser) { finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName From 70c70f45d46d8d7f3aabb5ce0a1d9ff938efb9e6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 23:23:51 +0100 Subject: [PATCH 03/88] sendCoins and listTransactions are now properly working --- .../graphql/resolver/TransactionResolver.ts | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 79692aa8c..51ac0aca7 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -48,7 +48,7 @@ async function updateStateBalance( balance.modified = received } else { const decayedBalance = calculateDecay(balance.amount, balance.recordDate, received).balance - balance.amount = Number(decayedBalance) + centAmount + balance.amount = decayedBalance + centAmount balance.modified = new Date() } if (balance.amount <= 0) { @@ -64,7 +64,7 @@ async function calculateNewBalance( userId: number, transactionDate: Date, centAmount: number, -): Promise { +): Promise { let newBalance = centAmount const transactionRepository = getCustomRepository(TransactionRepository) const lastUserTransaction = await transactionRepository.findLastForUser(userId) @@ -82,7 +82,7 @@ async function calculateNewBalance( throw new Error('error new balance <= 0') } - return BigInt(newBalance) + return newBalance } @Resolver() @@ -309,11 +309,8 @@ export class TransactionResolver { transactionSend.amount = BigInt(centAmount) transactionSend.received = receivedCallDate transactionSend.transactionId = randomInt(99999) - transactionSend.balance = await calculateNewBalance( - senderUser.id, - receivedCallDate, - -centAmount, - ) + const sendBalance = await calculateNewBalance(senderUser.id, receivedCallDate, -centAmount) + transactionSend.balance = BigInt(Math.trunc(sendBalance)) transactionSend.balanceDate = receivedCallDate transactionSend.sendSenderFinalBalance = transactionSend.balance await queryRunner.manager.insert(dbTransaction, transactionSend) @@ -327,11 +324,12 @@ export class TransactionResolver { transactionReceive.amount = BigInt(centAmount) transactionReceive.received = receivedCallDate transactionReceive.transactionId = randomInt(99999) - transactionReceive.balance = await calculateNewBalance( - senderUser.id, + const receiveBalance = await calculateNewBalance( + recipientUser.id, receivedCallDate, centAmount, ) + transactionReceive.balance = BigInt(Math.trunc(receiveBalance)) transactionReceive.balanceDate = receivedCallDate transactionReceive.sendSenderFinalBalance = transactionSend.balance await queryRunner.manager.insert(dbTransaction, transactionReceive) @@ -345,23 +343,28 @@ export class TransactionResolver { ) // Update Balance: recipiant + amount - const recipiantStateBalance = await updateStateBalance( + const recipientStateBalance = await updateStateBalance( recipientUser, centAmount, receivedCallDate, queryRunner, ) - if (senderStateBalance.amount !== Number(transactionSend.balance)) { + if (senderStateBalance.amount !== sendBalance) { + // eslint-disable-next-line no-console + console.log('db data corrupted, sender', senderStateBalance.amount, sendBalance) throw new Error('db data corrupted, sender') } - if (recipiantStateBalance.amount !== Number(transactionReceive.balance)) { - throw new Error('db data corrupted, recipiant') + if (recipientStateBalance.amount !== receiveBalance) { + // eslint-disable-next-line no-console + console.log('db data corrupted, sender', recipientStateBalance.amount, receiveBalance) + throw new Error('db data corrupted, recipient') } await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() + throw new Error(`Transaction was not successful: ${e}`) } finally { await queryRunner.release() } From 950beeaebcf86f02e215eb326bbb7b91418d3347 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 23:33:55 +0100 Subject: [PATCH 04/88] last spelling --- backend/src/graphql/resolver/TransactionResolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 51ac0aca7..919ab093b 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -84,7 +84,6 @@ async function calculateNewBalance( return newBalance } - @Resolver() export class TransactionResolver { @Authorized([RIGHTS.TRANSACTION_LIST]) @@ -342,7 +341,7 @@ export class TransactionResolver { queryRunner, ) - // Update Balance: recipiant + amount + // Update Balance: recipient + amount const recipientStateBalance = await updateStateBalance( recipientUser, centAmount, From ccb3c314dd30ab4ec3394a65376f2882b10596f0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:25:49 +0100 Subject: [PATCH 05/88] migration to clean transaction table --- .../0026-clean_transaction_table.ts | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 database/migrations/0026-clean_transaction_table.ts diff --git a/database/migrations/0026-clean_transaction_table.ts b/database/migrations/0026-clean_transaction_table.ts new file mode 100644 index 000000000..b5a0e0e2e --- /dev/null +++ b/database/migrations/0026-clean_transaction_table.ts @@ -0,0 +1,73 @@ +/* MIGRATION TO CLEAN THE TRANSACTION TABLE + * + * Remove several unused fields or those with duplicate data + * and rename fields to a proper name in `transactions` . + * + * This migration has data loss + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // drop column `transaction_id`, it is not needed + await queryFn('ALTER TABLE `transactions` DROP COLUMN `transaction_id`;') + // drop column `received`, it is a duplicate of balance_date + await queryFn('ALTER TABLE `transactions` DROP COLUMN `received`;') + // drop column `tx_hash`, it is not needed + await queryFn('ALTER TABLE `transactions` DROP COLUMN `tx_hash`;') + // drop column `signature`, it is not needed + await queryFn('ALTER TABLE `transactions` DROP COLUMN `signature`;') + // drop column `pubkey`, it is not needed + await queryFn('ALTER TABLE `transactions` DROP COLUMN `pubkey`;') + // drop column `creation_ident_hash`, it is not needed + await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_ident_hash`;') + + // rename `transaction_type_id` to `type_id` + await queryFn('ALTER TABLE `transactions` RENAME COLUMN transaction_type_id TO type_id;') + // rename `linked_state_user_transaction_id` to `linked_transaction_id` + await queryFn( + 'ALTER TABLE `transactions` RENAME COLUMN linked_state_user_transaction_id TO linked_transaction_id;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Not all data is recoverable here, some data is simulated, + // but we have data loss on: + // - transaction_id (we have data here, but its not the same as before) + // - tx_hash (null) + // - signature (null) + // - pubkey (null) + // - creation_ident_hash (null) + + await queryFn( + 'ALTER TABLE `transactions` RENAME COLUMN linked_transaction_id TO linked_state_user_transaction_id;', + ) + await queryFn('ALTER TABLE `transactions` RENAME COLUMN type_id TO transaction_type_id;') + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `linked_state_user_transaction_id`;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `pubkey` binary(32) DEFAULT NULL AFTER `linked_state_user_transaction_id`;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `signature` binary(64) DEFAULT NULL AFTER `linked_state_user_transaction_id`;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `tx_hash` binary(48) DEFAULT NULL AFTER `linked_state_user_transaction_id`;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `received` timestamp NULL DEFAULT NULL AFTER `balance_date`;', + ) + await queryFn('UPDATE `transactions` SET `received` = `balance_date`;') + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `received` timestamp NOT NULL DEFAULT current_timestamp();', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `transaction_id` int(10) unsigned DEFAULT NULL AFTER `user_id`;', + ) + await queryFn('UPDATE `transactions` SET `transaction_id` = `id`;') + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `transaction_id` int(10) unsigned NOT NULL;', + ) +} From d488286e43792257b8ae4ef43480074eff6c746f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:26:15 +0100 Subject: [PATCH 06/88] new entity definition --- .../Transaction.ts | 62 +++++++++++++++++++ database/entity/Transaction.ts | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 database/entity/0026-clean_transaction_table/Transaction.ts diff --git a/database/entity/0026-clean_transaction_table/Transaction.ts b/database/entity/0026-clean_transaction_table/Transaction.ts new file mode 100644 index 000000000..c23600386 --- /dev/null +++ b/database/entity/0026-clean_transaction_table/Transaction.ts @@ -0,0 +1,62 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' + +@Entity('transactions') +export class Transaction extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @Column({ name: 'transaction_id', unsigned: true, nullable: false }) + transactionId: number + + @Column({ name: 'type_id', unsigned: true, nullable: false }) + typeId: number + + @Column({ type: 'bigint', nullable: false }) + amount: BigInt + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ + name: 'send_sender_final_balance', + type: 'bigint', + nullable: true, + default: null, + }) + sendSenderFinalBalance: BigInt | null + + @Column({ name: 'balance', type: 'bigint', default: 0 }) + balance: BigInt + + @Column({ + name: 'balance_date', + type: 'timestamp', + default: () => 'CURRENT_TIMESTAMP', + nullable: false, + }) + balanceDate: Date + + @Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null }) + creationDate: Date + + @Column({ + name: 'linked_user_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedUserId?: number | null + + @Column({ + name: 'linked_transaction_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedTransactionId?: number | null +} diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index d98b73e5b..8d46ef6da 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0025-combine_transaction_tables2/Transaction' +export { Transaction } from './0026-clean_transaction_table/Transaction' From 5cb67cf5889e222fcc11af0c810ccc9daf5fb985 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:27:13 +0100 Subject: [PATCH 07/88] missing entity change --- database/entity/0026-clean_transaction_table/Transaction.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/database/entity/0026-clean_transaction_table/Transaction.ts b/database/entity/0026-clean_transaction_table/Transaction.ts index c23600386..e50f5d164 100644 --- a/database/entity/0026-clean_transaction_table/Transaction.ts +++ b/database/entity/0026-clean_transaction_table/Transaction.ts @@ -8,9 +8,6 @@ export class Transaction extends BaseEntity { @Column({ name: 'user_id', unsigned: true, nullable: false }) userId: number - @Column({ name: 'transaction_id', unsigned: true, nullable: false }) - transactionId: number - @Column({ name: 'type_id', unsigned: true, nullable: false }) typeId: number From 0c5466f65007a09efbf89227ce058f678922a42b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:32:45 +0100 Subject: [PATCH 08/88] fixed seeds --- database/src/factories/transaction.factory.ts | 7 +------ database/src/interface/TransactionContext.ts | 20 +------------------ database/src/seeds/helpers/user-helpers.ts | 7 +------ 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/database/src/factories/transaction.factory.ts b/database/src/factories/transaction.factory.ts index 87af75baf..44a6150f2 100644 --- a/database/src/factories/transaction.factory.ts +++ b/database/src/factories/transaction.factory.ts @@ -10,15 +10,10 @@ define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { } const transaction = new Transaction() - transaction.transactionTypeId = context.transactionTypeId // || 2 + transaction.typeId = context.typeId // || 2 transaction.userId = context.userId transaction.amount = context.amount - transaction.txHash = context.txHash || randomBytes(48) transaction.memo = context.memo - transaction.received = context.received || new Date() - transaction.signature = context.signature || randomBytes(64) - transaction.pubkey = context.pubkey || randomBytes(32) - transaction.creationIdentHash = context.creationIdentHash || randomBytes(32) transaction.creationDate = context.creationDate || new Date() // transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null transaction.linkedUserId = context.sendReceiverUserId || null diff --git a/database/src/interface/TransactionContext.ts b/database/src/interface/TransactionContext.ts index d424a5731..9da737974 100644 --- a/database/src/interface/TransactionContext.ts +++ b/database/src/interface/TransactionContext.ts @@ -1,21 +1,13 @@ -import { Transaction } from '../../entity/Transaction' import { User } from '../../entity/User' export interface TransactionContext { - transactionId: number - transactionTypeId: number + typeId: number userId: number balance: BigInt balanceDate: Date amount: BigInt - txHash?: Buffer memo: string - received?: Date - signature?: Buffer - pubkey?: Buffer - creationIdentHash?: Buffer creationDate?: Date - sendReceiverPublicKey?: Buffer sendReceiverUserId?: number sendSenderFinalBalance?: BigInt } @@ -26,13 +18,3 @@ export interface BalanceContext { amount?: number user?: User } - -export interface TransactionSendCoinContext { - senderPublic?: Buffer - userId?: number - recipiantPublic?: Buffer - recipiantUserId?: number - amount?: number - senderFinalBalance?: number - transaction?: Transaction -} diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index e4c0314b7..c00b5ad0d 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -69,17 +69,12 @@ const createTransactionContext = ( memo: string, ): TransactionContext => { return { - transactionId: randomInt(999999), - transactionTypeId: type, + typeId: type, userId: user.id, amount: BigInt(context.amount || 100000), balance: BigInt(context.amount || 100000), balanceDate: new Date(context.recordDate || Date.now()), - txHash: context.creationTxHash, memo, - received: context.recordDate, creationDate: context.creationDate, - signature: context.signature, - pubkey: context.pubKey, } } From 3a35cbfee56baeac3046002f0a4765c7459b573d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:41:44 +0100 Subject: [PATCH 09/88] fixed backend for new migration properly write linked Transactions when sendCoin --- backend/src/config/index.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 4 +--- .../graphql/resolver/TransactionResolver.ts | 23 +++++++++---------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 3dbf052b1..e26272436 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0025-combine_transaction_tables2', + DB_VERSION: '0026-clean_transaction_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 916525d02..17df7b384 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -318,13 +318,11 @@ export class AdminResolver { newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) let transaction = new Transaction() - transaction.transactionTypeId = TransactionTypeId.CREATION + transaction.typeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo - transaction.received = receivedCallDate transaction.userId = pendingCreation.userId transaction.amount = BigInt(parseInt(pendingCreation.amount.toString())) transaction.creationDate = pendingCreation.date - transaction.transactionId = randomInt(99999) transaction.balance = BigInt(newBalance) transaction.balanceDate = receivedCallDate transaction = await transaction.save() diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 919ab093b..fcb18ba0a 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -135,8 +135,8 @@ export class TransactionResolver { userTransactions.forEach((transaction: dbTransaction) => { involvedUserIds.push(transaction.userId) if ( - transaction.transactionTypeId === TransactionTypeId.SEND || - transaction.transactionTypeId === TransactionTypeId.RECEIVE + transaction.typeId === TransactionTypeId.SEND || + transaction.typeId === TransactionTypeId.RECEIVE ) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion involvedUserIds.push(transaction.linkedUserId!) // TODO ensure not null properly @@ -152,7 +152,7 @@ export class TransactionResolver { const userTransaction = userTransactions[i] const finalTransaction = new Transaction() finalTransaction.transactionId = userTransaction.id - finalTransaction.date = userTransaction.received.toISOString() + finalTransaction.date = userTransaction.balanceDate.toISOString() finalTransaction.memo = userTransaction.memo finalTransaction.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) const previousTransaction = i > 0 ? userTransactions[i - 1] : null @@ -183,7 +183,7 @@ export class TransactionResolver { finalTransaction.balance = roundFloorFrom4(Number(userTransaction.amount)) // Todo unsafe conversion const otherUser = userIndiced.find((u) => u.id === userTransaction.linkedUserId) - switch (userTransaction.transactionTypeId) { + switch (userTransaction.typeId) { case TransactionTypeId.CREATION: finalTransaction.name = 'Gradido Akademie' finalTransaction.type = TransactionType.CREATION @@ -300,14 +300,11 @@ export class TransactionResolver { const receivedCallDate = new Date() // transaction const transactionSend = new dbTransaction() - transactionSend.transactionTypeId = TransactionTypeId.SEND + transactionSend.typeId = TransactionTypeId.SEND transactionSend.memo = memo transactionSend.userId = senderUser.id - transactionSend.pubkey = senderUser.pubKey transactionSend.linkedUserId = recipientUser.id transactionSend.amount = BigInt(centAmount) - transactionSend.received = receivedCallDate - transactionSend.transactionId = randomInt(99999) const sendBalance = await calculateNewBalance(senderUser.id, receivedCallDate, -centAmount) transactionSend.balance = BigInt(Math.trunc(sendBalance)) transactionSend.balanceDate = receivedCallDate @@ -315,14 +312,11 @@ export class TransactionResolver { await queryRunner.manager.insert(dbTransaction, transactionSend) const transactionReceive = new dbTransaction() - transactionReceive.transactionTypeId = TransactionTypeId.RECEIVE + transactionReceive.typeId = TransactionTypeId.RECEIVE transactionReceive.memo = memo transactionReceive.userId = recipientUser.id - transactionReceive.pubkey = recipientUser.pubKey transactionReceive.linkedUserId = senderUser.id transactionReceive.amount = BigInt(centAmount) - transactionReceive.received = receivedCallDate - transactionReceive.transactionId = randomInt(99999) const receiveBalance = await calculateNewBalance( recipientUser.id, receivedCallDate, @@ -331,8 +325,13 @@ export class TransactionResolver { transactionReceive.balance = BigInt(Math.trunc(receiveBalance)) transactionReceive.balanceDate = receivedCallDate transactionReceive.sendSenderFinalBalance = transactionSend.balance + transactionReceive.linkedTransactionId = transactionSend.id await queryRunner.manager.insert(dbTransaction, transactionReceive) + // Save linked transaction id for send + transactionSend.linkedTransactionId = transactionReceive.id + await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) + // Update Balance: sender - amount const senderStateBalance = await updateStateBalance( senderUser, From 32d5eacd13531939d4fd671f6a7dd6406ce77453 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:52:54 +0100 Subject: [PATCH 10/88] lint fixes --- backend/src/graphql/resolver/AdminResolver.ts | 1 - backend/src/graphql/resolver/TransactionResolver.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 17df7b384..1a4bc560e 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -28,7 +28,6 @@ import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' import { TransactionTypeId } from '../enum/TransactionTypeId' import { Balance } from '@entity/Balance' -import { randomInt } from 'crypto' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index fcb18ba0a..4000eaef9 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -31,7 +31,6 @@ import { TransactionTypeId } from '../enum/TransactionTypeId' import { TransactionType } from '../enum/TransactionType' import { hasUserAmount, isHexPublicKey } from '../../util/validate' import { RIGHTS } from '../../auth/RIGHTS' -import { randomInt } from 'crypto' // helper helper function async function updateStateBalance( From cc395cf31f3290a98012c975a8ed6158c850c27f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:54:02 +0100 Subject: [PATCH 11/88] database lint fixes --- database/src/factories/transaction.factory.ts | 1 - database/src/seeds/helpers/user-helpers.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/database/src/factories/transaction.factory.ts b/database/src/factories/transaction.factory.ts index 44a6150f2..9e8000223 100644 --- a/database/src/factories/transaction.factory.ts +++ b/database/src/factories/transaction.factory.ts @@ -2,7 +2,6 @@ import Faker from 'faker' import { define } from 'typeorm-seeding' import { Transaction } from '../../entity/Transaction' import { TransactionContext } from '../interface/TransactionContext' -import { randomBytes } from 'crypto' define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { if (!context) { diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index c00b5ad0d..fe43f857e 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -6,7 +6,6 @@ import { ServerUser } from '../../../entity/ServerUser' import { Balance } from '../../../entity/Balance' import { Transaction } from '../../../entity/Transaction' import { Factory } from 'typeorm-seeding' -import { randomInt } from 'crypto' export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => { const user = await factory(User)(createUserContext(userData)).create() From c2dfd50654f7c294af9b2f73c7be8e0382da5ec5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 12:51:47 +0100 Subject: [PATCH 12/88] make transaction a const --- backend/src/graphql/resolver/AdminResolver.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 1a4bc560e..7c903b098 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -316,7 +316,7 @@ export class AdminResolver { } newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) - let transaction = new Transaction() + const transaction = new Transaction() transaction.typeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo transaction.userId = pendingCreation.userId @@ -324,8 +324,7 @@ export class AdminResolver { transaction.creationDate = pendingCreation.date transaction.balance = BigInt(newBalance) transaction.balanceDate = receivedCallDate - transaction = await transaction.save() - // if (!transaction) throw new Error('Could not create transaction') + await transaction.save() let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) if (!userBalance) { From c5486156ccb53852c15c219315587738e0fe12d8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 12:56:34 +0100 Subject: [PATCH 13/88] remove duplicate Transaction[] variable --- backend/src/graphql/resolver/TransactionResolver.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 4000eaef9..ec8a2d7e5 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -123,12 +123,11 @@ export class TransactionResolver { ) skipFirstTransaction = userTransactionsCount > offset + limit const decay = !(currentPage > 1) - let transactions: Transaction[] = [] + const transactions: Transaction[] = [] if (userTransactions.length) { if (order === Order.DESC) { userTransactions.reverse() } - const finalTransactions: Transaction[] = [] const involvedUserIds: number[] = [] userTransactions.forEach((transaction: dbTransaction) => { @@ -205,7 +204,7 @@ export class TransactionResolver { throw new Error('invalid transaction') } if (i > 0 || !skipFirstTransaction) { - finalTransactions.push(finalTransaction) + transactions.push(finalTransaction) } if (i === userTransactions.length - 1 && decay) { @@ -223,10 +222,9 @@ export class TransactionResolver { decayTransaction.decayDuration = decay.decayDuration decayTransaction.decayStart = decay.decayStart decayTransaction.decayEnd = decay.decayEnd - finalTransactions.push(decayTransaction) + transactions.push(decayTransaction) } } - transactions = finalTransactions if (order === Order.DESC) { transactions.reverse() From f6f8572a82a1a6e2ede6742a53eaedc129069812 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 13:32:30 +0100 Subject: [PATCH 14/88] removed duplicate calculation, removed unnecessary checks therefore --- .../graphql/resolver/TransactionResolver.ts | 54 ++++++------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index ec8a2d7e5..868fa8559 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -35,26 +35,25 @@ import { RIGHTS } from '../../auth/RIGHTS' // helper helper function async function updateStateBalance( user: dbUser, - centAmount: number, + balance: number, received: Date, queryRunner: QueryRunner, ): Promise { - let balance = await dbBalance.findOne({ userId: user.id }) - if (!balance) { - balance = new dbBalance() - balance.userId = user.id - balance.amount = centAmount - balance.modified = received + let userBalance = await dbBalance.findOne({ userId: user.id }) + if (!userBalance) { + userBalance = new dbBalance() + userBalance.userId = user.id + userBalance.amount = balance + userBalance.modified = received } else { - const decayedBalance = calculateDecay(balance.amount, balance.recordDate, received).balance - balance.amount = decayedBalance + centAmount - balance.modified = new Date() + userBalance.amount = balance + userBalance.modified = new Date() } - if (balance.amount <= 0) { + if (userBalance.amount <= 0) { throw new Error('error new balance <= 0') } - balance.recordDate = received - return queryRunner.manager.save(balance).catch((error) => { + userBalance.recordDate = received + return queryRunner.manager.save(userBalance).catch((error) => { throw new Error('error saving balance:' + error) }) } @@ -329,32 +328,11 @@ export class TransactionResolver { transactionSend.linkedTransactionId = transactionReceive.id await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) - // Update Balance: sender - amount - const senderStateBalance = await updateStateBalance( - senderUser, - -centAmount, - receivedCallDate, - queryRunner, - ) + // Update Balance sender + await updateStateBalance(senderUser, sendBalance, receivedCallDate, queryRunner) - // Update Balance: recipient + amount - const recipientStateBalance = await updateStateBalance( - recipientUser, - centAmount, - receivedCallDate, - queryRunner, - ) - - if (senderStateBalance.amount !== sendBalance) { - // eslint-disable-next-line no-console - console.log('db data corrupted, sender', senderStateBalance.amount, sendBalance) - throw new Error('db data corrupted, sender') - } - if (recipientStateBalance.amount !== receiveBalance) { - // eslint-disable-next-line no-console - console.log('db data corrupted, sender', recipientStateBalance.amount, receiveBalance) - throw new Error('db data corrupted, recipient') - } + // Update Balance recipient + await updateStateBalance(recipientUser, receiveBalance, receivedCallDate, queryRunner) await queryRunner.commitTransaction() } catch (e) { From 89cb88e84e153049122c2f2e8b4b5147785ffd81 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 13:55:20 +0100 Subject: [PATCH 15/88] trunc balance before writing to db --- backend/src/graphql/resolver/TransactionResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 868fa8559..a4a018655 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -329,10 +329,10 @@ export class TransactionResolver { await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) // Update Balance sender - await updateStateBalance(senderUser, sendBalance, receivedCallDate, queryRunner) + await updateStateBalance(senderUser, Math.trunc(sendBalance), receivedCallDate, queryRunner) // Update Balance recipient - await updateStateBalance(recipientUser, receiveBalance, receivedCallDate, queryRunner) + await updateStateBalance(recipientUser, Math.trunc(receiveBalance), receivedCallDate, queryRunner) await queryRunner.commitTransaction() } catch (e) { From 0bf40ecdf029b606acf483e8a5f251190746093f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 19:27:03 +0100 Subject: [PATCH 16/88] lint fix --- backend/src/graphql/resolver/TransactionResolver.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index a4a018655..4226efa83 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -332,7 +332,12 @@ export class TransactionResolver { await updateStateBalance(senderUser, Math.trunc(sendBalance), receivedCallDate, queryRunner) // Update Balance recipient - await updateStateBalance(recipientUser, Math.trunc(receiveBalance), receivedCallDate, queryRunner) + await updateStateBalance( + recipientUser, + Math.trunc(receiveBalance), + receivedCallDate, + queryRunner, + ) await queryRunner.commitTransaction() } catch (e) { From 3fef8910738440f5ee9d76c4124195576b0c6cf6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 23:27:00 +0100 Subject: [PATCH 17/88] fixed rounding error on send amount --- backend/src/graphql/resolver/TransactionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 4226efa83..be43a9b64 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -287,7 +287,7 @@ export class TransactionResolver { throw new Error('invalid recipient public key') } - const centAmount = Math.trunc(amount * 10000) + const centAmount = Math.round(amount * 10000) const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() From 3ffdf24f802fb8d72937c10466ec93c5c7e3dfb7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 04:21:36 +0100 Subject: [PATCH 18/88] migrate data types to decimal initial commit --- database/migrations/0027-decimal_types.ts | 222 ++++++++++++++++++++++ database/package.json | 1 + database/yarn.lock | 5 + 3 files changed, 228 insertions(+) create mode 100644 database/migrations/0027-decimal_types.ts diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts new file mode 100644 index 000000000..f6f951b5b --- /dev/null +++ b/database/migrations/0027-decimal_types.ts @@ -0,0 +1,222 @@ +/* MIGRATION TO INTRODUCE THE DECIMAL TYPE + * + * This migration adds fields of type DECIMAL + * and corrects the corresponding values of + * each by recalculating the history of all + * user transactions. + * + * Furthermore it increases precision of the + * stored values and stores additional data + * points to avoid repetitive calculations. + * + * It will also add a link to the last + * transaction, creating a linked list + * + * And it will convert all timestamps to + * datetime. + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import Decimal from 'decimal.js-light' + +// Set precision value +Decimal.set({ + precision: 20, + rounding: Decimal.ROUND_HALF_UP, +}) + +const DECAY_START_TIME = new Date('2021-05-13 17:46:31') // GMT+0 + +interface Decay { + balance: Decimal + decay: Decimal | null + start: Date | null + end: Date | null + duration: number | null + startBlock: Date +} + +export enum TransactionTypeId { + CREATION = 1, + SEND = 2, + RECEIVE = 3, +} + +function decayFormula(amount: Decimal, seconds: number): Decimal { + return amount.mul( + new Decimal('0.9999999780350404897320120231676707941346052083737611936473').pow(seconds), + ) +} + +function calculateDecay(amount: Decimal, from: Date, to: Date): Decay { + const fromMs = from.getTime() + const toMs = to.getTime() + const decayStartBlockMs = DECAY_START_TIME.getTime() + + if (toMs < fromMs) { + throw new Error('to < from, reverse decay calculation is invalid') + } + + // Initialize with no decay + const decay: Decay = { + balance: amount, + decay: null, + start: null, + end: null, + duration: null, + startBlock: DECAY_START_TIME, + } + + // decay started after end date; no decay + if (decayStartBlockMs > toMs) { + return decay + } + // decay started before start date; decay for full duration + if (decayStartBlockMs < fromMs) { + decay.start = from + decay.duration = (toMs - fromMs) / 1000 + } + // decay started between start and end date; decay from decay start till end date + else { + decay.start = DECAY_START_TIME + decay.duration = (toMs - decayStartBlockMs) / 1000 + } + + decay.end = to + decay.balance = decayFormula(amount, decay.duration) + decay.decay = decay.balance.minus(amount) + return decay +} + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Add Columns + + // add column `previous` for a link to the previous transaction + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `previous` int(10) unsigned DEFAULT NULL AFTER `user_id`;', + ) + // add column `dec_amount` with temporary NULL and DEFAULT NULL definition + // TODO default + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `dec_amount` DECIMAL(36,20) NULL DEFAULT NULL AFTER `type_id`;', + ) + // add column `dec_balance` with temporary NULL and DEFAULT NULL definition + // TODO default + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `dec_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_amount`;', + ) + // add new column `dec_decay` with temporary NULL and DEFAULT NULL definition + // TODO default + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `dec_decay` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_balance`;', + ) + // add new column `decay_start` with temporary NULL and DEFAULT NULL definition + // TODO default + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `decay_start` datetime DEFAULT NULL AFTER `dec_decay`;', + ) + + // Modify columns + + // modify date type of `balance_date` to datetime + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `balance_date` datetime NOT NULL DEFAULT current_timestamp() AFTER `dec_balance`;', + ) + // modify date type of `creation_date` to datetime + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `creation_date` datetime NULL DEFAULT NULL AFTER `balance`;', + ) + + // Temporary columns + + // temporary decimal column `temp_dec_send_sender_final_balance` + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_send_sender_final_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `linked_transaction_id`;', + ) + // temporary decimal column `temp_dec_diff_send_sender_final_balance` + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_send_sender_final_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_send_sender_final_balance`;', + ) + // temporary decimal column `temp_dec_old_balance` + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_old_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_diff_send_sender_final_balance`;', + ) + // temporary decimal column `temp_dec_diff_balance` + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_old_balance`;', + ) + + // Find all users & loop over them + const users = await queryFn('SELECT user_id FROM transactions GROUP BY user_id') + for (let u = 0; u < users.length; u++) { + // find all transactions for a user + const transactions = await queryFn( + `SELECT * FROM transactions WHERE user_id = ${users[u].user_id} ORDER BY balance_date ASC;`, + ) + let previous = null + let balance = new Decimal(0) + for (let t = 0; t < transactions.length; t++) { + const transaction = transactions[t] + + // This should also fix the rounding error on amount + let decAmount = new Decimal(transaction.amount).dividedBy(10000).toDecimalPlaces(2) + if (transaction.type_id === TransactionTypeId.SEND) { + decAmount = decAmount.mul(-1) + } + // dec_decay + const decayStart = previous ? previous.balance_date : transaction.balance_date + const decay = calculateDecay(balance, decayStart, transaction.balance_date) + balance = decay.balance.add(decAmount) + const tempDecSendSenderFinalBalance = transaction.send_sender_final_balance + ? new Decimal(transaction.send_sender_final_balance).dividedBy(10000) + : null + const tempDecDiffSendSenderFinalBalance = tempDecSendSenderFinalBalance + ? balance.minus(tempDecSendSenderFinalBalance) + : null + const tempDecOldBalance = new Decimal(transaction.balance).dividedBy(10000) + const tempDecDiffBalance = balance.minus(tempDecOldBalance) + + // Update + await queryFn(` + UPDATE transactions SET + previous = ${previous ? previous.id : null}, + dec_amount = ${decAmount.toString()}, + dec_balance = ${balance.toString()}, + dec_decay = ${decay.decay ? decay.decay.toString() : '0'}, + decay_start = "${decayStart.toISOString().slice(0, 19).replace('T', ' ')}", + temp_dec_send_sender_final_balance = ${ + tempDecSendSenderFinalBalance ? tempDecSendSenderFinalBalance.toString() : null + }, + temp_dec_diff_send_sender_final_balance = ${ + tempDecDiffSendSenderFinalBalance ? tempDecDiffSendSenderFinalBalance.toString() : null + }, + temp_dec_old_balance = ${tempDecOldBalance.toString()}, + temp_dec_diff_balance = ${tempDecDiffBalance.toString()} + WHERE id = ${transaction.id}; + `) + + // previous + previous = transaction + } + } +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_diff_balance`') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_old_balance`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_diff_send_sender_final_balance`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_send_sender_final_balance`;') + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `creation_date` timestamp NULL DEFAULT NULL AFTER `balance`;', + ) + await queryFn( + 'ALTER TABLE `transactions` MODIFY COLUMN `balance_date` timestamp NOT NULL DEFAULT current_timestamp() AFTER `dec_balance`;', + ) + await queryFn('ALTER TABLE `transactions` DROP COLUMN `decay_start`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `dec_decay`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `dec_balance`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `dec_amount`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `previous`;') +} diff --git a/database/package.json b/database/package.json index 89fec74c9..25e45bd71 100644 --- a/database/package.json +++ b/database/package.json @@ -38,6 +38,7 @@ }, "dependencies": { "crypto": "^1.0.1", + "decimal.js-light": "^2.5.1", "dotenv": "^10.0.0", "faker": "^5.5.3", "mysql2": "^2.3.0", diff --git a/database/yarn.lock b/database/yarn.lock index 55e2c7ff5..3bd75df27 100644 --- a/database/yarn.lock +++ b/database/yarn.lock @@ -560,6 +560,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" From 55ada3b79a165505a15b3a554ccc4ef5be6095b1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 04:21:55 +0100 Subject: [PATCH 19/88] adjusted backend db version requirement --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index e26272436..b98d7a011 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0026-clean_transaction_table', + DB_VERSION: '0027-decimal_types', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } From 43b8b649bddcea3eea5b90e2d606c8acf909ffa0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 05:09:51 +0100 Subject: [PATCH 20/88] timezone correction --- database/migrations/0027-decimal_types.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index f6f951b5b..413683815 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -178,6 +178,11 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis const tempDecOldBalance = new Decimal(transaction.balance).dividedBy(10000) const tempDecDiffBalance = balance.minus(tempDecOldBalance) + /* + if(transaction.user_id === 943){ + console.log(previous ? 'p' : 'n', transaction.user_id, decayStart, decay.start, decay.end, decay.duration, transaction.balance_date) + } + */ // Update await queryFn(` UPDATE transactions SET @@ -185,7 +190,10 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis dec_amount = ${decAmount.toString()}, dec_balance = ${balance.toString()}, dec_decay = ${decay.decay ? decay.decay.toString() : '0'}, - decay_start = "${decayStart.toISOString().slice(0, 19).replace('T', ' ')}", + decay_start = "${new Date(decayStart - new Date().getTimezoneOffset() * 60000) + .toISOString() + .slice(0, 19) + .replace('T', ' ')}", temp_dec_send_sender_final_balance = ${ tempDecSendSenderFinalBalance ? tempDecSendSenderFinalBalance.toString() : null }, From 302ea9945d73941f59e9a96e2e58c6684f5fbf8c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 16:55:05 +0100 Subject: [PATCH 21/88] refactor the methods and types --- database/migrations/0027-decimal_types.ts | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index 413683815..b3ee763e8 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -29,13 +29,13 @@ Decimal.set({ const DECAY_START_TIME = new Date('2021-05-13 17:46:31') // GMT+0 +// TODO: externalize all those definitions and functions into an external decay library interface Decay { balance: Decimal decay: Decimal | null start: Date | null end: Date | null duration: number | null - startBlock: Date } export enum TransactionTypeId { @@ -50,10 +50,15 @@ function decayFormula(amount: Decimal, seconds: number): Decimal { ) } -function calculateDecay(amount: Decimal, from: Date, to: Date): Decay { +function calculateDecay( + amount: Decimal, + from: Date, + to: Date, + startBlock: Date = DECAY_START_TIME, +): Decay { const fromMs = from.getTime() const toMs = to.getTime() - const decayStartBlockMs = DECAY_START_TIME.getTime() + const startBlockMs = startBlock.getTime() if (toMs < fromMs) { throw new Error('to < from, reverse decay calculation is invalid') @@ -66,22 +71,21 @@ function calculateDecay(amount: Decimal, from: Date, to: Date): Decay { start: null, end: null, duration: null, - startBlock: DECAY_START_TIME, } // decay started after end date; no decay - if (decayStartBlockMs > toMs) { + if (startBlockMs > toMs) { return decay } // decay started before start date; decay for full duration - if (decayStartBlockMs < fromMs) { + if (startBlockMs < fromMs) { decay.start = from decay.duration = (toMs - fromMs) / 1000 } // decay started between start and end date; decay from decay start till end date else { decay.start = DECAY_START_TIME - decay.duration = (toMs - decayStartBlockMs) / 1000 + decay.duration = (toMs - startBlockMs) / 1000 } decay.end = to @@ -165,7 +169,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis if (transaction.type_id === TransactionTypeId.SEND) { decAmount = decAmount.mul(-1) } - // dec_decay const decayStart = previous ? previous.balance_date : transaction.balance_date const decay = calculateDecay(balance, decayStart, transaction.balance_date) balance = decay.balance.add(decAmount) @@ -178,11 +181,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis const tempDecOldBalance = new Decimal(transaction.balance).dividedBy(10000) const tempDecDiffBalance = balance.minus(tempDecOldBalance) - /* - if(transaction.user_id === 943){ - console.log(previous ? 'p' : 'n', transaction.user_id, decayStart, decay.start, decay.end, decay.duration, transaction.balance_date) - } - */ // Update await queryFn(` UPDATE transactions SET From 0a6a578566c8da92193a30147adc103aaf11e569 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 17:23:05 +0100 Subject: [PATCH 22/88] remove default null datatypes, fixed decay_start value to be more accurate --- database/migrations/0027-decimal_types.ts | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index b3ee763e8..c3b602a2a 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -102,22 +102,18 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis 'ALTER TABLE `transactions` ADD COLUMN `previous` int(10) unsigned DEFAULT NULL AFTER `user_id`;', ) // add column `dec_amount` with temporary NULL and DEFAULT NULL definition - // TODO default await queryFn( 'ALTER TABLE `transactions` ADD COLUMN `dec_amount` DECIMAL(36,20) NULL DEFAULT NULL AFTER `type_id`;', ) // add column `dec_balance` with temporary NULL and DEFAULT NULL definition - // TODO default await queryFn( 'ALTER TABLE `transactions` ADD COLUMN `dec_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_amount`;', ) // add new column `dec_decay` with temporary NULL and DEFAULT NULL definition - // TODO default await queryFn( 'ALTER TABLE `transactions` ADD COLUMN `dec_decay` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_balance`;', ) - // add new column `decay_start` with temporary NULL and DEFAULT NULL definition - // TODO default + // add new column `decay_start` await queryFn( 'ALTER TABLE `transactions` ADD COLUMN `decay_start` datetime DEFAULT NULL AFTER `dec_decay`;', ) @@ -169,8 +165,14 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis if (transaction.type_id === TransactionTypeId.SEND) { decAmount = decAmount.mul(-1) } - const decayStart = previous ? previous.balance_date : transaction.balance_date - const decay = calculateDecay(balance, decayStart, transaction.balance_date) + const decayStartDate = previous ? previous.balance_date : transaction.balance_date + const decay = calculateDecay(balance, decayStartDate, transaction.balance_date) + const decayStart = decay.start + ? new Date(decay.start.getTime() - new Date().getTimezoneOffset() * 60000) + .toISOString() + .slice(0, 19) + .replace('T', ' ') + : null balance = decay.balance.add(decAmount) const tempDecSendSenderFinalBalance = transaction.send_sender_final_balance ? new Decimal(transaction.send_sender_final_balance).dividedBy(10000) @@ -188,10 +190,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis dec_amount = ${decAmount.toString()}, dec_balance = ${balance.toString()}, dec_decay = ${decay.decay ? decay.decay.toString() : '0'}, - decay_start = "${new Date(decayStart - new Date().getTimezoneOffset() * 60000) - .toISOString() - .slice(0, 19) - .replace('T', ' ')}", + decay_start = "${decayStart}", temp_dec_send_sender_final_balance = ${ tempDecSendSenderFinalBalance ? tempDecSendSenderFinalBalance.toString() : null }, @@ -207,6 +206,11 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis previous = transaction } } + + // Remove null as value & default value from `dec_amount`, `dec_balance` and `dec_decay` + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_amount` DECIMAL(36,20) NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_balance` DECIMAL(36,20) NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_decay` DECIMAL(36,20) NOT NULL;') } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { From e6813ea4088ed17610fc1ffef04e166caa8034cb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 18:47:06 +0100 Subject: [PATCH 23/88] increased precision --- database/migrations/0027-decimal_types.ts | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index c3b602a2a..78cd03830 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -23,7 +23,7 @@ import Decimal from 'decimal.js-light' // Set precision value Decimal.set({ - precision: 20, + precision: 25, rounding: Decimal.ROUND_HALF_UP, }) @@ -44,10 +44,8 @@ export enum TransactionTypeId { RECEIVE = 3, } -function decayFormula(amount: Decimal, seconds: number): Decimal { - return amount.mul( - new Decimal('0.9999999780350404897320120231676707941346052083737611936473').pow(seconds), - ) +function decayFormula(value: Decimal, seconds: number): Decimal { + return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds)) } function calculateDecay( @@ -103,15 +101,15 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis ) // add column `dec_amount` with temporary NULL and DEFAULT NULL definition await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `dec_amount` DECIMAL(36,20) NULL DEFAULT NULL AFTER `type_id`;', + 'ALTER TABLE `transactions` ADD COLUMN `dec_amount` DECIMAL(40,20) NULL DEFAULT NULL AFTER `type_id`;', ) // add column `dec_balance` with temporary NULL and DEFAULT NULL definition await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `dec_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_amount`;', + 'ALTER TABLE `transactions` ADD COLUMN `dec_balance` DECIMAL(40,20) NULL DEFAULT NULL AFTER `dec_amount`;', ) // add new column `dec_decay` with temporary NULL and DEFAULT NULL definition await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `dec_decay` DECIMAL(36,20) NULL DEFAULT NULL AFTER `dec_balance`;', + 'ALTER TABLE `transactions` ADD COLUMN `dec_decay` DECIMAL(40,20) NULL DEFAULT NULL AFTER `dec_balance`;', ) // add new column `decay_start` await queryFn( @@ -126,26 +124,26 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis ) // modify date type of `creation_date` to datetime await queryFn( - 'ALTER TABLE `transactions` MODIFY COLUMN `creation_date` datetime NULL DEFAULT NULL AFTER `balance`;', + 'ALTER TABLE `transactions` MODIFY COLUMN `creation_date` datetime DEFAULT NULL AFTER `balance`;', ) // Temporary columns // temporary decimal column `temp_dec_send_sender_final_balance` await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_send_sender_final_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `linked_transaction_id`;', + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_send_sender_final_balance` DECIMAL(40,20) NULL DEFAULT NULL AFTER `linked_transaction_id`;', ) // temporary decimal column `temp_dec_diff_send_sender_final_balance` await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_send_sender_final_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_send_sender_final_balance`;', + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_send_sender_final_balance` DECIMAL(40,20) NULL DEFAULT NULL AFTER `temp_dec_send_sender_final_balance`;', ) // temporary decimal column `temp_dec_old_balance` await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_old_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_diff_send_sender_final_balance`;', + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_old_balance` DECIMAL(40,20) NULL DEFAULT NULL AFTER `temp_dec_diff_send_sender_final_balance`;', ) // temporary decimal column `temp_dec_diff_balance` await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_balance` DECIMAL(36,20) NULL DEFAULT NULL AFTER `temp_dec_old_balance`;', + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_balance` DECIMAL(40,20) NULL DEFAULT NULL AFTER `temp_dec_old_balance`;', ) // Find all users & loop over them @@ -168,10 +166,12 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis const decayStartDate = previous ? previous.balance_date : transaction.balance_date const decay = calculateDecay(balance, decayStartDate, transaction.balance_date) const decayStart = decay.start - ? new Date(decay.start.getTime() - new Date().getTimezoneOffset() * 60000) + ? '"' + + new Date(decay.start.getTime() - 2 * new Date().getTimezoneOffset() * 60000) .toISOString() .slice(0, 19) - .replace('T', ' ') + .replace('T', ' ') + + '"' : null balance = decay.balance.add(decAmount) const tempDecSendSenderFinalBalance = transaction.send_sender_final_balance @@ -190,7 +190,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis dec_amount = ${decAmount.toString()}, dec_balance = ${balance.toString()}, dec_decay = ${decay.decay ? decay.decay.toString() : '0'}, - decay_start = "${decayStart}", + decay_start = ${decayStart}, temp_dec_send_sender_final_balance = ${ tempDecSendSenderFinalBalance ? tempDecSendSenderFinalBalance.toString() : null }, @@ -208,9 +208,9 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis } // Remove null as value & default value from `dec_amount`, `dec_balance` and `dec_decay` - await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_amount` DECIMAL(36,20) NOT NULL;') - await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_balance` DECIMAL(36,20) NOT NULL;') - await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_decay` DECIMAL(36,20) NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_amount` DECIMAL(40,20) NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_balance` DECIMAL(40,20) NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `dec_decay` DECIMAL(40,20) NOT NULL;') } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { From 3707ca2d2cd55a81e3b802da571a387dce0bc7c7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 19:24:14 +0100 Subject: [PATCH 24/88] fix timezone problem --- database/migrations/0027-decimal_types.ts | 10 ++++------ database/package.json | 14 +++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index 78cd03830..d645be9a6 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -14,6 +14,8 @@ * * And it will convert all timestamps to * datetime. + * + * WARNING: This Migration must be run in TZ=UTC */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ @@ -165,13 +167,9 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis } const decayStartDate = previous ? previous.balance_date : transaction.balance_date const decay = calculateDecay(balance, decayStartDate, transaction.balance_date) + // WARNING: `toISOString()` needs UTC Timezone to work properly! const decayStart = decay.start - ? '"' + - new Date(decay.start.getTime() - 2 * new Date().getTimezoneOffset() * 60000) - .toISOString() - .slice(0, 19) - .replace('T', ' ') + - '"' + ? '"' + decay.start.toISOString().slice(0, 19).replace('T', ' ') + '"' : null balance = decay.balance.add(decAmount) const tempDecSendSenderFinalBalance = transaction.send_sender_final_balance diff --git a/database/package.json b/database/package.json index 25e45bd71..29f5ba511 100644 --- a/database/package.json +++ b/database/package.json @@ -10,15 +10,15 @@ "scripts": { "build": "mkdir -p build/src/config/ && cp src/config/*.txt build/src/config/ && tsc --build", "clean": "tsc --build --clean", - "up": "node build/src/index.js up", - "down": "node build/src/index.js down", - "reset": "node build/src/index.js reset", - "dev_up": "ts-node src/index.ts up", - "dev_down": "ts-node src/index.ts down", - "dev_reset": "ts-node src/index.ts reset", + "up": "TZ=UTC node build/src/index.js up", + "down": "TZ=UTC node build/src/index.js down", + "reset": "TZ=UTC node build/src/index.js reset", + "dev_up": "TZ=UTC ts-node src/index.ts up", + "dev_down": "TZ=UTC ts-node src/index.ts down", + "dev_reset": "TZ=UTC ts-node src/index.ts reset", "lint": "eslint --max-warnings=0 --ext .js,.ts .", "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config", - "seed": "ts-node src/index.ts seed" + "seed": "TZ=UTC ts-node src/index.ts seed" }, "devDependencies": { "@types/faker": "^5.5.9", From 4cfee1ce38388d41874624cfb98a3b19c0b536b3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 20:01:42 +0100 Subject: [PATCH 25/88] no decay start date for first transactions --- database/migrations/0027-decimal_types.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index d645be9a6..d653ee05d 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -168,9 +168,10 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis const decayStartDate = previous ? previous.balance_date : transaction.balance_date const decay = calculateDecay(balance, decayStartDate, transaction.balance_date) // WARNING: `toISOString()` needs UTC Timezone to work properly! - const decayStart = decay.start - ? '"' + decay.start.toISOString().slice(0, 19).replace('T', ' ') + '"' - : null + const decayStart = + previous && decay.start + ? '"' + decay.start.toISOString().slice(0, 19).replace('T', ' ') + '"' + : null balance = decay.balance.add(decAmount) const tempDecSendSenderFinalBalance = transaction.send_sender_final_balance ? new Decimal(transaction.send_sender_final_balance).dividedBy(10000) From eb7a9299fd9d48ea16ab041efc8a001d9139726c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 20:02:10 +0100 Subject: [PATCH 26/88] updated entity & decimal transformer --- .../entity/0027-decimal_types/Transaction.ts | 146 ++++++++++++++++++ database/entity/Transaction.ts | 2 +- database/src/typeorm/DecimalTransformer.ts | 14 ++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 database/entity/0027-decimal_types/Transaction.ts create mode 100644 database/src/typeorm/DecimalTransformer.ts diff --git a/database/entity/0027-decimal_types/Transaction.ts b/database/entity/0027-decimal_types/Transaction.ts new file mode 100644 index 000000000..af1f68556 --- /dev/null +++ b/database/entity/0027-decimal_types/Transaction.ts @@ -0,0 +1,146 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('transactions') +export class Transaction extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @Column({ unsigned: true, nullable: true, default: null }) + previous: number + + @Column({ name: 'type_id', unsigned: true, nullable: false }) + typeId: number + + @Column({ + name: 'dec_amount', + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + decAmount: Decimal + + @Column({ + name: 'dec_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + decBalance: Decimal + + @Column({ + name: 'balance_date', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + nullable: false, + }) + balanceDate: Date + + @Column({ + name: 'dec_decay', + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + decDecay: Decimal + + @Column({ + name: 'decay_start', + type: 'datetime', + nullable: true, + default: null, + }) + decayStart: Date | null + + @Column({ type: 'bigint', nullable: false }) + amount: BigInt + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ + name: 'send_sender_final_balance', + type: 'bigint', + nullable: true, + default: null, + }) + sendSenderFinalBalance: BigInt | null + + @Column({ name: 'balance', type: 'bigint', default: 0 }) + balance: BigInt + + @Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null }) + creationDate: Date + + @Column({ + name: 'linked_user_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedUserId?: number | null + + @Column({ + name: 'linked_transaction_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedTransactionId?: number | null + + @Column({ + name: 'temp_dec_send_sender_final_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + tempDecSendSenderFinalBalance: Decimal + + @Column({ + name: 'temp_dec_diff_send_sender_final_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + tempDecDiffSendSenderFinalBalance: Decimal + + @Column({ + name: 'temp_dec_old_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + tempDecOldBalance: Decimal + + @Column({ + name: 'temp_dec_diff_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + tempDecDiffBalance: Decimal +} diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index 8d46ef6da..d652989e5 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0026-clean_transaction_table/Transaction' +export { Transaction } from './0027-decimal_types/Transaction' diff --git a/database/src/typeorm/DecimalTransformer.ts b/database/src/typeorm/DecimalTransformer.ts new file mode 100644 index 000000000..88a3539ad --- /dev/null +++ b/database/src/typeorm/DecimalTransformer.ts @@ -0,0 +1,14 @@ +import Decimal from 'decimal.js-light' +import { ValueTransformer } from 'typeorm' + +export const DecimalTransformer: ValueTransformer = { + /** + * Used to marshal Decimal when writing to the database. + */ + to: (decimal: Decimal | null): string | null => (decimal ? decimal.toString() : null), + + /** + * Used to unmarshal Decimal when reading from the database. + */ + from: (decimal: string | null): Decimal | null => (decimal ? new Decimal(decimal) : null), +} From e3c96463f1b618fa0231766337780e87694833fb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Feb 2022 20:48:03 +0100 Subject: [PATCH 27/88] clean transactions --- .../0028-clean_transaction_table.ts | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 database/migrations/0028-clean_transaction_table.ts diff --git a/database/migrations/0028-clean_transaction_table.ts b/database/migrations/0028-clean_transaction_table.ts new file mode 100644 index 000000000..82a5948f8 --- /dev/null +++ b/database/migrations/0028-clean_transaction_table.ts @@ -0,0 +1,81 @@ +/* MIGRATION TO CLEAN THE TRANSACTION TABLE + * + * This migration deletes and renames several + * columns of the `transactions` table. + * + * This migration has data loss + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Delete columns + + // delete column `amount` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `amount`;') + // delete column `send_sender_final_balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_sender_final_balance`;') + // delete column `balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `balance`;') + // delete column `temp_dec_send_sender_final_balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_send_sender_final_balance`;') + // delete column `temp_dec_diff_send_sender_final_balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_diff_send_sender_final_balance`;') + // delete column `temp_dec_old_balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_old_balance`;') + // delete column `temp_dec_diff_balance` + await queryFn('ALTER TABLE `transactions` DROP COLUMN `temp_dec_diff_balance`;') + + // Rename columns + + // rename column `dec_amount` to `amount` + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `dec_amount` to `amount`;') + + // rename column `dec_balance` to `balance` + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `dec_balance` to `balance`;') + + // rename column `dec_decay` to `decay` + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `dec_decay` to `decay`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Not all data is recoverable here, this data is simulated, + // We lose all incorrect balances and wrongly rounded amounts. + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `decay` to `dec_decay`;') + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `balance` to `dec_balance`;') + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `amount` to `dec_amount`;') + + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_balance` decimal(40,20) DEFAULT NULL AFTER linked_transaction_id;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_old_balance` decimal(40,20) DEFAULT NULL AFTER linked_transaction_id;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_diff_send_sender_final_balance` decimal(40,20) DEFAULT NULL AFTER linked_transaction_id;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `temp_dec_send_sender_final_balance` decimal(40,20) DEFAULT NULL AFTER linked_transaction_id;', + ) + await queryFn('ALTER TABLE `transactions` ADD COLUMN `balance` bigint(20) DEFAULT 0 AFTER memo;') + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL memo;', + ) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER decay_start;', + ) + + await queryFn(` + UPDATE transaction SET + temp_dec_diff_balance = 0, + temp_dec_old_balance = dec_balance, + temp_dec_diff_send_sender_final_balance = 0, + temp_dec_send_sender_final_balance = dec_balance + balance = dec_balance * 10000 + send_sender_final_balance = dec_balance * 10000 + amount = dec_amount * 10000 + `) + + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL AFTER;') +} From 850be8a89049c7a179a47dd42ba5c71017c59057 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 00:11:26 +0100 Subject: [PATCH 28/88] correced down query, delete satte_balances table and restore it --- .../0028-clean_transaction_table.ts | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/database/migrations/0028-clean_transaction_table.ts b/database/migrations/0028-clean_transaction_table.ts index 82a5948f8..0b9e2cc0d 100644 --- a/database/migrations/0028-clean_transaction_table.ts +++ b/database/migrations/0028-clean_transaction_table.ts @@ -37,11 +37,35 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis // rename column `dec_decay` to `decay` await queryFn('ALTER TABLE `transactions` RENAME COLUMN `dec_decay` to `decay`;') + + // Drop tables + + // drop `state_balances` + await queryFn('DROP TABLE `state_balances`;') } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { // Not all data is recoverable here, this data is simulated, // We lose all incorrect balances and wrongly rounded amounts. + + await queryFn(` + CREATE TABLE \`state_balances\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`state_user_id\` int(10) unsigned NOT NULL, + \`modified\` datetime NOT NULL, + \`record_date\` datetime DEFAULT NULL, + \`amount\` bigint(20) NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=568 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + `) + await queryFn(` + INSERT INTO \`state_balances\` + (state_user_id, modified, record_date, amount) + SELECT user_id as state_user_id, balance_date as modified, balance_date as record_date, amount * 10000 as amount FROM + (SELECT user_id as uid, MAX(balance_date) AS date FROM transactions GROUP BY uid) AS t + LEFT JOIN transactions ON t.uid = transactions.user_id AND t.date = transactions.balance_date; + `) + await queryFn('ALTER TABLE `transactions` RENAME COLUMN `decay` to `dec_decay`;') await queryFn('ALTER TABLE `transactions` RENAME COLUMN `balance` to `dec_balance`;') await queryFn('ALTER TABLE `transactions` RENAME COLUMN `amount` to `dec_amount`;') @@ -60,22 +84,22 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom ) await queryFn('ALTER TABLE `transactions` ADD COLUMN `balance` bigint(20) DEFAULT 0 AFTER memo;') await queryFn( - 'ALTER TABLE `transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL memo;', + 'ALTER TABLE `transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL AFTER memo;', ) await queryFn( 'ALTER TABLE `transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER decay_start;', ) await queryFn(` - UPDATE transaction SET + UPDATE transactions SET temp_dec_diff_balance = 0, temp_dec_old_balance = dec_balance, temp_dec_diff_send_sender_final_balance = 0, - temp_dec_send_sender_final_balance = dec_balance - balance = dec_balance * 10000 - send_sender_final_balance = dec_balance * 10000 - amount = dec_amount * 10000 + temp_dec_send_sender_final_balance = dec_balance, + balance = dec_balance * 10000, + send_sender_final_balance = dec_balance * 10000, + amount = dec_amount * 10000; `) - await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL AFTER;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL;') } From 32dea81a36f79969587883275fdc4d52336829c7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 00:19:16 +0100 Subject: [PATCH 29/88] updated entities --- .../entity/0027-decimal_types/Transaction.ts | 2 +- .../Transaction.ts | 85 +++++++++++++++++++ database/entity/Balance.ts | 1 - database/entity/Transaction.ts | 2 +- database/entity/index.ts | 2 - 5 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 database/entity/0028-clean_transaction_table/Transaction.ts delete mode 100644 database/entity/Balance.ts diff --git a/database/entity/0027-decimal_types/Transaction.ts b/database/entity/0027-decimal_types/Transaction.ts index af1f68556..e53508c61 100644 --- a/database/entity/0027-decimal_types/Transaction.ts +++ b/database/entity/0027-decimal_types/Transaction.ts @@ -79,7 +79,7 @@ export class Transaction extends BaseEntity { @Column({ name: 'balance', type: 'bigint', default: 0 }) balance: BigInt - @Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null }) + @Column({ name: 'creation_date', type: 'datetime', nullable: true, default: null }) creationDate: Date @Column({ diff --git a/database/entity/0028-clean_transaction_table/Transaction.ts b/database/entity/0028-clean_transaction_table/Transaction.ts new file mode 100644 index 000000000..252adb2a4 --- /dev/null +++ b/database/entity/0028-clean_transaction_table/Transaction.ts @@ -0,0 +1,85 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('transactions') +export class Transaction extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @Column({ unsigned: true, nullable: true, default: null }) + previous: number + + @Column({ name: 'type_id', unsigned: true, nullable: false }) + typeId: number + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + balance: Decimal + + @Column({ + name: 'balance_date', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + nullable: false, + }) + balanceDate: Date + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + decay: Decimal + + @Column({ + name: 'decay_start', + type: 'datetime', + nullable: true, + default: null, + }) + decayStart: Date | null + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ name: 'creation_date', type: 'datetime', nullable: true, default: null }) + creationDate: Date + + @Column({ + name: 'linked_user_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedUserId?: number | null + + @Column({ + name: 'linked_transaction_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedTransactionId?: number | null +} diff --git a/database/entity/Balance.ts b/database/entity/Balance.ts deleted file mode 100644 index c4fde6334..000000000 --- a/database/entity/Balance.ts +++ /dev/null @@ -1 +0,0 @@ -export { Balance } from './0001-init_db/Balance' diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index d652989e5..90871cb41 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0027-decimal_types/Transaction' +export { Transaction } from './0028-clean_transaction_table/Transaction' diff --git a/database/entity/index.ts b/database/entity/index.ts index 67f809b56..bee4e2b77 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -1,4 +1,3 @@ -import { Balance } from './Balance' import { LoginElopageBuys } from './LoginElopageBuys' import { LoginEmailOptIn } from './LoginEmailOptIn' import { Migration } from './Migration' @@ -10,7 +9,6 @@ import { AdminPendingCreation } from './AdminPendingCreation' export const entities = [ AdminPendingCreation, - Balance, LoginElopageBuys, LoginEmailOptIn, Migration, From f3f2d547a372c94d66ec3bf35f6d68d3d6ca16b0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 00:34:08 +0100 Subject: [PATCH 30/88] have database building again --- database/entity/0001-init_db/User.ts | 2 +- database/src/factories/balance.factory.ts | 18 ------------------ database/src/factories/transaction.factory.ts | 1 - database/src/interface/TransactionContext.ts | 14 +++----------- database/src/interface/UserInterface.ts | 7 +++---- database/src/seeds/helpers/user-helpers.ts | 18 ++++-------------- database/src/seeds/users/bibi-bloxberg.ts | 12 ++---------- database/src/seeds/users/bob-baumeister.ts | 12 ++---------- .../src/seeds/users/raeuber-hotzenplotz.ts | 12 ++---------- 9 files changed, 17 insertions(+), 79 deletions(-) delete mode 100644 database/src/factories/balance.factory.ts diff --git a/database/entity/0001-init_db/User.ts b/database/entity/0001-init_db/User.ts index be2c4c5ad..7d20007f7 100644 --- a/database/entity/0001-init_db/User.ts +++ b/database/entity/0001-init_db/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' -import { Balance } from '../Balance' +import { Balance } from './Balance' // Moriz: I do not like the idea of having two user tables @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) diff --git a/database/src/factories/balance.factory.ts b/database/src/factories/balance.factory.ts deleted file mode 100644 index 2c344be99..000000000 --- a/database/src/factories/balance.factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Faker from 'faker' -import { define } from 'typeorm-seeding' -import { Balance } from '../../entity/Balance' -import { BalanceContext } from '../interface/TransactionContext' - -define(Balance, (faker: typeof Faker, context?: BalanceContext) => { - if (!context || !context.user) { - throw new Error('Balance: No user present!') - } - - const balance = new Balance() - balance.modified = context.modified ? context.modified : faker.date.recent() - balance.recordDate = context.recordDate ? context.recordDate : faker.date.recent() - balance.amount = context.amount ? context.amount : 10000000 - balance.user = context.user - - return balance -}) diff --git a/database/src/factories/transaction.factory.ts b/database/src/factories/transaction.factory.ts index 9e8000223..15efa97c2 100644 --- a/database/src/factories/transaction.factory.ts +++ b/database/src/factories/transaction.factory.ts @@ -16,7 +16,6 @@ define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { transaction.creationDate = context.creationDate || new Date() // transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null transaction.linkedUserId = context.sendReceiverUserId || null - transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null return transaction }) diff --git a/database/src/interface/TransactionContext.ts b/database/src/interface/TransactionContext.ts index 9da737974..d10a3c689 100644 --- a/database/src/interface/TransactionContext.ts +++ b/database/src/interface/TransactionContext.ts @@ -1,20 +1,12 @@ -import { User } from '../../entity/User' +import Decimal from "decimal.js-light"; export interface TransactionContext { typeId: number userId: number - balance: BigInt + balance: Decimal balanceDate: Date - amount: BigInt + amount: Decimal memo: string creationDate?: Date sendReceiverUserId?: number - sendSenderFinalBalance?: BigInt -} - -export interface BalanceContext { - modified?: Date - recordDate?: Date - amount?: number - user?: User } diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts index 1f258f808..ca328c092 100644 --- a/database/src/interface/UserInterface.ts +++ b/database/src/interface/UserInterface.ts @@ -1,3 +1,5 @@ +import Decimal from 'decimal.js-light' + export interface UserInterface { // from user email?: string @@ -24,10 +26,7 @@ export interface UserInterface { // flag for balance (creation of 1000 GDD) addBalance?: boolean // balance - balanceModified?: Date recordDate?: Date creationDate?: Date - amount?: number - creationTxHash?: Buffer - signature?: Buffer + amount?: Decimal } diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index fe43f857e..7485c038e 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -1,11 +1,11 @@ import { UserContext, ServerUserContext } from '../../interface/UserContext' -import { BalanceContext, TransactionContext } from '../../interface/TransactionContext' +import { TransactionContext } from '../../interface/TransactionContext' import { UserInterface } from '../../interface/UserInterface' import { User } from '../../../entity/User' import { ServerUser } from '../../../entity/ServerUser' -import { Balance } from '../../../entity/Balance' import { Transaction } from '../../../entity/Transaction' import { Factory } from 'typeorm-seeding' +import Decimal from 'decimal.js-light' export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => { const user = await factory(User)(createUserContext(userData)).create() @@ -16,7 +16,6 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro if (userData.addBalance) { // create some GDD for the user - await factory(Balance)(createBalanceContext(userData, user)).create() await factory(Transaction)( createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'), ).create() @@ -52,15 +51,6 @@ const createServerUserContext = (context: UserInterface): ServerUserContext => { } } -const createBalanceContext = (context: UserInterface, user: User): BalanceContext => { - return { - modified: context.balanceModified, - recordDate: context.recordDate, - amount: context.amount, - user, - } -} - const createTransactionContext = ( context: UserInterface, user: User, @@ -70,8 +60,8 @@ const createTransactionContext = ( return { typeId: type, userId: user.id, - amount: BigInt(context.amount || 100000), - balance: BigInt(context.amount || 100000), + amount: context.amount || new Decimal(1000), + balance: context.amount || new Decimal(1000), balanceDate: new Date(context.recordDate || Date.now()), memo, creationDate: context.creationDate, diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts index f940c3447..a055a283b 100644 --- a/database/src/seeds/users/bibi-bloxberg.ts +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -1,3 +1,4 @@ +import Decimal from 'decimal.js-light' import { UserInterface } from '../../interface/UserInterface' export const bibiBloxberg: UserInterface = { @@ -19,16 +20,7 @@ export const bibiBloxberg: UserInterface = { 'knife normal level all hurdle crucial color avoid warrior stadium road bachelor affair topple hawk pottery right afford immune two ceiling budget glance hour ', isAdmin: false, addBalance: true, - balanceModified: new Date('2021-11-30T10:37:11'), recordDate: new Date('2021-11-30T10:37:11'), creationDate: new Date('2021-08-01 00:00:00'), - amount: 10000000, - creationTxHash: Buffer.from( - '51103dc0fc2ca5d5d75a9557a1e899304e5406cfdb1328d8df6414d527b0118100000000000000000000000000000000', - 'hex', - ), - signature: Buffer.from( - '2a2c71f3e41adc060bbc3086577e2d57d24eeeb0a7727339c3f85aad813808f601d7e1df56a26e0929d2e67fc054fca429ccfa283ed2782185c7f009fe008f0c', - 'hex', - ), + amount: new Decimal(1000), } diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts index 34597fa3f..4a50a7e63 100644 --- a/database/src/seeds/users/bob-baumeister.ts +++ b/database/src/seeds/users/bob-baumeister.ts @@ -1,3 +1,4 @@ +import Decimal from 'decimal.js-light' import { UserInterface } from '../../interface/UserInterface' export const bobBaumeister: UserInterface = { @@ -19,16 +20,7 @@ export const bobBaumeister: UserInterface = { 'detail master source effort unable waste tilt flush domain orchard art truck hint barrel response gate impose peanut secret merry three uncle wink resource ', isAdmin: false, addBalance: true, - balanceModified: new Date('2021-11-30T10:37:14'), recordDate: new Date('2021-11-30T10:37:14'), creationDate: new Date('2021-08-01 00:00:00'), - amount: 10000000, - creationTxHash: Buffer.from( - 'be095dc87acb94987e71168fee8ecbf50ecb43a180b1006e75d573b35725c69c00000000000000000000000000000000', - 'hex', - ), - signature: Buffer.from( - '1fbd6b9a3d359923b2501557f3bc79fa7e428127c8090fb16bc490b4d87870ab142b3817ddd902d22f0b26472a483233784a0e460c0622661752a13978903905', - 'hex', - ), + amount: new Decimal(1000), } diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts index bcd5f995b..879892f15 100644 --- a/database/src/seeds/users/raeuber-hotzenplotz.ts +++ b/database/src/seeds/users/raeuber-hotzenplotz.ts @@ -1,3 +1,4 @@ +import Decimal from 'decimal.js-light' import { UserInterface } from '../../interface/UserInterface' export const raeuberHotzenplotz: UserInterface = { @@ -19,16 +20,7 @@ export const raeuberHotzenplotz: UserInterface = { 'gospel trip tenant mouse spider skill auto curious man video chief response same little over expire drum display fancy clinic keen throw urge basket ', isAdmin: false, addBalance: true, - balanceModified: new Date('2021-11-30T10:37:13'), recordDate: new Date('2021-11-30T10:37:13'), creationDate: new Date('2021-08-01 00:00:00'), - amount: 10000000, - creationTxHash: Buffer.from( - '23ba44fd84deb59b9f32969ad0cb18bfa4588be1bdb99c396888506474c16c1900000000000000000000000000000000', - 'hex', - ), - signature: Buffer.from( - '756d3da061687c575d1dbc5073908f646aa5f498b0927b217c83b48af471450e571dfe8421fb8e1f1ebd1104526b7e7c6fa78684e2da59c8f7f5a8dc3d9e5b0b', - 'hex', - ), + amount: new Decimal(1000), } From 7644bf1834c01aecf6f9074574b54167bf62076f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 03:42:20 +0100 Subject: [PATCH 31/88] backend compiles --- backend/package.json | 1 + backend/src/config/index.ts | 8 + .../src/graphql/arg/TransactionSendArgs.ts | 5 +- backend/src/graphql/model/Balance.ts | 13 +- backend/src/graphql/model/Transaction.ts | 13 +- backend/src/graphql/model/TransactionList.ts | 15 +- backend/src/graphql/resolver/AdminResolver.ts | 23 +- .../src/graphql/resolver/BalanceResolver.ts | 24 +- .../graphql/resolver/TransactionResolver.ts | 251 ++++++------------ .../sendTransactionReceivedEmail.test.ts | 3 +- .../mailer/sendTransactionReceivedEmail.ts | 3 +- .../src/mailer/text/transactionReceived.ts | 4 +- backend/src/typeorm/repository/User.ts | 8 - backend/src/util/decay.test.ts | 19 +- backend/src/util/decay.ts | 53 ++-- backend/src/util/round.test.ts | 22 -- backend/src/util/round.ts | 17 -- backend/src/util/validate.ts | 29 +- backend/yarn.lock | 5 + 19 files changed, 201 insertions(+), 315 deletions(-) delete mode 100644 backend/src/util/round.test.ts delete mode 100644 backend/src/util/round.ts diff --git a/backend/package.json b/backend/package.json index b1a395e5f..43023ac90 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,6 +24,7 @@ "axios": "^0.21.1", "class-validator": "^0.13.1", "cors": "^2.8.5", + "decimal.js-light": "^2.5.1", "dotenv": "^10.0.0", "express": "^4.17.1", "graphql": "^15.5.1", diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index b98d7a011..2c4337f00 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,8 +1,16 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) import dotenv from 'dotenv' +import Decimal from 'decimal.js-light' dotenv.config() +// Set precision value +// TODO test if this works here +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) + const constants = { DB_VERSION: '0027-decimal_types', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index cf4e43d94..e75921383 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -1,12 +1,13 @@ import { ArgsType, Field } from 'type-graphql' +import Decimal from 'decimal.js-light' @ArgsType() export default class TransactionSendArgs { @Field(() => String) email: string - @Field(() => Number) - amount: number + @Field(() => Decimal) + amount: Decimal @Field(() => String) memo: string diff --git a/backend/src/graphql/model/Balance.ts b/backend/src/graphql/model/Balance.ts index 93d497e0c..7a3891ece 100644 --- a/backend/src/graphql/model/Balance.ts +++ b/backend/src/graphql/model/Balance.ts @@ -1,20 +1,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' @ObjectType() export class Balance { constructor(json: any) { - this.balance = Number(json.balance) - this.decay = Number(json.decay) + this.balance = json.balance + this.decay = json.decay this.decayDate = json.decay_date } - @Field(() => Number) - balance: number + @Field(() => Decimal) + balance: Decimal - @Field(() => Number) - decay: number + @Field(() => Decimal) + decay: Decimal @Field(() => String) decayDate: string diff --git a/backend/src/graphql/model/Transaction.ts b/backend/src/graphql/model/Transaction.ts index 3aa3c429d..e1b5fb625 100644 --- a/backend/src/graphql/model/Transaction.ts +++ b/backend/src/graphql/model/Transaction.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' import { Decay } from './Decay' @@ -12,19 +13,19 @@ import { Decay } from './Decay' export class Transaction { constructor() { this.type = '' - this.balance = 0 - this.totalBalance = 0 + this.balance = new Decimal(0) + this.totalBalance = new Decimal(0) this.memo = '' } @Field(() => String) type: string - @Field(() => Number) - balance: number + @Field(() => Decimal) + balance: Decimal - @Field(() => Number) - totalBalance: number + @Field(() => Decimal) + totalBalance: Decimal @Field({ nullable: true }) decayStart?: string diff --git a/backend/src/graphql/model/TransactionList.ts b/backend/src/graphql/model/TransactionList.ts index f1973b7e7..b9d9cb950 100644 --- a/backend/src/graphql/model/TransactionList.ts +++ b/backend/src/graphql/model/TransactionList.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' import { Transaction } from './Transaction' @@ -8,9 +9,8 @@ export class TransactionList { constructor() { this.gdtSum = 0 this.count = 0 - this.balance = 0 - this.decay = 0 - this.decayDate = '' + this.balance = new Decimal(0) + this.decayStartBlock = null } @Field(() => Number, { nullable: true }) @@ -20,13 +20,10 @@ export class TransactionList { count: number @Field(() => Number) - balance: number + balance: Decimal - @Field(() => Number) - decay: number - - @Field(() => String) - decayDate: string + @Field(() => Date, { nullable: true }) + decayStartBlock: Date | null @Field(() => [Transaction]) transactions: Transaction[] diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 7c903b098..1ee7f326c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -27,7 +27,7 @@ import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' import { TransactionTypeId } from '../enum/TransactionTypeId' -import { Balance } from '@entity/Balance' +import Decimal from 'decimal.js-light' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -306,35 +306,28 @@ export class AdminResolver { const transactionRepository = getCustomRepository(TransactionRepository) const lastUserTransaction = await transactionRepository.findLastForUser(pendingCreation.userId) - let newBalance = 0 + let newBalance = new Decimal(0) if (lastUserTransaction) { newBalance = calculateDecay( - Number(lastUserTransaction.balance), + lastUserTransaction.balance, lastUserTransaction.balanceDate, receivedCallDate, ).balance } - newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) + // TODO pending creations decimal + newBalance = newBalance.add(new Decimal(Number(pendingCreation.amount))) const transaction = new Transaction() transaction.typeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo transaction.userId = pendingCreation.userId - transaction.amount = BigInt(parseInt(pendingCreation.amount.toString())) + // TODO pending creations decimal + transaction.amount = new Decimal(Number(pendingCreation.amount)) transaction.creationDate = pendingCreation.date - transaction.balance = BigInt(newBalance) + transaction.balance = newBalance transaction.balanceDate = receivedCallDate await transaction.save() - let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) - if (!userBalance) { - userBalance = new Balance() - userBalance.userId = pendingCreation.userId - } - userBalance.amount = Number(newBalance) - userBalance.modified = receivedCallDate - userBalance.recordDate = receivedCallDate - await userBalance.save() await AdminPendingCreation.delete(pendingCreation) return true diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index fff073b99..e536093bb 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -6,9 +6,9 @@ import { getCustomRepository } from '@dbTools/typeorm' import { Balance } from '../model/Balance' import { UserRepository } from '../../typeorm/repository/User' import { calculateDecay } from '../../util/decay' -import { roundFloorFrom4 } from '../../util/round' import { RIGHTS } from '../../auth/RIGHTS' -import { Balance as dbBalance } from '@entity/Balance' +import { Transaction } from '@entity/Transaction' +import Decimal from 'decimal.js-light' @Resolver() export class BalanceResolver { @@ -18,24 +18,26 @@ export class BalanceResolver { // load user and balance const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) - const balanceEntity = await dbBalance.findOne({ userId: userEntity.id }) + const user = await userRepository.findByPubkeyHex(context.pubKey) const now = new Date() + const lastTransaction = await Transaction.findOne( + { userId: user.id }, + { order: { balanceDate: 'DESC' } }, + ) + // No balance found - if (!balanceEntity) { + if (!lastTransaction) { return new Balance({ - balance: 0, - decay: 0, + balance: new Decimal(0), + decay: new Decimal(0), decay_date: now.toString(), }) } return new Balance({ - balance: roundFloorFrom4(balanceEntity.amount), - decay: roundFloorFrom4( - calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance, - ), + balance: lastTransaction.balance, + decay: calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance, decay_date: now.toString(), }) } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index be43a9b64..0bc3a5a32 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' -import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm' +import { getCustomRepository, getConnection } from '@dbTools/typeorm' import CONFIG from '../../config' import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail' @@ -20,68 +20,16 @@ import { Order } from '../enum/Order' import { UserRepository } from '../../typeorm/repository/User' import { TransactionRepository } from '../../typeorm/repository/Transaction' -import { User as dbUser } from '@entity/User' +import { User as dbUser, User } from '@entity/User' import { Transaction as dbTransaction } from '@entity/Transaction' -import { Balance as dbBalance } from '@entity/Balance' import { apiPost } from '../../apis/HttpRequest' -import { roundFloorFrom4, roundCeilFrom4 } from '../../util/round' import { calculateDecay } from '../../util/decay' import { TransactionTypeId } from '../enum/TransactionTypeId' import { TransactionType } from '../enum/TransactionType' -import { hasUserAmount, isHexPublicKey } from '../../util/validate' +import { calculateBalance, isHexPublicKey } from '../../util/validate' import { RIGHTS } from '../../auth/RIGHTS' -// helper helper function -async function updateStateBalance( - user: dbUser, - balance: number, - received: Date, - queryRunner: QueryRunner, -): Promise { - let userBalance = await dbBalance.findOne({ userId: user.id }) - if (!userBalance) { - userBalance = new dbBalance() - userBalance.userId = user.id - userBalance.amount = balance - userBalance.modified = received - } else { - userBalance.amount = balance - userBalance.modified = new Date() - } - if (userBalance.amount <= 0) { - throw new Error('error new balance <= 0') - } - userBalance.recordDate = received - return queryRunner.manager.save(userBalance).catch((error) => { - throw new Error('error saving balance:' + error) - }) -} - -async function calculateNewBalance( - userId: number, - transactionDate: Date, - centAmount: number, -): Promise { - let newBalance = centAmount - const transactionRepository = getCustomRepository(TransactionRepository) - const lastUserTransaction = await transactionRepository.findLastForUser(userId) - if (lastUserTransaction) { - newBalance += Number( - calculateDecay( - Number(lastUserTransaction.balance), - lastUserTransaction.balanceDate, - transactionDate, - ).balance, - ) - } - - if (newBalance <= 0) { - throw new Error('error new balance <= 0') - } - - return newBalance -} @Resolver() export class TransactionResolver { @Authorized([RIGHTS.TRANSACTION_LIST]) @@ -97,21 +45,28 @@ export class TransactionResolver { }: Paginated, @Ctx() context: any, ): Promise { - // load user + // find user const userRepository = getCustomRepository(UserRepository) + // TODO: separate those usecases - this is a security issue const user = userId ? await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) : await userRepository.findByPubkeyHex(context.pubKey) - let limit = pageSize - let offset = 0 - let skipFirstTransaction = false - if (currentPage > 1) { - offset = (currentPage - 1) * pageSize - 1 - limit++ - } - if (offset && order === Order.ASC) { - offset-- + + // find current balance + const lastTransaction = await dbTransaction.findOne( + { userId: user.id }, + { order: { balanceDate: 'DESC' } }, + ) + + if (!lastTransaction) { + // TODO Have proper return type here + throw new Error('User has no transactions') } + + // find transactions + const limit = currentPage === 1 && order === Order.DESC ? pageSize - 1 : pageSize + const offset = + currentPage === 1 ? 0 : (currentPage - 1) * pageSize - (order === Order.DESC ? 1 : 0) const transactionRepository = getCustomRepository(TransactionRepository) const [userTransactions, userTransactionsCount] = await transactionRepository.findByUserPaged( user.id, @@ -120,66 +75,52 @@ export class TransactionResolver { order, onlyCreations, ) - skipFirstTransaction = userTransactionsCount > offset + limit - const decay = !(currentPage > 1) - const transactions: Transaction[] = [] - if (userTransactions.length) { - if (order === Order.DESC) { - userTransactions.reverse() + + // find involved users + let involvedUserIds: number[] = [] + userTransactions.forEach((transaction: dbTransaction) => { + involvedUserIds.push(transaction.userId) + if (transaction.linkedUserId) { + involvedUserIds.push(transaction.linkedUserId) } - const involvedUserIds: number[] = [] + }) + // remove duplicates + involvedUserIds = involvedUserIds.filter((value, index, self) => self.indexOf(value) === index) + // We need to show the name for deleted users for old transactions + const involvedUsers = await User.createQueryBuilder() + .withDeleted() + .where('user.id IN (:...userIds)', { involvedUserIds }) + .getMany() - userTransactions.forEach((transaction: dbTransaction) => { - involvedUserIds.push(transaction.userId) - if ( - transaction.typeId === TransactionTypeId.SEND || - transaction.typeId === TransactionTypeId.RECEIVE - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - involvedUserIds.push(transaction.linkedUserId!) // TODO ensure not null properly - } - }) - // remove duplicates - // https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates - const involvedUsersUnique = involvedUserIds.filter((v, i, a) => a.indexOf(v) === i) - const userRepository = getCustomRepository(UserRepository) - const userIndiced = await userRepository.getUsersIndiced(involvedUsersUnique) + const transactions: Transaction[] = [] + // decay transaction + if (currentPage === 1 && order === Order.DESC) { + const now = new Date() + const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now) + const balance = decay.balance.minus(lastTransaction.balance) + + const decayTransaction = new Transaction() + decayTransaction.type = 'decay' + decayTransaction.balance = balance + // TODO + // decayTransaction.decayDuration = decay.duration + // decayTransaction.decayStart = decay.start + // decayTransaction.decayEnd = decay.end + transactions.push(decayTransaction) + } + + if (userTransactions.length) { for (let i = 0; i < userTransactions.length; i++) { const userTransaction = userTransactions[i] const finalTransaction = new Transaction() finalTransaction.transactionId = userTransaction.id finalTransaction.date = userTransaction.balanceDate.toISOString() finalTransaction.memo = userTransaction.memo - finalTransaction.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) - const previousTransaction = i > 0 ? userTransactions[i - 1] : null + finalTransaction.totalBalance = userTransaction.balance + finalTransaction.balance = userTransaction.amount - if (previousTransaction) { - const currentTransaction = userTransaction - const decay = calculateDecay( - Number(previousTransaction.balance), - previousTransaction.balanceDate, - currentTransaction.balanceDate, - ) - const balance = Number(previousTransaction.balance) - decay.balance - - if (CONFIG.DECAY_START_TIME < currentTransaction.balanceDate) { - finalTransaction.decay = decay - finalTransaction.decay.balance = roundFloorFrom4(balance) - if ( - previousTransaction.balanceDate < CONFIG.DECAY_START_TIME && - currentTransaction.balanceDate > CONFIG.DECAY_START_TIME - ) { - finalTransaction.decay.decayStartBlock = ( - CONFIG.DECAY_START_TIME.getTime() / 1000 - ).toString() - } - } - } - - finalTransaction.balance = roundFloorFrom4(Number(userTransaction.amount)) // Todo unsafe conversion - - const otherUser = userIndiced.find((u) => u.id === userTransaction.linkedUserId) + const otherUser = involvedUsers.find((u) => u.id === userTransaction.linkedUserId) switch (userTransaction.typeId) { case TransactionTypeId.CREATION: finalTransaction.name = 'Gradido Akademie' @@ -202,31 +143,7 @@ export class TransactionResolver { default: throw new Error('invalid transaction') } - if (i > 0 || !skipFirstTransaction) { - transactions.push(finalTransaction) - } - - if (i === userTransactions.length - 1 && decay) { - const now = new Date() - const decay = calculateDecay( - Number(userTransaction.balance), - userTransaction.balanceDate, - now, - ) - const balance = Number(userTransaction.balance) - decay.balance - - const decayTransaction = new Transaction() - decayTransaction.type = 'decay' - decayTransaction.balance = roundCeilFrom4(balance) - decayTransaction.decayDuration = decay.decayDuration - decayTransaction.decayStart = decay.decayStart - decayTransaction.decayEnd = decay.decayEnd - transactions.push(decayTransaction) - } - } - - if (order === Order.DESC) { - transactions.reverse() + transactions.push(finalTransaction) } } @@ -244,16 +161,12 @@ export class TransactionResolver { } catch (err: any) {} // get balance - const balanceEntity = await dbBalance.findOne({ userId: user.id }) - if (balanceEntity) { - const now = new Date() - transactionList.balance = roundFloorFrom4(balanceEntity.amount) - // TODO: Add a decay object here instead of static data representing the decay. - transactionList.decay = roundFloorFrom4( - calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance, - ) - transactionList.decayDate = now.toString() - } + transactionList.balance = lastTransaction.balance + transactionList.decayStartBlock = CONFIG.DECAY_START_TIME + // const now = new Date() + // TODO this seems duplicated + // transactionList.decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now) + // transactionList.decayDate = now.toString() return transactionList } @@ -271,7 +184,9 @@ export class TransactionResolver { throw new Error('invalid sender public key') } // validate amount - if (!hasUserAmount(senderUser, amount)) { + const receivedCallDate = new Date() + const sendBalance = await calculateBalance(senderUser.id, amount.mul(-1), receivedCallDate) + if (!sendBalance) { throw new Error("user hasn't enough GDD or amount is < 0") } @@ -287,24 +202,19 @@ export class TransactionResolver { throw new Error('invalid recipient public key') } - const centAmount = Math.round(amount * 10000) - const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') try { - const receivedCallDate = new Date() // transaction const transactionSend = new dbTransaction() transactionSend.typeId = TransactionTypeId.SEND transactionSend.memo = memo transactionSend.userId = senderUser.id transactionSend.linkedUserId = recipientUser.id - transactionSend.amount = BigInt(centAmount) - const sendBalance = await calculateNewBalance(senderUser.id, receivedCallDate, -centAmount) - transactionSend.balance = BigInt(Math.trunc(sendBalance)) + transactionSend.amount = amount + transactionSend.balance = sendBalance transactionSend.balanceDate = receivedCallDate - transactionSend.sendSenderFinalBalance = transactionSend.balance await queryRunner.manager.insert(dbTransaction, transactionSend) const transactionReceive = new dbTransaction() @@ -312,15 +222,13 @@ export class TransactionResolver { transactionReceive.memo = memo transactionReceive.userId = recipientUser.id transactionReceive.linkedUserId = senderUser.id - transactionReceive.amount = BigInt(centAmount) - const receiveBalance = await calculateNewBalance( - recipientUser.id, - receivedCallDate, - centAmount, - ) - transactionReceive.balance = BigInt(Math.trunc(receiveBalance)) + transactionReceive.amount = amount + const receiveBalance = await calculateBalance(recipientUser.id, amount, receivedCallDate) + if (!receiveBalance) { + throw new Error('Sender user account corrupted') + } + transactionReceive.balance = receiveBalance transactionReceive.balanceDate = receivedCallDate - transactionReceive.sendSenderFinalBalance = transactionSend.balance transactionReceive.linkedTransactionId = transactionSend.id await queryRunner.manager.insert(dbTransaction, transactionReceive) @@ -328,17 +236,6 @@ export class TransactionResolver { transactionSend.linkedTransactionId = transactionReceive.id await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) - // Update Balance sender - await updateStateBalance(senderUser, Math.trunc(sendBalance), receivedCallDate, queryRunner) - - // Update Balance recipient - await updateStateBalance( - recipientUser, - Math.trunc(receiveBalance), - receivedCallDate, - queryRunner, - ) - await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() diff --git a/backend/src/mailer/sendTransactionReceivedEmail.test.ts b/backend/src/mailer/sendTransactionReceivedEmail.test.ts index 29f227185..5fd013650 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.test.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.test.ts @@ -1,5 +1,6 @@ import { sendTransactionReceivedEmail } from './sendTransactionReceivedEmail' import { sendEMail } from './sendEMail' +import Decimal from 'decimal.js-light' jest.mock('./sendEMail', () => { return { @@ -16,7 +17,7 @@ describe('sendTransactionReceivedEmail', () => { recipientFirstName: 'Peter', recipientLastName: 'Lustig', email: 'peter@lustig.de', - amount: 42.0, + amount: new Decimal(42.0), memo: 'Vielen herzlichen Dank für den neuen Hexenbesen!', }) }) diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 3560f6548..3b417b10a 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,3 +1,4 @@ +import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' import { transactionReceived } from './text/transactionReceived' @@ -7,7 +8,7 @@ export const sendTransactionReceivedEmail = (data: { recipientFirstName: string recipientLastName: string email: string - amount: number + amount: Decimal memo: string }): Promise => { return sendEMail({ diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 3df2b718a..f685c60ae 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -1,3 +1,5 @@ +import Decimal from 'decimal.js-light' + export const transactionReceived = { de: { subject: 'Gradido Überweisung', @@ -7,7 +9,7 @@ export const transactionReceived = { recipientFirstName: string recipientLastName: string email: string - amount: number + amount: Decimal memo: string }): string => `Hallo ${data.recipientFirstName} ${data.recipientLastName} diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index 92b1b4738..01f61dcbc 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -9,14 +9,6 @@ export class UserRepository extends Repository { .getOneOrFail() } - async getUsersIndiced(userIds: number[]): Promise { - return this.createQueryBuilder('user') - .withDeleted() // We need to show the name for deleted users for old transactions - .select(['user.id', 'user.firstName', 'user.lastName', 'user.email']) - .where('user.id IN (:...userIds)', { userIds }) - .getMany() - } - async findBySearchCriteriaPagedFiltered( select: string[], searchCriteria: string, diff --git a/backend/src/util/decay.test.ts b/backend/src/util/decay.test.ts index 2db9411b5..1653376c1 100644 --- a/backend/src/util/decay.test.ts +++ b/backend/src/util/decay.test.ts @@ -1,29 +1,30 @@ +import Decimal from 'decimal.js-light' import 'reflect-metadata' // This might be wise to load in a test setup file import { decayFormula, calculateDecay } from './decay' describe('utils/decay', () => { describe('decayFormula', () => { it('has base 0.99999997802044727', () => { - const amount = 1.0 + const amount = new Decimal(1.0) const seconds = 1 expect(decayFormula(amount, seconds)).toBe(0.99999997802044727) }) // Not sure if the following skiped tests make sence!? it('has negative decay?', async () => { - const amount = -1.0 + const amount = new Decimal(1.0) const seconds = 1 - expect(await decayFormula(amount, seconds)).toBe(-0.99999997802044727) + expect(decayFormula(amount, seconds)).toBe(-0.99999997802044727) }) it('has correct backward calculation', async () => { - const amount = 1.0 + const amount = new Decimal(1.0) const seconds = -1 - expect(await decayFormula(amount, seconds)).toBe(1.0000000219795533) + expect(decayFormula(amount, seconds)).toBe(1.0000000219795533) }) // not possible, nodejs hasn't enough accuracy it('has correct forward calculation', async () => { - const amount = 1.0 / 0.99999997802044727 + const amount = new Decimal(1.0).div(0.99999997802044727) const seconds = 1 - expect(await decayFormula(amount, seconds)).toBe(1.0) + expect(decayFormula(amount, seconds)).toBe(1.0) }) }) it.skip('has base 0.99999997802044727', async () => { @@ -31,11 +32,11 @@ describe('utils/decay', () => { now.setSeconds(1) const oneSecondAgo = new Date(now.getTime()) oneSecondAgo.setSeconds(0) - expect(await calculateDecay(1.0, oneSecondAgo, now)).toBe(0.99999997802044727) + expect(calculateDecay(new Decimal(1.0), oneSecondAgo, now)).toBe(0.99999997802044727) }) it('returns input amount when from and to is the same', async () => { const now = new Date() - expect((await calculateDecay(100.0, now, now)).balance).toBe(100.0) + expect(calculateDecay(new Decimal(100.0), now, now).balance).toBe(100.0) }) }) diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index 7cb4ce384..21ecfa151 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -1,45 +1,60 @@ +import Decimal from 'decimal.js-light' import CONFIG from '../config' -import { Decay } from '../graphql/model/Decay' -function decayFormula(amount: number, seconds: number): number { - return amount * Math.pow(0.99999997802044727, seconds) // This number represents 50% decay a year +// TODO: externalize all those definitions and functions into an external decay library +interface Decay { + balance: Decimal + decay: Decimal | null + start: Date | null + end: Date | null + duration: number | null } -function calculateDecay(amount: number, from: Date, to: Date): Decay { +function decayFormula(value: Decimal, seconds: number): Decimal { + return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds)) +} + +function calculateDecay( + amount: Decimal, + from: Date, + to: Date, + startBlock: Date = CONFIG.DECAY_START_TIME, +): Decay { const fromMs = from.getTime() const toMs = to.getTime() - const decayStartBlockMs = CONFIG.DECAY_START_TIME.getTime() + const startBlockMs = startBlock.getTime() if (toMs < fromMs) { throw new Error('to < from, reverse decay calculation is invalid') } // Initialize with no decay - const decay = new Decay({ + const decay: Decay = { balance: amount, - decayStart: null, - decayEnd: null, - decayDuration: 0, - decayStartBlock: (decayStartBlockMs / 1000).toString(), - }) + decay: null, + start: null, + end: null, + duration: null, + } // decay started after end date; no decay - if (decayStartBlockMs > toMs) { + if (startBlockMs > toMs) { return decay } // decay started before start date; decay for full duration - else if (decayStartBlockMs < fromMs) { - decay.decayStart = (fromMs / 1000).toString() - decay.decayDuration = (toMs - fromMs) / 1000 + if (startBlockMs < fromMs) { + decay.start = from + decay.duration = (toMs - fromMs) / 1000 } // decay started between start and end date; decay from decay start till end date else { - decay.decayStart = (decayStartBlockMs / 1000).toString() - decay.decayDuration = (toMs - decayStartBlockMs) / 1000 + decay.start = startBlock + decay.duration = (toMs - startBlockMs) / 1000 } - decay.decayEnd = (toMs / 1000).toString() - decay.balance = decayFormula(amount, decay.decayDuration) + decay.end = to + decay.balance = decayFormula(amount, decay.duration) + decay.decay = decay.balance.minus(amount) return decay } diff --git a/backend/src/util/round.test.ts b/backend/src/util/round.test.ts deleted file mode 100644 index a33867157..000000000 --- a/backend/src/util/round.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { roundCeilFrom4, roundFloorFrom4, roundCeilFrom2, roundFloorFrom2 } from './round' - -describe('utils/round', () => { - it('roundCeilFrom4', () => { - const amount = 11617 - expect(roundCeilFrom4(amount)).toBe(1.17) - }) - // Not sure if the following skiped tests make sence!? - it('roundFloorFrom4', () => { - const amount = 11617 - expect(roundFloorFrom4(amount)).toBe(1.16) - }) - it('roundCeilFrom2', () => { - const amount = 1216 - expect(roundCeilFrom2(amount)).toBe(13) - }) - // not possible, nodejs hasn't enough accuracy - it('roundFloorFrom2', () => { - const amount = 1216 - expect(roundFloorFrom2(amount)).toBe(12) - }) -}) diff --git a/backend/src/util/round.ts b/backend/src/util/round.ts deleted file mode 100644 index 18e15afcd..000000000 --- a/backend/src/util/round.ts +++ /dev/null @@ -1,17 +0,0 @@ -function roundCeilFrom4(decimal: number): number { - return Math.ceil(decimal / 100) / 100 -} - -function roundFloorFrom4(decimal: number): number { - return Math.floor(decimal / 100) / 100 -} - -function roundCeilFrom2(decimal: number): number { - return Math.ceil(decimal / 100) -} - -function roundFloorFrom2(decimal: number): number { - return Math.floor(decimal / 100) -} - -export { roundCeilFrom4, roundFloorFrom4, roundCeilFrom2, roundFloorFrom2 } diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index edd663e9e..dc3aadbec 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,7 +1,6 @@ -import { User as dbUser } from '@entity/User' -import { Balance as dbBalance } from '@entity/Balance' -import { getRepository } from '@dbTools/typeorm' import { calculateDecay } from './decay' +import Decimal from 'decimal.js-light' +import { Transaction } from '@entity/Transaction' function isStringBoolean(value: string): boolean { const lowerValue = value.toLowerCase() @@ -15,14 +14,22 @@ function isHexPublicKey(publicKey: string): boolean { return /^[0-9A-Fa-f]{64}$/i.test(publicKey) } -async function hasUserAmount(user: dbUser, amount: number): Promise { - if (amount < 0) return false - const balanceRepository = getRepository(dbBalance) - const balance = await balanceRepository.findOne({ userId: user.id }) - if (!balance) return false +async function calculateBalance( + userId: number, + amount: Decimal, + time: Date, +): Promise { + if (amount.lessThan(0)) return null - const decay = calculateDecay(balance.amount, balance.recordDate, new Date()).balance - return decay > amount + const lastTransaction = await Transaction.findOne({ userId }, { order: { balanceDate: 'DESC' } }) + if (!lastTransaction) return null + + const accountBalance = calculateDecay( + lastTransaction.balance, + lastTransaction.balanceDate, + time, + ).balance.add(amount) + return accountBalance.greaterThan(0) ? accountBalance : null } -export { isHexPublicKey, hasUserAmount, isStringBoolean } +export { isHexPublicKey, calculateBalance, isStringBoolean } diff --git a/backend/yarn.lock b/backend/yarn.lock index c76b5f00f..330a6d590 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1961,6 +1961,11 @@ debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" From c35f9ad6752f8c89e29e08b899a123198d96471c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 03:42:49 +0100 Subject: [PATCH 32/88] correct start block reference, lint --- database/migrations/0027-decimal_types.ts | 2 +- database/src/interface/TransactionContext.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/database/migrations/0027-decimal_types.ts b/database/migrations/0027-decimal_types.ts index d653ee05d..7b158d6ee 100644 --- a/database/migrations/0027-decimal_types.ts +++ b/database/migrations/0027-decimal_types.ts @@ -84,7 +84,7 @@ function calculateDecay( } // decay started between start and end date; decay from decay start till end date else { - decay.start = DECAY_START_TIME + decay.start = startBlock decay.duration = (toMs - startBlockMs) / 1000 } diff --git a/database/src/interface/TransactionContext.ts b/database/src/interface/TransactionContext.ts index d10a3c689..8eeb579a0 100644 --- a/database/src/interface/TransactionContext.ts +++ b/database/src/interface/TransactionContext.ts @@ -1,4 +1,4 @@ -import Decimal from "decimal.js-light"; +import Decimal from 'decimal.js-light' export interface TransactionContext { typeId: number From dd8ddbad42d8a75640c952228717fe299ee5d7e0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 17:15:15 +0100 Subject: [PATCH 33/88] missing null on field creationDate --- database/entity/0028-clean_transaction_table/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0028-clean_transaction_table/Transaction.ts b/database/entity/0028-clean_transaction_table/Transaction.ts index 252adb2a4..7989e918a 100644 --- a/database/entity/0028-clean_transaction_table/Transaction.ts +++ b/database/entity/0028-clean_transaction_table/Transaction.ts @@ -63,7 +63,7 @@ export class Transaction extends BaseEntity { memo: string @Column({ name: 'creation_date', type: 'datetime', nullable: true, default: null }) - creationDate: Date + creationDate: Date | null @Column({ name: 'linked_user_id', From 97b4e16f40de609d626223bab273265050cedabb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 17:17:43 +0100 Subject: [PATCH 34/88] new graphql interface, things build --- backend/src/graphql/enum/TransactionTypeId.ts | 2 + backend/src/graphql/model/Decay.ts | 45 ++++---- backend/src/graphql/model/Transaction.ts | 86 +++++++++------ backend/src/graphql/model/TransactionList.ts | 24 +++-- backend/src/graphql/model/User.ts | 95 ++++++++-------- .../graphql/resolver/TransactionResolver.ts | 101 +++++++----------- backend/src/graphql/resolver/UserResolver.ts | 19 +--- backend/src/util/communityUser.ts | 44 ++++++++ backend/src/util/decay.ts | 8 +- backend/src/util/virtualDecayTransaction.ts | 52 +++++++++ 10 files changed, 279 insertions(+), 197 deletions(-) create mode 100644 backend/src/util/communityUser.ts create mode 100644 backend/src/util/virtualDecayTransaction.ts diff --git a/backend/src/graphql/enum/TransactionTypeId.ts b/backend/src/graphql/enum/TransactionTypeId.ts index b5b75aa20..497ad5055 100644 --- a/backend/src/graphql/enum/TransactionTypeId.ts +++ b/backend/src/graphql/enum/TransactionTypeId.ts @@ -4,6 +4,8 @@ export enum TransactionTypeId { CREATION = 1, SEND = 2, RECEIVE = 3, + // This is a virtual property, never occurring on the database + DECAY = 4, } registerEnumType(TransactionTypeId, { diff --git a/backend/src/graphql/model/Decay.ts b/backend/src/graphql/model/Decay.ts index a09f7c97e..a76e66939 100644 --- a/backend/src/graphql/model/Decay.ts +++ b/backend/src/graphql/model/Decay.ts @@ -1,33 +1,36 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field, Int } from 'type-graphql' +import Decimal from 'decimal.js-light' +import { ObjectType, Field } from 'type-graphql' @ObjectType() export class Decay { - constructor(json?: any) { - if (json) { - this.balance = Number(json.balance) - this.decayStart = json.decay_start - this.decayEnd = json.decay_end - this.decayDuration = json.decay_duration - this.decayStartBlock = json.decay_start_block - } + constructor( + balance: Decimal, + decay: Decimal | null, + start: Date | null, + end: Date | null, + duration: number | null, + ) { + this.balance = balance + this.decay = decay + this.start = start + this.end = end + this.duration = duration } - @Field(() => Number) - balance: number + @Field(() => Decimal) + balance: Decimal - // timestamp in seconds - @Field(() => Int, { nullable: true }) - decayStart: string + @Field(() => Decimal, { nullable: true }) + decay: Decimal | null - // timestamp in seconds - @Field(() => Int, { nullable: true }) - decayEnd: string + @Field(() => Date, { nullable: true }) + start: Date | null - @Field(() => String, { nullable: true }) - decayDuration?: number + @Field(() => Date, { nullable: true }) + end: Date | null - @Field(() => Int, { nullable: true }) - decayStartBlock?: string + @Field(() => Number, { nullable: true }) + duration: number | null } diff --git a/backend/src/graphql/model/Transaction.ts b/backend/src/graphql/model/Transaction.ts index e1b5fb625..4c8dbbe38 100644 --- a/backend/src/graphql/model/Transaction.ts +++ b/backend/src/graphql/model/Transaction.ts @@ -3,54 +3,70 @@ import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' import { Decay } from './Decay' - -// we need a better solution for the decay block: -// the first transaction on the first page shows the decay since the last transaction -// the format is actually a Decay and not a Transaction. -// Therefore we have a lot of nullable fields, which should be always present +import { Transaction as dbTransaction } from '@entity/Transaction' +import { TransactionTypeId } from '../enum/TransactionTypeId' +import { User } from './User' @ObjectType() export class Transaction { - constructor() { - this.type = '' - this.balance = new Decimal(0) - this.totalBalance = new Decimal(0) - this.memo = '' + constructor(transaction: dbTransaction, user: User, linkedUser: User | null = null) { + this.id = transaction.id + this.user = user + this.previous = transaction.previous + this.typeId = transaction.typeId + this.amount = transaction.amount + this.balance = transaction.balance + this.balanceDate = transaction.balanceDate + if (!transaction.decayStart) { + this.decay = new Decay(transaction.balance, null, null, null, null) + } else { + this.decay = new Decay( + transaction.balance, + transaction.decay, + transaction.decayStart, + transaction.balanceDate, + (transaction.balanceDate.getTime() - transaction.decayStart.getTime()) / 1000, + ) + } + this.memo = transaction.memo + this.creationDate = transaction.creationDate + this.linkedUser = linkedUser + this.linkedTransactionId = transaction.linkedTransactionId } - @Field(() => String) - type: string + @Field(() => Number) + id: number + + @Field(() => User) + user: User + + @Field(() => Number, { nullable: true }) + previous: number | null + + @Field(() => TransactionTypeId) + typeId: TransactionTypeId + + @Field(() => Decimal) + amount: Decimal @Field(() => Decimal) balance: Decimal - @Field(() => Decimal) - totalBalance: Decimal + @Field(() => Date) + balanceDate: Date - @Field({ nullable: true }) - decayStart?: string - - @Field({ nullable: true }) - decayEnd?: string - - @Field({ nullable: true }) - decayDuration?: number + @Field(() => Decay) + decay: Decay @Field(() => String) memo: string + @Field(() => Date, { nullable: true }) + creationDate: Date | null + + @Field(() => User, { nullable: true }) + linkedUser: User | null + @Field(() => Number, { nullable: true }) - transactionId?: number - - @Field({ nullable: true }) - name?: string - - @Field({ nullable: true }) - email?: string - - @Field({ nullable: true }) - date?: string - - @Field({ nullable: true }) - decay?: Decay + linkedTransactionId?: number | null } diff --git a/backend/src/graphql/model/TransactionList.ts b/backend/src/graphql/model/TransactionList.ts index b9d9cb950..470eca7ef 100644 --- a/backend/src/graphql/model/TransactionList.ts +++ b/backend/src/graphql/model/TransactionList.ts @@ -2,19 +2,27 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' +import CONFIG from '../../config' import { Transaction } from './Transaction' @ObjectType() export class TransactionList { - constructor() { - this.gdtSum = 0 - this.count = 0 - this.balance = new Decimal(0) - this.decayStartBlock = null + constructor( + balance: Decimal, + transactions: Transaction[], + count: number, + balanceGDT?: number | null, + decayStartBlock: Date = CONFIG.DECAY_START_TIME, + ) { + this.balance = balance + this.transactions = transactions + this.count = count + this.balanceGDT = balanceGDT || null + this.decayStartBlock = decayStartBlock } @Field(() => Number, { nullable: true }) - gdtSum: number | null + balanceGDT: number | null @Field(() => Number) count: number @@ -22,8 +30,8 @@ export class TransactionList { @Field(() => Number) balance: Decimal - @Field(() => Date, { nullable: true }) - decayStartBlock: Date | null + @Field(() => Date) + decayStartBlock: Date @Field(() => [Transaction]) transactions: Transaction[] diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index ed538bcfe..c23ea0a58 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -1,75 +1,76 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { ObjectType, Field, Int } from 'type-graphql' +import { ObjectType, Field } from 'type-graphql' import { KlickTipp } from './KlickTipp' +import { User as dbUser } from '@entity/User' @ObjectType() export class User { - /* - @Field(() => ID) - @PrimaryGeneratedColumn() - id: number - */ - constructor(json?: any) { - if (json) { - this.id = json.id - this.email = json.email - this.firstName = json.first_name - this.lastName = json.last_name - this.pubkey = json.public_hex - this.language = json.language - this.publisherId = json.publisher_id - this.isAdmin = json.isAdmin - } + constructor(user: dbUser) { + this.id = user.id + this.email = user.email + this.firstName = user.firstName + this.lastName = user.lastName + this.deletedAt = user.deletedAt + this.createdAt = user.createdAt + this.emailChecked = user.emailChecked + this.language = user.language + this.publisherId = user.publisherId + // TODO + this.isAdmin = null + this.coinanimation = null + this.klickTipp = null + this.hasElopage = null } @Field(() => Number) id: number + // `public_key` binary(32) DEFAULT NULL, + // `privkey` binary(80) DEFAULT NULL, + + // TODO privacy issue here @Field(() => String) email: string - @Field(() => String) - firstName: string + @Field(() => String, { nullable: true }) + firstName: string | null - @Field(() => String) - lastName: string + @Field(() => String, { nullable: true }) + lastName: string | null - @Field(() => String) - pubkey: string - /* - @Field(() => String) - pubkey: string + @Field(() => Date, { nullable: true }) + deletedAt: Date | null - // not sure about the type here. Maybe better to have a string - @Field(() => number) - created: number + // `password` bigint(20) unsigned DEFAULT 0, + // `email_hash` binary(32) DEFAULT NULL, - @Field(() =>>> Boolean) + @Field(() => Date) + createdAt: Date + + @Field(() => Boolean) emailChecked: boolean - */ @Field(() => String) language: string - /* - @Field(() => Boolean) - disabled: boolean - */ + // This is not the users publisherId, but the one of the users who recommend him + @Field(() => Number, { nullable: true }) + publisherId: number | null - // what is publisherId? - @Field(() => Int, { nullable: true }) - publisherId?: number + // `passphrase` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, - @Field(() => Boolean) - isAdmin: boolean - - @Field(() => Boolean) - coinanimation: boolean - - @Field(() => KlickTipp) - klickTipp: KlickTipp + // TODO this is a bit inconsistent with what we query from the database + // therefore all those fields are now nullable with default value null + @Field(() => Boolean, { nullable: true }) + isAdmin: boolean | null @Field(() => Boolean, { nullable: true }) - hasElopage?: boolean + coinanimation: boolean | null + + @Field(() => KlickTipp, { nullable: true }) + klickTipp: KlickTipp | null + + @Field(() => Boolean, { nullable: true }) + hasElopage: boolean | null } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 0bc3a5a32..3e84646dd 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -20,15 +20,17 @@ import { Order } from '../enum/Order' import { UserRepository } from '../../typeorm/repository/User' import { TransactionRepository } from '../../typeorm/repository/Transaction' -import { User as dbUser, User } from '@entity/User' +import { User as dbUser } from '@entity/User' import { Transaction as dbTransaction } from '@entity/Transaction' import { apiPost } from '../../apis/HttpRequest' import { calculateDecay } from '../../util/decay' import { TransactionTypeId } from '../enum/TransactionTypeId' -import { TransactionType } from '../enum/TransactionType' import { calculateBalance, isHexPublicKey } from '../../util/validate' import { RIGHTS } from '../../auth/RIGHTS' +import { User } from '../model/User' +import { communityUser } from '../../util/communityUser' +import { virtualDecayTransaction } from '../../util/virtualDecayTransaction' @Resolver() export class TransactionResolver { @@ -87,88 +89,57 @@ export class TransactionResolver { // remove duplicates involvedUserIds = involvedUserIds.filter((value, index, self) => self.indexOf(value) === index) // We need to show the name for deleted users for old transactions - const involvedUsers = await User.createQueryBuilder() + const involvedDbUsers = await dbUser + .createQueryBuilder() .withDeleted() .where('user.id IN (:...userIds)', { involvedUserIds }) .getMany() + const involvedUsers = involvedDbUsers.map((u) => new User(u)) + const self = new User(user) const transactions: Transaction[] = [] // decay transaction if (currentPage === 1 && order === Order.DESC) { const now = new Date() - const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now) - const balance = decay.balance.minus(lastTransaction.balance) - - const decayTransaction = new Transaction() - decayTransaction.type = 'decay' - decayTransaction.balance = balance - // TODO - // decayTransaction.decayDuration = decay.duration - // decayTransaction.decayStart = decay.start - // decayTransaction.decayEnd = decay.end - transactions.push(decayTransaction) + transactions.push( + virtualDecayTransaction(lastTransaction.balance, lastTransaction.balanceDate, now, self), + ) } - if (userTransactions.length) { - for (let i = 0; i < userTransactions.length; i++) { - const userTransaction = userTransactions[i] - const finalTransaction = new Transaction() - finalTransaction.transactionId = userTransaction.id - finalTransaction.date = userTransaction.balanceDate.toISOString() - finalTransaction.memo = userTransaction.memo - finalTransaction.totalBalance = userTransaction.balance - finalTransaction.balance = userTransaction.amount - - const otherUser = involvedUsers.find((u) => u.id === userTransaction.linkedUserId) - switch (userTransaction.typeId) { - case TransactionTypeId.CREATION: - finalTransaction.name = 'Gradido Akademie' - finalTransaction.type = TransactionType.CREATION - break - case TransactionTypeId.SEND: - finalTransaction.type = TransactionType.SEND - if (otherUser) { - finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName - finalTransaction.email = otherUser.email - } - break - case TransactionTypeId.RECEIVE: - finalTransaction.type = TransactionType.RECIEVE - if (otherUser) { - finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName - finalTransaction.email = otherUser.email - } - break - default: - throw new Error('invalid transaction') - } - transactions.push(finalTransaction) + for (let i = 0; i < userTransactions.length; i++) { + const userTransaction = userTransactions[i] + let linkedUser = null + if (userTransaction.typeId === TransactionTypeId.CREATION) { + linkedUser = communityUser + } else { + linkedUser = involvedUsers.find((u) => u.id === userTransaction.linkedUserId) } + transactions.push(new Transaction(userTransaction, self, linkedUser)) } - const transactionList = new TransactionList() - transactionList.count = userTransactionsCount - transactionList.transactions = transactions - - // get gdt sum - transactionList.gdtSum = null + // get GDT + let balanceGDT = null try { const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, { email: user.email, }) - if (resultGDTSum.success) transactionList.gdtSum = Number(resultGDTSum.data.sum) || 0 - } catch (err: any) {} + if (!resultGDTSum.success) { + throw new Error('Call not successful') + } + balanceGDT = Number(resultGDTSum.data.sum) || 0 + } catch (err: any) { + // eslint-disable-next-line no-console + console.log('Could not query GDT Server', err) + } - // get balance - transactionList.balance = lastTransaction.balance - transactionList.decayStartBlock = CONFIG.DECAY_START_TIME - // const now = new Date() - // TODO this seems duplicated - // transactionList.decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now) - // transactionList.decayDate = now.toString() - - return transactionList + // Construct Result + return new TransactionList( + lastTransaction.balance, + transactions, + userTransactionsCount, + balanceGDT, + ) } @Authorized([RIGHTS.SEND_COINS]) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index dfa685ed0..fa358ad1f 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -216,14 +216,8 @@ export class UserResolver { // TODO refactor and do not have duplicate code with login(see below) const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(context.pubKey) - const user = new User() - user.id = userEntity.id - user.email = userEntity.email - user.firstName = userEntity.firstName - user.lastName = userEntity.lastName - user.pubkey = userEntity.pubKey.toString('hex') - user.language = userEntity.language - + const user = new User(userEntity) + // user.pubkey = userEntity.pubKey.toString('hex') // Elopage Status & Stored PublisherId user.hasElopage = await this.hasElopage(context) @@ -271,12 +265,9 @@ export class UserResolver { throw new Error('No user with this credentials') } - const user = new User() - user.id = dbUser.id - user.email = email - user.firstName = dbUser.firstName - user.lastName = dbUser.lastName - user.pubkey = dbUser.pubKey.toString('hex') + const user = new User(dbUser) + // user.email = email + // user.pubkey = dbUser.pubKey.toString('hex') user.language = dbUser.language // Elopage Status & Stored PublisherId diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts new file mode 100644 index 000000000..5dfec9327 --- /dev/null +++ b/backend/src/util/communityUser.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' +import { User as dbUser } from '@entity/User' +import { User } from '../graphql/model/User' + +const communityDbUser: dbUser = { + id: -1, + email: 'support@gradido.net', + firstName: 'Gradido', + lastName: 'Akademie', + pubKey: Buffer.from(''), + privKey: Buffer.from(''), + deletedAt: null, + password: BigInt(0), + emailHash: Buffer.from(''), + createdAt: new Date(), + emailChecked: false, + language: '', + publisherId: 0, + passphrase: '', + settings: [], + hasId: function (): boolean { + throw new Error('Function not implemented.') + }, + save: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + remove: function (options?: RemoveOptions): Promise { + throw new Error('Function not implemented.') + }, + softRemove: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + recover: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + reload: function (): Promise { + throw new Error('Function not implemented.') + }, +} +const communityUser = new User(communityDbUser) + +export { communityDbUser, communityUser } diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index 21ecfa151..f0f1d181a 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -1,14 +1,8 @@ import Decimal from 'decimal.js-light' import CONFIG from '../config' +import { Decay } from '../graphql/model/Decay' // TODO: externalize all those definitions and functions into an external decay library -interface Decay { - balance: Decimal - decay: Decimal | null - start: Date | null - end: Date | null - duration: number | null -} function decayFormula(value: Decimal, seconds: number): Decimal { return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds)) diff --git a/backend/src/util/virtualDecayTransaction.ts b/backend/src/util/virtualDecayTransaction.ts new file mode 100644 index 000000000..f5f35823f --- /dev/null +++ b/backend/src/util/virtualDecayTransaction.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import Decimal from 'decimal.js-light' +import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' +import { Transaction as dbTransaction } from '@entity/Transaction' +import { calculateDecay } from './decay' +import { TransactionTypeId } from '../graphql/enum/TransactionTypeId' +import { Transaction } from '../graphql/model/Transaction' +import { User } from '../graphql/model/User' + +const virtualDecayTransaction = ( + balance: Decimal, + balanceDate: Date, + time: Date = new Date(), + user: User, +): Transaction => { + const decay = calculateDecay(balance, balanceDate, time) + // const balance = decay.balance.minus(lastTransaction.balance) + const decayDbTransaction: dbTransaction = { + id: -1, + userId: -1, + previous: -1, + typeId: TransactionTypeId.DECAY, + amount: new Decimal(0), + balance: decay.balance, + balanceDate: time, + decay: decay.decay ? decay.decay : new Decimal(0), + decayStart: decay.start, + memo: '', + creationDate: null, + hasId: function (): boolean { + throw new Error('Function not implemented.') + }, + save: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + remove: function (options?: RemoveOptions): Promise { + throw new Error('Function not implemented.') + }, + softRemove: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + recover: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + reload: function (): Promise { + throw new Error('Function not implemented.') + }, + } + return new Transaction(decayDbTransaction, user) +} + +export { virtualDecayTransaction } From 31c5f0234453f18c7543cac8b313551a3da37576 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 17:19:59 +0100 Subject: [PATCH 35/88] lint fixes --- backend/src/graphql/resolver/TransactionResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 3e84646dd..d0a2a19d3 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -24,7 +24,6 @@ import { User as dbUser } from '@entity/User' import { Transaction as dbTransaction } from '@entity/Transaction' import { apiPost } from '../../apis/HttpRequest' -import { calculateDecay } from '../../util/decay' import { TransactionTypeId } from '../enum/TransactionTypeId' import { calculateBalance, isHexPublicKey } from '../../util/validate' import { RIGHTS } from '../../auth/RIGHTS' From b2288e1adc9b468ccf314ae6cb495cf7c975619b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 17:21:00 +0100 Subject: [PATCH 36/88] missing comment --- backend/src/graphql/resolver/TransactionResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index d0a2a19d3..969597312 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -106,6 +106,7 @@ export class TransactionResolver { ) } + // transactions for (let i = 0; i < userTransactions.length; i++) { const userTransaction = userTransactions[i] let linkedUser = null From 85b9fb9b86e582a69d884ebeb62759308790c63b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 26 Feb 2022 17:29:15 +0100 Subject: [PATCH 37/88] updated required database version --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 2c4337f00..f357f5027 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -12,7 +12,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0027-decimal_types', + DB_VERSION: '0028-clean_transaction_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } From 67cddadd1e11b03d737529a2d3a57b560e637acf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 02:07:33 +0100 Subject: [PATCH 38/88] graphql recognizes decimal type now and successfully starts --- backend/src/graphql/scalar/Decimal.ts | 26 ++++++++++++++++++++++++++ backend/src/graphql/schema.ts | 3 +++ 2 files changed, 29 insertions(+) create mode 100644 backend/src/graphql/scalar/Decimal.ts diff --git a/backend/src/graphql/scalar/Decimal.ts b/backend/src/graphql/scalar/Decimal.ts new file mode 100644 index 000000000..001596fb3 --- /dev/null +++ b/backend/src/graphql/scalar/Decimal.ts @@ -0,0 +1,26 @@ +import { GraphQLScalarType, Kind } from 'graphql' +import Decimal from 'decimal.js-light' + +export default new GraphQLScalarType({ + name: 'Decimal', + description: 'The `Decimal` scalar type to represent currency values', + + serialize(value: unknown) { + if (!(value instanceof Decimal)) { + throw new Error('DecimalScalar can only serialize Decimal values') + } + return value.toString() + }, + + parseValue(value) { + return new Decimal(value) + }, + + parseLiteral(ast) { + if (ast.kind !== Kind.STRING) { + throw new TypeError(`${String(ast)} is not a valid decimal value.`) + } + + return new Decimal(ast.value) + }, +}) diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index 02caf2784..97bd3b632 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -1,13 +1,16 @@ import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' +import Decimal from 'decimal.js-light' import path from 'path' import isAuthorized from './directive/isAuthorized' +import DecimalScalar from './scalar/Decimal' const schema = async (): Promise => { return buildSchema({ resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)], authChecker: isAuthorized, + scalarsMap: [{ type: Decimal, scalar: DecimalScalar }], }) } From 9c27568f77c5883dec94b9838d1a8315bdb07014 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:09:18 +0100 Subject: [PATCH 39/88] include decimal with precision definition --- backend/src/config/index.ts | 8 -------- backend/src/graphql/arg/TransactionSendArgs.ts | 2 +- backend/src/graphql/model/Balance.ts | 2 +- backend/src/graphql/model/Decay.ts | 2 +- backend/src/graphql/model/Transaction.ts | 2 +- backend/src/graphql/model/TransactionList.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 2 +- backend/src/graphql/resolver/BalanceResolver.ts | 2 +- backend/src/graphql/schema.ts | 2 +- backend/src/mailer/sendTransactionReceivedEmail.test.ts | 2 +- backend/src/mailer/sendTransactionReceivedEmail.ts | 2 +- backend/src/mailer/text/transactionReceived.ts | 2 +- backend/src/util/decay.test.ts | 2 +- backend/src/util/validate.ts | 2 +- backend/src/util/virtualDecayTransaction.ts | 2 +- 15 files changed, 14 insertions(+), 22 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index f357f5027..75c985e63 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,16 +1,8 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) import dotenv from 'dotenv' -import Decimal from 'decimal.js-light' dotenv.config() -// Set precision value -// TODO test if this works here -Decimal.set({ - precision: 25, - rounding: Decimal.ROUND_HALF_UP, -}) - const constants = { DB_VERSION: '0028-clean_transaction_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index e75921383..49a3fb5e8 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -1,5 +1,5 @@ import { ArgsType, Field } from 'type-graphql' -import Decimal from 'decimal.js-light' +import Decimal from '../../util/decimal' @ArgsType() export default class TransactionSendArgs { diff --git a/backend/src/graphql/model/Balance.ts b/backend/src/graphql/model/Balance.ts index 7a3891ece..7d2be38c4 100644 --- a/backend/src/graphql/model/Balance.ts +++ b/backend/src/graphql/model/Balance.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' +import Decimal from '../../util/decimal' @ObjectType() export class Balance { diff --git a/backend/src/graphql/model/Decay.ts b/backend/src/graphql/model/Decay.ts index a76e66939..ab70ab6f8 100644 --- a/backend/src/graphql/model/Decay.ts +++ b/backend/src/graphql/model/Decay.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' +import Decimal from '../../util/decimal' @ObjectType() export class Decay { diff --git a/backend/src/graphql/model/Transaction.ts b/backend/src/graphql/model/Transaction.ts index 4c8dbbe38..4d19479c7 100644 --- a/backend/src/graphql/model/Transaction.ts +++ b/backend/src/graphql/model/Transaction.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' import { Decay } from './Decay' import { Transaction as dbTransaction } from '@entity/Transaction' +import Decimal from '../../util/decimal' import { TransactionTypeId } from '../enum/TransactionTypeId' import { User } from './User' diff --git a/backend/src/graphql/model/TransactionList.ts b/backend/src/graphql/model/TransactionList.ts index 470eca7ef..91beb01ac 100644 --- a/backend/src/graphql/model/TransactionList.ts +++ b/backend/src/graphql/model/TransactionList.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import Decimal from 'decimal.js-light' import { ObjectType, Field } from 'type-graphql' import CONFIG from '../../config' +import Decimal from '../../util/decimal' import { Transaction } from './Transaction' @ObjectType() diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 1ee7f326c..b1b68e521 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -27,7 +27,7 @@ import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' import { TransactionTypeId } from '../enum/TransactionTypeId' -import Decimal from 'decimal.js-light' +import Decimal from '../../util/decimal' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index e536093bb..1680ba8d7 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -8,7 +8,7 @@ import { UserRepository } from '../../typeorm/repository/User' import { calculateDecay } from '../../util/decay' import { RIGHTS } from '../../auth/RIGHTS' import { Transaction } from '@entity/Transaction' -import Decimal from 'decimal.js-light' +import Decimal from '../../util/decimal' @Resolver() export class BalanceResolver { diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index 97bd3b632..54ee89c42 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -1,10 +1,10 @@ import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' -import Decimal from 'decimal.js-light' import path from 'path' import isAuthorized from './directive/isAuthorized' import DecimalScalar from './scalar/Decimal' +import Decimal from '../util/decimal' const schema = async (): Promise => { return buildSchema({ diff --git a/backend/src/mailer/sendTransactionReceivedEmail.test.ts b/backend/src/mailer/sendTransactionReceivedEmail.test.ts index 5fd013650..ce1d50920 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.test.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.test.ts @@ -1,6 +1,6 @@ import { sendTransactionReceivedEmail } from './sendTransactionReceivedEmail' import { sendEMail } from './sendEMail' -import Decimal from 'decimal.js-light' +import Decimal from '../util/decimal' jest.mock('./sendEMail', () => { return { diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 3b417b10a..d4a97eab7 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,4 +1,4 @@ -import Decimal from 'decimal.js-light' +import Decimal from '../util/decimal' import { sendEMail } from './sendEMail' import { transactionReceived } from './text/transactionReceived' diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index f685c60ae..747e30bdf 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -1,4 +1,4 @@ -import Decimal from 'decimal.js-light' +import Decimal from '../../util/decimal' export const transactionReceived = { de: { diff --git a/backend/src/util/decay.test.ts b/backend/src/util/decay.test.ts index 1653376c1..31d8905b2 100644 --- a/backend/src/util/decay.test.ts +++ b/backend/src/util/decay.test.ts @@ -1,4 +1,4 @@ -import Decimal from 'decimal.js-light' +import Decimal from './decimal' import 'reflect-metadata' // This might be wise to load in a test setup file import { decayFormula, calculateDecay } from './decay' diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index dc3aadbec..2f9017447 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,5 +1,5 @@ import { calculateDecay } from './decay' -import Decimal from 'decimal.js-light' +import Decimal from './decimal' import { Transaction } from '@entity/Transaction' function isStringBoolean(value: string): boolean { diff --git a/backend/src/util/virtualDecayTransaction.ts b/backend/src/util/virtualDecayTransaction.ts index f5f35823f..c6b7391f1 100644 --- a/backend/src/util/virtualDecayTransaction.ts +++ b/backend/src/util/virtualDecayTransaction.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import Decimal from 'decimal.js-light' +import Decimal from './decimal' import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' import { Transaction as dbTransaction } from '@entity/Transaction' import { calculateDecay } from './decay' From 003090ed6cd1ced4742783394c1903b281192340 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:09:30 +0100 Subject: [PATCH 40/88] fix user query --- backend/src/graphql/resolver/TransactionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 969597312..6f774a389 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -91,7 +91,7 @@ export class TransactionResolver { const involvedDbUsers = await dbUser .createQueryBuilder() .withDeleted() - .where('user.id IN (:...userIds)', { involvedUserIds }) + .where('id IN (:...userIds)', { userIds: involvedUserIds }) .getMany() const involvedUsers = involvedDbUsers.map((u) => new User(u)) From 2c77785b77d61b2163b94c1658e254ffa796fef6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:09:53 +0100 Subject: [PATCH 41/88] instanceof not working with Decimal --- backend/src/graphql/scalar/Decimal.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/scalar/Decimal.ts b/backend/src/graphql/scalar/Decimal.ts index 001596fb3..d10d36337 100644 --- a/backend/src/graphql/scalar/Decimal.ts +++ b/backend/src/graphql/scalar/Decimal.ts @@ -1,14 +1,11 @@ import { GraphQLScalarType, Kind } from 'graphql' -import Decimal from 'decimal.js-light' +import Decimal from '../../util/decimal' export default new GraphQLScalarType({ name: 'Decimal', description: 'The `Decimal` scalar type to represent currency values', - serialize(value: unknown) { - if (!(value instanceof Decimal)) { - throw new Error('DecimalScalar can only serialize Decimal values') - } + serialize(value: Decimal) { return value.toString() }, From 371d7921fefa2cec53bd90a056dcda8720acfd2c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:12:07 +0100 Subject: [PATCH 42/88] the decay values seems to have problems - maybe because its out of scope of numeric. Use string instead --- backend/src/util/decay.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index f0f1d181a..49317ad85 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -1,11 +1,14 @@ -import Decimal from 'decimal.js-light' +import Decimal from './decimal' import CONFIG from '../config' import { Decay } from '../graphql/model/Decay' // TODO: externalize all those definitions and functions into an external decay library function decayFormula(value: Decimal, seconds: number): Decimal { - return value.mul(new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds)) + // TODO why do we need to convert this here to a stting to work properly? + return value.mul( + new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(), + ) } function calculateDecay( From 50ef39d1a42e23b08bca5c10ccd834d2ea8dbfaf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:12:25 +0100 Subject: [PATCH 43/88] fixed transaction query in frontend --- frontend/src/graphql/queries.js | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index ff211fc07..c833fb845 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -55,28 +55,25 @@ export const transactionsQuery = gql` order: $order onlyCreations: $onlyCreations ) { - gdtSum + balanceGDT count balance - decay - decayDate + decayStartBlock transactions { - type - balance - decayStart - decayEnd - decayDuration + id + typeId + amount + balanceDate memo - transactionId - name - email - date + linkedUser { + firstName + lastName + } decay { - balance - decayStart - decayEnd - decayDuration - decayStartBlock + decay + start + end + duration } } } From 65861fded884e458b70851b7bc9a3db46eb28b71 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:45:36 +0100 Subject: [PATCH 44/88] have some things working in frontend --- frontend/src/components/DecayInformation.vue | 14 +++-- .../src/views/Layout/DashboardLayout_gdd.vue | 5 +- .../AccountOverview/GddTransactionList.vue | 52 ++++++++++--------- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/frontend/src/components/DecayInformation.vue b/frontend/src/components/DecayInformation.vue index 0daa45a18..f092d90dd 100644 --- a/frontend/src/components/DecayInformation.vue +++ b/frontend/src/components/DecayInformation.vue @@ -22,8 +22,8 @@
{{ $t('decay.decay_introduced') }} :
- - {{ $d(new Date(decay.decayStart * 1000), 'long') }} + + {{ $d(new Date(decay.start), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -109,20 +109,18 @@ export default { name: 'DecayInformation', props: { - balance: { type: Number }, type: { type: String, default: '' }, decay: { balance: '', - decayDuration: '', - decayStart: 0, - decayEnd: 0, - decayStartBlock: 0, + duration: '', + start: 0, + end: 0, }, decaytyp: { type: String, default: '' }, }, computed: { duration() { - return this.$moment.duration((this.decay.decayEnd - this.decay.decayStart) * 1000)._data + return this.$moment.duration(new Date(this.decay.end) - new Date(this.decay.start))._data }, }, } diff --git a/frontend/src/views/Layout/DashboardLayout_gdd.vue b/frontend/src/views/Layout/DashboardLayout_gdd.vue index a473469b6..a1771997e 100755 --- a/frontend/src/views/Layout/DashboardLayout_gdd.vue +++ b/frontend/src/views/Layout/DashboardLayout_gdd.vue @@ -92,9 +92,10 @@ export default { const { data: { transactionList }, } = result - this.GdtBalance = transactionList.gdtSum === null ? null : Number(transactionList.gdtSum) + this.GdtBalance = + transactionList.balanceGDT === null ? null : Number(transactionList.balanceGDT) this.transactions = transactionList.transactions - this.balance = Number(transactionList.decay) + this.balance = Number(transactionList.balance) this.bookedBalance = Number(transactionList.balance) this.transactionCount = transactionList.count this.pending = false diff --git a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue index 4e2df58d1..ad3480e96 100644 --- a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue +++ b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue @@ -12,17 +12,22 @@ {{ $t('error.empty-transactionlist') }}
-
+
@@ -36,7 +41,7 @@
- +
@@ -46,22 +51,22 @@
- {{ getProperties(type).operator }} + {{ getProperties(typeId).operator }} - {{ $n(balance, 'decimal') }} + {{ $n(Math.abs(Number(amount)), 'decimal') }}
- {{ type !== 'decay' ? name : $t('decay.decay_since_last_transaction') }} + {{ typeId !== 'DECAY' ? `${linkedUser.firstName} ${linkedUser.lastName}` : $t('decay.decay_since_last_transaction') }}
- +
{{ $t('form.memo') }}
@@ -71,13 +76,13 @@
- +
{{ $t('form.date') }}
- {{ $d(new Date(date), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }} + {{ $d(new Date(balanceDate), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -86,7 +91,7 @@
- +
@@ -98,7 +103,7 @@
- +
@@ -113,14 +118,13 @@ - +
@@ -146,10 +150,10 @@ import PaginationButtons from '../../../components/PaginationButtons' import DecayInformation from '../../../components/DecayInformation' const iconsByType = { - send: { icon: 'arrow-left-circle', classes: 'text-danger', operator: '−' }, - receive: { icon: 'arrow-right-circle', classes: 'gradido-global-color-accent', operator: '+' }, - creation: { icon: 'gift', classes: 'gradido-global-color-accent', operator: '+' }, - decay: { icon: 'droplet-half', classes: 'gradido-global-color-gray', operator: '−' }, + SEND: { icon: 'arrow-left-circle', classes: 'text-danger', operator: '−' }, + RECEIVE: { icon: 'arrow-right-circle', classes: 'gradido-global-color-accent', operator: '+' }, + CREATION: { icon: 'gift', classes: 'gradido-global-color-accent', operator: '+' }, + DECAY: { icon: 'droplet-half', classes: 'gradido-global-color-gray', operator: '−' }, } export default { From 64c01e8feee26768080cfa60fbce6b967acb2c6d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 03:50:59 +0100 Subject: [PATCH 45/88] missing file --- backend/src/util/decimal.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 backend/src/util/decimal.ts diff --git a/backend/src/util/decimal.ts b/backend/src/util/decimal.ts new file mode 100644 index 000000000..3f32a850e --- /dev/null +++ b/backend/src/util/decimal.ts @@ -0,0 +1,8 @@ +import Decimal from 'decimal.js-light' + +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) + +export default Decimal From 1dd692397b0887bfb37384e4068119e08b3fe7df Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 04:53:29 +0100 Subject: [PATCH 46/88] solve precision varriance --- database/src/typeorm/DecimalTransformer.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/database/src/typeorm/DecimalTransformer.ts b/database/src/typeorm/DecimalTransformer.ts index 88a3539ad..92efaf3bc 100644 --- a/database/src/typeorm/DecimalTransformer.ts +++ b/database/src/typeorm/DecimalTransformer.ts @@ -1,6 +1,11 @@ import Decimal from 'decimal.js-light' import { ValueTransformer } from 'typeorm' +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) + export const DecimalTransformer: ValueTransformer = { /** * Used to marshal Decimal when writing to the database. From e988cf9371046f7ccc3a901ef0f44be1ddd75b1f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 13:05:27 +0100 Subject: [PATCH 47/88] defining precision in config seems to work properly afterall --- backend/src/config/index.ts | 6 ++++++ backend/src/graphql/arg/TransactionSendArgs.ts | 2 +- backend/src/graphql/model/Balance.ts | 2 +- backend/src/graphql/model/Decay.ts | 2 +- backend/src/graphql/model/Transaction.ts | 2 +- backend/src/graphql/model/TransactionList.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 2 +- backend/src/graphql/resolver/BalanceResolver.ts | 2 +- backend/src/graphql/scalar/Decimal.ts | 2 +- backend/src/graphql/schema.ts | 2 +- backend/src/mailer/sendTransactionReceivedEmail.test.ts | 2 +- backend/src/mailer/sendTransactionReceivedEmail.ts | 2 +- backend/src/mailer/text/transactionReceived.ts | 2 +- backend/src/util/decay.test.ts | 2 +- backend/src/util/decay.ts | 2 +- backend/src/util/decimal.ts | 8 -------- backend/src/util/validate.ts | 2 +- backend/src/util/virtualDecayTransaction.ts | 2 +- 18 files changed, 22 insertions(+), 24 deletions(-) delete mode 100644 backend/src/util/decimal.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 75c985e63..ed8a893a2 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,8 +1,14 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) import dotenv from 'dotenv' +import Decimal from 'decimal.js-light' dotenv.config() +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) + const constants = { DB_VERSION: '0028-clean_transaction_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 49a3fb5e8..e75921383 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -1,5 +1,5 @@ import { ArgsType, Field } from 'type-graphql' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' @ArgsType() export default class TransactionSendArgs { diff --git a/backend/src/graphql/model/Balance.ts b/backend/src/graphql/model/Balance.ts index 7d2be38c4..aaeecd0d7 100644 --- a/backend/src/graphql/model/Balance.ts +++ b/backend/src/graphql/model/Balance.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { ObjectType, Field } from 'type-graphql' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' @ObjectType() export class Balance { diff --git a/backend/src/graphql/model/Decay.ts b/backend/src/graphql/model/Decay.ts index ab70ab6f8..71cacb216 100644 --- a/backend/src/graphql/model/Decay.ts +++ b/backend/src/graphql/model/Decay.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { ObjectType, Field } from 'type-graphql' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' @ObjectType() export class Decay { diff --git a/backend/src/graphql/model/Transaction.ts b/backend/src/graphql/model/Transaction.ts index 4d19479c7..4c9caed4e 100644 --- a/backend/src/graphql/model/Transaction.ts +++ b/backend/src/graphql/model/Transaction.ts @@ -3,7 +3,7 @@ import { ObjectType, Field } from 'type-graphql' import { Decay } from './Decay' import { Transaction as dbTransaction } from '@entity/Transaction' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' import { TransactionTypeId } from '../enum/TransactionTypeId' import { User } from './User' diff --git a/backend/src/graphql/model/TransactionList.ts b/backend/src/graphql/model/TransactionList.ts index 91beb01ac..d4fcb65eb 100644 --- a/backend/src/graphql/model/TransactionList.ts +++ b/backend/src/graphql/model/TransactionList.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { ObjectType, Field } from 'type-graphql' import CONFIG from '../../config' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' import { Transaction } from './Transaction' @ObjectType() diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index b1b68e521..1ee7f326c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -27,7 +27,7 @@ import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' import { TransactionTypeId } from '../enum/TransactionTypeId' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index 1680ba8d7..e536093bb 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -8,7 +8,7 @@ import { UserRepository } from '../../typeorm/repository/User' import { calculateDecay } from '../../util/decay' import { RIGHTS } from '../../auth/RIGHTS' import { Transaction } from '@entity/Transaction' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' @Resolver() export class BalanceResolver { diff --git a/backend/src/graphql/scalar/Decimal.ts b/backend/src/graphql/scalar/Decimal.ts index d10d36337..da5a99e0c 100644 --- a/backend/src/graphql/scalar/Decimal.ts +++ b/backend/src/graphql/scalar/Decimal.ts @@ -1,5 +1,5 @@ import { GraphQLScalarType, Kind } from 'graphql' -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' export default new GraphQLScalarType({ name: 'Decimal', diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index 54ee89c42..f14f45efa 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -4,7 +4,7 @@ import path from 'path' import isAuthorized from './directive/isAuthorized' import DecimalScalar from './scalar/Decimal' -import Decimal from '../util/decimal' +import Decimal from 'decimal.js-light' const schema = async (): Promise => { return buildSchema({ diff --git a/backend/src/mailer/sendTransactionReceivedEmail.test.ts b/backend/src/mailer/sendTransactionReceivedEmail.test.ts index ce1d50920..5fd013650 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.test.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.test.ts @@ -1,6 +1,6 @@ import { sendTransactionReceivedEmail } from './sendTransactionReceivedEmail' import { sendEMail } from './sendEMail' -import Decimal from '../util/decimal' +import Decimal from 'decimal.js-light' jest.mock('./sendEMail', () => { return { diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index d4a97eab7..3b417b10a 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,4 +1,4 @@ -import Decimal from '../util/decimal' +import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' import { transactionReceived } from './text/transactionReceived' diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 747e30bdf..f685c60ae 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -1,4 +1,4 @@ -import Decimal from '../../util/decimal' +import Decimal from 'decimal.js-light' export const transactionReceived = { de: { diff --git a/backend/src/util/decay.test.ts b/backend/src/util/decay.test.ts index 31d8905b2..1653376c1 100644 --- a/backend/src/util/decay.test.ts +++ b/backend/src/util/decay.test.ts @@ -1,4 +1,4 @@ -import Decimal from './decimal' +import Decimal from 'decimal.js-light' import 'reflect-metadata' // This might be wise to load in a test setup file import { decayFormula, calculateDecay } from './decay' diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index 49317ad85..061269dab 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -1,4 +1,4 @@ -import Decimal from './decimal' +import Decimal from 'decimal.js-light' import CONFIG from '../config' import { Decay } from '../graphql/model/Decay' diff --git a/backend/src/util/decimal.ts b/backend/src/util/decimal.ts deleted file mode 100644 index 3f32a850e..000000000 --- a/backend/src/util/decimal.ts +++ /dev/null @@ -1,8 +0,0 @@ -import Decimal from 'decimal.js-light' - -Decimal.set({ - precision: 25, - rounding: Decimal.ROUND_HALF_UP, -}) - -export default Decimal diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index 2f9017447..dc3aadbec 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,5 +1,5 @@ import { calculateDecay } from './decay' -import Decimal from './decimal' +import Decimal from 'decimal.js-light' import { Transaction } from '@entity/Transaction' function isStringBoolean(value: string): boolean { diff --git a/backend/src/util/virtualDecayTransaction.ts b/backend/src/util/virtualDecayTransaction.ts index c6b7391f1..f5f35823f 100644 --- a/backend/src/util/virtualDecayTransaction.ts +++ b/backend/src/util/virtualDecayTransaction.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import Decimal from './decimal' +import Decimal from 'decimal.js-light' import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' import { Transaction as dbTransaction } from '@entity/Transaction' import { calculateDecay } from './decay' From 81b7a4915d47f92d1bd1cd8ba90be05f3662e37f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 13:08:44 +0100 Subject: [PATCH 48/88] fix frontend decay --- backend/src/util/virtualDecayTransaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/util/virtualDecayTransaction.ts b/backend/src/util/virtualDecayTransaction.ts index f5f35823f..df3482716 100644 --- a/backend/src/util/virtualDecayTransaction.ts +++ b/backend/src/util/virtualDecayTransaction.ts @@ -20,7 +20,7 @@ const virtualDecayTransaction = ( userId: -1, previous: -1, typeId: TransactionTypeId.DECAY, - amount: new Decimal(0), + amount: decay.decay ? decay.decay : new Decimal(0), // new Decimal(0), // this kinda is wrong, but helps with the frontend query balance: decay.balance, balanceDate: time, decay: decay.decay ? decay.decay : new Decimal(0), From 08bc5188810c8c3df796ac510291b7f27a9316ff Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 13:14:55 +0100 Subject: [PATCH 49/88] lint fixes --- .../AccountOverview/GddTransactionList.vue | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue index ad3480e96..881bb1b19 100644 --- a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue +++ b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue @@ -12,15 +12,7 @@ {{ $t('error.empty-transactionlist') }}
@@ -60,7 +52,11 @@
- {{ typeId !== 'DECAY' ? `${linkedUser.firstName} ${linkedUser.lastName}` : $t('decay.decay_since_last_transaction') }} + {{ + typeId !== 'DECAY' + ? `${linkedUser.firstName} ${linkedUser.lastName}` + : $t('decay.decay_since_last_transaction') + }}
@@ -82,7 +78,8 @@
- {{ $d(new Date(balanceDate), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }} + {{ $d(new Date(balanceDate), 'long') }} + {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -91,7 +88,12 @@
- +
@@ -103,7 +105,12 @@
- +
@@ -120,12 +127,7 @@
- +
From c4b6f0cc1823cdd2da975eef41fb50949a1f8907 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 27 Feb 2022 13:25:25 +0100 Subject: [PATCH 50/88] work on the frontend to be working again --- frontend/src/components/DecayInformation.vue | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/DecayInformation.vue b/frontend/src/components/DecayInformation.vue index f092d90dd..76e36c468 100644 --- a/frontend/src/components/DecayInformation.vue +++ b/frontend/src/components/DecayInformation.vue @@ -1,7 +1,7 @@