From ec2d5cb98dcd95764a8c1afdad9b1e604a9b4f94 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 15 Nov 2024 19:58:50 +0100 Subject: [PATCH] handle deletiono of transaction link with dlt --- .../dltConnector/enum/DltTransactionType.ts | 8 ++ .../AbstractTransactionToDlt.role.ts | 5 +- .../TransactionLinkDeleteToDlt.role.ts | 83 +++++++++++++++++++ .../TransactionLinkToDlt.role.ts | 4 +- .../transactionToDlt/TransactionToDlt.role.ts | 32 ++++--- .../transactionToDlt/UserToDlt.role.ts | 4 +- .../transactionToDlt.context.ts | 2 + .../resolver/TransactionLinkResolver.ts | 8 ++ .../0088-merge_dlt_tables/DltTransaction.ts | 3 + .../0088-merge_dlt_tables/Transaction.ts | 2 +- .../0088-merge_dlt_tables/TransactionLink.ts | 2 +- database/migrations/0088-merge_dlt_tables.ts | 8 +- .../src/manager/KeyPairCacheManager.ts | 5 +- 13 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 backend/src/apis/dltConnector/enum/DltTransactionType.ts create mode 100644 backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkDeleteToDlt.role.ts diff --git a/backend/src/apis/dltConnector/enum/DltTransactionType.ts b/backend/src/apis/dltConnector/enum/DltTransactionType.ts new file mode 100644 index 000000000..80e12daa5 --- /dev/null +++ b/backend/src/apis/dltConnector/enum/DltTransactionType.ts @@ -0,0 +1,8 @@ +export enum DltTransactionType { + REGISTER_ADDRESS = 1, + CREATION = 2, + TRANSFER = 3, + DEFERRED_TRANSFER = 4, + REDEEM_DEFERRED_TRANSFER = 5, + DELETE_DEFERRED_TRANSFER = 6, +} diff --git a/backend/src/apis/dltConnector/interaction/transactionToDlt/AbstractTransactionToDlt.role.ts b/backend/src/apis/dltConnector/interaction/transactionToDlt/AbstractTransactionToDlt.role.ts index e21b853e6..ac77f4195 100644 --- a/backend/src/apis/dltConnector/interaction/transactionToDlt/AbstractTransactionToDlt.role.ts +++ b/backend/src/apis/dltConnector/interaction/transactionToDlt/AbstractTransactionToDlt.role.ts @@ -22,7 +22,7 @@ export abstract class AbstractTransactionToDltRole { const dltTransaction = DltTransaction.create() dltTransaction.messageId = messageId dltTransaction.error = error - this.setJoinId(dltTransaction) + this.setJoinIdAndType(dltTransaction) await DltTransaction.save(dltTransaction) if (dltTransaction.error) { logger.error( @@ -36,7 +36,7 @@ export abstract class AbstractTransactionToDltRole { } // intern - protected abstract setJoinId(dltTransaction: DltTransaction): void + protected abstract setJoinIdAndType(dltTransaction: DltTransaction): void // helper protected createQueryForPendingItems( @@ -50,7 +50,6 @@ export abstract class AbstractTransactionToDltRole { .andWhere('dltTransaction.transaction_id IS NULL') .andWhere('dltTransaction.transaction_link_Id IS NULL') .orderBy(orderBy) - .limit(1) } protected createDltTransactionEntry(messageId: string, error: string | null): DltTransaction { diff --git a/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkDeleteToDlt.role.ts b/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkDeleteToDlt.role.ts new file mode 100644 index 000000000..742a0b96d --- /dev/null +++ b/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkDeleteToDlt.role.ts @@ -0,0 +1,83 @@ +import { DltTransaction } from '@entity/DltTransaction' +import { TransactionLink } from '@entity/TransactionLink' + +import { DltTransactionType } from '@dltConnector/enum/DltTransactionType' +import { TransactionType } from '@dltConnector/enum/TransactionType' +import { CommunityUser } from '@dltConnector/model/CommunityUser' +import { IdentifierSeed } from '@dltConnector/model/IdentifierSeed' +import { TransactionDraft } from '@dltConnector/model/TransactionDraft' +import { UserIdentifier } from '@dltConnector/model/UserIdentifier' + +import { LogError } from '@/server/LogError' + +import { AbstractTransactionToDltRole } from './AbstractTransactionToDlt.role' + +/** + * redeem deferred transfer transaction by creator, so "deleting" it + */ +export class TransactionLinkDeleteToDltRole extends AbstractTransactionToDltRole { + async initWithLast(): Promise { + const queryBuilder = this.createQueryForPendingItems( + TransactionLink.createQueryBuilder().leftJoinAndSelect('TransactionLink.user', 'user'), + 'TransactionLink.id = dltTransaction.transactionLinkId and dltTransaction.type_id <> 4', + // eslint-disable-next-line camelcase + { TransactionLink_deletedAt: 'ASC', User_id: 'ASC' }, + ) + .andWhere('TransactionLink.deletedAt IS NOT NULL') + .withDeleted() + const queryBuilder2 = TransactionLink.createQueryBuilder() + .leftJoinAndSelect('TransactionLink.user', 'user') + .where('TransactionLink.deletedAt IS NOT NULL') + .andWhere(() => { + const subQuery = DltTransaction.createQueryBuilder() + .select('1') + .where('DltTransaction.transaction_link_id = TransactionLink.id') + .andWhere('DltTransaction.type_id = :typeId', { + typeId: DltTransactionType.DELETE_DEFERRED_TRANSFER, + }) + .getQuery() + return `NOT EXIST (${subQuery})` + }) + .withDeleted() + // eslint-disable-next-line camelcase + .orderBy({ TransactionLink_deletedAt: 'ASC', User_id: 'ASC' }) + // console.log('query: ', queryBuilder.getSql()) + this.self = await queryBuilder.getOne() + return this + } + + public getTimestamp(): number { + if (!this.self) { + return Infinity + } + if (!this.self.deletedAt) { + throw new LogError('not deleted transaction link selected') + } + return this.self.deletedAt.getTime() + } + + public convertToGraphqlInput(): TransactionDraft { + if (!this.self) { + throw new LogError('try to create dlt entry for empty transaction link') + } + if (!this.self.deletedAt) { + throw new LogError('not deleted transaction link selected') + } + const draft = new TransactionDraft() + draft.amount = this.self.amount.abs().toString() + const user = this.self.user + draft.user = new UserIdentifier(user.communityUuid, new IdentifierSeed(this.self.code)) + draft.linkedUser = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID)) + draft.createdAt = this.self.deletedAt.toISOString() + draft.type = TransactionType.GRADIDO_TRANSFER + return draft + } + + protected setJoinIdAndType(dltTransaction: DltTransaction): void { + if (!this.self) { + throw new LogError('try to create dlt entry for empty transaction link') + } + dltTransaction.transactionLinkId = this.self.id + dltTransaction.typeId = DltTransactionType.DELETE_DEFERRED_TRANSFER + } +} diff --git a/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkToDlt.role.ts b/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkToDlt.role.ts index 8aeccfa91..916aacbfc 100644 --- a/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkToDlt.role.ts +++ b/backend/src/apis/dltConnector/interaction/transactionToDlt/TransactionLinkToDlt.role.ts @@ -1,6 +1,7 @@ import { DltTransaction } from '@entity/DltTransaction' import { TransactionLink } from '@entity/TransactionLink' +import { DltTransactionType } from '@dltConnector/enum/DltTransactionType' import { TransactionType } from '@dltConnector/enum/TransactionType' import { CommunityUser } from '@dltConnector/model/CommunityUser' import { IdentifierSeed } from '@dltConnector/model/IdentifierSeed' @@ -47,10 +48,11 @@ export class TransactionLinkToDltRole extends AbstractTransactionToDltRole { + private type: DltTransactionType async initWithLast(): Promise { this.self = await this.createQueryForPendingItems( Transaction.createQueryBuilder().leftJoinAndSelect( @@ -27,7 +29,7 @@ export class TransactionToDltRole extends AbstractTransactionToDltRole :typeId', { typeId: TransactionTypeId.RECEIVE }) .getOne() return this } @@ -55,6 +57,19 @@ export class TransactionToDltRole extends AbstractTransactionToDltRole { return draft } - protected setJoinId(dltTransaction: DltTransaction): void { + protected setJoinIdAndType(dltTransaction: DltTransaction): void { if (!this.self) { throw new LogError('try to create dlt entry for empty user') } dltTransaction.userId = this.self.id + dltTransaction.typeId = DltTransactionType.REGISTER_ADDRESS } } diff --git a/backend/src/apis/dltConnector/interaction/transactionToDlt/transactionToDlt.context.ts b/backend/src/apis/dltConnector/interaction/transactionToDlt/transactionToDlt.context.ts index 7bf271cd0..bd49c8400 100644 --- a/backend/src/apis/dltConnector/interaction/transactionToDlt/transactionToDlt.context.ts +++ b/backend/src/apis/dltConnector/interaction/transactionToDlt/transactionToDlt.context.ts @@ -6,6 +6,7 @@ import { DltConnectorClient } from '@/apis/dltConnector/DltConnectorClient' import { backendLogger as logger } from '@/server/logger' import { AbstractTransactionToDltRole } from './AbstractTransactionToDlt.role' +import { TransactionLinkDeleteToDltRole } from './TransactionLinkDeleteToDlt.role' import { TransactionLinkToDltRole } from './TransactionLinkToDlt.role' import { TransactionToDltRole } from './TransactionToDlt.role' import { UserToDltRole } from './UserToDlt.role' @@ -23,6 +24,7 @@ export async function transactionToDlt(dltConnector: DltConnectorClient): Promis new TransactionToDltRole().initWithLast(), new UserToDltRole().initWithLast(), new TransactionLinkToDltRole().initWithLast(), + new TransactionLinkDeleteToDltRole().initWithLast(), ]) // sort array to get oldest at first place diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 88f84b6e2..68bcd5f09 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -33,6 +33,10 @@ import { Context, getUser, getClientTimezoneOffset } from '@/server/context' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' import { calculateDecay } from '@/util/decay' +import { + InterruptiveSleepManager, + TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY, +} from '@/util/InterruptiveSleepManager' import { TRANSACTION_LINK_LOCK } from '@/util/TRANSACTION_LINK_LOCK' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { fullName } from '@/util/utilities' @@ -300,6 +304,8 @@ export class TransactionLinkResolver { contributionLink, contributionLink.amount, ) + // notify dlt-connector loop for new work + InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Creation from contribution link was not successful', e) @@ -354,6 +360,8 @@ export class TransactionLinkResolver { transactionLink, transactionLink.amount, ) + // notify dlt-connector loop for new work + InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY) } finally { releaseLinkLock() } diff --git a/database/entity/0088-merge_dlt_tables/DltTransaction.ts b/database/entity/0088-merge_dlt_tables/DltTransaction.ts index 6525c229d..012bddf0b 100644 --- a/database/entity/0088-merge_dlt_tables/DltTransaction.ts +++ b/database/entity/0088-merge_dlt_tables/DltTransaction.ts @@ -17,6 +17,9 @@ export class DltTransaction extends BaseEntity { @Column({ name: 'transaction_link_id', type: 'int', unsigned: true, nullable: true }) transactionLinkId?: number | null + @Column({ name: 'type_id', unsigned: true, nullable: false }) + typeId: number + @Column({ name: 'message_id', length: 64, diff --git a/database/entity/0088-merge_dlt_tables/Transaction.ts b/database/entity/0088-merge_dlt_tables/Transaction.ts index d5abed738..8eec4e1a9 100644 --- a/database/entity/0088-merge_dlt_tables/Transaction.ts +++ b/database/entity/0088-merge_dlt_tables/Transaction.ts @@ -171,6 +171,6 @@ export class Transaction extends BaseEntity { previousTransaction?: Transaction | null @ManyToOne(() => TransactionLink, (transactionLink) => transactionLink.transactions) - @JoinColumn({ name: 'transactionLinkId' }) + @JoinColumn({ name: 'transaction_link_id' }) transactionLink?: TransactionLink | null } diff --git a/database/entity/0088-merge_dlt_tables/TransactionLink.ts b/database/entity/0088-merge_dlt_tables/TransactionLink.ts index 937544bd9..18bcf9892 100644 --- a/database/entity/0088-merge_dlt_tables/TransactionLink.ts +++ b/database/entity/0088-merge_dlt_tables/TransactionLink.ts @@ -80,6 +80,6 @@ export class TransactionLink extends BaseEntity { user: User @OneToMany(() => Transaction, (transaction) => transaction.transactionLink) - @JoinColumn({ referencedColumnName: 'transactionLinkId' }) + @JoinColumn({ referencedColumnName: 'transaction_link_id' }) transactions: Transaction[] } diff --git a/database/migrations/0088-merge_dlt_tables.ts b/database/migrations/0088-merge_dlt_tables.ts index 7964ffd94..ab3db11eb 100644 --- a/database/migrations/0088-merge_dlt_tables.ts +++ b/database/migrations/0088-merge_dlt_tables.ts @@ -7,7 +7,9 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis ALTER TABLE \`dlt_transactions\` CHANGE \`transaction_id\` \`transaction_id\` INT(10) UNSIGNED NULL DEFAULT NULL, ADD \`user_id\` INT UNSIGNED NULL DEFAULT NULL AFTER \`transaction_id\`, - ADD \`transaction_link_id\` INT UNSIGNED NULL DEFAULT NULL AFTER \`user_id\`; + ADD \`transaction_link_id\` INT UNSIGNED NULL DEFAULT NULL AFTER \`user_id\` + ADD \`type_id\` INT UNSIGNED NOT NULL AFTER \`transaction_link_id\` + ; `) } @@ -28,6 +30,8 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom ALTER TABLE \`dlt_transactions\` CHANGE \`transaction_id\` \`transaction_id\` INT(10) UNSIGNED NOT NULL, DROP COLUMN \`user_id\`, - DROP COLUMN \`transaction_link_id\`; + DROP COLUMN \`transaction_link_id\` + DROP COLUMN \`type_id\` + ; `) } diff --git a/dlt-connector/src/manager/KeyPairCacheManager.ts b/dlt-connector/src/manager/KeyPairCacheManager.ts index 5f5d94f41..f492d127b 100644 --- a/dlt-connector/src/manager/KeyPairCacheManager.ts +++ b/dlt-connector/src/manager/KeyPairCacheManager.ts @@ -1,7 +1,7 @@ import { KeyPairEd25519 } from 'gradido-blockchain-js' -import { IdentifierSeed } from '@/graphql/input/IdentifierSeed' import { UserIdentifier } from '@/graphql/input/UserIdentifier' +import { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' // Source: https://refactoring.guru/design-patterns/singleton/typescript/example @@ -52,7 +52,8 @@ export class KeyPairCacheManager { public addKeyPair(input: UserIdentifier | string, keyPair: KeyPairEd25519): void { const key = this.getKey(input) if (this.cache.has(key)) { - throw new LogError('key already exist, cannot add', key) + logger.warn('key already exist, cannot add', key) + return } this.cache.set(key, keyPair) }