From 4aeb1b60a225413484691afd222c04e926c3507e Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 19 Sep 2023 21:19:22 +0200 Subject: [PATCH] lokaler zwischenstand --- backend/src/config/index.ts | 3 +- .../federation/client/1_0/SendCoinsClient.ts | 26 ++++++----- .../client/1_0/model/SendCoinsResult.ts | 8 ++-- .../client/1_0/query/voteForSendCoins.ts | 6 ++- .../resolver/TransactionResolver.test.ts | 36 +++++++++++++++- .../graphql/resolver/TransactionResolver.ts | 12 ++++++ .../resolver/util/processXComSendCoins.ts | 43 +++++++++++-------- .../graphql/api/1_0/model/SendCoinsResult.ts | 17 ++++++++ .../api/1_0/resolver/SendCoinsResolver.ts | 9 ++-- 9 files changed, 119 insertions(+), 41 deletions(-) create mode 100644 federation/src/graphql/api/1_0/model/SendCoinsResult.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 07e2ded36..98a0b8323 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -19,7 +19,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v19.2023-09-01', + EXPECTED: 'v20.2023-09-19', CURRENT: '', }, } @@ -122,6 +122,7 @@ if ( } const federation = { + FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API || '1_0', FEDERATION_VALIDATE_COMMUNITY_TIMER: Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000, FEDERATION_XCOM_SENDCOINS_ENABLED: diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 7f1fad186..f9c318e81 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -5,6 +5,7 @@ import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' import { SendCoinsArgs } from './model/SendCoinsArgs' +import { SendCoinsResult } from './model/SendCoinsResult' import { revertSendCoins } from './query/revertSendCoins' import { revertSettledSendCoins } from './query/revertSettledSendCoins' import { settleSendCoins } from './query/settleSendCoins' @@ -30,27 +31,30 @@ export class SendCoinsClient { }) } - voteForSendCoins = async (args: SendCoinsArgs): Promise => { + voteForSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint) try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.voteForSendCoins?.voteForSendCoins) { + if (!data?.voteForSendCoins?.vote) { logger.warn( 'X-Com: voteForSendCoins failed with: ', // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins.voteForSendCoins, + data.voteForSendCoins.recipGradidoID, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + data.voteForSendCoins.recipName, ) - return + return new SendCoinsResult() } - logger.debug( - 'X-Com: voteForSendCoins successful with result=', - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins, - ) - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access - return data.voteForSendCoins.voteForSendCoins + const result = new SendCoinsResult() + result.vote = true + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + result.recipGradidoID = data.voteForSendCoins.recipGradidoID + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + result.recipName = data.voteForSendCoins.recipName + logger.debug('X-Com: voteForSendCoins successful with result=', result) + return result } catch (err) { throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err) } diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts index 1897410cc..3eb1419b5 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsResult.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -9,9 +9,9 @@ export class SendCoinsResult { @Field(() => Boolean) vote: boolean - @Field(() => String) - receiverFirstName: string + @Field(() => String, { nullable: true }) + recipGradidoID: string | null - @Field(() => String) - receiverLastName: string + @Field(() => String, { nullable: true }) + recipName: string | null } diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index 0f16ff32b..9658cecfd 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -20,6 +20,10 @@ export const voteForSendCoins = gql` senderCommunityUuid: $senderCommunityUuid senderUserUuid: $senderUserUuid senderUserName: $senderUserName - ) + ) { + vote + recipGradidoID + recipName + } } ` diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index a59ca5c0a..20765e952 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -58,7 +58,7 @@ let bob: User let peter: User let homeCom: DbCommunity -// let foreignCom: DbCommunity +let foreignCom: DbCommunity describe('send coins', () => { beforeAll(async () => { @@ -67,7 +67,7 @@ describe('send coins', () => { await userFactory(testEnv, stephenHawking) await userFactory(testEnv, garrickOllivander) homeCom = DbCommunity.create() - homeCom.communityUuid = 'homeCom-UUID' + homeCom.communityUuid = '7f474922-b6d8-4b64-8cd0-ebf0a1d8756e' homeCom.creationDate = new Date('2000-01-01') homeCom.description = 'homeCom description' homeCom.foreign = false @@ -77,6 +77,17 @@ describe('send coins', () => { homeCom.url = 'homeCom url' homeCom = await DbCommunity.save(homeCom) + foreignCom = DbCommunity.create() + foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d8756e' + foreignCom.creationDate = new Date('2000-06-06') + foreignCom.description = 'homeCom description' + foreignCom.foreign = true + foreignCom.name = 'foreignCom name' + foreignCom.privateKey = Buffer.from('foreignCom privateKey') + foreignCom.publicKey = Buffer.from('foreignCom publicKey') + foreignCom.url = 'foreignCom url' + foreignCom = await DbCommunity.save(foreignCom) + bobData = { email: 'bob@baumeister.de', password: 'Aa12345_', @@ -583,6 +594,27 @@ describe('send coins', () => { }) }) + describe.only('X-Com send coins via gradido ID', () => { + it('sends the coins', async () => { + await expect( + mutate({ + mutation: sendCoins, + variables: { + recipientCommunityIdentifier: foreignCom.communityUuid, + recipientIdentifier: peter?.gradidoID, + amount: 10, + memo: 'x-com send via gradido ID', + }, + }), + ).resolves.toMatchObject({ + data: { + sendCoins: true, + }, + errors: undefined, + }) + }) + }) + describe('more transactions to test semaphore', () => { it('sends the coins four times in a row', async () => { await expect( diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index fed605a54..baadd5c55 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -43,6 +43,7 @@ import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkSummary } from './util/transactionLinkSummary' +import { processXComPendingSendCoins } from './util/processXComSendCoins' export const executeTransaction = async ( amount: Decimal, @@ -545,6 +546,17 @@ export class TransactionResolver { if (!(await isCommunityAuthenticated(recipientCommunityIdentifier))) { throw new LogError('recipient commuity is connected, but still not authenticated yet!') } + const recipCom = await dbCommunity.findOneOrFail({ + where: { communityUuid: recipientCommunityIdentifier }, + }) + await processXComPendingSendCoins( + recipCom, + homeCom, + amount, + memo, + senderUser, + recipientIdentifier, + ) } return true } diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 3bae8acd3..2512d69c6 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -19,27 +19,24 @@ import { fullName } from '@/util/utilities' import { settlePendingSenderTransaction } from './settlePendingSenderTransaction' export async function processXComPendingSendCoins( - receiverFCom: DbFederatedCommunity, receiverCom: DbCommunity, senderCom: DbCommunity, - creationDate: Date, amount: Decimal, memo: string, sender: dbUser, - recipient: dbUser, + recipientIdentifier: string, ): Promise { try { logger.debug( `XCom: processXComPendingSendCoins...`, - receiverFCom, receiverCom, senderCom, - creationDate, amount, memo, sender, - recipient, + recipientIdentifier, ) + const creationDate = new Date() // first calculate the sender balance and check if the transaction is allowed const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate) if (!senderBalance) { @@ -47,24 +44,32 @@ export async function processXComPendingSendCoins( } logger.debug(`X-Com: calculated senderBalance = `, senderBalance) + const receiverFCom = await DbFederatedCommunity.findOneOrFail({ + where: { + publicKey: receiverCom.publicKey, + apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API, + }, + }) const client = SendCoinsClientFactory.getInstance(receiverFCom) // eslint-disable-next-line camelcase if (client instanceof V1_0_SendCoinsClient) { const args = new SendCoinsArgs() - args.recipientCommunityUuid = receiverCom.communityUuid - ? receiverCom.communityUuid - : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID - args.recipientUserIdentifier = recipient.gradidoID + if (receiverCom.communityUuid) { + args.recipientCommunityUuid = receiverCom.communityUuid + } + args.recipientUserIdentifier = recipientIdentifier args.creationDate = creationDate.toISOString() args.amount = amount args.memo = memo - args.senderCommunityUuid = senderCom.communityUuid ? senderCom.communityUuid : 'homeCom-UUID' + if (senderCom.communityUuid) { + args.senderCommunityUuid = senderCom.communityUuid + } args.senderUserUuid = sender.gradidoID args.senderUserName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: ready for voteForSendCoins with args=`, args) - const recipientName = await client.voteForSendCoins(args) - logger.debug(`X-Com: returnd from voteForSendCoins:`, recipientName) - if (recipientName) { + const sendCoinsResult = await client.voteForSendCoins(args) + logger.debug(`X-Com: returnd from voteForSendCoins:`, sendCoinsResult) + if (sendCoinsResult.vote) { // writing the pending transaction on receiver-side was successfull, so now write the sender side try { const pendingTx = DbPendingTransaction.create() @@ -73,11 +78,11 @@ export async function processXComPendingSendCoins( pendingTx.balanceDate = creationDate pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0) pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null - pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid - ? receiverCom.communityUuid - : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID - pendingTx.linkedUserGradidoID = recipient.gradidoID - pendingTx.linkedUserName = recipientName + if (receiverCom.communityUuid) { + pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid + } + pendingTx.linkedUserGradidoID = sendCoinsResult.recipGradidoID + pendingTx.linkedUserName = sendCoinsResult.recipName pendingTx.memo = memo pendingTx.previous = senderBalance ? senderBalance.lastTransactionId : null pendingTx.state = PendingTransactionState.NEW diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts new file mode 100644 index 000000000..3eb1419b5 --- /dev/null +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -0,0 +1,17 @@ +import { ArgsType, Field } from 'type-graphql' + +@ArgsType() +export class SendCoinsResult { + constructor() { + this.vote = false + } + + @Field(() => Boolean) + vote: boolean + + @Field(() => String, { nullable: true }) + recipGradidoID: string | null + + @Field(() => String, { nullable: true }) + recipName: string | null +} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 4196e015c..0f6f38633 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -14,6 +14,7 @@ import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTra // import { checkTradingLevel } from '@/graphql/util/checkTradingLevel' import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTransaction' import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier' +import { SendCoinsResult } from '../model/SendCoinsResult' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -31,7 +32,7 @@ export class SendCoinsResolver { senderUserUuid, senderUserName, }: SendCoinsArgs, - ): Promise { + ): Promise { logger.debug( `voteForSendCoins() via apiVersion=1_0 ...`, recipientCommunityUuid, @@ -43,7 +44,7 @@ export class SendCoinsResolver { senderUserUuid, senderUserName, ) - let result: string + const result = new SendCoinsResult() // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ communityUuid: recipientCommunityUuid, @@ -88,7 +89,9 @@ export class SendCoinsResolver { pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) await DbPendingTransaction.insert(pendingTx) - result = pendingTx.userName + result.vote = true + result.recipName = pendingTx.userName + result.recipGradidoID = pendingTx.userGradidoID logger.debug(`voteForSendCoins()-1_0... successfull`) } catch (err) { throw new LogError(`Error in voteForSendCoins: `, err)