From 48b18bc6be1d030d8d7a48b91d0db67cf74f356f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 12:27:54 +0100 Subject: [PATCH 01/28] corrected comment --- database/migrations/0024-combine_transaction_tables.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/migrations/0024-combine_transaction_tables.ts b/database/migrations/0024-combine_transaction_tables.ts index b0595aa03..5b8ec8be8 100644 --- a/database/migrations/0024-combine_transaction_tables.ts +++ b/database/migrations/0024-combine_transaction_tables.ts @@ -1,6 +1,6 @@ -/* MIGRATION TO COMBINE ALL TRANSACTION TABLES +/* MIGRATION TO COMBINE SEVERAL TRANSACTION TABLES * - * Combine all transaction tables into one table with all data + * Combine several transaction tables into one table with all data */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ From f55e9f49b7ef388f09979d9aad0b0792930cc0f4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 15:00:34 +0100 Subject: [PATCH 02/28] migrate the database state_user_transactions + transactions -> transactions --- .../0025-combine_transaction_tables2.ts | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 database/migrations/0025-combine_transaction_tables2.ts diff --git a/database/migrations/0025-combine_transaction_tables2.ts b/database/migrations/0025-combine_transaction_tables2.ts new file mode 100644 index 000000000..3abf77354 --- /dev/null +++ b/database/migrations/0025-combine_transaction_tables2.ts @@ -0,0 +1,218 @@ +/* MIGRATION TO COMBINE AND REFACTOR SOME TRANSACTION TABLES + * + * Combine `state_user_transactions` and `transactions` tables. + * This changes the structure of transactions from 1 transaction for + * each send-coins to two transactions per send-coin + */ + +/* 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>) { + /* + * This migration has a possible incompatibility + * due to the construction of the tables. + * For our production data it works well. + * With this migration we decide for int instead of bigint + * to handle things more easily + * + * CREATE TABLE `transactions` ( + * `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + * ... + * ) + * CREATE TABLE `state_user_transactions` ( + * ... + * `transaction_id` int(10) unsigned NOT NULL, + * ... + * ) + */ + + // rename `state_user_id` to `user_id` + await queryFn('ALTER TABLE `state_user_transactions` RENAME COLUMN state_user_id TO user_id;') + // Create new `amount` column, with a temporary default of null + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER `transaction_type_id`;', + ) + // Create new `send_sender_final_balance` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL AFTER `amount`;', + ) + // Create new `memo` column, with a temporary default of null + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `memo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `amount`;', + ) + // Create new `received` column, with a temporary default of null + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `received` timestamp NULL DEFAULT NULL AFTER `balance_date`;', + ) + // Create new `creation_date` column + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `creation_date` timestamp NULL DEFAULT NULL AFTER `received`;', + ) + // Create new `linked_user_id` column (former `send_receiver_user_id`) + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `linked_user_id` int(10) unsigned DEFAULT NULL AFTER `creation_date`;', + ) + // Create new `linked_state_user_transaction_id` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `linked_state_user_transaction_id` int(10) unsigned DEFAULT NULL AFTER `linked_user_id`;', + ) + // Create new `tx_hash` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `tx_hash` binary(48) DEFAULT NULL AFTER `linked_state_user_transaction_id`;', + ) + // Create new `signature` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `signature` binary(64) DEFAULT NULL AFTER `tx_hash`;', + ) + // Create new `pubkey` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `pubkey` binary(32) DEFAULT NULL AFTER `signature`;', + ) + // Create new `creation_ident_hash` + await queryFn( + 'ALTER TABLE `state_user_transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `pubkey`;', + ) + + // Insert Data from `transactions` for creations + await queryFn(` + UPDATE state_user_transactions + LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id + SET state_user_transactions.amount = transactions.amount, + state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance, + state_user_transactions.memo = transactions.memo, + state_user_transactions.received = transactions.received, + state_user_transactions.creation_date = transactions.creation_date, + state_user_transactions.linked_user_id = transactions.send_receiver_user_id, + state_user_transactions.linked_state_user_transaction_id = NULL, + state_user_transactions.tx_hash = transactions.tx_hash, + state_user_transactions.signature = transactions.signature, + state_user_transactions.pubkey = transactions.pubkey, + state_user_transactions.creation_ident_hash = transactions.creation_ident_hash + WHERE state_user_transactions.transaction_type_id = 1; + `) + + // Insert Data from `transactions` for sendCoin sender + await queryFn(` + UPDATE state_user_transactions + LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id + SET state_user_transactions.amount = transactions.amount, + state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance, + state_user_transactions.memo = transactions.memo, + state_user_transactions.received = transactions.received, + state_user_transactions.creation_date = transactions.creation_date, + state_user_transactions.linked_user_id = transactions.send_receiver_user_id, + state_user_transactions.linked_state_user_transaction_id = ( + SELECT id FROM state_user_transactions AS sut + WHERE sut.transaction_type_id = 2 + AND sut.transaction_id = state_user_transactions.transaction_id + AND sut.user_id = transactions.send_receiver_user_id + ), + state_user_transactions.tx_hash = transactions.tx_hash, + state_user_transactions.signature = transactions.signature, + state_user_transactions.pubkey = transactions.pubkey, + state_user_transactions.creation_ident_hash = transactions.creation_ident_hash + WHERE state_user_transactions.transaction_type_id = 2 + AND state_user_transactions.user_id = transactions.user_id; + `) + + // Insert Data from `transactions` for sendCoin receiver + await queryFn(` + UPDATE state_user_transactions + LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id + SET state_user_transactions.amount = transactions.amount, + state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance, + state_user_transactions.memo = transactions.memo, + state_user_transactions.received = transactions.received, + state_user_transactions.creation_date = transactions.creation_date, + state_user_transactions.linked_user_id = transactions.user_id, + state_user_transactions.linked_state_user_transaction_id = ( + SELECT id FROM state_user_transactions AS sut + WHERE sut.transaction_type_id = 2 + AND sut.transaction_id = state_user_transactions.transaction_id + AND sut.user_id = transactions.user_id + ), + state_user_transactions.tx_hash = transactions.tx_hash, + state_user_transactions.signature = transactions.signature, + state_user_transactions.pubkey = transactions.send_receiver_public_key, + state_user_transactions.creation_ident_hash = transactions.creation_ident_hash, + state_user_transactions.transaction_type_id = 3 + WHERE state_user_transactions.transaction_type_id = 2 + AND state_user_transactions.user_id = transactions.send_receiver_user_id; + `) + + // Modify defaults after our inserts + await queryFn('ALTER TABLE `state_user_transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL;') + await queryFn( + 'ALTER TABLE `state_user_transactions` MODIFY COLUMN `memo` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL', + ) + await queryFn( + 'ALTER TABLE `state_user_transactions` MODIFY COLUMN `received` timestamp NOT NULL DEFAULT current_timestamp()', + ) + + // Drop table `transactions` + await queryFn('DROP TABLE `transactions`;') + + // Rename table `transaction_send_coins` to `transactions` + await queryFn('RENAME TABLE `state_user_transactions` TO `transactions`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('RENAME TABLE `transactions` TO `state_user_transactions`;') + await queryFn(`CREATE TABLE \`transactions\` ( + \`id\` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + \`transaction_type_id\` int(10) unsigned NOT NULL, + \`user_id\` int(10) unsigned NOT NULL, + \`amount\` bigint(20) NOT NULL, + \`tx_hash\` binary(48) DEFAULT NULL, + \`memo\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + \`received\` timestamp NOT NULL DEFAULT current_timestamp(), + \`signature\` binary(64) DEFAULT NULL, + \`pubkey\` binary(32) DEFAULT NULL, + \`creation_ident_hash\` binary(32) DEFAULT NULL, + \`creation_date\` timestamp NULL DEFAULT NULL, + \`send_receiver_public_key\` binary(32) DEFAULT NULL, + \`send_receiver_user_id\` int(10) unsigned DEFAULT NULL, + \`send_sender_final_balance\` bigint(20) DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=3424 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `) + await queryFn(` + INSERT INTO transactions ( + id, transaction_type_id, user_id, amount, + tx_hash, memo, received, signature, pubkey, + creation_ident_hash, creation_date, + send_receiver_public_key, send_receiver_user_id, + send_sender_final_balance + ) + SELECT transaction_id AS id, transaction_type_id, + user_id, amount, tx_hash, memo, received, + signature, pubkey, creation_ident_hash, + creation_date, send_receiver_public_key, + linked_user_id AS send_receiver_user_id, + send_sender_final_balance + FROM state_user_transactions LEFT JOIN ( + SELECT id, pubkey AS send_receiver_public_key + FROM state_user_transactions AS sut + WHERE sut.transaction_type_id = 3 + ) AS sutj ON sutj.id = state_user_transactions.id + WHERE transaction_type_id IN (1,2) + `) + await queryFn( + 'UPDATE state_user_transactions SET transaction_type_id = 2 WHERE transaction_type_id = 3;', + ) + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `creation_ident_hash`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `pubkey`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `signature`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `tx_hash`;') + await queryFn( + 'ALTER TABLE `state_user_transactions` DROP COLUMN `linked_state_user_transaction_id`;', + ) + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `linked_user_id`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `creation_date`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `received`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `memo`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `send_sender_final_balance`;') + await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `amount`;') + await queryFn('ALTER TABLE `state_user_transactions` RENAME COLUMN user_id TO state_user_id;') +} From ab1df1c35d7d47a4d8e4412f930041ced23e96d8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 15:14:24 +0100 Subject: [PATCH 03/28] updated entities --- .../Transaction.ts | 83 +++++++++++++++++++ database/entity/Transaction.ts | 2 +- database/entity/UserTransaction.ts | 1 - database/entity/index.ts | 2 - 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 database/entity/0025-combine_transaction_tables2/Transaction.ts delete mode 100644 database/entity/UserTransaction.ts diff --git a/database/entity/0025-combine_transaction_tables2/Transaction.ts b/database/entity/0025-combine_transaction_tables2/Transaction.ts new file mode 100644 index 000000000..f707e0811 --- /dev/null +++ b/database/entity/0025-combine_transaction_tables2/Transaction.ts @@ -0,0 +1,83 @@ +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: 'transaction_type_id', unsigned: true, nullable: false }) + transactionTypeId: 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: number + + @Column({ + name: 'balance_date', + type: 'timestamp', + default: () => 'CURRENT_TIMESTAMP', + nullable: false, + }) + balanceDate: Date + + @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + received: 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_state_user_transaction_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedStateUserTransactionId?: number | null + + @Column({ type: 'binary', length: 64, nullable: true, default: null }) + signature: Buffer + + @Column({ name: 'tx_hash', type: 'binary', length: 48, default: null, nullable: true }) + txHash: Buffer + + @Column({ type: 'binary', length: 32, nullable: true, default: null }) + pubkey: Buffer + + @Column({ + name: 'creation_ident_hash', + type: 'binary', + length: 32, + nullable: true, + default: null, + }) + creationIdentHash: Buffer +} diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index 5c9d2df88..d98b73e5b 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0024-combine_transaction_tables/Transaction' +export { Transaction } from './0025-combine_transaction_tables2/Transaction' diff --git a/database/entity/UserTransaction.ts b/database/entity/UserTransaction.ts deleted file mode 100644 index bcbe6a65a..000000000 --- a/database/entity/UserTransaction.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserTransaction } from './0001-init_db/UserTransaction' diff --git a/database/entity/index.ts b/database/entity/index.ts index 9371b5420..67f809b56 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -6,7 +6,6 @@ import { ServerUser } from './ServerUser' import { Transaction } from './Transaction' import { User } from './User' import { UserSetting } from './UserSetting' -import { UserTransaction } from './UserTransaction' import { AdminPendingCreation } from './AdminPendingCreation' export const entities = [ @@ -19,5 +18,4 @@ export const entities = [ Transaction, User, UserSetting, - UserTransaction, ] From 4bb9f12783e9a675fc55e898a4cf38704bc2ac1c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 15:52:13 +0100 Subject: [PATCH 04/28] fixed database build --- database/src/factories/transaction.factory.ts | 4 +-- .../src/factories/user-transaction.factory.ts | 19 ------------ database/src/interface/TransactionContext.ts | 13 ++------ database/src/seeds/helpers/user-helpers.ts | 30 ++++--------------- 4 files changed, 11 insertions(+), 55 deletions(-) delete mode 100644 database/src/factories/user-transaction.factory.ts diff --git a/database/src/factories/transaction.factory.ts b/database/src/factories/transaction.factory.ts index 2637ae298..87af75baf 100644 --- a/database/src/factories/transaction.factory.ts +++ b/database/src/factories/transaction.factory.ts @@ -20,8 +20,8 @@ define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { transaction.pubkey = context.pubkey || randomBytes(32) transaction.creationIdentHash = context.creationIdentHash || randomBytes(32) transaction.creationDate = context.creationDate || new Date() - transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null - transaction.sendReceiverUserId = context.sendReceiverUserId || null + // transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null + transaction.linkedUserId = context.sendReceiverUserId || null transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null return transaction diff --git a/database/src/factories/user-transaction.factory.ts b/database/src/factories/user-transaction.factory.ts deleted file mode 100644 index 7ea79235b..000000000 --- a/database/src/factories/user-transaction.factory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Faker from 'faker' -import { define } from 'typeorm-seeding' -import { UserTransaction } from '../../entity/UserTransaction' -import { UserTransactionContext } from '../interface/TransactionContext' - -define(UserTransaction, (faker: typeof Faker, context?: UserTransactionContext) => { - if (!context || !context.userId || !context.transactionId) { - throw new Error('UserTransaction: No userId and/or transactionId present!') - } - - const userTransaction = new UserTransaction() - userTransaction.userId = context.userId - userTransaction.transactionId = context.transactionId - userTransaction.transactionTypeId = context.transactionTypeId ? context.transactionTypeId : 1 - userTransaction.balance = context.balance ? context.balance : 100000 - userTransaction.balanceDate = context.balanceDate ? context.balanceDate : new Date() - - return userTransaction -}) diff --git a/database/src/interface/TransactionContext.ts b/database/src/interface/TransactionContext.ts index add9fc71c..d424a5731 100644 --- a/database/src/interface/TransactionContext.ts +++ b/database/src/interface/TransactionContext.ts @@ -2,8 +2,11 @@ import { Transaction } from '../../entity/Transaction' import { User } from '../../entity/User' export interface TransactionContext { + transactionId: number transactionTypeId: number userId: number + balance: BigInt + balanceDate: Date amount: BigInt txHash?: Buffer memo: string @@ -33,13 +36,3 @@ export interface TransactionSendCoinContext { senderFinalBalance?: number transaction?: Transaction } - -export interface UserTransactionContext { - userId?: number - transactionId?: number - transactionTypeId?: number - balance?: number - balanceDate?: Date - signature?: Buffer - pubkey?: Buffer -} diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index a195fc181..e4c0314b7 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -1,16 +1,12 @@ import { UserContext, ServerUserContext } from '../../interface/UserContext' -import { - BalanceContext, - TransactionContext, - UserTransactionContext, -} from '../../interface/TransactionContext' +import { BalanceContext, 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 { UserTransaction } from '../../../entity/UserTransaction' 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() @@ -22,12 +18,9 @@ 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() - const transaction = await factory(Transaction)( + await factory(Transaction)( createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'), ).create() - await factory(UserTransaction)( - createUserTransactionContext(userData, user, transaction), - ).create() } } @@ -76,27 +69,16 @@ const createTransactionContext = ( memo: string, ): TransactionContext => { return { + transactionId: randomInt(999999), transactionTypeId: 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, - } -} - -const createUserTransactionContext = ( - context: UserInterface, - user: User, - transaction: Transaction, -): UserTransactionContext => { - return { - userId: user.id, - transactionId: transaction.id, - transactionTypeId: transaction.transactionTypeId, - balance: context.amount, - balanceDate: context.recordDate, signature: context.signature, pubkey: context.pubKey, } From ebde61c66450c400780adb1bdaf7c4c0186865cb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 21:46:02 +0100 Subject: [PATCH 05/28] corrected type to BigInt --- database/entity/0025-combine_transaction_tables2/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0025-combine_transaction_tables2/Transaction.ts b/database/entity/0025-combine_transaction_tables2/Transaction.ts index f707e0811..77f23e4e4 100644 --- a/database/entity/0025-combine_transaction_tables2/Transaction.ts +++ b/database/entity/0025-combine_transaction_tables2/Transaction.ts @@ -29,7 +29,7 @@ export class Transaction extends BaseEntity { sendSenderFinalBalance: BigInt | null @Column({ name: 'balance', type: 'bigint', default: 0 }) - balance: number + balance: BigInt @Column({ name: 'balance_date', From 7a837be7b28064b4bb474e7317b922043919afee Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 21:46:40 +0100 Subject: [PATCH 06/28] backend is building --- backend/src/graphql/enum/TransactionTypeId.ts | 1 + backend/src/graphql/resolver/AdminResolver.ts | 50 ++--- .../graphql/resolver/TransactionResolver.ts | 172 +++++++++--------- .../{UserTransaction.ts => Transaction.ts} | 12 +- 4 files changed, 108 insertions(+), 127 deletions(-) rename backend/src/typeorm/repository/{UserTransaction.ts => Transaction.ts} (76%) diff --git a/backend/src/graphql/enum/TransactionTypeId.ts b/backend/src/graphql/enum/TransactionTypeId.ts index 4ff3671cf..b5b75aa20 100644 --- a/backend/src/graphql/enum/TransactionTypeId.ts +++ b/backend/src/graphql/enum/TransactionTypeId.ts @@ -3,6 +3,7 @@ import { registerEnumType } from 'type-graphql' export enum TransactionTypeId { CREATION = 1, SEND = 2, + RECEIVE = 3, } registerEnumType(TransactionTypeId, { diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 0b8e3f52b..f547e1b3f 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -13,8 +13,7 @@ import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs' import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs' import SearchUsersArgs from '../arg/SearchUsersArgs' import { Transaction } from '@entity/Transaction' -import { UserTransaction } from '@entity/UserTransaction' -import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' +import { TransactionRepository } from '../../typeorm/repository/Transaction' import { calculateDecay } from '../../util/decay' import { AdminPendingCreation } from '@entity/AdminPendingCreation' import { hasElopageBuys } from '../../util/hasElopageBuys' @@ -22,6 +21,7 @@ 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? @@ -246,6 +246,20 @@ export class AdminResolver { } const receivedCallDate = new Date() + + const transactionRepository = getCustomRepository(TransactionRepository) + const lastUserTransaction = await transactionRepository.findLastForUser(pendingCreation.userId) + + let newBalance = 0 + if (lastUserTransaction) { + newBalance = calculateDecay( + Number(lastUserTransaction.balance), + lastUserTransaction.balanceDate, + receivedCallDate, + ).balance + } + newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) + let transaction = new Transaction() transaction.transactionTypeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo @@ -253,35 +267,11 @@ export class AdminResolver { 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() - if (!transaction) throw new Error('Could not create transaction') - - const userTransactionRepository = getCustomRepository(UserTransactionRepository) - const lastUserTransaction = await userTransactionRepository.findLastForUser( - pendingCreation.userId, - ) - let newBalance = 0 - if (!lastUserTransaction) { - newBalance = 0 - } else { - newBalance = calculateDecay( - lastUserTransaction.balance, - lastUserTransaction.balanceDate, - receivedCallDate, - ).balance - } - newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) - - const newUserTransaction = new UserTransaction() - newUserTransaction.userId = pendingCreation.userId - newUserTransaction.transactionId = transaction.id - newUserTransaction.transactionTypeId = transaction.transactionTypeId - newUserTransaction.balance = Number(newBalance) - newUserTransaction.balanceDate = transaction.received - - await userTransactionRepository.save(newUserTransaction).catch((error) => { - throw new Error('Error saving user transaction: ' + error) - }) + // if (!transaction) throw new Error('Could not create transaction') let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) if (!userBalance) { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1a1f20251..34443d9ae 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -17,10 +17,9 @@ import Paginated from '../arg/Paginated' import { Order } from '../enum/Order' import { UserRepository } from '../../typeorm/repository/User' -import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' +import { TransactionRepository } from '../../typeorm/repository/Transaction' import { User as dbUser } from '@entity/User' -import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction' import { Transaction as dbTransaction } from '@entity/Transaction' import { Balance as dbBalance } from '@entity/Balance' @@ -31,10 +30,11 @@ 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 function async function calculateAndAddDecayTransactions( - userTransactions: dbUserTransaction[], + userTransactions: dbTransaction[], user: dbUser, decay: boolean, skipFirstTransaction: boolean, @@ -43,7 +43,7 @@ async function calculateAndAddDecayTransactions( const transactionIds: number[] = [] const involvedUserIds: number[] = [] - userTransactions.forEach((userTransaction: dbUserTransaction) => { + userTransactions.forEach((userTransaction: dbTransaction) => { transactionIds.push(userTransaction.transactionId) }) @@ -52,9 +52,12 @@ async function calculateAndAddDecayTransactions( transactions.forEach((transaction: dbTransaction) => { transactionIndiced[transaction.id] = transaction involvedUserIds.push(transaction.userId) - if (transaction.transactionTypeId === TransactionTypeId.SEND) { + if ( + transaction.transactionTypeId === TransactionTypeId.SEND || + transaction.transactionTypeId === TransactionTypeId.RECEIVE + ) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - involvedUserIds.push(transaction.sendReceiverUserId!) // TODO ensure not null properly + involvedUserIds.push(transaction.linkedUserId!) // TODO ensure not null properly } }) // remove duplicates @@ -70,17 +73,17 @@ async function calculateAndAddDecayTransactions( finalTransaction.transactionId = transaction.id finalTransaction.date = transaction.received.toISOString() finalTransaction.memo = transaction.memo - finalTransaction.totalBalance = roundFloorFrom4(userTransaction.balance) + finalTransaction.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) const previousTransaction = i > 0 ? userTransactions[i - 1] : null if (previousTransaction) { const currentTransaction = userTransaction const decay = calculateDecay( - previousTransaction.balance, + Number(previousTransaction.balance), previousTransaction.balanceDate, currentTransaction.balanceDate, ) - const balance = previousTransaction.balance - decay.balance + const balance = Number(previousTransaction.balance) - decay.balance if (CONFIG.DECAY_START_TIME < currentTransaction.balanceDate) { finalTransaction.decay = decay @@ -110,23 +113,23 @@ async function calculateAndAddDecayTransactions( finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { // send coin - let otherUser: dbUser | undefined + const otherUser = userIndiced.find((u) => u.id === transaction.linkedUserId) finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion - if (transaction.userId === user.id) { - finalTransaction.type = TransactionType.SEND - otherUser = userIndiced.find((u) => u.id === transaction.sendReceiverUserId) - // finalTransaction.pubkey = sendCoin.recipiantPublic - } else if (transaction.sendReceiverUserId === user.id) { - finalTransaction.type = TransactionType.RECIEVE - otherUser = userIndiced.find((u) => u.id === transaction.userId) - // finalTransaction.pubkey = sendCoin.senderPublic - } else { - throw new Error('invalid transaction') - } + finalTransaction.type = TransactionType.SEND if (otherUser) { finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName finalTransaction.email = otherUser.email } + } else if (userTransaction.transactionTypeId === TransactionTypeId.RECEIVE) { + const otherUser = userIndiced.find((u) => u.id === transaction.linkedUserId) + finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion + finalTransaction.type = TransactionType.RECIEVE + if (otherUser) { + finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName + finalTransaction.email = otherUser.email + } + } else { + throw new Error('invalid transaction') } if (i > 0 || !skipFirstTransaction) { finalTransactions.push(finalTransaction) @@ -134,8 +137,12 @@ async function calculateAndAddDecayTransactions( if (i === userTransactions.length - 1 && decay) { const now = new Date() - const decay = calculateDecay(userTransaction.balance, userTransaction.balanceDate, now) - const balance = userTransaction.balance - decay.balance + const decay = calculateDecay( + Number(userTransaction.balance), + userTransaction.balanceDate, + now, + ) + const balance = Number(userTransaction.balance) - decay.balance const decayTransaction = new Transaction() decayTransaction.type = 'decay' @@ -176,22 +183,20 @@ async function updateStateBalance( }) } -// helper helper function -async function addUserTransaction( - user: dbUser, - transaction: dbTransaction, +async function calculateNewBalance( + userId: number, + transactionDate: Date, centAmount: number, - queryRunner: QueryRunner, -): Promise { +): Promise { let newBalance = centAmount - const userTransactionRepository = getCustomRepository(UserTransactionRepository) - const lastUserTransaction = await userTransactionRepository.findLastForUser(user.id) + const transactionRepository = getCustomRepository(TransactionRepository) + const lastUserTransaction = await transactionRepository.findLastForUser(userId) if (lastUserTransaction) { newBalance += Number( calculateDecay( Number(lastUserTransaction.balance), lastUserTransaction.balanceDate, - transaction.received, + transactionDate, ).balance, ) } @@ -200,16 +205,7 @@ async function addUserTransaction( throw new Error('error new balance <= 0') } - const newUserTransaction = new dbUserTransaction() - newUserTransaction.userId = user.id - newUserTransaction.transactionId = transaction.id - newUserTransaction.transactionTypeId = transaction.transactionTypeId - newUserTransaction.balance = newBalance - newUserTransaction.balanceDate = transaction.received - - return queryRunner.manager.save(newUserTransaction).catch((error) => { - throw new Error('Error saving user transaction: ' + error) - }) + return BigInt(newBalance) } @Resolver() @@ -242,9 +238,14 @@ export class TransactionResolver { if (offset && order === Order.ASC) { offset-- } - const userTransactionRepository = getCustomRepository(UserTransactionRepository) - const [userTransactions, userTransactionsCount] = - await userTransactionRepository.findByUserPaged(user.id, limit, offset, order, onlyCreations) + const transactionRepository = getCustomRepository(TransactionRepository) + const [userTransactions, userTransactionsCount] = await transactionRepository.findByUserPaged( + user.id, + limit, + offset, + order, + onlyCreations, + ) skipFirstTransaction = userTransactionsCount > offset + limit const decay = !(currentPage > 1) let transactions: Transaction[] = [] @@ -326,39 +327,49 @@ export class TransactionResolver { await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') try { + const receivedCallDate = new Date() // transaction - const transaction = new dbTransaction() - transaction.transactionTypeId = TransactionTypeId.SEND - transaction.memo = memo - transaction.userId = senderUser.id - transaction.pubkey = senderUser.pubKey - transaction.sendReceiverUserId = recipientUser.id - transaction.sendReceiverPublicKey = recipientUser.pubKey - transaction.amount = BigInt(centAmount) - - await queryRunner.manager.insert(dbTransaction, transaction) - - // Insert Transaction: sender - amount - const senderUserTransactionBalance = await addUserTransaction( - senderUser, - transaction, + const transactionSend = new dbTransaction() + transactionSend.transactionTypeId = 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) + transactionSend.balance = await calculateNewBalance( + senderUser.id, + receivedCallDate, -centAmount, - queryRunner, ) + transactionSend.balanceDate = receivedCallDate + transactionSend.sendSenderFinalBalance = transactionSend.balance + await queryRunner.manager.insert(dbTransaction, transactionSend) - // Insert Transaction: recipient + amount - const recipiantUserTransactionBalance = await addUserTransaction( - recipientUser, - transaction, + const transactionReceive = new dbTransaction() + transactionReceive.transactionTypeId = 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) + transactionReceive.balance = await calculateNewBalance( + senderUser.id, + receivedCallDate, centAmount, - queryRunner, ) + transactionReceive.balanceDate = receivedCallDate + transactionReceive.sendSenderFinalBalance = transactionSend.balance + await queryRunner.manager.insert(dbTransaction, transactionReceive) // Update Balance: sender - amount const senderStateBalance = await updateStateBalance( senderUser, -centAmount, - transaction.received, + receivedCallDate, queryRunner, ) @@ -366,41 +377,20 @@ export class TransactionResolver { const recipiantStateBalance = await updateStateBalance( recipientUser, centAmount, - transaction.received, + receivedCallDate, queryRunner, ) - if (senderStateBalance.amount !== senderUserTransactionBalance.balance) { + if (senderStateBalance.amount !== Number(transactionSend.balance)) { throw new Error('db data corrupted, sender') } - if (recipiantStateBalance.amount !== recipiantUserTransactionBalance.balance) { + if (recipiantStateBalance.amount !== Number(transactionReceive.balance)) { throw new Error('db data corrupted, recipiant') } - // TODO: WTF? - // I just assume that due to implicit type conversion the decimal places were cut. - // Using `Math.trunc` to simulate this behaviour - transaction.sendSenderFinalBalance = BigInt(Math.trunc(senderStateBalance.amount)) - - await queryRunner.manager.save(transaction).catch((error) => { - throw new Error('error saving transaction with tx hash: ' + error) - }) - await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() - // TODO: This is broken code - we should never correct an autoincrement index in production - // according to dario it is required tho to properly work. The index of the table is used as - // index for the transaction which requires a chain without gaps - const count = await queryRunner.manager.count(dbTransaction) - // fix autoincrement value which seems not effected from rollback - await queryRunner - .query('ALTER TABLE `transactions` auto_increment = ?', [count]) - .catch((error) => { - // eslint-disable-next-line no-console - console.log('problems with reset auto increment: %o', error) - }) - throw e } finally { await queryRunner.release() } diff --git a/backend/src/typeorm/repository/UserTransaction.ts b/backend/src/typeorm/repository/Transaction.ts similarity index 76% rename from backend/src/typeorm/repository/UserTransaction.ts rename to backend/src/typeorm/repository/Transaction.ts index d699d07ea..2abcb4090 100644 --- a/backend/src/typeorm/repository/UserTransaction.ts +++ b/backend/src/typeorm/repository/Transaction.ts @@ -1,17 +1,17 @@ import { EntityRepository, Repository } from '@dbTools/typeorm' +import { Transaction } from '@entity/Transaction' import { Order } from '../../graphql/enum/Order' -import { UserTransaction } from '@entity/UserTransaction' import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId' -@EntityRepository(UserTransaction) -export class UserTransactionRepository extends Repository { +@EntityRepository(Transaction) +export class TransactionRepository extends Repository { findByUserPaged( userId: number, limit: number, offset: number, order: Order, onlyCreation?: boolean, - ): Promise<[UserTransaction[], number]> { + ): Promise<[Transaction[], number]> { if (onlyCreation) { return this.createQueryBuilder('userTransaction') .where('userTransaction.userId = :userId', { userId }) @@ -31,10 +31,10 @@ export class UserTransactionRepository extends Repository { .getManyAndCount() } - findLastForUser(userId: number): Promise { + findLastForUser(userId: number): Promise { return this.createQueryBuilder('userTransaction') .where('userTransaction.userId = :userId', { userId }) - .orderBy('userTransaction.transactionId', 'DESC') + .orderBy('userTransaction.balanceDate', 'DESC') .getOne() } } From f10029441ccd57de5b5b2e4cdfa09e92bdf64356 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:01:43 +0100 Subject: [PATCH 07/28] refactor as transaction type as switch --- .../graphql/resolver/TransactionResolver.ts | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 34443d9ae..1b0c4f947 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -99,37 +99,34 @@ async function calculateAndAddDecayTransactions( } } - // sender or receiver when user has sent money - // group name if creation - // type: gesendet / empfangen / geschöpft - // transaktion nr / id - // date - // balance - if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) { - // creation - finalTransaction.name = 'Gradido Akademie' - finalTransaction.type = TransactionType.CREATION - // finalTransaction.targetDate = creation.targetDate - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion - } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { - // send coin - const otherUser = userIndiced.find((u) => u.id === transaction.linkedUserId) - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion - finalTransaction.type = TransactionType.SEND - if (otherUser) { - finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName - finalTransaction.email = otherUser.email - } - } else if (userTransaction.transactionTypeId === TransactionTypeId.RECEIVE) { - const otherUser = userIndiced.find((u) => u.id === transaction.linkedUserId) - finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion - finalTransaction.type = TransactionType.RECIEVE - if (otherUser) { - finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName - finalTransaction.email = otherUser.email - } - } else { - throw new Error('invalid transaction') + const otherUser = userIndiced.find((u) => u.id === transaction.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 + finalTransaction.email = otherUser.email + } + 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 + finalTransaction.email = otherUser.email + } + break + default: + throw new Error('invalid transaction') } if (i > 0 || !skipFirstTransaction) { finalTransactions.push(finalTransaction) From 6f369067236ca6ee63953a4922877217e4195c05 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:17:56 +0100 Subject: [PATCH 08/28] removed unnecessary code --- backend/src/graphql/resolver/TransactionResolver.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1b0c4f947..c32913057 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -35,21 +35,14 @@ import { randomInt } from 'crypto' // Helper function async function calculateAndAddDecayTransactions( userTransactions: dbTransaction[], - user: dbUser, decay: boolean, skipFirstTransaction: boolean, ): Promise { const finalTransactions: Transaction[] = [] - const transactionIds: number[] = [] const involvedUserIds: number[] = [] - userTransactions.forEach((userTransaction: dbTransaction) => { - transactionIds.push(userTransaction.transactionId) - }) - - const transactions = await dbTransaction.find({ where: { id: In(transactionIds) } }) const transactionIndiced: dbTransaction[] = [] - transactions.forEach((transaction: dbTransaction) => { + userTransactions.forEach((transaction: dbTransaction) => { transactionIndiced[transaction.id] = transaction involvedUserIds.push(transaction.userId) if ( @@ -252,7 +245,6 @@ export class TransactionResolver { } transactions = await calculateAndAddDecayTransactions( userTransactions, - user, decay, skipFirstTransaction, ) From b02076fa49fdbc99895637c3613cd8f30aff3c20 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:20:20 +0100 Subject: [PATCH 09/28] combined functions --- .../graphql/resolver/TransactionResolver.ts | 226 +++++++++--------- 1 file changed, 107 insertions(+), 119 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index c32913057..b9d50963f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -32,120 +32,6 @@ import { hasUserAmount, isHexPublicKey } from '../../util/validate' import { RIGHTS } from '../../auth/RIGHTS' import { randomInt } from 'crypto' -// Helper function -async function calculateAndAddDecayTransactions( - userTransactions: dbTransaction[], - decay: boolean, - skipFirstTransaction: boolean, -): Promise { - 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 || - transaction.transactionTypeId === 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) - - 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.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) - const previousTransaction = i > 0 ? userTransactions[i - 1] : null - - 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() - } - } - } - - const otherUser = userIndiced.find((u) => u.id === transaction.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 - finalTransaction.email = otherUser.email - } - 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 - finalTransaction.email = otherUser.email - } - break - default: - throw new Error('invalid transaction') - } - if (i > 0 || !skipFirstTransaction) { - finalTransactions.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 - finalTransactions.push(decayTransaction) - } - } - return finalTransactions -} - // helper helper function async function updateStateBalance( user: dbUser, @@ -243,11 +129,113 @@ export class TransactionResolver { if (order === Order.DESC) { userTransactions.reverse() } - transactions = await calculateAndAddDecayTransactions( - userTransactions, - decay, - skipFirstTransaction, - ) + 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 || + transaction.transactionTypeId === 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) + + 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.totalBalance = roundFloorFrom4(Number(userTransaction.balance)) + const previousTransaction = i > 0 ? userTransactions[i - 1] : null + + 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() + } + } + } + + const otherUser = userIndiced.find((u) => u.id === transaction.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 + finalTransaction.email = otherUser.email + } + 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 + finalTransaction.email = otherUser.email + } + break + default: + throw new Error('invalid transaction') + } + if (i > 0 || !skipFirstTransaction) { + finalTransactions.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 + finalTransactions.push(decayTransaction) + } + } + transactions = finalTransactions + if (order === Order.DESC) { transactions.reverse() } From c113e75edd5a9e3ff78aabe438bfa887d57aaa36 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:35:31 +0100 Subject: [PATCH 10/28] removed unused include --- 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 b9d50963f..3e66dd1a5 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' -import { getCustomRepository, getConnection, QueryRunner, In } from '@dbTools/typeorm' +import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm' import CONFIG from '../../config' import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail' From 29562c9f5505c410f34630f22b9887a8387abb7d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:44:10 +0100 Subject: [PATCH 11/28] updated database requirement --- backend/src/config/index.ts | 2 +- .../Transaction.ts | 0 database/entity/Transaction.ts | 2 +- ...ansaction_tables2.ts => 0026-combine_transaction_tables2.ts} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename database/entity/{0025-combine_transaction_tables2 => 0026-combine_transaction_tables2}/Transaction.ts (100%) rename database/migrations/{0025-combine_transaction_tables2.ts => 0026-combine_transaction_tables2.ts} (100%) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 580dd1a0c..925783ff1 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-emails_to_lower', + DB_VERSION: '0026-combine_transaction_tables2', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } diff --git a/database/entity/0025-combine_transaction_tables2/Transaction.ts b/database/entity/0026-combine_transaction_tables2/Transaction.ts similarity index 100% rename from database/entity/0025-combine_transaction_tables2/Transaction.ts rename to database/entity/0026-combine_transaction_tables2/Transaction.ts diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index d98b73e5b..a49e9d0a1 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-combine_transaction_tables2/Transaction' diff --git a/database/migrations/0025-combine_transaction_tables2.ts b/database/migrations/0026-combine_transaction_tables2.ts similarity index 100% rename from database/migrations/0025-combine_transaction_tables2.ts rename to database/migrations/0026-combine_transaction_tables2.ts From 5974c39c5146263505b5c2807cc5deaa802708b5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 22:58:22 +0100 Subject: [PATCH 12/28] 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 02af6ec53cd6745ddd264f91ab064483e0a35cfd Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 23:23:51 +0100 Subject: [PATCH 13/28] 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 10d2372475b4ea37cbcd6c543f062f94fd704811 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Feb 2022 23:33:55 +0100 Subject: [PATCH 14/28] 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 345ed8a3b5b1beb0f5c32bd6f1816fc212b15fee Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:25:49 +0100 Subject: [PATCH 15/28] 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 8aaa6117bab28fbfc4d848e58c4d3ee6ecf1d733 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:26:15 +0100 Subject: [PATCH 16/28] new entity definition --- .../Transaction.ts | 62 +++++++++++++++++++ database/entity/Transaction.ts | 2 +- ...ble.ts => 0027-clean_transaction_table.ts} | 0 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 database/entity/0027-clean_transaction_table/Transaction.ts rename database/migrations/{0026-clean_transaction_table.ts => 0027-clean_transaction_table.ts} (100%) diff --git a/database/entity/0027-clean_transaction_table/Transaction.ts b/database/entity/0027-clean_transaction_table/Transaction.ts new file mode 100644 index 000000000..c23600386 --- /dev/null +++ b/database/entity/0027-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 a49e9d0a1..af6a17424 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0026-combine_transaction_tables2/Transaction' +export { Transaction } from './0027-clean_transaction_table/Transaction' diff --git a/database/migrations/0026-clean_transaction_table.ts b/database/migrations/0027-clean_transaction_table.ts similarity index 100% rename from database/migrations/0026-clean_transaction_table.ts rename to database/migrations/0027-clean_transaction_table.ts From 33484f4335b65cef84a8ef5eaede57c3d19042e5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:27:13 +0100 Subject: [PATCH 17/28] missing entity change --- database/entity/0027-clean_transaction_table/Transaction.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/database/entity/0027-clean_transaction_table/Transaction.ts b/database/entity/0027-clean_transaction_table/Transaction.ts index c23600386..e50f5d164 100644 --- a/database/entity/0027-clean_transaction_table/Transaction.ts +++ b/database/entity/0027-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 909f88cbe8f1e617edbeb3f84836ba2cd36ec875 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:32:45 +0100 Subject: [PATCH 18/28] 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 dd0d4a19a2a101e731c8cf165031d53eaf5f0345 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:41:44 +0100 Subject: [PATCH 19/28] 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 925783ff1..8869dd1aa 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-combine_transaction_tables2', + DB_VERSION: '0027-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 0e8a74b01..955605068 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -321,13 +321,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 b0b669ac342786eaf168a1d7021467198e561cd8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:52:54 +0100 Subject: [PATCH 20/28] 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 955605068..6d328032d 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 cab9426054a184cb11a2718c3e9d1631db388a1a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 00:54:02 +0100 Subject: [PATCH 21/28] 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 4e017e4017e2505fc1f5ae93836d0e22a27ca295 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 12:51:47 +0100 Subject: [PATCH 22/28] 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 6d328032d..936b5d4e2 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -319,7 +319,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 @@ -327,8 +327,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 f137314438a99783d974d7c22d5001df9b04452f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 12:56:34 +0100 Subject: [PATCH 23/28] 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 277a329f0452fa61b19fe348535dacabe976a9b3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 13:32:30 +0100 Subject: [PATCH 24/28] 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 2f37cb6e0d20880bfcba504eb4ae245fee9364e7 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 13:55:20 +0100 Subject: [PATCH 25/28] 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 c9145d8adaa9f4642a66308126c0648073046766 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 19:27:03 +0100 Subject: [PATCH 26/28] 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 c266d58358a96d13325abf6664cc086f998541fc Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Feb 2022 23:27:00 +0100 Subject: [PATCH 27/28] 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 c66384375839d7c3f683e4fe2e0a072da79fc48e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 3 Mar 2022 16:54:34 +0100 Subject: [PATCH 28/28] type_id in raw sql query --- backend/src/graphql/resolver/AdminResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 936b5d4e2..b6d8c38cd 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -374,7 +374,7 @@ async function getUserCreations(ids: number[], includePending = true): Promise= ${dateFilter} ${unionString}) AS result GROUP BY month, userId