From 9d362a6c03192d132349e711f2d8bde447e9b5ae Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 02:21:55 +0100 Subject: [PATCH 01/27] combine transaction tables --- .../0024-combine_transaction_tables.ts | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 database/migrations/0024-combine_transaction_tables.ts diff --git a/database/migrations/0024-combine_transaction_tables.ts b/database/migrations/0024-combine_transaction_tables.ts new file mode 100644 index 000000000..d8dde9f11 --- /dev/null +++ b/database/migrations/0024-combine_transaction_tables.ts @@ -0,0 +1,113 @@ +/* MIGRATION TO COMBINE ALL TRANSACTION TABLES + * + * Combine all transaction tables into one table with all data + */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Create new `user_id` column (former `state_user_id`), with a temporary default of null + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `user_id` int(10) unsigned DEFAULT NULL AFTER `transaction_type_id`;', + ) + // Create new `amount` column, with a temporary default of null + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER `user_id`;', + ) + // Create new `creation_ident_hash` column (former `ident_hash`) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `pubkey`;', + ) + // Create new `creation_date` column (former `target_date`) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `creation_date` timestamp NULL DEFAULT NULL AFTER `creation_ident_hash`;', + ) + // Create new `send_receiver_public_key` column (former `receiver_public_key`) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `send_receiver_public_key` binary(32) DEFAULT NULL AFTER `creation_date`;', + ) + // Create new `send_receiver_user_id` column (former `receiver_user_id`) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `send_receiver_user_id` int(10) unsigned DEFAULT NULL AFTER `send_receiver_public_key`;', + ) + // Create new `send_sender_final_balance` column (former `sender_final_balance`) + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL AFTER `send_receiver_user_id`;', + ) + + // Insert Data from `transaction_creations` + await queryFn(` + UPDATE transactions + INNER JOIN transaction_creations ON transaction_creations.transaction_id = transactions.id + SET transactions.user_id = transaction_creations.state_user_id, + transactions.amount = transaction_creations.amount, + transactions.creation_ident_hash = transaction_creations.ident_hash, + transactions.creation_date = transaction_creations.target_date; + `) + + // Insert Data from `transaction_send_coins` + // Note: we drop `sender_public_key` in favor of `pubkey` from the original `transactions` table + // the data from `transaction_send_coins` seems incomplete for half the dataset (zeroed pubkey) + // with one key being different. + await queryFn(` + UPDATE transactions + INNER JOIN transaction_send_coins ON transaction_send_coins.transaction_id = transactions.id + SET transactions.user_id = transaction_send_coins.state_user_id, + transactions.amount = transaction_send_coins.amount, + transactions.send_receiver_public_key = transaction_send_coins.receiver_public_key, + transactions.send_receiver_user_id = transaction_send_coins.receiver_user_id, + transactions.send_sender_final_balance = transaction_send_coins.sender_final_balance; + `) + + // Modify defaults after our inserts + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `user_id` int(10) unsigned NOT NULL;') + await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL;') + + // Drop table `transaction_creations` + await queryFn('DROP TABLE `transaction_creations`;') + // Drop table `transaction_send_coins` + await queryFn('DROP TABLE `transaction_send_coins`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_sender_final_balance`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_user_id`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_public_key`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_date`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_ident_hash`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `amount`;') + await queryFn('ALTER TABLE `transactions` DROP COLUMN `user_id`;') +} + +/* +CREATE TABLE `transactions` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `transaction_type_id` int(10) unsigned 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, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3421 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci +*/ + +// CREATE TABLE `transaction_creations` ( +// `id` int(10) unsigned NOT NULL AUTO_INCREMENT, +// `transaction_id` int(10) unsigned NOT NULL, +// `state_user_id` int(10) unsigned NOT NULL, +// `amount` bigint(20) NOT NULL, +// `ident_hash` binary(32) DEFAULT NULL, +// `target_date` timestamp NULL DEFAULT NULL, +// PRIMARY KEY (`id`) +// ) ENGINE=InnoDB AUTO_INCREMENT=2769 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + +// CREATE TABLE `transaction_send_coins` ( +// `id` int(10) unsigned NOT NULL AUTO_INCREMENT, +// `transaction_id` int(10) unsigned NOT NULL, +// `sender_public_key` binary(32) NOT NULL, +// `state_user_id` int(10) unsigned DEFAULT 0, +// `receiver_public_key` binary(32) NOT NULL, +// `receiver_user_id` int(10) unsigned DEFAULT 0, +// `amount` bigint(20) NOT NULL, +// `sender_final_balance` bigint(20) NOT NULL, +// PRIMARY KEY (`id`) +// ) ENGINE=InnoDB AUTO_INCREMENT=659 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci From 0bdd52989a08a5ff3cd4d78dd683849c598bad35 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 02:51:04 +0100 Subject: [PATCH 02/27] down migration --- .../0024-combine_transaction_tables.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/database/migrations/0024-combine_transaction_tables.ts b/database/migrations/0024-combine_transaction_tables.ts index d8dde9f11..ce0f1cb44 100644 --- a/database/migrations/0024-combine_transaction_tables.ts +++ b/database/migrations/0024-combine_transaction_tables.ts @@ -68,6 +68,53 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis } export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE \`transaction_send_coins\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`transaction_id\` int(10) unsigned NOT NULL, + \`sender_public_key\` binary(32) NOT NULL, + \`state_user_id\` int(10) unsigned DEFAULT 0, + \`receiver_public_key\` binary(32) NOT NULL, + \`receiver_user_id\` int(10) unsigned DEFAULT 0, + \`amount\` bigint(20) NOT NULL, + \`sender_final_balance\` bigint(20) NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=659 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + `) + await queryFn(` + CREATE TABLE \`transaction_creations\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`transaction_id\` int(10) unsigned NOT NULL, + \`state_user_id\` int(10) unsigned NOT NULL, + \`amount\` bigint(20) NOT NULL, + \`ident_hash\` binary(32) DEFAULT NULL, + \`target_date\` timestamp NULL DEFAULT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=2769 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + `) + + await queryFn(` + INSERT INTO transaction_send_coins + ( transaction_id, sender_public_key, state_user_id, + receiver_public_key, receiver_user_id, + amount, sender_final_balance ) + ( SELECT id AS transaction_id, IF(pubkey, pubkey, 0x00000000000000000000000000000000) AS sender_public_key, user_id AS state_user_id, + send_receiver_public_key AS receiver_public_key, send_receiver_user_id AS receiver_user_id, + amount, send_sender_final_balance AS sender_final_balance + FROM transactions + WHERE transaction_type_id = 2 ); + `) + + await queryFn(` + INSERT INTO transaction_creations + ( transaction_id, state_user_id, + amount, ident_hash, target_date ) + ( SELECT id AS transaction_id, user_id AS state_user_id, + amount, creation_ident_hash AS ident_hash, creation_date AS target_date + FROM transactions + WHERE transaction_type_id = 1 ); + `) + await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_sender_final_balance`;') await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_user_id`;') await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_public_key`;') From bfde22b261e707533778bbf0a4e36b320842ac87 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 03:10:54 +0100 Subject: [PATCH 03/27] updated entities --- database/entity/0001-init_db/Transaction.ts | 4 +- .../Transaction.ts | 4 +- .../Transaction.ts | 65 +++++++++++++++++++ database/entity/Transaction.ts | 2 +- database/entity/TransactionCreation.ts | 1 - database/entity/TransactionSendCoin.ts | 1 - database/entity/index.ts | 4 -- 7 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 database/entity/0024-combine_transaction_tables/Transaction.ts delete mode 100644 database/entity/TransactionCreation.ts delete mode 100644 database/entity/TransactionSendCoin.ts diff --git a/database/entity/0001-init_db/Transaction.ts b/database/entity/0001-init_db/Transaction.ts index 3c2397108..a33fbd0be 100644 --- a/database/entity/0001-init_db/Transaction.ts +++ b/database/entity/0001-init_db/Transaction.ts @@ -1,6 +1,6 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' -import { TransactionCreation } from '../TransactionCreation' -import { TransactionSendCoin } from '../TransactionSendCoin' +import { TransactionCreation } from './TransactionCreation' +import { TransactionSendCoin } from './TransactionSendCoin' @Entity('transactions') export class Transaction extends BaseEntity { diff --git a/database/entity/0016-transaction_signatures/Transaction.ts b/database/entity/0016-transaction_signatures/Transaction.ts index 8c8251baf..5410d010b 100644 --- a/database/entity/0016-transaction_signatures/Transaction.ts +++ b/database/entity/0016-transaction_signatures/Transaction.ts @@ -1,6 +1,6 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' -import { TransactionCreation } from '../TransactionCreation' -import { TransactionSendCoin } from '../TransactionSendCoin' +import { TransactionCreation } from '../0001-init_db/TransactionCreation' +import { TransactionSendCoin } from '../0001-init_db/TransactionSendCoin' @Entity('transactions') export class Transaction extends BaseEntity { diff --git a/database/entity/0024-combine_transaction_tables/Transaction.ts b/database/entity/0024-combine_transaction_tables/Transaction.ts new file mode 100644 index 000000000..fde1f3057 --- /dev/null +++ b/database/entity/0024-combine_transaction_tables/Transaction.ts @@ -0,0 +1,65 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' + +@Entity('transactions') +export class Transaction extends BaseEntity { + // TODO the id is defined as bigint(20) - there might be problems with that: https://github.com/typeorm/typeorm/issues/2400 + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'transaction_type_id', unsigned: true, length: 10, nullable: false }) + transactionTypeId: number + + @Column({ name: 'user_id', unsigned: true, length: 10, nullable: false }) + userId: number + + @Column({ type: 'bigint', length: 20, nullable: false }) + amount: BigInt + + @Column({ name: 'tx_hash', type: 'binary', length: 48, default: null, nullable: true }) + txHash: Buffer + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ type: 'timestamp', nullable: false, default: () => 'CURRENT_TIMESTAMP' }) + received: Date + + @Column({ type: 'binary', length: 64, nullable: true, default: null }) + signature: 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 + + @Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null }) + creationDate: Date + + @Column({ + name: 'send_receiver_public_key', + type: 'binary', + length: 32, + nullable: true, + default: null, + }) + sendReceiverPublicKey: Buffer + + @Column({ + name: 'send_receiver_user_id', + unsigned: true, + length: 10, + nullable: true, + default: null, + }) + sendReceiverUserId: number + + @Column({ name: 'send_sender_final_balance', length: 20, nullable: true, default: null }) + sendSenderFinalBalance: BigInt +} diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index f17479f9f..5c9d2df88 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0016-transaction_signatures/Transaction' +export { Transaction } from './0024-combine_transaction_tables/Transaction' diff --git a/database/entity/TransactionCreation.ts b/database/entity/TransactionCreation.ts deleted file mode 100644 index 100e948a1..000000000 --- a/database/entity/TransactionCreation.ts +++ /dev/null @@ -1 +0,0 @@ -export { TransactionCreation } from './0001-init_db/TransactionCreation' diff --git a/database/entity/TransactionSendCoin.ts b/database/entity/TransactionSendCoin.ts deleted file mode 100644 index 5c47d3961..000000000 --- a/database/entity/TransactionSendCoin.ts +++ /dev/null @@ -1 +0,0 @@ -export { TransactionSendCoin } from './0001-init_db/TransactionSendCoin' diff --git a/database/entity/index.ts b/database/entity/index.ts index 37fe6eb55..9371b5420 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -4,8 +4,6 @@ import { LoginEmailOptIn } from './LoginEmailOptIn' import { Migration } from './Migration' import { ServerUser } from './ServerUser' import { Transaction } from './Transaction' -import { TransactionCreation } from './TransactionCreation' -import { TransactionSendCoin } from './TransactionSendCoin' import { User } from './User' import { UserSetting } from './UserSetting' import { UserTransaction } from './UserTransaction' @@ -19,8 +17,6 @@ export const entities = [ Migration, ServerUser, Transaction, - TransactionCreation, - TransactionSendCoin, User, UserSetting, UserTransaction, From 6b78161182ad510cd537ba99fde0b4504a1b0b30 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:02:05 +0100 Subject: [PATCH 04/27] fixed seeds --- .../Transaction.ts | 6 ++--- .../factories/transaction-creation.factory.ts | 18 ------------- database/src/factories/transaction.factory.ts | 25 +++++++++++------- database/src/interface/TransactionContext.ts | 25 +++++++----------- database/src/interface/UserInterface.ts | 4 +-- database/src/seeds/helpers/user-helpers.ts | 26 +++++-------------- database/src/seeds/users/bibi-bloxberg.ts | 14 +++------- database/src/seeds/users/bob-baumeister.ts | 14 +++------- .../src/seeds/users/garrick-ollivander.ts | 8 +++--- database/src/seeds/users/peter-lustig.ts | 8 +++--- .../src/seeds/users/raeuber-hotzenplotz.ts | 14 +++------- 11 files changed, 54 insertions(+), 108 deletions(-) delete mode 100644 database/src/factories/transaction-creation.factory.ts diff --git a/database/entity/0024-combine_transaction_tables/Transaction.ts b/database/entity/0024-combine_transaction_tables/Transaction.ts index fde1f3057..130f1a9da 100644 --- a/database/entity/0024-combine_transaction_tables/Transaction.ts +++ b/database/entity/0024-combine_transaction_tables/Transaction.ts @@ -49,7 +49,7 @@ export class Transaction extends BaseEntity { nullable: true, default: null, }) - sendReceiverPublicKey: Buffer + sendReceiverPublicKey: Buffer | null @Column({ name: 'send_receiver_user_id', @@ -58,8 +58,8 @@ export class Transaction extends BaseEntity { nullable: true, default: null, }) - sendReceiverUserId: number + sendReceiverUserId: number | null @Column({ name: 'send_sender_final_balance', length: 20, nullable: true, default: null }) - sendSenderFinalBalance: BigInt + sendSenderFinalBalance: BigInt | null } diff --git a/database/src/factories/transaction-creation.factory.ts b/database/src/factories/transaction-creation.factory.ts deleted file mode 100644 index ec0b9e8a6..000000000 --- a/database/src/factories/transaction-creation.factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Faker from 'faker' -import { define } from 'typeorm-seeding' -import { TransactionCreation } from '../../entity/TransactionCreation' -import { TransactionCreationContext } from '../interface/TransactionContext' - -define(TransactionCreation, (faker: typeof Faker, context?: TransactionCreationContext) => { - if (!context || !context.userId || !context.transaction) { - throw new Error('TransactionCreation: No userId and/or transaction present!') - } - - const transactionCreation = new TransactionCreation() - transactionCreation.userId = context.userId - transactionCreation.amount = context.amount ? context.amount : 100000 - transactionCreation.targetDate = context.targetDate ? context.targetDate : new Date() - transactionCreation.transaction = context.transaction - - return transactionCreation -}) diff --git a/database/src/factories/transaction.factory.ts b/database/src/factories/transaction.factory.ts index 10d242265..2637ae298 100644 --- a/database/src/factories/transaction.factory.ts +++ b/database/src/factories/transaction.factory.ts @@ -5,17 +5,24 @@ import { TransactionContext } from '../interface/TransactionContext' import { randomBytes } from 'crypto' define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { - if (!context) context = {} + if (!context) { + throw new Error('TransactionContext not well defined.') + } const transaction = new Transaction() - transaction.transactionTypeId = context.transactionTypeId ? context.transactionTypeId : 2 - transaction.txHash = context.txHash ? context.txHash : randomBytes(48) - transaction.memo = context.memo || context.memo === '' ? context.memo : faker.lorem.sentence() - transaction.received = context.received ? context.received : new Date() - transaction.signature = context.signature ? context.signature : randomBytes(64) - transaction.pubkey = context.signaturePubkey ? context.signaturePubkey : randomBytes(32) - if (context.transactionSendCoin) transaction.transactionSendCoin = context.transactionSendCoin - if (context.transactionCreation) transaction.transactionCreation = context.transactionCreation + transaction.transactionTypeId = context.transactionTypeId // || 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.sendReceiverUserId = context.sendReceiverUserId || null + transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null return transaction }) diff --git a/database/src/interface/TransactionContext.ts b/database/src/interface/TransactionContext.ts index 15b66bad6..add9fc71c 100644 --- a/database/src/interface/TransactionContext.ts +++ b/database/src/interface/TransactionContext.ts @@ -1,18 +1,20 @@ import { Transaction } from '../../entity/Transaction' -import { TransactionSendCoin } from '../../entity/TransactionSendCoin' -import { TransactionCreation } from '../../entity/TransactionCreation' import { User } from '../../entity/User' export interface TransactionContext { - transactionTypeId?: number + transactionTypeId: number + userId: number + amount: BigInt txHash?: Buffer - memo?: string + memo: string received?: Date - blockchainTypeId?: number signature?: Buffer - signaturePubkey?: Buffer - transactionSendCoin?: TransactionSendCoin - transactionCreation?: TransactionCreation + pubkey?: Buffer + creationIdentHash?: Buffer + creationDate?: Date + sendReceiverPublicKey?: Buffer + sendReceiverUserId?: number + sendSenderFinalBalance?: BigInt } export interface BalanceContext { @@ -32,13 +34,6 @@ export interface TransactionSendCoinContext { transaction?: Transaction } -export interface TransactionCreationContext { - userId?: number - amount?: number - targetDate?: Date - transaction?: Transaction -} - export interface UserTransactionContext { userId?: number transactionId?: number diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts index 30b05db38..1f258f808 100644 --- a/database/src/interface/UserInterface.ts +++ b/database/src/interface/UserInterface.ts @@ -11,7 +11,6 @@ export interface UserInterface { emailChecked?: boolean language?: string deletedAt?: Date - groupId?: number publisherId?: number passphrase?: string // from server user @@ -27,9 +26,8 @@ export interface UserInterface { // balance balanceModified?: Date recordDate?: Date - targetDate?: Date + creationDate?: Date amount?: number creationTxHash?: Buffer signature?: Buffer - signaturePubkey?: Buffer } diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index 74bdd4326..a195fc181 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -2,7 +2,6 @@ import { UserContext, ServerUserContext } from '../../interface/UserContext' import { BalanceContext, TransactionContext, - TransactionCreationContext, UserTransactionContext, } from '../../interface/TransactionContext' import { UserInterface } from '../../interface/UserInterface' @@ -11,7 +10,6 @@ import { ServerUser } from '../../../entity/ServerUser' import { Balance } from '../../../entity/Balance' import { Transaction } from '../../../entity/Transaction' import { UserTransaction } from '../../../entity/UserTransaction' -import { TransactionCreation } from '../../../entity/TransactionCreation' import { Factory } from 'typeorm-seeding' export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => { @@ -25,10 +23,7 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro // create some GDD for the user await factory(Balance)(createBalanceContext(userData, user)).create() const transaction = await factory(Transaction)( - createTransactionContext(userData, 1, 'Herzlich Willkommen bei Gradido!'), - ).create() - await factory(TransactionCreation)( - createTransactionCreationContext(userData, user, transaction), + createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'), ).create() await factory(UserTransaction)( createUserTransactionContext(userData, user, transaction), @@ -76,27 +71,18 @@ const createBalanceContext = (context: UserInterface, user: User): BalanceContex const createTransactionContext = ( context: UserInterface, + user: User, type: number, memo: string, ): TransactionContext => { return { transactionTypeId: type, + userId: user.id, + amount: BigInt(context.amount || 100000), txHash: context.creationTxHash, memo, received: context.recordDate, - } -} - -const createTransactionCreationContext = ( - context: UserInterface, - user: User, - transaction: Transaction, -): TransactionCreationContext => { - return { - userId: user.id, - amount: context.amount, - targetDate: context.targetDate, - transaction, + creationDate: context.creationDate, } } @@ -112,6 +98,6 @@ const createUserTransactionContext = ( balance: context.amount, balanceDate: context.recordDate, signature: context.signature, - pubkey: context.signaturePubkey, + pubkey: context.pubKey, } } diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts index e8291b213..f940c3447 100644 --- a/database/src/seeds/users/bibi-bloxberg.ts +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -1,8 +1,9 @@ -export const bibiBloxberg = { +import { UserInterface } from '../../interface/UserInterface' + +export const bibiBloxberg: UserInterface = { email: 'bibi@bloxberg.de', firstName: 'Bibi', lastName: 'Bloxberg', - username: 'bibi', // description: 'Hex Hex', password: BigInt('12825419584724616625'), pubKey: Buffer.from('42de7e4754625b730018c3b4ea745a4d043d9d867af352d0f08871793dfa6743', 'hex'), @@ -14,16 +15,13 @@ export const bibiBloxberg = { createdAt: new Date('2021-11-26T11:32:16'), emailChecked: true, language: 'de', - disabled: false, - groupId: 1, passphrase: 'knife normal level all hurdle crucial color avoid warrior stadium road bachelor affair topple hawk pottery right afford immune two ceiling budget glance hour ', - mnemonicType: 2, isAdmin: false, addBalance: true, balanceModified: new Date('2021-11-30T10:37:11'), recordDate: new Date('2021-11-30T10:37:11'), - targetDate: new Date('2021-08-01 00:00:00'), + creationDate: new Date('2021-08-01 00:00:00'), amount: 10000000, creationTxHash: Buffer.from( '51103dc0fc2ca5d5d75a9557a1e899304e5406cfdb1328d8df6414d527b0118100000000000000000000000000000000', @@ -33,8 +31,4 @@ export const bibiBloxberg = { '2a2c71f3e41adc060bbc3086577e2d57d24eeeb0a7727339c3f85aad813808f601d7e1df56a26e0929d2e67fc054fca429ccfa283ed2782185c7f009fe008f0c', 'hex', ), - signaturePubkey: Buffer.from( - '7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', - 'hex', - ), } diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts index 1c9f992e6..34597fa3f 100644 --- a/database/src/seeds/users/bob-baumeister.ts +++ b/database/src/seeds/users/bob-baumeister.ts @@ -1,8 +1,9 @@ -export const bobBaumeister = { +import { UserInterface } from '../../interface/UserInterface' + +export const bobBaumeister: UserInterface = { email: 'bob@baumeister.de', firstName: 'Bob', lastName: 'der Baumeister', - username: 'bob', // description: 'Können wir das schaffen? Ja, wir schaffen das!', password: BigInt('3296644341468822636'), pubKey: Buffer.from('a509d9a146374fc975e3677db801ae8a4a83bff9dea96da64053ff6de6b2dd7e', 'hex'), @@ -14,16 +15,13 @@ export const bobBaumeister = { createdAt: new Date('2021-11-26T11:36:31'), emailChecked: true, language: 'de', - disabled: false, - groupId: 1, passphrase: 'detail master source effort unable waste tilt flush domain orchard art truck hint barrel response gate impose peanut secret merry three uncle wink resource ', - mnemonicType: 2, isAdmin: false, addBalance: true, balanceModified: new Date('2021-11-30T10:37:14'), recordDate: new Date('2021-11-30T10:37:14'), - targetDate: new Date('2021-08-01 00:00:00'), + creationDate: new Date('2021-08-01 00:00:00'), amount: 10000000, creationTxHash: Buffer.from( 'be095dc87acb94987e71168fee8ecbf50ecb43a180b1006e75d573b35725c69c00000000000000000000000000000000', @@ -33,8 +31,4 @@ export const bobBaumeister = { '1fbd6b9a3d359923b2501557f3bc79fa7e428127c8090fb16bc490b4d87870ab142b3817ddd902d22f0b26472a483233784a0e460c0622661752a13978903905', 'hex', ), - signaturePubkey: Buffer.from( - '7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', - 'hex', - ), } diff --git a/database/src/seeds/users/garrick-ollivander.ts b/database/src/seeds/users/garrick-ollivander.ts index bddd5aa0f..3f18a875c 100644 --- a/database/src/seeds/users/garrick-ollivander.ts +++ b/database/src/seeds/users/garrick-ollivander.ts @@ -1,8 +1,9 @@ -export const garrickOllivander = { +import { UserInterface } from '../../interface/UserInterface' + +export const garrickOllivander: UserInterface = { email: 'garrick@ollivander.com', firstName: 'Garrick', lastName: 'Ollivander', - username: 'garrick', // description: `Curious ... curious ... // Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`, password: BigInt('0'), @@ -10,11 +11,8 @@ export const garrickOllivander = { createdAt: new Date('2022-01-10T10:23:17'), emailChecked: false, language: 'en', - disabled: false, - groupId: 1, passphrase: 'human glide theory clump wish history other duty door fringe neck industry ostrich equal plate diesel tornado neck people antenna door category moon hen ', - mnemonicType: 2, isAdmin: false, addBalance: false, } diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts index da6c31777..5b4b98488 100644 --- a/database/src/seeds/users/peter-lustig.ts +++ b/database/src/seeds/users/peter-lustig.ts @@ -1,8 +1,9 @@ -export const peterLustig = { +import { UserInterface } from '../../interface/UserInterface' + +export const peterLustig: UserInterface = { email: 'peter@lustig.de', firstName: 'Peter', lastName: 'Lustig', - username: 'peter', // description: 'Latzhose und Nickelbrille', password: BigInt('3917921995996627700'), pubKey: Buffer.from('7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', 'hex'), @@ -14,11 +15,8 @@ export const peterLustig = { createdAt: new Date('2020-11-25T10:48:43'), emailChecked: true, language: 'de', - disabled: false, - groupId: 1, passphrase: 'okay property choice naive calm present weird increase stuff royal vibrant frame attend wood one else tribe pull hedgehog woman kitchen hawk snack smart ', - mnemonicType: 2, role: 'admin', serverUserPassword: '$2y$10$TzIWLeZoKs251gwrhSQmHeKhKI/EQ4EV5ClfAT8Ufnb4lcUXPa5X.', activated: 1, diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts index aaf862d14..bcd5f995b 100644 --- a/database/src/seeds/users/raeuber-hotzenplotz.ts +++ b/database/src/seeds/users/raeuber-hotzenplotz.ts @@ -1,8 +1,9 @@ -export const raeuberHotzenplotz = { +import { UserInterface } from '../../interface/UserInterface' + +export const raeuberHotzenplotz: UserInterface = { email: 'raeuber@hotzenplotz.de', firstName: 'Räuber', lastName: 'Hotzenplotz', - username: 'räuber', // description: 'Pfefferpistole', password: BigInt('12123692783243004812'), pubKey: Buffer.from('d7c70f94234dff071d982aa8f41583876c356599773b5911b39080da2b8c2d2b', 'hex'), @@ -14,16 +15,13 @@ export const raeuberHotzenplotz = { createdAt: new Date('2021-11-26T11:32:16'), emailChecked: true, language: 'de', - disabled: false, - groupId: 1, passphrase: 'gospel trip tenant mouse spider skill auto curious man video chief response same little over expire drum display fancy clinic keen throw urge basket ', - mnemonicType: 2, isAdmin: false, addBalance: true, balanceModified: new Date('2021-11-30T10:37:13'), recordDate: new Date('2021-11-30T10:37:13'), - targetDate: new Date('2021-08-01 00:00:00'), + creationDate: new Date('2021-08-01 00:00:00'), amount: 10000000, creationTxHash: Buffer.from( '23ba44fd84deb59b9f32969ad0cb18bfa4588be1bdb99c396888506474c16c1900000000000000000000000000000000', @@ -33,8 +31,4 @@ export const raeuberHotzenplotz = { '756d3da061687c575d1dbc5073908f646aa5f498b0927b217c83b48af471450e571dfe8421fb8e1f1ebd1104526b7e7c6fa78684e2da59c8f7f5a8dc3d9e5b0b', 'hex', ), - signaturePubkey: Buffer.from( - '7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', - 'hex', - ), } From 9ba7d6341ab746ec3d9731c7ca1d204011f5230d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:06:08 +0100 Subject: [PATCH 05/27] fixed lint warning --- database/entity/0024-combine_transaction_tables/Transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0024-combine_transaction_tables/Transaction.ts b/database/entity/0024-combine_transaction_tables/Transaction.ts index 130f1a9da..cd7e6833e 100644 --- a/database/entity/0024-combine_transaction_tables/Transaction.ts +++ b/database/entity/0024-combine_transaction_tables/Transaction.ts @@ -1,4 +1,4 @@ -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' @Entity('transactions') export class Transaction extends BaseEntity { From 281a4fbc9b51b88cd6ef8a1d705f603a37ad019e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:06:18 +0100 Subject: [PATCH 06/27] fixed lint warning --- database/src/factories/user.factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/src/factories/user.factory.ts b/database/src/factories/user.factory.ts index cb2ccb105..d04dcea4c 100644 --- a/database/src/factories/user.factory.ts +++ b/database/src/factories/user.factory.ts @@ -1,7 +1,7 @@ import Faker from 'faker' import { define } from 'typeorm-seeding' import { User } from '../../entity/User' -import { randomBytes, randomInt } from 'crypto' +import { randomBytes } from 'crypto' import { UserContext } from '../interface/UserContext' define(User, (faker: typeof Faker, context?: UserContext) => { From c88b67faa4b66dc09d0a87fb60f454c075af53e2 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:15:14 +0100 Subject: [PATCH 07/27] removed length properties and define nullable userId as int --- .../Transaction.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/database/entity/0024-combine_transaction_tables/Transaction.ts b/database/entity/0024-combine_transaction_tables/Transaction.ts index cd7e6833e..5834c007a 100644 --- a/database/entity/0024-combine_transaction_tables/Transaction.ts +++ b/database/entity/0024-combine_transaction_tables/Transaction.ts @@ -6,13 +6,13 @@ export class Transaction extends BaseEntity { @PrimaryGeneratedColumn('increment', { unsigned: true }) id: number - @Column({ name: 'transaction_type_id', unsigned: true, length: 10, nullable: false }) + @Column({ name: 'transaction_type_id', unsigned: true, nullable: false }) transactionTypeId: number - @Column({ name: 'user_id', unsigned: true, length: 10, nullable: false }) + @Column({ name: 'user_id', unsigned: true, nullable: false }) userId: number - @Column({ type: 'bigint', length: 20, nullable: false }) + @Column({ type: 'bigint', nullable: false }) amount: BigInt @Column({ name: 'tx_hash', type: 'binary', length: 48, default: null, nullable: true }) @@ -53,13 +53,18 @@ export class Transaction extends BaseEntity { @Column({ name: 'send_receiver_user_id', + type: 'int', unsigned: true, - length: 10, nullable: true, default: null, }) - sendReceiverUserId: number | null + sendReceiverUserId?: number | null - @Column({ name: 'send_sender_final_balance', length: 20, nullable: true, default: null }) + @Column({ + name: 'send_sender_final_balance', + type: 'bigint', + nullable: true, + default: null, + }) sendSenderFinalBalance: BigInt | null } From 93ec25f72f11807802ad635ac70bb2698e230643 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:27:31 +0100 Subject: [PATCH 08/27] remove comment --- .../0024-combine_transaction_tables.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/database/migrations/0024-combine_transaction_tables.ts b/database/migrations/0024-combine_transaction_tables.ts index ce0f1cb44..af564ce2d 100644 --- a/database/migrations/0024-combine_transaction_tables.ts +++ b/database/migrations/0024-combine_transaction_tables.ts @@ -123,38 +123,3 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom await queryFn('ALTER TABLE `transactions` DROP COLUMN `amount`;') await queryFn('ALTER TABLE `transactions` DROP COLUMN `user_id`;') } - -/* -CREATE TABLE `transactions` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `transaction_type_id` int(10) unsigned 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, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=3421 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci -*/ - -// CREATE TABLE `transaction_creations` ( -// `id` int(10) unsigned NOT NULL AUTO_INCREMENT, -// `transaction_id` int(10) unsigned NOT NULL, -// `state_user_id` int(10) unsigned NOT NULL, -// `amount` bigint(20) NOT NULL, -// `ident_hash` binary(32) DEFAULT NULL, -// `target_date` timestamp NULL DEFAULT NULL, -// PRIMARY KEY (`id`) -// ) ENGINE=InnoDB AUTO_INCREMENT=2769 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci - -// CREATE TABLE `transaction_send_coins` ( -// `id` int(10) unsigned NOT NULL AUTO_INCREMENT, -// `transaction_id` int(10) unsigned NOT NULL, -// `sender_public_key` binary(32) NOT NULL, -// `state_user_id` int(10) unsigned DEFAULT 0, -// `receiver_public_key` binary(32) NOT NULL, -// `receiver_user_id` int(10) unsigned DEFAULT 0, -// `amount` bigint(20) NOT NULL, -// `sender_final_balance` bigint(20) NOT NULL, -// PRIMARY KEY (`id`) -// ) ENGINE=InnoDB AUTO_INCREMENT=659 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci From 0c15bed74eaf32c569342057afa3b5a07d2c822b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 04:46:36 +0100 Subject: [PATCH 09/27] backend is buildable --- backend/src/graphql/resolver/AdminResolver.ts | 22 +++++------ .../graphql/resolver/TransactionResolver.ts | 38 +++++++------------ 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 7430868b1..11d7f201c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -15,7 +15,6 @@ import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs' import SearchUsersArgs from '../arg/SearchUsersArgs' import moment from 'moment' import { Transaction } from '@entity/Transaction' -import { TransactionCreation } from '@entity/TransactionCreation' import { UserTransaction } from '@entity/UserTransaction' import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' import { BalanceRepository } from '../../typeorm/repository/Balance' @@ -24,6 +23,7 @@ import { AdminPendingCreation } from '@entity/AdminPendingCreation' import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' +import { TransactionTypeId } from '../enum/TransactionTypeId' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -229,17 +229,12 @@ export class AdminResolver { transaction.transactionTypeId = 1 transaction.memo = pendingCreation.memo transaction.received = receivedCallDate + transaction.userId = pendingCreation.userId + transaction.amount = BigInt(parseInt(pendingCreation.amount.toString())) + transaction.creationDate = pendingCreation.date transaction = await transactionRepository.save(transaction) if (!transaction) throw new Error('Could not create transaction') - let transactionCreation = new TransactionCreation() - transactionCreation.transactionId = transaction.id - transactionCreation.userId = pendingCreation.userId - transactionCreation.amount = parseInt(pendingCreation.amount.toString()) - transactionCreation.targetDate = pendingCreation.date - transactionCreation = await TransactionCreation.save(transactionCreation) - if (!transactionCreation) throw new Error('Could not create transactionCreation') - const userTransactionRepository = getCustomRepository(UserTransactionRepository) const lastUserTransaction = await userTransactionRepository.findLastForUser( pendingCreation.userId, @@ -289,10 +284,11 @@ async function getUserCreations(id: number): Promise { const lastMonthNumber = moment().subtract(1, 'month').format('M') const currentMonthNumber = moment().format('M') - const createdAmountsQuery = await TransactionCreation.createQueryBuilder('transaction_creations') - .select('MONTH(transaction_creations.target_date)', 'target_month') - .addSelect('SUM(transaction_creations.amount)', 'sum') - .where('transaction_creations.state_user_id = :id', { id }) + const createdAmountsQuery = await Transaction.createQueryBuilder('transactions') + .select('MONTH(transactions.creation_date)', 'target_month') + .addSelect('SUM(transactions.amount)', 'sum') + .where('transactions.user_id = :id', { id }) + .andWhere('transactions.transaction_type_id = :type', { type: TransactionTypeId.CREATION }) .andWhere({ targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, { date: dateBeforeLastMonth, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 8d9e0c6c3..9c7c8b93f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -24,7 +24,6 @@ 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 { TransactionSendCoin as dbTransactionSendCoin } from '@entity/TransactionSendCoin' import { Balance as dbBalance } from '@entity/Balance' import { apiPost } from '../../apis/HttpRequest' @@ -57,8 +56,8 @@ async function calculateAndAddDecayTransactions( transactions.forEach((transaction: dbTransaction) => { transactionIndiced[transaction.id] = transaction if (transaction.transactionTypeId === TransactionTypeId.SEND) { - involvedUserIds.push(transaction.transactionSendCoin.userId) - involvedUserIds.push(transaction.transactionSendCoin.recipiantUserId) + involvedUserIds.push(transaction.userId) + involvedUserIds.push(transaction.sendReceiverUserId!) // TODO ensure not null properly } }) // remove duplicates @@ -108,24 +107,21 @@ async function calculateAndAddDecayTransactions( // balance if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) { // creation - const creation = transaction.transactionCreation - finalTransaction.name = 'Gradido Akademie' finalTransaction.type = TransactionType.CREATION // finalTransaction.targetDate = creation.targetDate - finalTransaction.balance = roundFloorFrom4(creation.amount) + finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { // send coin - const sendCoin = transaction.transactionSendCoin let otherUser: dbUser | undefined - finalTransaction.balance = roundFloorFrom4(sendCoin.amount) - if (sendCoin.userId === user.id) { + finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion + if (transaction.userId === user.id) { finalTransaction.type = TransactionType.SEND - otherUser = userIndiced[sendCoin.recipiantUserId] + otherUser = userIndiced[transaction.sendReceiverUserId!] // TODO properly enforce null // finalTransaction.pubkey = sendCoin.recipiantPublic - } else if (sendCoin.recipiantUserId === user.id) { + } else if (transaction.sendReceiverUserId === user.id) { finalTransaction.type = TransactionType.RECIEVE - otherUser = userIndiced[sendCoin.userId] + otherUser = userIndiced[transaction.userId] // finalTransaction.pubkey = sendCoin.senderPublic } else { throw new Error('invalid transaction') @@ -373,6 +369,11 @@ export class TransactionResolver { let 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) // TODO: NO! this is problematic in its construction const insertResult = await queryRunner.manager.insert(dbTransaction, transaction) @@ -421,18 +422,7 @@ export class TransactionResolver { throw new Error('db data corrupted, recipiant') } - // transactionSendCoin - const transactionSendCoin = new dbTransactionSendCoin() - transactionSendCoin.transactionId = transaction.id - transactionSendCoin.userId = senderUser.id - transactionSendCoin.senderPublic = senderUser.pubKey - transactionSendCoin.recipiantUserId = recipientUser.id - transactionSendCoin.recipiantPublic = recipientUser.pubKey - transactionSendCoin.amount = centAmount - transactionSendCoin.senderFinalBalance = senderStateBalance.amount - await queryRunner.manager.save(transactionSendCoin).catch((error) => { - throw new Error('error saving transaction send coin: ' + error) - }) + transaction.sendSenderFinalBalance = BigInt(senderStateBalance.amount) await queryRunner.manager.save(transaction).catch((error) => { throw new Error('error saving transaction with tx hash: ' + error) From fc0675c461596291088b80c628c39f8f192cfb8c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 12:01:52 +0100 Subject: [PATCH 10/27] updated database version in backend --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 990cde078..8961fc358 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: '0023-users_disabled_soft_delete', + DB_VERSION: '0024-combine_transaction_tables', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 } From babe2b4930403b04d7f4c5710fe1a7a704ef779e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 12:54:20 +0100 Subject: [PATCH 11/27] wtf, this stopped working for me? Why? --- frontend/src/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index 0ce43310b..73125dd07 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -70,7 +70,7 @@ const dateTimeFormats = { long: { year: 'numeric', month: 'long', - day: 'long', + day: 'numeric', weekday: 'short', hour: 'numeric', minute: 'numeric', From ecfcde8dd4168da8651c4a4fad4ec16f72a1bd56 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 12:56:10 +0100 Subject: [PATCH 12/27] simplified query --- backend/src/typeorm/repository/Transaction.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/backend/src/typeorm/repository/Transaction.ts b/backend/src/typeorm/repository/Transaction.ts index 0a97067a2..dcf312753 100644 --- a/backend/src/typeorm/repository/Transaction.ts +++ b/backend/src/typeorm/repository/Transaction.ts @@ -6,16 +6,6 @@ export class TransactionRepository extends Repository { async joinFullTransactionsByIds(transactionIds: number[]): Promise { return this.createQueryBuilder('transaction') .where('transaction.id IN (:...transactions)', { transactions: transactionIds }) - .leftJoinAndSelect( - 'transaction.transactionSendCoin', - 'transactionSendCoin', - // 'transactionSendCoin.transaction_id = transaction.id', - ) - .leftJoinAndSelect( - 'transaction.transactionCreation', - 'transactionCreation', - // 'transactionSendCoin.transaction_id = transaction.id', - ) .getMany() } } From c9424361a6bf0bc5dbe6a5c517d95279ea97c789 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 13:20:13 +0100 Subject: [PATCH 13/27] simplified query and also include `withDeleted` to show the name of an user marked as deleted --- backend/src/typeorm/repository/User.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index f9faf6c5b..3155053d9 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -10,20 +10,15 @@ export class UserRepository extends Repository { } async getUsersIndiced(userIds: number[]): Promise { - if (!userIds.length) return [] - const users = await this.createQueryBuilder('user') + return this.createQueryBuilder('user') + .withDeleted() // We need to show the name for deleted users for old transactions .select(['user.id', 'user.firstName', 'user.lastName', 'user.email']) - .where('user.id IN (:...users)', { users: userIds }) + .where('user.id IN (:...userIds)', { userIds }) .getMany() - const usersIndiced: User[] = [] - users.forEach((value) => { - usersIndiced[value.id] = value - }) - return usersIndiced } async findBySearchCriteria(searchCriteria: string): Promise { - return await this.createQueryBuilder('user') + return this.createQueryBuilder('user') .withDeleted() .where( 'user.firstName like :name or user.lastName like :lastName or user.email like :email', From 70a0a2664b3df07b115daabecc0dbcc37919ec33 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 20 Feb 2022 13:21:12 +0100 Subject: [PATCH 14/27] properly use js array operators (find) --- 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 9c7c8b93f..edbe6373e 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -117,11 +117,11 @@ async function calculateAndAddDecayTransactions( finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion if (transaction.userId === user.id) { finalTransaction.type = TransactionType.SEND - otherUser = userIndiced[transaction.sendReceiverUserId!] // TODO properly enforce null + otherUser = userIndiced.find((u) => u.id === transaction.sendReceiverUserId) // TODO properly enforce null // finalTransaction.pubkey = sendCoin.recipiantPublic } else if (transaction.sendReceiverUserId === user.id) { finalTransaction.type = TransactionType.RECIEVE - otherUser = userIndiced[transaction.userId] + otherUser = userIndiced.find((u) => u.id === transaction.userId) // finalTransaction.pubkey = sendCoin.senderPublic } else { throw new Error('invalid transaction') From e1034cb1fdabd32b73b44e7f1a7eedcf055de395 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 00:18:08 +0100 Subject: [PATCH 15/27] fixed data conversion error on sendCoins --- backend/src/graphql/resolver/TransactionResolver.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index edbe6373e..417760982 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -114,6 +114,7 @@ async function calculateAndAddDecayTransactions( } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { // send coin let otherUser: dbUser | undefined + console.log('converting', transaction.amount) finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion if (transaction.userId === user.id) { finalTransaction.type = TransactionType.SEND @@ -422,7 +423,10 @@ export class TransactionResolver { throw new Error('db data corrupted, recipiant') } - transaction.sendSenderFinalBalance = BigInt(senderStateBalance.amount) + // 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) From 9574af9278d912f3f325d0cc6a324cc7f8e0eabe Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 00:38:39 +0100 Subject: [PATCH 16/27] removed balance & transaction repository since its no longer needed --- backend/src/graphql/resolver/AdminResolver.ts | 13 +++++-------- backend/src/graphql/resolver/BalanceResolver.ts | 5 ++--- backend/src/graphql/resolver/TransactionResolver.ts | 12 +++--------- backend/src/typeorm/repository/Balance.ts | 9 --------- backend/src/typeorm/repository/Transaction.ts | 11 ----------- 5 files changed, 10 insertions(+), 40 deletions(-) delete mode 100644 backend/src/typeorm/repository/Balance.ts delete mode 100644 backend/src/typeorm/repository/Transaction.ts diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 11d7f201c..54898f770 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -8,7 +8,6 @@ import { PendingCreation } from '../model/PendingCreation' import { CreatePendingCreations } from '../model/CreatePendingCreations' import { UpdatePendingCreation } from '../model/UpdatePendingCreation' import { RIGHTS } from '../../auth/RIGHTS' -import { TransactionRepository } from '../../typeorm/repository/Transaction' import { UserRepository } from '../../typeorm/repository/User' import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs' import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs' @@ -17,13 +16,13 @@ import moment from 'moment' import { Transaction } from '@entity/Transaction' import { UserTransaction } from '@entity/UserTransaction' import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' -import { BalanceRepository } from '../../typeorm/repository/Balance' import { calculateDecay } from '../../util/decay' import { AdminPendingCreation } from '@entity/AdminPendingCreation' import { hasElopageBuys } from '../../util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' import { TransactionTypeId } from '../enum/TransactionTypeId' +import { Balance } from '@entity/Balance' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -223,7 +222,6 @@ export class AdminResolver { if (moderatorUser.id === pendingCreation.userId) throw new Error('Moderator can not confirm own pending creation') - const transactionRepository = getCustomRepository(TransactionRepository) const receivedCallDate = new Date() let transaction = new Transaction() transaction.transactionTypeId = 1 @@ -232,7 +230,7 @@ export class AdminResolver { transaction.userId = pendingCreation.userId transaction.amount = BigInt(parseInt(pendingCreation.amount.toString())) transaction.creationDate = pendingCreation.date - transaction = await transactionRepository.save(transaction) + transaction = await transaction.save() if (!transaction) throw new Error('Could not create transaction') const userTransactionRepository = getCustomRepository(UserTransactionRepository) @@ -262,15 +260,14 @@ export class AdminResolver { throw new Error('Error saving user transaction: ' + error) }) - const balanceRepository = getCustomRepository(BalanceRepository) - let userBalance = await balanceRepository.findByUser(pendingCreation.userId) + let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) - if (!userBalance) userBalance = balanceRepository.create() + if (!userBalance) userBalance = new Balance() userBalance.userId = pendingCreation.userId userBalance.amount = Number(newBalance) userBalance.modified = receivedCallDate userBalance.recordDate = receivedCallDate - await balanceRepository.save(userBalance) + await userBalance.save() await AdminPendingCreation.delete(pendingCreation) return true diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index 0ecfbb7cd..fff073b99 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -4,11 +4,11 @@ import { Resolver, Query, Ctx, Authorized } from 'type-graphql' import { getCustomRepository } from '@dbTools/typeorm' import { Balance } from '../model/Balance' -import { BalanceRepository } from '../../typeorm/repository/Balance' import { UserRepository } from '../../typeorm/repository/User' import { calculateDecay } from '../../util/decay' import { roundFloorFrom4 } from '../../util/round' import { RIGHTS } from '../../auth/RIGHTS' +import { Balance as dbBalance } from '@entity/Balance' @Resolver() export class BalanceResolver { @@ -16,11 +16,10 @@ export class BalanceResolver { @Query(() => Balance) async balance(@Ctx() context: any): Promise { // load user and balance - const balanceRepository = getCustomRepository(BalanceRepository) const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(context.pubKey) - const balanceEntity = await balanceRepository.findByUser(userEntity.id) + const balanceEntity = await dbBalance.findOne({ userId: userEntity.id }) const now = new Date() // No balance found diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 417760982..41879f550 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -16,10 +16,8 @@ import Paginated from '../arg/Paginated' import { Order } from '../enum/Order' -import { BalanceRepository } from '../../typeorm/repository/Balance' 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' @@ -49,8 +47,7 @@ async function calculateAndAddDecayTransactions( transactionIds.push(userTransaction.transactionId) }) - const transactionRepository = getCustomRepository(TransactionRepository) - const transactions = await transactionRepository.joinFullTransactionsByIds(transactionIds) + const transactions = await dbTransaction.find({ where: { id: transactionIds } }) const transactionIndiced: dbTransaction[] = [] transactions.forEach((transaction: dbTransaction) => { @@ -114,7 +111,6 @@ async function calculateAndAddDecayTransactions( } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { // send coin let otherUser: dbUser | undefined - console.log('converting', transaction.amount) finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion if (transaction.userId === user.id) { finalTransaction.type = TransactionType.SEND @@ -212,8 +208,7 @@ async function updateStateBalance( received: Date, queryRunner: QueryRunner, ): Promise { - const balanceRepository = getCustomRepository(BalanceRepository) - let balance = await balanceRepository.findByUser(user.id) + let balance = await dbBalance.findOne({ userId: user.id }) if (!balance) { balance = new dbBalance() balance.userId = user.id @@ -311,8 +306,7 @@ export class TransactionResolver { } catch (err: any) {} // get balance - const balanceRepository = getCustomRepository(BalanceRepository) - const balanceEntity = await balanceRepository.findByUser(userEntity.id) + const balanceEntity = await dbBalance.findOne({ userId: userEntity.id }) if (balanceEntity) { const now = new Date() transactions.balance = roundFloorFrom4(balanceEntity.amount) diff --git a/backend/src/typeorm/repository/Balance.ts b/backend/src/typeorm/repository/Balance.ts deleted file mode 100644 index 856329443..000000000 --- a/backend/src/typeorm/repository/Balance.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { EntityRepository, Repository } from '@dbTools/typeorm' -import { Balance } from '@entity/Balance' - -@EntityRepository(Balance) -export class BalanceRepository extends Repository { - findByUser(userId: number): Promise { - return this.createQueryBuilder('balance').where('balance.userId = :userId', { userId }).getOne() - } -} diff --git a/backend/src/typeorm/repository/Transaction.ts b/backend/src/typeorm/repository/Transaction.ts deleted file mode 100644 index dcf312753..000000000 --- a/backend/src/typeorm/repository/Transaction.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { EntityRepository, Repository } from '@dbTools/typeorm' -import { Transaction } from '@entity/Transaction' - -@EntityRepository(Transaction) -export class TransactionRepository extends Repository { - async joinFullTransactionsByIds(transactionIds: number[]): Promise { - return this.createQueryBuilder('transaction') - .where('transaction.id IN (:...transactions)', { transactions: transactionIds }) - .getMany() - } -} From 2d0d02c7f6c100d25712bf64a764bf14eb23da96 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 01:01:57 +0100 Subject: [PATCH 17/27] lots of refacoting in TransactionResolver --- .../graphql/resolver/TransactionResolver.ts | 128 +++++++----------- 1 file changed, 51 insertions(+), 77 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 41879f550..35f960f48 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -150,57 +150,6 @@ async function calculateAndAddDecayTransactions( return finalTransactions } -// Helper function -async function listTransactions( - currentPage: number, - pageSize: number, - order: Order, - user: dbUser, - onlyCreations: boolean, -): Promise { - let limit = pageSize - let offset = 0 - let skipFirstTransaction = false - if (currentPage > 1) { - offset = (currentPage - 1) * pageSize - 1 - limit++ - } - - if (offset && order === Order.ASC) { - offset-- - } - const userTransactionRepository = getCustomRepository(UserTransactionRepository) - let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged( - user.id, - limit, - offset, - order, - onlyCreations, - ) - skipFirstTransaction = userTransactionsCount > offset + limit - const decay = !(currentPage > 1) - let transactions: Transaction[] = [] - if (userTransactions.length) { - if (order === Order.DESC) { - userTransactions = userTransactions.reverse() - } - transactions = await calculateAndAddDecayTransactions( - userTransactions, - user, - decay, - skipFirstTransaction, - ) - if (order === Order.DESC) { - transactions = transactions.reverse() - } - } - - const transactionList = new TransactionList() - transactionList.count = userTransactionsCount - transactionList.transactions = transactions - return transactionList -} - // helper helper function async function updateStateBalance( user: dbUser, @@ -281,42 +230,71 @@ export class TransactionResolver { ): Promise { // load user const userRepository = getCustomRepository(UserRepository) - let userEntity: dbUser | undefined - if (userId) { - userEntity = await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) - } else { - userEntity = await userRepository.findByPubkeyHex(context.pubKey) - } + const user = userId + ? await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) + : await userRepository.findByPubkeyHex(context.pubKey) - const transactions = await listTransactions( - currentPage, - pageSize, + let limit = pageSize + let offset = 0 + let skipFirstTransaction = false + if (currentPage > 1) { + offset = (currentPage - 1) * pageSize - 1 + limit++ + } + if (offset && order === Order.ASC) { + offset-- + } + const userTransactionRepository = getCustomRepository(UserTransactionRepository) + let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged( + user.id, + limit, + offset, order, - userEntity, onlyCreations, ) + skipFirstTransaction = userTransactionsCount > offset + limit + const decay = !(currentPage > 1) + let transactions: Transaction[] = [] + if (userTransactions.length) { + if (order === Order.DESC) { + userTransactions = userTransactions.reverse() + } + transactions = await calculateAndAddDecayTransactions( + userTransactions, + user, + decay, + skipFirstTransaction, + ) + if (order === Order.DESC) { + transactions = transactions.reverse() + } + } + + const transactionList = new TransactionList() + transactionList.count = userTransactionsCount + transactionList.transactions = transactions // get gdt sum - transactions.gdtSum = null + transactionList.gdtSum = null try { const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, { - email: userEntity.email, + email: user.email, }) - if (resultGDTSum.success) transactions.gdtSum = Number(resultGDTSum.data.sum) || 0 + if (resultGDTSum.success) transactionList.gdtSum = Number(resultGDTSum.data.sum) || 0 } catch (err: any) {} // get balance - const balanceEntity = await dbBalance.findOne({ userId: userEntity.id }) + const balanceEntity = await dbBalance.findOne({ userId: user.id }) if (balanceEntity) { const now = new Date() - transactions.balance = roundFloorFrom4(balanceEntity.amount) - transactions.decay = roundFloorFrom4( + transactionList.balance = roundFloorFrom4(balanceEntity.amount) + transactionList.decay = roundFloorFrom4( calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance, ) - transactions.decayDate = now.toString() + transactionList.decayDate = now.toString() } - return transactions + return transactionList } @Authorized([RIGHTS.SEND_COINS]) @@ -324,7 +302,7 @@ export class TransactionResolver { async sendCoins( @Args() { email, amount, memo }: TransactionSendArgs, @Ctx() context: any, - ): Promise { + ): Promise { // TODO this is subject to replay attacks // validate sender user (logged in) const userRepository = getCustomRepository(UserRepository) @@ -332,8 +310,9 @@ export class TransactionResolver { if (senderUser.pubKey.length !== 32) { throw new Error('invalid sender public key') } + // validate amount if (!hasUserAmount(senderUser, amount)) { - throw new Error("user hasn't enough GDD") + throw new Error("user hasn't enough GDD or amount is < 0") } // validate recipient user @@ -349,11 +328,6 @@ export class TransactionResolver { throw new Error('invalid recipient public key') } - // validate amount - if (amount <= 0) { - throw new Error('invalid amount') - } - const centAmount = Math.trunc(amount * 10000) const queryRunner = getConnection().createQueryRunner() @@ -456,6 +430,6 @@ export class TransactionResolver { memo, }) - return 'success' + return true } } From acc49581cbf90bbfc7c68fb7616307478bdaae2d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 01:16:15 +0100 Subject: [PATCH 18/27] more refactors, fixed some todos and simplified things --- .../graphql/resolver/TransactionResolver.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 35f960f48..a490cec8a 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 } from '@dbTools/typeorm' +import { getCustomRepository, getConnection, QueryRunner, In } from '@dbTools/typeorm' import CONFIG from '../../config' import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail' @@ -47,7 +47,7 @@ async function calculateAndAddDecayTransactions( transactionIds.push(userTransaction.transactionId) }) - const transactions = await dbTransaction.find({ where: { id: transactionIds } }) + const transactions = await dbTransaction.find({ where: { id: In(transactionIds) } }) const transactionIndiced: dbTransaction[] = [] transactions.forEach((transaction: dbTransaction) => { @@ -114,7 +114,7 @@ async function calculateAndAddDecayTransactions( 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) // TODO properly enforce null + otherUser = userIndiced.find((u) => u.id === transaction.sendReceiverUserId) // finalTransaction.pubkey = sendCoin.recipiantPublic } else if (transaction.sendReceiverUserId === user.id) { finalTransaction.type = TransactionType.RECIEVE @@ -304,7 +304,6 @@ export class TransactionResolver { @Ctx() context: any, ): Promise { // TODO this is subject to replay attacks - // validate sender user (logged in) const userRepository = getCustomRepository(UserRepository) const senderUser = await userRepository.findByPubkeyHex(context.pubKey) if (senderUser.pubKey.length !== 32) { @@ -316,7 +315,6 @@ export class TransactionResolver { } // validate recipient user - // TODO: the detour over the public key is unnecessary const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true }) if (!recipientUser) { throw new Error('recipient not known') @@ -335,7 +333,7 @@ export class TransactionResolver { await queryRunner.startTransaction('READ UNCOMMITTED') try { // transaction - let transaction = new dbTransaction() + const transaction = new dbTransaction() transaction.transactionTypeId = TransactionTypeId.SEND transaction.memo = memo transaction.userId = senderUser.id @@ -344,13 +342,7 @@ export class TransactionResolver { transaction.sendReceiverPublicKey = recipientUser.pubKey transaction.amount = BigInt(centAmount) - // TODO: NO! this is problematic in its construction - const insertResult = await queryRunner.manager.insert(dbTransaction, transaction) - transaction = await queryRunner.manager - .findOneOrFail(dbTransaction, insertResult.generatedMaps[0].id) - .catch((error) => { - throw new Error('error loading saved transaction: ' + error) - }) + await queryRunner.manager.insert(dbTransaction, transaction) // Insert Transaction: sender - amount const senderUserTransactionBalance = await addUserTransaction( From 270ccbf1cf90f3ec37be0a6a1331b094aa91df14 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 01:23:58 +0100 Subject: [PATCH 19/27] removed unused symbol --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 3e387a611..81bd9b1a8 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -3,7 +3,7 @@ import fs from 'fs' import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql' -import { getConnection, getCustomRepository, getRepository, QueryRunner } from '@dbTools/typeorm' +import { getConnection, getCustomRepository, QueryRunner } from '@dbTools/typeorm' import CONFIG from '../../config' import { User } from '../model/User' import { User as DbUser } from '@entity/User' From 3040992c51cbccc10d1b9e956d2c874f10bdbcdc Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 01:46:01 +0100 Subject: [PATCH 20/27] fix query to use proper field --- 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 54898f770..6112e1d50 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -287,7 +287,7 @@ async function getUserCreations(id: number): Promise { .where('transactions.user_id = :id', { id }) .andWhere('transactions.transaction_type_id = :type', { type: TransactionTypeId.CREATION }) .andWhere({ - targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, { + creationDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, { date: dateBeforeLastMonth, endDate: dateNextMonth, }), From d715fd9c2fca31b44c362eae7ea48ae859486172 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 01:49:53 +0100 Subject: [PATCH 21/27] also set user.id as involved User on a creation --- backend/src/graphql/resolver/TransactionResolver.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index a490cec8a..b705694fe 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -48,12 +48,11 @@ async function calculateAndAddDecayTransactions( }) const transactions = await dbTransaction.find({ where: { id: In(transactionIds) } }) - const transactionIndiced: dbTransaction[] = [] transactions.forEach((transaction: dbTransaction) => { transactionIndiced[transaction.id] = transaction + involvedUserIds.push(transaction.userId) if (transaction.transactionTypeId === TransactionTypeId.SEND) { - involvedUserIds.push(transaction.userId) involvedUserIds.push(transaction.sendReceiverUserId!) // TODO ensure not null properly } }) @@ -146,7 +145,6 @@ async function calculateAndAddDecayTransactions( finalTransactions.push(decayTransaction) } } - return finalTransactions } @@ -233,7 +231,6 @@ export class TransactionResolver { const user = userId ? await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) : await userRepository.findByPubkeyHex(context.pubKey) - let limit = pageSize let offset = 0 let skipFirstTransaction = false From 2f9cb424285d66d431a1b96e6ffc956797f2a987 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 11:52:59 +0100 Subject: [PATCH 22/27] Update frontend/src/i18n.js Co-authored-by: Moriz Wahl --- frontend/src/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index 73125dd07..f4f969008 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -71,7 +71,7 @@ const dateTimeFormats = { year: 'numeric', month: 'long', day: 'numeric', - weekday: 'short', + weekday: 'long', hour: 'numeric', minute: 'numeric', }, From ea3ecbc6799f45059697826a6dfe5912aa0e691a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 12:00:02 +0100 Subject: [PATCH 23/27] admin resolver: make sure to write userId only when we create a new balance --- backend/src/graphql/resolver/AdminResolver.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 6112e1d50..0bb4822b3 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -261,9 +261,10 @@ export class AdminResolver { }) let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) - - if (!userBalance) userBalance = new Balance() - userBalance.userId = pendingCreation.userId + if (!userBalance) { + userBalance = new Balance() + userBalance.userId = pendingCreation.userId + } userBalance.amount = Number(newBalance) userBalance.modified = receivedCallDate userBalance.recordDate = receivedCallDate From 314a1b1ed45c9a63159b473efc00854b358c9fbf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 12:08:37 +0100 Subject: [PATCH 24/27] add todo --- backend/src/graphql/resolver/TransactionResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index b705694fe..d2d19a6a2 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -285,6 +285,7 @@ export class TransactionResolver { if (balanceEntity) { const now = new Date() transactionList.balance = roundFloorFrom4(balanceEntity.amount) + // TODO: Add a decay object here instead of static data representing the decay. transactionList.decay = roundFloorFrom4( calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance, ) From 21587287527983b6b2ecc16f54aa6a3b923415bf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 12:41:11 +0100 Subject: [PATCH 25/27] enum instead of value definition --- 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 0bb4822b3..f7f13ec55 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -224,7 +224,7 @@ export class AdminResolver { const receivedCallDate = new Date() let transaction = new Transaction() - transaction.transactionTypeId = 1 + transaction.transactionTypeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo transaction.received = receivedCallDate transaction.userId = pendingCreation.userId From bf64de06b11f741bf79dfe1d0a6aabdb450ac6c5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 13:00:20 +0100 Subject: [PATCH 26/27] reverse is destructing --- 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 d2d19a6a2..f0653da92 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -254,7 +254,7 @@ export class TransactionResolver { let transactions: Transaction[] = [] if (userTransactions.length) { if (order === Order.DESC) { - userTransactions = userTransactions.reverse() + userTransactions.reverse() } transactions = await calculateAndAddDecayTransactions( userTransactions, @@ -263,7 +263,7 @@ export class TransactionResolver { skipFirstTransaction, ) if (order === Order.DESC) { - transactions = transactions.reverse() + transactions.reverse() } } From a344011403db6e46ead41ed8a0020ead0623e589 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 21 Feb 2022 13:02:15 +0100 Subject: [PATCH 27/27] lint fix --- backend/src/graphql/resolver/TransactionResolver.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index f0653da92..7227f9621 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -242,13 +242,8 @@ export class TransactionResolver { offset-- } const userTransactionRepository = getCustomRepository(UserTransactionRepository) - let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged( - user.id, - limit, - offset, - order, - onlyCreations, - ) + const [userTransactions, userTransactionsCount] = + await userTransactionRepository.findByUserPaged(user.id, limit, offset, order, onlyCreations) skipFirstTransaction = userTransactionsCount > offset + limit const decay = !(currentPage > 1) let transactions: Transaction[] = []