mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
direct sending transaction types to dlt
This commit is contained in:
parent
9f22e5b16d
commit
4e3d119f12
@ -7,8 +7,12 @@ import {
|
||||
Community as DbCommunity,
|
||||
Contribution as DbContribution,
|
||||
DltTransaction as DbDltTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
getHomeCommunity,
|
||||
getCommunityByUuid,
|
||||
getHomeCommunity,
|
||||
getUserById,
|
||||
UserLoggingView,
|
||||
} from 'database'
|
||||
import { TransactionDraft } from './model/TransactionDraft'
|
||||
|
||||
@ -92,7 +96,15 @@ export async function transferTransaction(
|
||||
memo: string,
|
||||
createdAt: Date
|
||||
): Promise<DbDltTransaction | null> {
|
||||
|
||||
// load community if not already loaded, maybe they are remote communities
|
||||
if (!senderUser.community) {
|
||||
senderUser.community = await getCommunityByUuid(senderUser.communityUuid)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
recipientUser.community = await getCommunityByUuid(recipientUser.communityUuid)
|
||||
}
|
||||
logger.info(`sender user: ${new UserLoggingView(senderUser)}`)
|
||||
logger.info(`recipient user: ${new UserLoggingView(recipientUser)}`)
|
||||
const draft = TransactionDraft.createTransfer(senderUser, recipientUser, amount, memo, createdAt)
|
||||
if (draft && dltConnectorClient) {
|
||||
const clientResponse = dltConnectorClient.sendTransaction(draft)
|
||||
@ -104,4 +116,50 @@ export async function transferTransaction(
|
||||
return null
|
||||
}
|
||||
|
||||
export async function deferredTransferTransaction(senderUser: DbUser, transactionLink: DbTransactionLink)
|
||||
: Promise<DbDltTransaction | null> {
|
||||
// load community if not already loaded
|
||||
if (!senderUser.community) {
|
||||
senderUser.community = await getCommunityByUuid(senderUser.communityUuid)
|
||||
}
|
||||
const draft = TransactionDraft.createDeferredTransfer(senderUser, transactionLink)
|
||||
if (draft && dltConnectorClient) {
|
||||
const clientResponse = dltConnectorClient.sendTransaction(draft)
|
||||
let dltTransaction = new DbDltTransaction()
|
||||
dltTransaction.typeId = DltTransactionType.DEFERRED_TRANSFER
|
||||
dltTransaction = await checkDltConnectorResult(dltTransaction, clientResponse)
|
||||
return await dltTransaction.save()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function redeemDeferredTransferTransaction(transactionLink: DbTransactionLink, amount: string, createdAt: Date, recipientUser: DbUser)
|
||||
: Promise<DbDltTransaction | null> {
|
||||
// load user and communities if not already loaded
|
||||
if (!transactionLink.user) {
|
||||
logger.debug('load sender user')
|
||||
transactionLink.user = await getUserById(transactionLink.userId, true, false)
|
||||
}
|
||||
if (!transactionLink.user.community) {
|
||||
logger.debug('load sender community')
|
||||
transactionLink.user.community = await getCommunityByUuid(transactionLink.user.communityUuid)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
logger.debug('load recipient community')
|
||||
recipientUser.community = await getCommunityByUuid(recipientUser.communityUuid)
|
||||
}
|
||||
logger.debug(`sender: ${new UserLoggingView(transactionLink.user)}`)
|
||||
logger.debug(`recipient: ${new UserLoggingView(recipientUser)}`)
|
||||
const draft = TransactionDraft.redeemDeferredTransfer(transactionLink, amount, createdAt, recipientUser)
|
||||
if (draft && dltConnectorClient) {
|
||||
const clientResponse = dltConnectorClient.sendTransaction(draft)
|
||||
let dltTransaction = new DbDltTransaction()
|
||||
dltTransaction.typeId = DltTransactionType.REDEEM_DEFERRED_TRANSFER
|
||||
dltTransaction = await checkDltConnectorResult(dltTransaction, clientResponse)
|
||||
return await dltTransaction.save()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -3,10 +3,17 @@ import { AccountType } from '@dltConnector/enum/AccountType'
|
||||
import { TransactionType } from '@dltConnector/enum/TransactionType'
|
||||
|
||||
import { AccountIdentifier } from './AccountIdentifier'
|
||||
import { Community as DbCommunity, Contribution as DbContribution, User as DbUser } from 'database'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
Contribution as DbContribution,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser
|
||||
} from 'database'
|
||||
import { CommunityAccountIdentifier } from './CommunityAccountIdentifier'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { IdentifierSeed } from './IdentifierSeed'
|
||||
import { CODE_VALID_DAYS_DURATION } from '@/graphql/resolver/const/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.dltConnector.model.TransactionDraft`)
|
||||
|
||||
@ -21,7 +28,7 @@ export class TransactionDraft {
|
||||
createdAt: string
|
||||
// only for creation transaction
|
||||
targetDate?: string
|
||||
// only for deferred transaction
|
||||
// only for deferred transaction, duration in seconds
|
||||
timeoutDuration?: number
|
||||
// only for register address
|
||||
accountType?: AccountType
|
||||
@ -75,4 +82,48 @@ export class TransactionDraft {
|
||||
draft.memo = memo
|
||||
return draft
|
||||
}
|
||||
|
||||
static createDeferredTransfer(sendingUser: DbUser, transactionLink: DbTransactionLink)
|
||||
: TransactionDraft | null {
|
||||
if (!sendingUser.community) {
|
||||
throw new Error(`missing community for user ${sendingUser.id}`)
|
||||
}
|
||||
const senderUserTopic = sendingUser.community.hieroTopicId
|
||||
if (!senderUserTopic) {
|
||||
throw new Error(`missing topicId for community ${sendingUser.community.id}`)
|
||||
}
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new CommunityAccountIdentifier(sendingUser.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(senderUserTopic, new IdentifierSeed(transactionLink.code))
|
||||
draft.type = TransactionType.GRADIDO_DEFERRED_TRANSFER
|
||||
draft.createdAt = transactionLink.createdAt.toISOString()
|
||||
draft.amount = transactionLink.amount.toString()
|
||||
draft.memo = transactionLink.memo
|
||||
draft.timeoutDuration = CODE_VALID_DAYS_DURATION * 24 * 60 * 60
|
||||
return draft
|
||||
}
|
||||
|
||||
static redeemDeferredTransfer(transactionLink: DbTransactionLink, amount: string, createdAt: Date, recipientUser: DbUser): TransactionDraft | null {
|
||||
if (!transactionLink.user.community) {
|
||||
throw new Error(`missing community for user ${transactionLink.user.id}`)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
throw new Error(`missing community for user ${recipientUser.id}`)
|
||||
}
|
||||
const senderUserTopic = transactionLink.user.community.hieroTopicId
|
||||
if (!senderUserTopic) {
|
||||
throw new Error(`missing topicId for community ${transactionLink.user.community.id}`)
|
||||
}
|
||||
const recipientUserTopic = recipientUser.community.hieroTopicId
|
||||
if (!recipientUserTopic) {
|
||||
throw new Error(`missing topicId for community ${recipientUser.community.id}`)
|
||||
}
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new IdentifierSeed(transactionLink.code))
|
||||
draft.linkedUser = new AccountIdentifier(recipientUserTopic, new CommunityAccountIdentifier(recipientUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_REDEEM_DEFERRED_TRANSFER
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = amount
|
||||
return draft
|
||||
}
|
||||
}
|
||||
@ -436,9 +436,6 @@ export class ContributionResolver {
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('contribution', id)
|
||||
|
||||
let transaction: DbTransaction
|
||||
let dltTransactionPromise: Promise<DbDltTransaction | null>
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
try {
|
||||
@ -463,8 +460,7 @@ export class ContributionResolver {
|
||||
throw new LogError('Can not confirm contribution since the user was deleted')
|
||||
}
|
||||
const receivedCallDate = new Date()
|
||||
dltTransactionPromise = contributionTransaction(contribution, moderatorUser, receivedCallDate)
|
||||
|
||||
const dltTransactionPromise = contributionTransaction(contribution, moderatorUser, receivedCallDate)
|
||||
const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false)
|
||||
validateContribution(
|
||||
creations,
|
||||
@ -476,7 +472,6 @@ export class ContributionResolver {
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
|
||||
|
||||
const lastTransaction = await getLastTransaction(contribution.userId)
|
||||
logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined')
|
||||
|
||||
@ -493,7 +488,7 @@ export class ContributionResolver {
|
||||
}
|
||||
newBalance = newBalance.add(contribution.amount.toString())
|
||||
|
||||
transaction = new DbTransaction()
|
||||
let transaction = new DbTransaction()
|
||||
transaction.typeId = TransactionTypeId.CREATION
|
||||
transaction.memo = contribution.memo
|
||||
transaction.userId = contribution.userId
|
||||
@ -520,7 +515,7 @@ export class ContributionResolver {
|
||||
await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
|
||||
|
||||
logger.info('creation commited successfuly.')
|
||||
await sendContributionConfirmedEmail({
|
||||
firstName: user.firstName,
|
||||
@ -536,6 +531,17 @@ export class ContributionResolver {
|
||||
contribution.createdAt,
|
||||
),
|
||||
})
|
||||
|
||||
// update transaction id in dlt transaction tables
|
||||
// wait for finishing transaction by dlt-connector/hiero
|
||||
const dltStartTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
if(dltTransaction) {
|
||||
dltTransaction.transactionId = transaction.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
const dltEndTime = new Date()
|
||||
logger.debug(`dlt-connector contribution finished in ${dltEndTime.getTime() - dltStartTime.getTime()} ms`)
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Creation was not successful', e)
|
||||
@ -546,16 +552,6 @@ export class ContributionResolver {
|
||||
} finally {
|
||||
releaseLock()
|
||||
}
|
||||
// update transaction id in dlt transaction tables
|
||||
// wait for finishing transaction by dlt-connector/hiero
|
||||
const startTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
if(dltTransaction) {
|
||||
dltTransaction.transactionId = transaction.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
const endTime = new Date()
|
||||
logger.debug(`dlt-connector contribution finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@ -14,10 +14,13 @@ import { User } from '@model/User'
|
||||
import { QueryLinkResult } from '@union/QueryLinkResult'
|
||||
import { Decay, interpretEncryptedTransferArgs, TransactionTypeId } from 'core'
|
||||
import {
|
||||
AppDatabase, Community as DbCommunity, Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink, FederatedCommunity as DbFederatedCommunity, Transaction as DbTransaction,
|
||||
AppDatabase, Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink, FederatedCommunity as DbFederatedCommunity,
|
||||
DltTransaction as DbDltTransaction,
|
||||
Transaction as DbTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
findModeratorCreatingContributionLink,
|
||||
findTransactionLinkByCode,
|
||||
getHomeCommunity
|
||||
} from 'database'
|
||||
@ -33,14 +36,10 @@ import {
|
||||
} from '@/event/Events'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import {
|
||||
InterruptiveSleepManager,
|
||||
TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY,
|
||||
} from '@/util/InterruptiveSleepManager'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
import { fullName } from 'core'
|
||||
import { TRANSACTION_LINK_LOCK, TRANSACTIONS_LOCK } from 'database'
|
||||
import { calculateDecay, decode, DisburseJwtPayloadType, encode, encryptAndSign, EncryptedJWEJwtPayloadType, RedeemJwtPayloadType, verify } from 'shared'
|
||||
import { calculateDecay, decode, DisburseJwtPayloadType, encode, encryptAndSign, RedeemJwtPayloadType, verify } from 'shared'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { DisbursementClient as V1_0_DisbursementClient } from '@/federation/client/1_0/DisbursementClient'
|
||||
@ -58,6 +57,8 @@ import {
|
||||
import { getUserCreation, validateContribution } from './util/creations'
|
||||
import { transactionLinkList } from './util/transactionLinkList'
|
||||
import { SignedTransferPayloadType } from 'shared'
|
||||
import { contributionTransaction, deferredTransferTransaction, redeemDeferredTransferTransaction } from '@/apis/dltConnector'
|
||||
import { CODE_VALID_DAYS_DURATION } from './const/const'
|
||||
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionLinkResolver.${method}`)
|
||||
|
||||
@ -71,7 +72,7 @@ export const transactionLinkCode = (date: Date): string => {
|
||||
)
|
||||
}
|
||||
|
||||
const CODE_VALID_DAYS_DURATION = 14
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export const transactionLinkExpireDate = (date: Date): Date => {
|
||||
@ -109,11 +110,20 @@ export class TransactionLinkResolver {
|
||||
transactionLink.code = transactionLinkCode(createdDate)
|
||||
transactionLink.createdAt = createdDate
|
||||
transactionLink.validUntil = validUntil
|
||||
const dltTransactionPromise = deferredTransferTransaction(user, transactionLink)
|
||||
await DbTransactionLink.save(transactionLink).catch((e) => {
|
||||
throw new LogError('Unable to save transaction link', e)
|
||||
})
|
||||
await EVENT_TRANSACTION_LINK_CREATE(user, transactionLink, amount)
|
||||
|
||||
// wait for dlt transaction to be created
|
||||
const startTime = Date.now()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = Date.now()
|
||||
createLogger('createTransactionLink').debug(`dlt transaction created in ${endTime - startTime} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionLinkId = transactionLink.id
|
||||
await DbDltTransaction.save(dltTransaction)
|
||||
}
|
||||
return new TransactionLink(transactionLink, new User(user))
|
||||
}
|
||||
|
||||
@ -137,7 +147,6 @@ export class TransactionLinkResolver {
|
||||
user.id,
|
||||
)
|
||||
}
|
||||
|
||||
if (transactionLink.redeemedBy) {
|
||||
throw new LogError('Transaction link already redeemed', transactionLink.redeemedBy)
|
||||
}
|
||||
@ -146,7 +155,19 @@ export class TransactionLinkResolver {
|
||||
throw new LogError('Transaction link could not be deleted', e)
|
||||
})
|
||||
|
||||
transactionLink.user = user
|
||||
const dltTransactionPromise = redeemDeferredTransferTransaction(transactionLink, transactionLink.amount.toString(), transactionLink.deletedAt!, user)
|
||||
|
||||
await EVENT_TRANSACTION_LINK_DELETE(user, transactionLink)
|
||||
// wait for dlt transaction to be created
|
||||
const startTime = Date.now()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = Date.now()
|
||||
createLogger('deleteTransactionLink').debug(`dlt transaction created in ${endTime - startTime} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionLinkId = transactionLink.id
|
||||
await DbDltTransaction.save(dltTransaction)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -279,7 +300,7 @@ export class TransactionLinkResolver {
|
||||
throw new LogError('Contribution link has unknown cycle', contributionLink.cycle)
|
||||
}
|
||||
}
|
||||
|
||||
const moderatorPromise = findModeratorCreatingContributionLink(contributionLink)
|
||||
const creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
methodLogger.info('open creations', creations)
|
||||
validateContribution(creations, contributionLink.amount, now, clientTimezoneOffset)
|
||||
@ -293,6 +314,12 @@ export class TransactionLinkResolver {
|
||||
contribution.contributionType = ContributionType.LINK
|
||||
contribution.contributionStatus = ContributionStatus.CONFIRMED
|
||||
|
||||
let dltTransactionPromise: Promise<DbDltTransaction | null> = Promise.resolve(null)
|
||||
const moderator = await moderatorPromise
|
||||
if (moderator) {
|
||||
dltTransactionPromise = contributionTransaction(contribution, moderator, now)
|
||||
}
|
||||
|
||||
await queryRunner.manager.insert(DbContribution, contribution)
|
||||
|
||||
const lastTransaction = await getLastTransaction(user.id)
|
||||
@ -338,6 +365,17 @@ export class TransactionLinkResolver {
|
||||
contributionLink,
|
||||
contributionLink.amount,
|
||||
)
|
||||
if (dltTransactionPromise) {
|
||||
const startTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = new Date()
|
||||
methodLogger.info(`dlt-connector transaction finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionId = transaction.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Creation from contribution link was not successful', e)
|
||||
@ -346,10 +384,7 @@ export class TransactionLinkResolver {
|
||||
}
|
||||
} finally {
|
||||
releaseLock()
|
||||
}
|
||||
// notify dlt-connector loop for new work
|
||||
InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY)
|
||||
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
const now = new Date()
|
||||
@ -399,8 +434,6 @@ export class TransactionLinkResolver {
|
||||
} finally {
|
||||
releaseLinkLock()
|
||||
}
|
||||
// notify dlt-connector loop for new work
|
||||
InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,9 @@ import {
|
||||
Transaction as dbTransaction,
|
||||
TransactionLink as dbTransactionLink,
|
||||
User as dbUser,
|
||||
findUserByIdentifier
|
||||
findUserByIdentifier,
|
||||
TransactionLoggingView,
|
||||
UserLoggingView
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
|
||||
@ -43,7 +45,7 @@ import { GdtResolver } from './GdtResolver'
|
||||
import { getCommunityName, isHomeCommunity } from './util/communities'
|
||||
import { getTransactionList } from './util/getTransactionList'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
import { transferTransaction } from '@/apis/dltConnector'
|
||||
import { transferTransaction, redeemDeferredTransferTransaction } from '@/apis/dltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionResolver`)
|
||||
@ -60,13 +62,15 @@ export const executeTransaction = async (
|
||||
let dltTransactionPromise: Promise<DbDltTransaction | null> = Promise.resolve(null)
|
||||
if (!transactionLink) {
|
||||
dltTransactionPromise = transferTransaction(sender, recipient, amount.toString(), memo, receivedCallDate)
|
||||
} else {
|
||||
dltTransactionPromise = redeemDeferredTransferTransaction(transactionLink, amount.toString(), receivedCallDate, recipient)
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
|
||||
try {
|
||||
logger.info('executeTransaction', amount, memo, sender, recipient)
|
||||
logger.info('executeTransaction', memo)
|
||||
|
||||
if (await countOpenPendingTransactions([sender.gradidoID, recipient.gradidoID]) > 0) {
|
||||
throw new LogError(
|
||||
@ -85,7 +89,7 @@ export const executeTransaction = async (
|
||||
receivedCallDate,
|
||||
transactionLink,
|
||||
)
|
||||
logger.debug(`calculated Balance=${sendBalance}`)
|
||||
logger.debug(`calculated balance=${sendBalance?.balance.toString()} decay=${sendBalance?.decay.decay.toString()} lastTransactionId=${sendBalance?.lastTransactionId}`)
|
||||
if (!sendBalance) {
|
||||
throw new LogError('User has not enough GDD or amount is < 0', sendBalance)
|
||||
}
|
||||
@ -144,7 +148,7 @@ export const executeTransaction = async (
|
||||
// Save linked transaction id for send
|
||||
transactionSend.linkedTransactionId = transactionReceive.id
|
||||
await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend)
|
||||
logger.debug('send Transaction updated', transactionSend)
|
||||
logger.debug('send Transaction updated', new TransactionLoggingView(transactionSend).toJSON())
|
||||
|
||||
if (transactionLink) {
|
||||
logger.info('transactionLink', transactionLink)
|
||||
@ -158,21 +162,23 @@ export const executeTransaction = async (
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
// update dltTransaction with transactionId
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionId = transactionSend.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
|
||||
await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount)
|
||||
|
||||
await EVENT_TRANSACTION_RECEIVE(
|
||||
recipient,
|
||||
sender,
|
||||
transactionReceive,
|
||||
transactionReceive.amount,
|
||||
)
|
||||
// update dltTransaction with transactionId
|
||||
const startTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = new Date()
|
||||
logger.debug(`dlt-connector transaction finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionId = transactionSend.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Transaction was not successful', e)
|
||||
|
||||
@ -12,3 +12,4 @@ export const MEMO_MAX_CHARS = 512
|
||||
export const MEMO_MIN_CHARS = 5
|
||||
export const DEFAULT_PAGINATION_PAGE_SIZE = 25
|
||||
export const FRONTEND_CONTRIBUTIONS_ITEM_ANCHOR_PREFIX = 'contributionListItem-'
|
||||
export const CODE_VALID_DAYS_DURATION = 14
|
||||
@ -1,4 +1,4 @@
|
||||
import { delay } from './utilities'
|
||||
import { delay } from 'core'
|
||||
|
||||
/**
|
||||
* Sleep, that can be interrupted
|
||||
|
||||
@ -8,7 +8,7 @@ enum OptInType {
|
||||
}
|
||||
|
||||
export class UserContactLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserContact) {
|
||||
public constructor(private self: UserContact, private showUser = true) {
|
||||
super()
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ export class UserContactLoggingView extends AbstractLoggingView {
|
||||
return {
|
||||
id: this.self.id,
|
||||
type: this.self.type,
|
||||
user: this.self.user
|
||||
user: this.showUser && this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
email: this.self.email?.substring(0, 3) + '...',
|
||||
|
||||
@ -4,6 +4,7 @@ import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
|
||||
import { UserContactLoggingView } from './UserContactLogging.view'
|
||||
import { UserRoleLoggingView } from './UserRoleLogging.view'
|
||||
import { CommunityLoggingView } from './CommunityLogging.view'
|
||||
|
||||
enum PasswordEncryptionType {
|
||||
NO_PASSWORD = 0,
|
||||
@ -21,10 +22,12 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
id: this.self.id,
|
||||
foreign: this.self.foreign,
|
||||
gradidoID: this.self.gradidoID,
|
||||
communityUuid: this.self.communityUuid,
|
||||
community: this.self.community
|
||||
? new CommunityLoggingView(this.self.community).toJSON()
|
||||
: { id: this.self.communityUuid },
|
||||
alias: this.self.alias?.substring(0, 3) + '...',
|
||||
emailContact: this.self.emailContact
|
||||
? new UserContactLoggingView(this.self.emailContact).toJSON()
|
||||
? new UserContactLoggingView(this.self.emailContact, false).toJSON()
|
||||
: { id: this.self.emailId },
|
||||
firstName: this.self.firstName?.substring(0, 3) + '...',
|
||||
lastName: this.self.lastName?.substring(0, 3) + '...',
|
||||
@ -35,7 +38,7 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
hideAmountGDD: this.self.hideAmountGDD,
|
||||
hideAmountGDT: this.self.hideAmountGDT,
|
||||
userRoles: this.self.userRoles
|
||||
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole).toJSON())
|
||||
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole, false).toJSON())
|
||||
: undefined,
|
||||
referrerId: this.self.referrerId,
|
||||
contributionLinkId: this.self.contributionLinkId,
|
||||
@ -50,7 +53,7 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
: undefined,
|
||||
userContacts: this.self.userContacts
|
||||
? this.self.userContacts.map((userContact) =>
|
||||
new UserContactLoggingView(userContact).toJSON(),
|
||||
new UserContactLoggingView(userContact, false).toJSON(),
|
||||
)
|
||||
: undefined,
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@ import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class UserRoleLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserRole) {
|
||||
public constructor(private self: UserRole, private showUser = true) {
|
||||
super()
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
user: this.self.user
|
||||
user: this.showUser && this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
role: this.self.role,
|
||||
|
||||
19
database/src/queries/events.ts
Normal file
19
database/src/queries/events.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {
|
||||
ContributionLink as DbContributionLink,
|
||||
Event as DbEvent,
|
||||
User as DbUser
|
||||
} from '../entity'
|
||||
|
||||
export async function findModeratorCreatingContributionLink(contributionLink: DbContributionLink): Promise<DbUser | undefined> {
|
||||
const event = await DbEvent.findOne(
|
||||
{
|
||||
where: {
|
||||
involvedContributionLinkId: contributionLink.id,
|
||||
// todo: move event types into db
|
||||
type: 'ADMIN_CONTRIBUTION_LINK_CREATE'
|
||||
},
|
||||
relations: { actingUser: true }
|
||||
}
|
||||
)
|
||||
return event?.actingUser
|
||||
}
|
||||
@ -5,5 +5,6 @@ export * from './communities'
|
||||
export * from './pendingTransactions'
|
||||
export * from './transactions'
|
||||
export * from './transactionLinks'
|
||||
export * from './events'
|
||||
|
||||
export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries`
|
||||
|
||||
@ -12,6 +12,13 @@ export async function aliasExists(alias: string): Promise<boolean> {
|
||||
return user !== null
|
||||
}
|
||||
|
||||
export async function getUserById(id: number, withCommunity: boolean = false, withEmailContact: boolean = false): Promise<DbUser> {
|
||||
return DbUser.findOneOrFail({
|
||||
where: { id },
|
||||
relations: { community: withCommunity, emailContact: withEmailContact },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier could be gradidoID, alias or email of user
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"": {
|
||||
"name": "dlt-connector",
|
||||
"dependencies": {
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js",
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -11,6 +12,7 @@
|
||||
"@hashgraph/sdk": "^2.70.0",
|
||||
"@sinclair/typebox": "^0.34.33",
|
||||
"@sinclair/typemap": "^0.10.1",
|
||||
"@types/async-exit-hook": "^2.0.2",
|
||||
"@types/bun": "^1.2.17",
|
||||
"dotenv": "^10.0.0",
|
||||
"elysia": "1.3.8",
|
||||
@ -247,6 +249,8 @@
|
||||
|
||||
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||
|
||||
"@types/async-exit-hook": ["@types/async-exit-hook@2.0.2", "", {}, "sha512-RJbTNivnnn+JzNiQTtUgwo/1S6QUHwI5JfXCeUPsqZXB4LuvRwvHhbKFSS5jFDYpk8XoEAYVW2cumBOdGpXL2Q=="],
|
||||
|
||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
|
||||
"@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
||||
@ -301,6 +305,8 @@
|
||||
|
||||
"asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="],
|
||||
|
||||
"async-exit-hook": ["async-exit-hook@2.0.1", "", {}, "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="],
|
||||
|
||||
"async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
TopicInfoQuery,
|
||||
TopicMessageSubmitTransaction,
|
||||
TopicUpdateTransaction,
|
||||
TransactionId,
|
||||
TransactionReceipt,
|
||||
TransactionResponse,
|
||||
Wallet,
|
||||
@ -30,6 +31,9 @@ export class HieroClient {
|
||||
wallet: Wallet
|
||||
client: Client
|
||||
logger: Logger
|
||||
// transaction counter for logging
|
||||
transactionInternNr: number = 0
|
||||
pendingPromises: Promise<void>[] = []
|
||||
|
||||
private constructor() {
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
||||
@ -53,12 +57,23 @@ export class HieroClient {
|
||||
return HieroClient.instance
|
||||
}
|
||||
|
||||
public async waitForPendingPromises() {
|
||||
const startTime = new Date()
|
||||
this.logger.info(`waiting for ${this.pendingPromises.length} pending promises`)
|
||||
await Promise.all(this.pendingPromises)
|
||||
const endTime = new Date()
|
||||
this.logger.info(`all pending promises resolved, used time: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
}
|
||||
|
||||
public async sendMessage(
|
||||
topicId: HieroId,
|
||||
transaction: GradidoTransaction,
|
||||
): Promise<{ receipt: TransactionReceipt; response: TransactionResponse }> {
|
||||
let startTime = new Date()
|
||||
this.logger.addContext('topicId', topicId.toString())
|
||||
): Promise<TransactionId | null> {
|
||||
const startTime = new Date()
|
||||
this.transactionInternNr++
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
||||
logger.addContext('trNr', this.transactionInternNr)
|
||||
logger.addContext('topicId', topicId.toString())
|
||||
const serializedTransaction = transaction.getSerializedTransaction()
|
||||
if (!serializedTransaction) {
|
||||
throw new Error('cannot serialize transaction')
|
||||
@ -68,29 +83,34 @@ export class HieroClient {
|
||||
topicId,
|
||||
message: serializedTransaction.data(),
|
||||
}).freezeWithSigner(this.wallet)
|
||||
let endTime = new Date()
|
||||
this.logger.info(`prepare message, until freeze, cost: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
startTime = new Date()
|
||||
const signedHieroTransaction = await hieroTransaction.signWithSigner(this.wallet)
|
||||
endTime = new Date()
|
||||
this.logger.info(`sign message, cost: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
startTime = new Date()
|
||||
const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet)
|
||||
endTime = new Date()
|
||||
this.logger.info(`send message, cost: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
startTime = new Date()
|
||||
const sendReceipt = await sendResponse.getReceiptWithSigner(this.wallet)
|
||||
endTime = new Date()
|
||||
this.logger.info(`get receipt, cost: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
this.logger.info(
|
||||
`message sent to topic ${topicId}, status: ${sendReceipt.status.toString()}, transaction id: ${sendResponse.transactionId.toString()}`,
|
||||
// sign and execute transaction needs some time, so let it run in background
|
||||
const pendingPromiseIndex = this.pendingPromises.push(
|
||||
hieroTransaction.signWithSigner(this.wallet).then(async (signedHieroTransaction) => {
|
||||
const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet)
|
||||
logger.info(`message sent to topic ${topicId}, transaction id: ${sendResponse.transactionId.toString()}`)
|
||||
if (logger.isInfoEnabled()) {
|
||||
// only for logging
|
||||
sendResponse.getReceiptWithSigner(this.wallet).then((receipt) => {
|
||||
logger.info(
|
||||
`message send status: ${receipt.status.toString()}`,
|
||||
)
|
||||
})
|
||||
// only for logging
|
||||
sendResponse.getRecordWithSigner(this.wallet).then((record) => {
|
||||
logger.info(`message sent, cost: ${record.transactionFee.toString()}`)
|
||||
const localEndTime = new Date()
|
||||
logger.info(`HieroClient.sendMessage used time (full process): ${localEndTime.getTime() - startTime.getTime()}ms`)
|
||||
})
|
||||
}
|
||||
}).catch((e) => {
|
||||
logger.error(e)
|
||||
}).finally(() => {
|
||||
this.pendingPromises.splice(pendingPromiseIndex, 1)
|
||||
})
|
||||
)
|
||||
startTime = new Date()
|
||||
const record = await sendResponse.getRecordWithSigner(this.wallet)
|
||||
endTime = new Date()
|
||||
this.logger.info(`get record, cost: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
this.logger.info(`message sent, cost: ${record.transactionFee.toString()}`)
|
||||
return { receipt: sendReceipt, response: sendResponse }
|
||||
const endTime = new Date()
|
||||
logger.info(`HieroClient.sendMessage used time: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
return hieroTransaction.transactionId
|
||||
}
|
||||
|
||||
public async getBalance(): Promise<AccountBalance> {
|
||||
|
||||
@ -46,9 +46,36 @@ async function main() {
|
||||
// listen for rpc request from backend (graphql replaced with elysiaJS)
|
||||
new Elysia().use(appRoutes).listen(CONFIG.DLT_CONNECTOR_PORT, () => {
|
||||
logger.info(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`)
|
||||
setupGracefulShutdown(logger)
|
||||
})
|
||||
}
|
||||
|
||||
function setupGracefulShutdown(logger: Logger) {
|
||||
const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM']
|
||||
signals.forEach(sig => {
|
||||
process.on(sig, async () => {
|
||||
logger.info(`[shutdown] Got ${sig}, cleaning up…`)
|
||||
await gracefulShutdown(logger)
|
||||
process.exit(0)
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform === "win32") {
|
||||
const rl = require("readline").createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
})
|
||||
rl.on("SIGINT", () => {
|
||||
process.emit("SIGINT" as any)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function gracefulShutdown(logger: Logger) {
|
||||
logger.info('graceful shutdown')
|
||||
await HieroClient.getInstance().waitForPendingPromises()
|
||||
}
|
||||
|
||||
function loadConfig(): Logger {
|
||||
// configure log4js
|
||||
// TODO: replace late by loader from config-schema
|
||||
@ -113,3 +140,7 @@ main().catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
function exitHook(arg0: () => void) {
|
||||
throw new Error('Function not implemented.')
|
||||
}
|
||||
|
||||
|
||||
@ -50,10 +50,12 @@ export async function SendToHieroContext(
|
||||
topic: HieroId,
|
||||
): Promise<string> => {
|
||||
const client = HieroClient.getInstance()
|
||||
const resultMessage = await client.sendMessage(topic, gradidoTransaction)
|
||||
const transactionId = resultMessage.response.transactionId.toString()
|
||||
logger.info('transmitted Gradido Transaction to Hiero', { transactionId })
|
||||
return transactionId
|
||||
const transactionId = await client.sendMessage(topic, gradidoTransaction)
|
||||
if (!transactionId) {
|
||||
throw new Error('missing transaction id from hiero')
|
||||
}
|
||||
logger.info('transmitted Gradido Transaction to Hiero', { transactionId: transactionId.toString() })
|
||||
return transactionId.toString()
|
||||
}
|
||||
|
||||
// choose correct role based on transaction type and input type
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user