mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'master' into dlt_connector_direct_usage
This commit is contained in:
commit
9f22e5b16d
@ -63,7 +63,6 @@ LOG_LEVEL=INFO
|
||||
|
||||
# Federation
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED=false
|
||||
|
||||
# GMS
|
||||
# GMS_ACTIVE=true
|
||||
|
||||
@ -63,7 +63,6 @@ WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
|
||||
|
||||
# Federation
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER=$FEDERATION_VALIDATE_COMMUNITY_TIMER
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED=$FEDERATION_XCOM_SENDCOINS_ENABLED
|
||||
|
||||
# GMS
|
||||
GMS_ACTIVE=$GMS_ACTIVE
|
||||
|
||||
@ -59,21 +59,20 @@ export class TransactionDraft {
|
||||
|
||||
static createTransfer(sendingUser: DbUser, receivingUser: DbUser, amount: string, memo: string, createdAt: Date): TransactionDraft | null {
|
||||
if (!sendingUser.community || !receivingUser.community) {
|
||||
logger.warn(`missing community for user ${sendingUser.id} and/or ${receivingUser.id}`)
|
||||
return null
|
||||
throw new Error(`missing community for user ${sendingUser.id} and/or ${receivingUser.id}`)
|
||||
}
|
||||
if (sendingUser.community.hieroTopicId && receivingUser.community.hieroTopicId) {
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(sendingUser.community.hieroTopicId, new CommunityAccountIdentifier(sendingUser.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(receivingUser.community.hieroTopicId, new CommunityAccountIdentifier(receivingUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_TRANSFER
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = amount
|
||||
draft.memo = memo
|
||||
return draft
|
||||
} else {
|
||||
logger.warn(`missing topicId for community ${community.id}`)
|
||||
const senderUserTopic = sendingUser.community.hieroTopicId
|
||||
const receiverUserTopic = receivingUser.community.hieroTopicId
|
||||
if (!senderUserTopic || !receiverUserTopic) {
|
||||
throw new Error(`missing topicId for community ${sendingUser.community.id} and/or ${receivingUser.community.id}`)
|
||||
}
|
||||
return null
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new CommunityAccountIdentifier(sendingUser.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(receiverUserTopic, new CommunityAccountIdentifier(receivingUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_TRANSFER
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = amount
|
||||
draft.memo = memo
|
||||
return draft
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { httpAgent, httpsAgent } from '@/apis/ConnectionAgents'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { GmsUser } from './model/GmsUser'
|
||||
|
||||
@ -99,19 +99,9 @@ const webhook = {
|
||||
process.env.APP_SECRET = server.JWT_SECRET
|
||||
|
||||
const federation = {
|
||||
FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API ?? '1_0',
|
||||
// ?? operator don't work here as expected
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER: Number(
|
||||
process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER ?? 60000,
|
||||
),
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED:
|
||||
process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false,
|
||||
// default value for community-uuid is equal uuid of stage-3
|
||||
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID:
|
||||
process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc',
|
||||
FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS: parseInt(
|
||||
process.env.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS ?? '3',
|
||||
),
|
||||
}
|
||||
|
||||
const gms = {
|
||||
|
||||
@ -209,39 +209,13 @@ export const schema = Joi.object({
|
||||
.description('Time in minutes before a new code can be requested')
|
||||
.required(),
|
||||
|
||||
FEDERATION_BACKEND_SEND_ON_API: Joi.string()
|
||||
.pattern(/^\d+_\d+$/)
|
||||
.default('1_0')
|
||||
.description('API Version of sending requests to another communities, e.g., "1_0"')
|
||||
.required(),
|
||||
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER: Joi.number()
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER: Joi.number()
|
||||
.integer()
|
||||
.min(1000)
|
||||
.default(60000)
|
||||
.description('Timer interval in milliseconds for community validation')
|
||||
.required(),
|
||||
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED: Joi.boolean()
|
||||
.default(false)
|
||||
.description('Enable or disable the federation send coins feature')
|
||||
.optional(),
|
||||
|
||||
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: Joi.string()
|
||||
.uuid()
|
||||
.default('56a55482-909e-46a4-bfa2-cd025e894ebc')
|
||||
.description(
|
||||
'UUID of the receiver community for federation cross-community transactions if the receiver is unknown',
|
||||
)
|
||||
.required(),
|
||||
|
||||
FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS: Joi.number()
|
||||
.integer()
|
||||
.min(0)
|
||||
.default(3)
|
||||
.description('Maximum number of retries for reverting send coins transactions')
|
||||
.required(),
|
||||
|
||||
GMS_CREATE_USER_THROW_ERRORS: Joi.boolean()
|
||||
.default(false)
|
||||
.when('GMS_ACTIVE', { is: true, then: Joi.required(), otherwise: Joi.optional() })
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { decimalSeparatorByLanguage } from '@/util/utilities'
|
||||
import { decimalSeparatorByLanguage } from 'core'
|
||||
|
||||
import { sendEmailTranslated } from './sendEmailTranslated'
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { CommunityLoggingView, Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, FederatedCommunityLoggingView, getHomeCommunity } from 'database'
|
||||
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { CONFIG } from '@/config'
|
||||
import { CONFIG as CONFIG_CORE } from 'core'
|
||||
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { encryptAndSign, OpenConnectionJwtPayloadType } from 'shared'
|
||||
@ -27,7 +27,7 @@ export async function startCommunityAuthentication(
|
||||
methodLogger.debug('homeComA', new CommunityLoggingView(homeComA!))
|
||||
const homeFedComA = await DbFederatedCommunity.findOneByOrFail({
|
||||
foreign: false,
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
apiVersion: CONFIG_CORE.FEDERATION_BACKEND_SEND_ON_API,
|
||||
})
|
||||
methodLogger.debug('homeFedComA', new FederatedCommunityLoggingView(homeFedComA))
|
||||
const comB = await DbCommunity.findOneByOrFail({ publicKey: fedComB.publicKey })
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { getLogger } from 'log4js'
|
||||
import { EncryptedTransferArgs } from 'core/src/graphql/model/EncryptedTransferArgs'
|
||||
import { openConnection } from './query/openConnection'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.AuthenticationClient`)
|
||||
|
||||
47
backend/src/federation/client/1_0/DisbursementClient.ts
Normal file
47
backend/src/federation/client/1_0/DisbursementClient.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { processDisburseJwtOnSenderCommunity as processDisburseJwtOnSenderCommunityQuery } from './query/processDisburseJwtOnSenderCommunity'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.DisbursementClient`)
|
||||
|
||||
export class DisbursementClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
client: GraphQLClient
|
||||
|
||||
constructor(dbCom: DbFederatedCommunity) {
|
||||
this.dbCom = dbCom
|
||||
this.endpoint = ensureUrlEndsWithSlash(dbCom.endPoint).concat(dbCom.apiVersion).concat('/')
|
||||
this.client = new GraphQLClient(this.endpoint, {
|
||||
method: 'POST',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async sendDisburseJwtToSenderCommunity(args: EncryptedTransferArgs): Promise<string | null> {
|
||||
logger.debug('sendDisburseJwtToSenderCommunity against endpoint=', this.endpoint)
|
||||
try {
|
||||
const { data } = await this.client.rawRequest<{ processDisburseJwtOnSenderCommunity: string }>(processDisburseJwtOnSenderCommunityQuery, { args })
|
||||
const response = data?.processDisburseJwtOnSenderCommunity
|
||||
if (response) {
|
||||
logger.debug('received response:', response)
|
||||
return response
|
||||
}
|
||||
} catch (err) {
|
||||
const errmsg = `sendDisburseJwtToSenderCommunity failed for endpoint=${this.endpoint}, err=${err}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { GraphQLClient } from 'graphql-request'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getPublicCommunityInfo } from '@/federation/client/1_0/query/getPublicCommunityInfo'
|
||||
import { getPublicKey } from '@/federation/client/1_0/query/getPublicKey'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { PublicCommunityInfoLoggingView } from './logging/PublicCommunityInfoLogging.view'
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
export const processDisburseJwtOnSenderCommunity = gql`
|
||||
mutation ($args: EncryptedTransferArgs!) {
|
||||
processDisburseJwtOnSenderCommunity(data: $args)
|
||||
}
|
||||
`
|
||||
3
backend/src/federation/client/1_1/DisbursementClient.ts
Normal file
3
backend/src/federation/client/1_1/DisbursementClient.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { DisbursementClient as V1_0_DisbursementClient } from '@/federation/client/1_0/DisbursementClient'
|
||||
|
||||
export class DisbursementClient extends V1_0_DisbursementClient {}
|
||||
@ -1,3 +0,0 @@
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
|
||||
|
||||
export class SendCoinsClient extends V1_0_SendCoinsClient {}
|
||||
@ -3,7 +3,7 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
|
||||
import { AuthenticationClient as V1_1_AuthenticationClient } from '@/federation/client/1_1/AuthenticationClient'
|
||||
import { ApiVersionType } from '@/federation/enum/apiVersionType'
|
||||
import { ApiVersionType } from 'core'
|
||||
|
||||
type AuthenticationClient = V1_0_AuthenticationClient | V1_1_AuthenticationClient
|
||||
|
||||
|
||||
58
backend/src/federation/client/DisbursementClientFactory.ts
Normal file
58
backend/src/federation/client/DisbursementClientFactory.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
|
||||
import { DisbursementClient as V1_0_DisbursementClient } from '@/federation/client/1_0/DisbursementClient'
|
||||
import { DisbursementClient as V1_1_DisbursementClient } from '@/federation/client/1_1/DisbursementClient'
|
||||
import { ApiVersionType } from 'core'
|
||||
|
||||
type DisbursementClient = V1_0_DisbursementClient | V1_1_DisbursementClient
|
||||
|
||||
interface DisbursementClientInstance {
|
||||
id: number
|
||||
|
||||
client: DisbursementClient
|
||||
}
|
||||
|
||||
export class DisbursementClientFactory {
|
||||
private static instanceArray: DisbursementClientInstance[] = []
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
|
||||
private constructor() {}
|
||||
|
||||
private static createDisbursementClient = (dbCom: DbFederatedCommunity) => {
|
||||
switch (dbCom.apiVersion) {
|
||||
case ApiVersionType.V1_0:
|
||||
return new V1_0_DisbursementClient(dbCom)
|
||||
case ApiVersionType.V1_1:
|
||||
return new V1_1_DisbursementClient(dbCom)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(dbCom: DbFederatedCommunity): DisbursementClient | null {
|
||||
const instance = DisbursementClientFactory.instanceArray.find(
|
||||
(instance) => instance.id === dbCom.id,
|
||||
)
|
||||
if (instance) {
|
||||
return instance.client
|
||||
}
|
||||
const client = DisbursementClientFactory.createDisbursementClient(dbCom)
|
||||
if (client) {
|
||||
DisbursementClientFactory.instanceArray.push({
|
||||
id: dbCom.id,
|
||||
client,
|
||||
} as DisbursementClientInstance)
|
||||
}
|
||||
return client
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
|
||||
import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient'
|
||||
|
||||
import { FederationClient as V1_1_FederationClient } from '@/federation/client/1_1/FederationClient'
|
||||
import { ApiVersionType } from '@/federation/enum/apiVersionType'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ApiVersionType, ensureUrlEndsWithSlash } from 'core'
|
||||
|
||||
type FederationClient = V1_0_FederationClient | V1_1_FederationClient
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import { createKeyPair } from 'shared'
|
||||
import { getLogger } from 'log4js'
|
||||
import { startCommunityAuthentication } from './authenticateCommunities'
|
||||
import { PublicCommunityInfoLoggingView } from './client/1_0/logging/PublicCommunityInfoLogging.view'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
import { ApiVersionType } from 'core'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.validateCommunities`)
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
|
||||
@ObjectType()
|
||||
export class FederatedCommunity {
|
||||
|
||||
@ -2,9 +2,8 @@ import { Transaction as dbTransaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { Decay, TransactionTypeId } from 'core'
|
||||
|
||||
import { Decay } from './Decay'
|
||||
import { User } from './User'
|
||||
|
||||
@ObjectType()
|
||||
|
||||
@ -7,14 +7,14 @@ import { Balance } from '@model/Balance'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { BalanceLoggingView } from '@/logging/BalanceLogging.view'
|
||||
import { DecayLoggingView } from '@/logging/DecayLogging.view'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { DecayLoggingView } from 'core'
|
||||
import { calculateDecay } from 'shared'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { getLogger } from 'log4js'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
|
||||
@Resolver()
|
||||
|
||||
@ -50,7 +50,7 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { getFirstDayOfPreviousNMonth } from '@/util/utilities'
|
||||
import { getFirstDayOfPreviousNMonth } from 'core'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { getLogger as originalGetLogger } from 'log4js'
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ import { Paginated } from '@arg/Paginated'
|
||||
import { SearchContributionsFilterArgs } from '@arg/SearchContributionsFilterArgs'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { ContributionType } from '@enum/ContributionType'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
|
||||
import { Contribution, ContributionListResult } from '@model/Contribution'
|
||||
import { OpenCreation } from '@model/OpenCreation'
|
||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||
import { TransactionTypeId } from 'core'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import {
|
||||
@ -43,9 +43,9 @@ import {
|
||||
import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUnconfirmedContribution/UpdateUnconfirmedContribution.context'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { TRANSACTIONS_LOCK } from 'database'
|
||||
import { fullName } from 'core'
|
||||
import { calculateDecay, Decay } from 'shared'
|
||||
import { fullName } from '@/util/utilities'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { ContributionMessageType } from '@enum/ContributionMessageType'
|
||||
@ -59,8 +59,7 @@ import {
|
||||
import { getOpenCreations, getUserCreation, validateContribution } from './util/creations'
|
||||
import { extractGraphQLFields } from './util/extractGraphQLFields'
|
||||
import { findContributions } from './util/findContributions'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { InterruptiveSleepManager, TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/util/InterruptiveSleepManager'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { contributionTransaction } from '@/apis/dltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
@ -32,7 +32,7 @@ import { listTransactionLinksAdmin } from '@/seeds/graphql/queries'
|
||||
import { transactionLinks } from '@/seeds/transactionLink/index'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { TRANSACTIONS_LOCK } from 'database'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
@ -43,7 +43,7 @@ const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
// mock semaphore to allow use fake timers
|
||||
jest.mock('@/util/TRANSACTIONS_LOCK')
|
||||
jest.mock('database/src/util/TRANSACTIONS_LOCK')
|
||||
TRANSACTIONS_LOCK.acquire = jest.fn().mockResolvedValue(jest.fn())
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
|
||||
@ -6,22 +6,20 @@ import { TransactionLinkFilters } from '@arg/TransactionLinkFilters'
|
||||
import { ContributionCycleType } from '@enum/ContributionCycleType'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { ContributionType } from '@enum/ContributionType'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { Community } from '@model/Community'
|
||||
import { ContributionLink } from '@model/ContributionLink'
|
||||
import { Decay } from '@model/Decay'
|
||||
import { RedeemJwtLink } from '@model/RedeemJwtLink'
|
||||
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
||||
import { User } from '@model/User'
|
||||
import { QueryLinkResult } from '@union/QueryLinkResult'
|
||||
import { Decay, interpretEncryptedTransferArgs, TransactionTypeId } from 'core'
|
||||
import {
|
||||
AppDatabase,
|
||||
Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink,
|
||||
Transaction as DbTransaction,
|
||||
AppDatabase, Community as DbCommunity, Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink, FederatedCommunity as DbFederatedCommunity, Transaction as DbTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
getHomeCommunity,
|
||||
findTransactionLinkByCode,
|
||||
getHomeCommunity
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||
@ -39,24 +37,29 @@ import {
|
||||
InterruptiveSleepManager,
|
||||
TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY,
|
||||
} from '@/util/InterruptiveSleepManager'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { TRANSACTION_LINK_LOCK } from '@/util/TRANSACTION_LINK_LOCK'
|
||||
import { fullName } from '@/util/utilities'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
import { calculateDecay, decode, DisburseJwtPayloadType, encode, RedeemJwtPayloadType, verify } from 'shared'
|
||||
import { fullName } from 'core'
|
||||
import { TRANSACTION_LINK_LOCK, TRANSACTIONS_LOCK } from 'database'
|
||||
import { calculateDecay, decode, DisburseJwtPayloadType, encode, encryptAndSign, EncryptedJWEJwtPayloadType, RedeemJwtPayloadType, verify } from 'shared'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { DisbursementClient as V1_0_DisbursementClient } from '@/federation/client/1_0/DisbursementClient'
|
||||
import { DisbursementClientFactory } from '@/federation/client/DisbursementClientFactory'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { executeTransaction } from './TransactionResolver'
|
||||
import {
|
||||
getAuthenticatedCommunities,
|
||||
getCommunityByPublicKey,
|
||||
getCommunityByUuid,
|
||||
} from './util/communities'
|
||||
import { getUserCreation, validateContribution } from './util/creations'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { transactionLinkList } from './util/transactionLinkList'
|
||||
import { SignedTransferPayloadType } from 'shared'
|
||||
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionLinkResolver`)
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionLinkResolver.${method}`)
|
||||
|
||||
// TODO: do not export, test it inside the resolver
|
||||
export const transactionLinkCode = (date: Date): string => {
|
||||
@ -151,9 +154,9 @@ export class TransactionLinkResolver {
|
||||
@Authorized([RIGHTS.QUERY_TRANSACTION_LINK])
|
||||
@Query(() => QueryLinkResult)
|
||||
async queryTransactionLink(@Arg('code') code: string): Promise<typeof QueryLinkResult> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.queryTransactionLink...')
|
||||
const methodLogger = createLogger('queryTransactionLink')
|
||||
methodLogger.addContext('handshakeID', randombytes_random().toString())
|
||||
methodLogger.debug('queryTransactionLink...')
|
||||
if (code.match(/^CL-/)) {
|
||||
const contributionLink = await DbContributionLink.findOneOrFail({
|
||||
where: { code: code.replace('CL-', '') },
|
||||
@ -164,17 +167,14 @@ export class TransactionLinkResolver {
|
||||
let txLinkFound = false
|
||||
let dbTransactionLink!: DbTransactionLink
|
||||
try {
|
||||
dbTransactionLink = await DbTransactionLink.findOneOrFail({
|
||||
where: { code },
|
||||
withDeleted: true,
|
||||
})
|
||||
dbTransactionLink = await findTransactionLinkByCode(code)
|
||||
txLinkFound = true
|
||||
} catch (_err) {
|
||||
txLinkFound = false
|
||||
}
|
||||
// normal redeem code
|
||||
if (txLinkFound) {
|
||||
logger.debug(
|
||||
methodLogger.debug(
|
||||
'TransactionLinkResolver.queryTransactionLink... normal redeem code found=',
|
||||
txLinkFound,
|
||||
)
|
||||
@ -189,7 +189,7 @@ export class TransactionLinkResolver {
|
||||
return new TransactionLink(dbTransactionLink, new User(user), redeemedBy, communities)
|
||||
} else {
|
||||
// redeem jwt-token
|
||||
return await this.queryRedeemJwtLink(code, logger)
|
||||
return await this.queryRedeemJwtLink(code, methodLogger)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,8 +200,8 @@ export class TransactionLinkResolver {
|
||||
@Arg('code', () => String) code: string,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
const methodLogger = createLogger('redeemTransactionLink')
|
||||
methodLogger.addContext('code', code.substring(0, 6))
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
// const homeCom = await DbCommunity.findOneOrFail({ where: { foreign: false } })
|
||||
const user = getUser(context)
|
||||
@ -209,7 +209,7 @@ export class TransactionLinkResolver {
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
try {
|
||||
logger.info('redeem contribution link...')
|
||||
methodLogger.info('redeem contribution link...')
|
||||
const now = new Date()
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
@ -224,7 +224,7 @@ export class TransactionLinkResolver {
|
||||
if (!contributionLink) {
|
||||
throw new LogError('No contribution link found to given code', code)
|
||||
}
|
||||
logger.info('...contribution link found with id', contributionLink.id)
|
||||
methodLogger.info('...contribution link found with id', contributionLink.id)
|
||||
if (new Date(contributionLink.validFrom).getTime() > now.getTime()) {
|
||||
throw new LogError('Contribution link is not valid yet', contributionLink.validFrom)
|
||||
}
|
||||
@ -281,7 +281,7 @@ export class TransactionLinkResolver {
|
||||
}
|
||||
|
||||
const creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
logger.info('open creations', creations)
|
||||
methodLogger.info('open creations', creations)
|
||||
validateContribution(creations, contributionLink.amount, now, clientTimezoneOffset)
|
||||
const contribution = new DbContribution()
|
||||
contribution.userId = user.id
|
||||
@ -387,7 +387,7 @@ export class TransactionLinkResolver {
|
||||
transactionLink.memo,
|
||||
linkedUser,
|
||||
user,
|
||||
logger,
|
||||
methodLogger,
|
||||
transactionLink,
|
||||
)
|
||||
await EVENT_TRANSACTION_LINK_REDEEM(
|
||||
@ -419,9 +419,9 @@ export class TransactionLinkResolver {
|
||||
@Arg('alias', { nullable: true }) alias?: string,
|
||||
@Arg('validUntil', { nullable: true }) validUntil?: string,
|
||||
): Promise<string> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwt... args=', {
|
||||
const methodLogger = createLogger('createRedeemJwt')
|
||||
methodLogger.addContext('code', code.substring(0, 6))
|
||||
methodLogger.debug('args=', {
|
||||
gradidoId,
|
||||
senderCommunityUuid,
|
||||
senderCommunityName,
|
||||
@ -433,27 +433,65 @@ export class TransactionLinkResolver {
|
||||
alias,
|
||||
validUntil,
|
||||
})
|
||||
try {
|
||||
const redeemJwtPayloadType = new RedeemJwtPayloadType(
|
||||
senderCommunityUuid,
|
||||
gradidoId,
|
||||
alias ?? firstName ?? '',
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil ?? '',
|
||||
)
|
||||
// encode/sign the jwt with the private key of the sender/home community
|
||||
const senderCom = await getCommunityByUuid(senderCommunityUuid)
|
||||
if (!senderCom) {
|
||||
throw new LogError('Sender community not found')
|
||||
}
|
||||
if (!senderCom.privateJwtKey) {
|
||||
throw new LogError('Sender community privateJwtKey is not set')
|
||||
}
|
||||
const recipientCom = await getCommunityByUuid(recipientCommunityUuid)
|
||||
if (!recipientCom) {
|
||||
throw new LogError('Recipient community not found')
|
||||
}
|
||||
if (!recipientCom.publicJwtKey) {
|
||||
throw new LogError('Recipient community publicJwtKey is not set')
|
||||
}
|
||||
const redeemJwt = await encryptAndSign(redeemJwtPayloadType, senderCom.privateJwtKey!, recipientCom.publicJwtKey!)
|
||||
if (!redeemJwt) {
|
||||
throw new LogError('Redeem JWT was not created successfully')
|
||||
}
|
||||
// prepare the args for the client invocation
|
||||
const args = new EncryptedTransferArgs()
|
||||
args.publicKey = senderCom.publicKey.toString('hex')
|
||||
args.jwt = redeemJwt
|
||||
args.handshakeID = randombytes_random().toString()
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('successfully created RedeemJWT-Response with args:', args)
|
||||
}
|
||||
const signedTransferPayload = new SignedTransferPayloadType(
|
||||
args.publicKey,
|
||||
args.jwt,
|
||||
args.handshakeID,
|
||||
)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('successfully created RedeemJWT-Response with signedTransferPayload:', signedTransferPayload)
|
||||
}
|
||||
const signedTransferJwt = await encode(signedTransferPayload, senderCom.privateJwtKey!)
|
||||
if (!signedTransferJwt) {
|
||||
throw new LogError('SignedTransfer JWT was not created successfully')
|
||||
}
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('successfully created RedeemJWT-Response with signedTransferJwt:', signedTransferJwt)
|
||||
}
|
||||
|
||||
const redeemJwtPayloadType = new RedeemJwtPayloadType(
|
||||
senderCommunityUuid,
|
||||
gradidoId,
|
||||
alias ?? firstName ?? '',
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil ?? '',
|
||||
)
|
||||
// TODO:encode/sign the jwt normally with the private key of the sender/home community, but interims with uuid
|
||||
const homeCom = await getHomeCommunity()
|
||||
if (!homeCom) {
|
||||
throw new LogError('Home community not found')
|
||||
return signedTransferJwt
|
||||
} catch (e) {
|
||||
const errmsg = `Error on creating Redeem JWT: error=${e}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
}
|
||||
if (!homeCom.communityUuid) {
|
||||
throw new LogError('Home community UUID is not set')
|
||||
}
|
||||
const redeemJwt = await encode(redeemJwtPayloadType, homeCom.communityUuid)
|
||||
// TODO: encrypt the payload with the public key of the target community
|
||||
return redeemJwt
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.DISBURSE_TRANSACTION_LINK])
|
||||
@ -472,40 +510,80 @@ export class TransactionLinkResolver {
|
||||
@Arg('validUntil', { nullable: true }) validUntil?: string,
|
||||
@Arg('recipientAlias', { nullable: true }) recipientAlias?: string,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.disburseTransactionLink... args=', {
|
||||
senderGradidoId,
|
||||
senderCommunityUuid,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil,
|
||||
const handshakeID = randombytes_random().toString()
|
||||
const methodLogger = createLogger(`disburseTransactionLink`)
|
||||
methodLogger.addContext('handshakeID', handshakeID)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('args=', {
|
||||
senderGradidoId,
|
||||
senderCommunityUuid,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil,
|
||||
recipientAlias,
|
||||
})
|
||||
const disburseJwt = await this.createDisburseJwt(
|
||||
senderCommunityUuid,
|
||||
senderGradidoId,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil ?? '',
|
||||
recipientAlias ?? '',
|
||||
)
|
||||
try {
|
||||
logger.debug('TransactionLinkResolver.disburseTransactionLink... disburseJwt=', disburseJwt)
|
||||
// now send the disburseJwt to the sender community to invoke a x-community-tx to disbures the redeemLink
|
||||
// await sendDisburseJwtToSenderCommunity(context, disburseJwt)
|
||||
} catch (e) {
|
||||
throw new LogError('Disburse JWT was not sent successfully', e)
|
||||
})
|
||||
}
|
||||
const senderCom = await getCommunityByUuid(senderCommunityUuid)
|
||||
if (!senderCom) {
|
||||
const errmsg = `Sender community not found with uuid=${senderCommunityUuid}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
}
|
||||
const senderFedCom = await DbFederatedCommunity.findOneBy({ publicKey: senderCom.publicKey })
|
||||
if (!senderFedCom) {
|
||||
const errmsg = `Sender federated community not found with publicKey=${senderCom.publicKey}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
}
|
||||
const recipientCom = await getCommunityByUuid(recipientCommunityUuid)
|
||||
if (!recipientCom) {
|
||||
const errmsg = `Recipient community not found with uuid=${recipientCommunityUuid}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
}
|
||||
const client = DisbursementClientFactory.getInstance(senderFedCom)
|
||||
if (client instanceof V1_0_DisbursementClient) {
|
||||
const disburseJwtPayload = new DisburseJwtPayloadType(
|
||||
handshakeID,
|
||||
senderCommunityUuid,
|
||||
senderGradidoId,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil!,
|
||||
recipientAlias!,
|
||||
)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('disburseJwtPayload=', disburseJwtPayload)
|
||||
}
|
||||
const jws = await encryptAndSign(disburseJwtPayload, recipientCom.privateJwtKey!, senderCom.publicJwtKey!)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('jws=', jws)
|
||||
}
|
||||
const args = new EncryptedTransferArgs()
|
||||
args.publicKey = recipientCom.publicKey.toString('hex')
|
||||
args.jwt = jws
|
||||
args.handshakeID = handshakeID
|
||||
try {
|
||||
// now send the disburseJwt to the sender community to invoke a x-community-tx to disbures the redeemLink
|
||||
const result = await client.sendDisburseJwtToSenderCommunity(args)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug('Disburse JWT was sent successfully with result=', result)
|
||||
}
|
||||
} catch (e) {
|
||||
const errmsg = `Disburse JWT was not sent successfully with error=${e}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -546,98 +624,77 @@ export class TransactionLinkResolver {
|
||||
}
|
||||
|
||||
async queryRedeemJwtLink(code: string, logger: Logger): Promise<RedeemJwtLink> {
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwtLink... redeem jwt-token found')
|
||||
// decode token first to get the senderCommunityUuid as input for verify token
|
||||
const decodedPayload = decode(code)
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwtLink... decodedPayload=', decodedPayload)
|
||||
if (
|
||||
decodedPayload != null &&
|
||||
decodedPayload.tokentype === RedeemJwtPayloadType.REDEEM_ACTIVATION_TYPE
|
||||
) {
|
||||
const redeemJwtPayload = new RedeemJwtPayloadType(
|
||||
decodedPayload.sendercommunityuuid as string,
|
||||
decodedPayload.sendergradidoid as string,
|
||||
decodedPayload.sendername as string,
|
||||
decodedPayload.redeemcode as string,
|
||||
decodedPayload.amount as string,
|
||||
decodedPayload.memo as string,
|
||||
decodedPayload.validuntil as string,
|
||||
)
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... redeemJwtPayload=',
|
||||
redeemJwtPayload,
|
||||
)
|
||||
const senderCom = await getCommunityByUuid(redeemJwtPayload.sendercommunityuuid)
|
||||
logger.debug('queryRedeemJwtLink... redeem jwt-token found')
|
||||
|
||||
// decode token first to get the EncryptedTransferArgs with the senderCommunity.publicKey as input to verify token
|
||||
const decodedPayload = decode(code) as SignedTransferPayloadType
|
||||
logger.debug('queryRedeemJwtLink... decodedPayload=', decodedPayload)
|
||||
logger.debug('switch logger-context to received token-handshakeID:' + decodedPayload.handshakeID)
|
||||
logger.addContext('handshakeID', decodedPayload.handshakeID)
|
||||
if(decodedPayload !== null && decodedPayload.tokentype === SignedTransferPayloadType.SIGNED_TRANSFER_TYPE) {
|
||||
const signedTransferPayload = new SignedTransferPayloadType(
|
||||
decodedPayload.publicKey,
|
||||
decodedPayload.jwt,
|
||||
decodedPayload.handshakeID)
|
||||
logger.debug('queryRedeemJwtLink... signedTransferPayload=', signedTransferPayload)
|
||||
const senderCom = await getCommunityByPublicKey(Buffer.from(signedTransferPayload.publicKey, 'hex'))
|
||||
if (!senderCom) {
|
||||
throw new LogError('Sender community not found:', redeemJwtPayload.sendercommunityuuid)
|
||||
const errmsg = `Sender community not found with publicKey=${signedTransferPayload.publicKey}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwtLink... senderCom=', senderCom)
|
||||
if (!senderCom.communityUuid) {
|
||||
throw new LogError('Sender community UUID is not set')
|
||||
}
|
||||
// now with the sender community UUID the jwt token can be verified
|
||||
const verifiedJwtPayload = await verify('handshakeID', code, senderCom.communityUuid)
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... nach verify verifiedJwtPayload=',
|
||||
verifiedJwtPayload,
|
||||
)
|
||||
logger.debug('queryRedeemJwtLink... senderCom=', senderCom)
|
||||
const jweVerifyResult = await verify(signedTransferPayload.handshakeID, signedTransferPayload.jwt, senderCom.publicJwtKey!)
|
||||
logger.debug('queryRedeemJwtLink... jweVerifyResult=', jweVerifyResult)
|
||||
let verifiedRedeemJwtPayload: RedeemJwtPayloadType | null = null
|
||||
if (verifiedJwtPayload !== null) {
|
||||
if (verifiedJwtPayload.exp !== undefined) {
|
||||
const expDate = new Date(verifiedJwtPayload.exp * 1000)
|
||||
if (jweVerifyResult === null) {
|
||||
const errmsg = `Error on verify transferred redeem token with publicKey=${signedTransferPayload.publicKey}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
} else {
|
||||
const encryptedTransferArgs = new EncryptedTransferArgs()
|
||||
encryptedTransferArgs.publicKey = signedTransferPayload.publicKey
|
||||
encryptedTransferArgs.jwt = signedTransferPayload.jwt
|
||||
encryptedTransferArgs.handshakeID = signedTransferPayload.handshakeID
|
||||
|
||||
verifiedRedeemJwtPayload = await interpretEncryptedTransferArgs(encryptedTransferArgs) as RedeemJwtPayloadType
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`queryRedeemJwtLink() ...`, verifiedRedeemJwtPayload)
|
||||
}
|
||||
if (!verifiedRedeemJwtPayload) {
|
||||
const errmsg = `invalid authentication payload of requesting community with publicKey` + signedTransferPayload.publicKey
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if (verifiedRedeemJwtPayload.tokentype !== RedeemJwtPayloadType.REDEEM_ACTIVATION_TYPE) {
|
||||
const errmsg = `Wrong tokentype in redeem JWT: type=` + verifiedRedeemJwtPayload.tokentype + ' vs expected ' + RedeemJwtPayloadType.REDEEM_ACTIVATION_TYPE
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if(senderCom?.communityUuid !== verifiedRedeemJwtPayload.sendercommunityuuid) {
|
||||
const errmsg = `Mismatch of sender community UUID in redeem JWT against transfer JWT: uuid=` + senderCom.communityUuid + ' vs ' + verifiedRedeemJwtPayload.sendercommunityuuid
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if (verifiedRedeemJwtPayload.exp !== undefined) {
|
||||
const expDate = new Date(verifiedRedeemJwtPayload.exp * 1000)
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... expDate, exp =',
|
||||
'queryRedeemJwtLink... expDate, exp =',
|
||||
expDate,
|
||||
verifiedJwtPayload.exp,
|
||||
verifiedRedeemJwtPayload.exp,
|
||||
)
|
||||
if (expDate < new Date()) {
|
||||
throw new LogError('Redeem JWT-Token expired! jwtPayload.exp=', expDate)
|
||||
const errmsg = `Redeem JWT-Token expired! jwtPayload.exp=${expDate}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
if (verifiedJwtPayload.tokentype === RedeemJwtPayloadType.REDEEM_ACTIVATION_TYPE) {
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... verifiedJwtPayload.tokentype=',
|
||||
verifiedJwtPayload.tokentype,
|
||||
)
|
||||
verifiedRedeemJwtPayload = new RedeemJwtPayloadType(
|
||||
verifiedJwtPayload.sendercommunityuuid as string,
|
||||
verifiedJwtPayload.sendergradidoid as string,
|
||||
verifiedJwtPayload.sendername as string,
|
||||
verifiedJwtPayload.redeemcode as string,
|
||||
verifiedJwtPayload.amount as string,
|
||||
verifiedJwtPayload.memo as string,
|
||||
verifiedJwtPayload.validuntil as string,
|
||||
)
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... nach verify verifiedRedeemJwtPayload=',
|
||||
verifiedRedeemJwtPayload,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (verifiedRedeemJwtPayload === null) {
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... verifiedRedeemJwtPayload===null',
|
||||
)
|
||||
verifiedRedeemJwtPayload = new RedeemJwtPayloadType(
|
||||
decodedPayload.sendercommunityuuid as string,
|
||||
decodedPayload.sendergradidoid as string,
|
||||
decodedPayload.sendername as string,
|
||||
decodedPayload.redeemcode as string,
|
||||
decodedPayload.amount as string,
|
||||
decodedPayload.memo as string,
|
||||
decodedPayload.validuntil as string,
|
||||
)
|
||||
} else {
|
||||
// TODO: as long as the verification fails, fallback to simply decoded payload
|
||||
verifiedRedeemJwtPayload = redeemJwtPayload
|
||||
logger.debug(
|
||||
'TransactionLinkResolver.queryRedeemJwtLink... fallback to decode verifiedRedeemJwtPayload=',
|
||||
verifiedRedeemJwtPayload,
|
||||
)
|
||||
}
|
||||
const homeCommunity = await getHomeCommunity()
|
||||
if (!homeCommunity) {
|
||||
throw new LogError('Home community not found')
|
||||
const errmsg = `Home community not found`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
const recipientCommunity = new Community(homeCommunity)
|
||||
const senderCommunity = new Community(senderCom)
|
||||
@ -653,59 +710,9 @@ export class TransactionLinkResolver {
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwtLink... redeemJwtLink=', redeemJwtLink)
|
||||
return redeemJwtLink
|
||||
} else {
|
||||
throw new LogError(
|
||||
'Redeem with wrong type of JWT-Token or expired! decodedPayload=',
|
||||
decodedPayload,
|
||||
)
|
||||
const errmsg = `transfer of redeem JWT with wrong envelope! code=${code}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
|
||||
async createDisburseJwt(
|
||||
senderCommunityUuid: string,
|
||||
senderGradidoId: string,
|
||||
recipientCommunityUuid: string,
|
||||
recipientCommunityName: string,
|
||||
recipientGradidoId: string,
|
||||
recipientFirstName: string,
|
||||
code: string,
|
||||
amount: string,
|
||||
memo: string,
|
||||
validUntil: string,
|
||||
recipientAlias: string,
|
||||
): Promise<string> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.createDisburseJwt... args=', {
|
||||
senderCommunityUuid,
|
||||
senderGradidoId,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil,
|
||||
recipientAlias,
|
||||
})
|
||||
|
||||
const disburseJwtPayloadType = new DisburseJwtPayloadType(
|
||||
senderCommunityUuid,
|
||||
senderGradidoId,
|
||||
recipientCommunityUuid,
|
||||
recipientCommunityName,
|
||||
recipientGradidoId,
|
||||
recipientFirstName,
|
||||
code,
|
||||
amount,
|
||||
memo,
|
||||
validUntil,
|
||||
recipientAlias,
|
||||
)
|
||||
// TODO:encode/sign the jwt normally with the private key of the recipient community, but interims with uuid
|
||||
const disburseJwt = await encode(disburseJwtPayloadType, recipientCommunityUuid)
|
||||
logger.debug('TransactionLinkResolver.createDisburseJwt... disburseJwt=', disburseJwt)
|
||||
// TODO: encrypt the payload with the public key of the target/sender community
|
||||
return disburseJwt
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,12 +13,12 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
// import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { EventType } from '@/event/Events'
|
||||
import { SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
|
||||
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
// import { V1_0_SendCoinsClient } from 'core'
|
||||
// import { SendCoinsArgs } from 'core'
|
||||
// import { SendCoinsResult } from 'core'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import {
|
||||
confirmContribution,
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
countOpenPendingTransactions,
|
||||
Community as DbCommunity,
|
||||
DltTransaction as DbDltTransaction,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
Transaction as dbTransaction,
|
||||
TransactionLink as dbTransactionLink,
|
||||
User as dbUser,
|
||||
@ -16,11 +15,10 @@ import { In, IsNull } from 'typeorm'
|
||||
import { Paginated } from '@arg/Paginated'
|
||||
import { TransactionSendArgs } from '@arg/TransactionSendArgs'
|
||||
import { Order } from '@enum/Order'
|
||||
import { PendingTransactionState, SendCoinsResponseJwtPayloadType } from 'shared'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { Transaction } from '@model/Transaction'
|
||||
import { TransactionList } from '@model/TransactionList'
|
||||
import { User } from '@model/User'
|
||||
import { processXComCompleteTransaction, TransactionTypeId } from 'core'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { CONFIG } from '@/config'
|
||||
@ -29,31 +27,21 @@ import {
|
||||
sendTransactionReceivedEmail,
|
||||
} from '@/emails/sendEmailVariants'
|
||||
import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Events'
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import {
|
||||
InterruptiveSleepManager,
|
||||
TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY,
|
||||
} from '@/util/InterruptiveSleepManager'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { communityUser } from '@/util/communityUser'
|
||||
import { fullName } from '@/util/utilities'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
import { virtualDecayTransaction, virtualLinkTransaction } from '@/util/virtualTransactions'
|
||||
import { fullName } from 'core'
|
||||
import { TRANSACTIONS_LOCK } from 'database'
|
||||
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { getCommunityName, isHomeCommunity } from './util/communities'
|
||||
import { getTransactionList } from './util/getTransactionList'
|
||||
import {
|
||||
processXComCommittingSendCoins,
|
||||
processXComPendingSendCoins,
|
||||
} from './util/processXComSendCoins'
|
||||
import { storeForeignUser } from './util/storeForeignUser'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
import { transferTransaction } from '@/apis/dltConnector'
|
||||
|
||||
@ -482,78 +470,14 @@ export class TransactionResolver {
|
||||
await executeTransaction(amount, memo, senderUser, recipientUser, logger)
|
||||
logger.info('successful executeTransaction')
|
||||
} else {
|
||||
// processing a x-community sendCoins
|
||||
logger.info('X-Com: processing a x-community transaction...')
|
||||
if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) {
|
||||
throw new LogError('X-Community sendCoins disabled per configuration!')
|
||||
}
|
||||
const recipCom = await getCommunityByIdentifier(recipientCommunityIdentifier)
|
||||
logger.debug('recipient community: ', recipCom?.id)
|
||||
if (recipCom === null) {
|
||||
throw new LogError(
|
||||
`no recipient community found for identifier: ${recipientCommunityIdentifier}`,
|
||||
)
|
||||
}
|
||||
if (recipCom !== null && recipCom.authenticatedAt === null) {
|
||||
throw new LogError('recipient community is connected, but still not authenticated yet!')
|
||||
}
|
||||
let pendingResult: SendCoinsResponseJwtPayloadType | null = null
|
||||
let committingResult: SendCoinsResult
|
||||
const creationDate = new Date()
|
||||
|
||||
try {
|
||||
pendingResult = await processXComPendingSendCoins(
|
||||
recipCom,
|
||||
homeCom,
|
||||
creationDate,
|
||||
amount,
|
||||
memo,
|
||||
senderUser,
|
||||
recipientIdentifier,
|
||||
)
|
||||
logger.debug('processXComPendingSendCoins result: ', pendingResult)
|
||||
if (pendingResult && pendingResult.vote && pendingResult.recipGradidoID) {
|
||||
logger.debug('vor processXComCommittingSendCoins... ')
|
||||
committingResult = await processXComCommittingSendCoins(
|
||||
recipCom,
|
||||
homeCom,
|
||||
creationDate,
|
||||
amount,
|
||||
memo,
|
||||
senderUser,
|
||||
pendingResult,
|
||||
)
|
||||
logger.debug('processXComCommittingSendCoins result: ', committingResult)
|
||||
if (!committingResult.vote) {
|
||||
logger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult)
|
||||
throw new LogError(
|
||||
'FATAL ERROR: on processXComCommittingSendCoins with ',
|
||||
recipientCommunityIdentifier,
|
||||
recipientIdentifier,
|
||||
amount.toString(),
|
||||
memo,
|
||||
)
|
||||
}
|
||||
// after successful x-com-tx store the recipient as foreign user
|
||||
logger.debug('store recipient as foreign user...')
|
||||
if (await storeForeignUser(recipCom, committingResult)) {
|
||||
logger.info(
|
||||
'X-Com: new foreign user inserted successfully...',
|
||||
recipCom.communityUuid,
|
||||
committingResult.recipGradidoID,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
throw new LogError(
|
||||
'ERROR: on processXComCommittingSendCoins with ',
|
||||
recipientCommunityIdentifier,
|
||||
recipientIdentifier,
|
||||
amount.toString(),
|
||||
memo,
|
||||
err,
|
||||
)
|
||||
}
|
||||
await processXComCompleteTransaction(
|
||||
senderUser.communityUuid,
|
||||
senderUser.gradidoID,
|
||||
recipientCommunityIdentifier,
|
||||
recipientIdentifier,
|
||||
amount.valueOf(),
|
||||
memo,
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { printTimeDuration } from '@/util/time'
|
||||
import { objectValuesToArray } from '@/util/utilities'
|
||||
import { objectValuesToArray } from 'core'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {
|
||||
AppDatabase,
|
||||
ContributionLink as DbContributionLink,
|
||||
DltTransaction as DbDltTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
UserContact as DbUserContact,
|
||||
@ -86,7 +85,7 @@ import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { communityDbUser } from '@/util/communityUser'
|
||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||
import { durationInMinutesFromDates, getTimeDurationObject, printTimeDuration } from '@/util/time'
|
||||
import { delay } from '@/util/utilities'
|
||||
import { delay } from 'core'
|
||||
|
||||
import random from 'random-bigint'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
|
||||
@ -4,7 +4,7 @@ import { verifyAuthToken } from '@/apis/gms/GmsClient'
|
||||
import { CONFIG } from '@/config'
|
||||
import { GmsUserAuthenticationResult } from '@/graphql/model/GmsUserAuthenticationResult'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from 'core'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(
|
||||
|
||||
@ -78,6 +78,11 @@ export async function getCommunityByUuid(communityUuid: string): Promise<DbCommu
|
||||
where: [{ communityUuid }],
|
||||
})
|
||||
}
|
||||
export async function getCommunityByPublicKey(publicKey: Buffer): Promise<DbCommunity | null> {
|
||||
return await DbCommunity.findOne({
|
||||
where: [{ publicKey: publicKey }],
|
||||
})
|
||||
}
|
||||
|
||||
export async function getAuthenticatedCommunities(): Promise<DbCommunity[]> {
|
||||
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||
|
||||
@ -6,7 +6,7 @@ import { OpenCreation } from '@model/OpenCreation'
|
||||
import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '@/graphql/resolver/const/const'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { getFirstDayOfPreviousNMonth } from '@/util/utilities'
|
||||
import { getFirstDayOfPreviousNMonth } from 'core'
|
||||
import { AppDatabase } from 'database'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
|
||||
@ -0,0 +1,799 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community, DltTransaction, Transaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
// import { GraphQLClient } from 'graphql-request'
|
||||
// import { Response } from 'graphql-request/dist/types'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { Response } from 'graphql-request/dist/types'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { TransactionTypeId } from 'core'
|
||||
import { creations } from '@/seeds/creation'
|
||||
import { creationFactory } from '@/seeds/factory/creation'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`,
|
||||
)
|
||||
|
||||
/*
|
||||
// Mock the GraphQLClient
|
||||
jest.mock('graphql-request', () => {
|
||||
const originalModule = jest.requireActual('graphql-request')
|
||||
|
||||
let testCursor = 0
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
GraphQLClient: jest.fn().mockImplementation((url: string) => {
|
||||
if (url === 'invalid') {
|
||||
throw new Error('invalid url')
|
||||
}
|
||||
return {
|
||||
// why not using mockResolvedValueOnce or mockReturnValueOnce?
|
||||
// I have tried, but it didn't work and return every time the first value
|
||||
request: jest.fn().mockImplementation(() => {
|
||||
testCursor++
|
||||
if (testCursor === 4) {
|
||||
return Promise.resolve(
|
||||
// invalid, is 33 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516212A',
|
||||
},
|
||||
},
|
||||
)
|
||||
} else if (testCursor === 5) {
|
||||
throw Error('Connection error')
|
||||
} else {
|
||||
return Promise.resolve(
|
||||
// valid, is 32 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
let mutate: ApolloServerTestClient['mutate'],
|
||||
query: ApolloServerTestClient['query'],
|
||||
con: Connection
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
}
|
||||
*/
|
||||
|
||||
async function createHomeCommunity(): Promise<Community> {
|
||||
const homeCommunity = Community.create()
|
||||
homeCommunity.foreign = false
|
||||
homeCommunity.communityUuid = uuidv4()
|
||||
homeCommunity.url = 'localhost'
|
||||
homeCommunity.publicKey = Buffer.from('0x6e6a6c6d6feffe', 'hex')
|
||||
await Community.save(homeCommunity)
|
||||
return homeCommunity
|
||||
}
|
||||
|
||||
async function createTxCREATION1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(100)
|
||||
tx.balanceDate = new Date('01.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION1'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION1.userGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txCREATION 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('01.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('01.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxCREATION2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(200)
|
||||
tx.balanceDate = new Date('02.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION2'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION2.userGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txCREATION 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('02.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('02.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxCREATION3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(300)
|
||||
tx.balanceDate = new Date('03.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION3'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION3.userGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txCREATION 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('03.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('03.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxSend1ToReceive2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(100)
|
||||
tx.balance = new Decimal(1000)
|
||||
tx.balanceDate = new Date('11.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND1 to txRECEIVE2'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND1.userGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txSEND 1'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE2.linkedUserGradidoID'
|
||||
tx.linkedUserId = 2
|
||||
tx.linkedUserName = 'txRECEIVE 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('11.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('11.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive2FromSend1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(100)
|
||||
tx.balance = new Decimal(1300)
|
||||
tx.balanceDate = new Date('11.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND1 to txRECEIVE2'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE2.linkedUserGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txRECEIVE 2'
|
||||
tx.linkedUserGradidoID = 'txSEND1.userGradidoID'
|
||||
tx.linkedUserId = 1
|
||||
tx.linkedUserName = 'txSEND 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('11.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('11.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
/*
|
||||
async function createTxSend2ToReceive3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(200)
|
||||
tx.balance = new Decimal(1100)
|
||||
tx.balanceDate = new Date('23.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND2 to txRECEIVE3'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND2.userGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txSEND 2'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE3.linkedUserGradidoID'
|
||||
tx.linkedUserId = 3
|
||||
tx.linkedUserName = 'txRECEIVE 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('23.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('23.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive3FromSend2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(200)
|
||||
tx.balance = new Decimal(1500)
|
||||
tx.balanceDate = new Date('23.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND2 to txRECEIVE3'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE3.linkedUserGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txRECEIVE 3'
|
||||
tx.linkedUserGradidoID = 'txSEND2.userGradidoID'
|
||||
tx.linkedUserId = 2
|
||||
tx.linkedUserName = 'txSEND 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('23.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('23.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxSend3ToReceive1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(300)
|
||||
tx.balance = new Decimal(1200)
|
||||
tx.balanceDate = new Date('31.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND3 to txRECEIVE1'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND3.userGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txSEND 3'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE1.linkedUserGradidoID'
|
||||
tx.linkedUserId = 1
|
||||
tx.linkedUserName = 'txRECEIVE 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('31.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('31.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive1FromSend3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(300)
|
||||
tx.balance = new Decimal(1300)
|
||||
tx.balanceDate = new Date('31.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND3 to txRECEIVE1'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE1.linkedUserGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txRECEIVE 1'
|
||||
tx.linkedUserGradidoID = 'txSEND3.userGradidoID'
|
||||
tx.linkedUserId = 3
|
||||
tx.linkedUserName = 'txSEND 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('31.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('31.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
*/
|
||||
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('create and send Transactions to DltConnector', () => {
|
||||
let txCREATION1: Transaction
|
||||
let txCREATION2: Transaction
|
||||
let txCREATION3: Transaction
|
||||
let txSEND1to2: Transaction
|
||||
let txRECEIVE2From1: Transaction
|
||||
// let txSEND2To3: Transaction
|
||||
// let txRECEIVE3From2: Transaction
|
||||
// let txSEND3To1: Transaction
|
||||
// let txRECEIVE1From3: Transaction
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
describe('with 3 creations but inactive dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
txCREATION1 = await createTxCREATION1(false)
|
||||
txCREATION2 = await createTxCREATION2(false)
|
||||
txCREATION3 = await createTxCREATION3(false)
|
||||
await createHomeCommunity()
|
||||
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
await sendTransactionsToDltConnector()
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[0].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[1].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[2].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
|
||||
expect(logger.info).nthCalledWith(2, 'sending to DltConnector currently not configured...')
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 3 creations and active dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, raeuberHotzenplotz)
|
||||
await userFactory(testEnv, bobBaumeister)
|
||||
let count = 0
|
||||
for (const creation of creations) {
|
||||
await creationFactory(testEnv, creation)
|
||||
count++
|
||||
// we need only 3 for testing
|
||||
if (count >= 3) {
|
||||
break
|
||||
}
|
||||
}
|
||||
await createHomeCommunity()
|
||||
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
|
||||
await sendTransactionsToDltConnector()
|
||||
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[0].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[1].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[2].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 3 verified creations, 1 sendCoins and active dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
txCREATION1 = await createTxCREATION1(true)
|
||||
txCREATION2 = await createTxCREATION2(true)
|
||||
txCREATION3 = await createTxCREATION3(true)
|
||||
await createHomeCommunity()
|
||||
|
||||
txSEND1to2 = await createTxSend1ToReceive2(false)
|
||||
txRECEIVE2From1 = await createTxReceive2FromSend1(false)
|
||||
|
||||
/*
|
||||
txSEND2To3 = await createTxSend2ToReceive3()
|
||||
txRECEIVE3From2 = await createTxReceive3FromSend2()
|
||||
txSEND3To1 = await createTxSend3ToReceive1()
|
||||
txRECEIVE1From3 = await createTxReceive1FromSend3()
|
||||
*/
|
||||
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
|
||||
await sendTransactionsToDltConnector()
|
||||
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
/*
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
*/
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION1.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c1',
|
||||
verified: true,
|
||||
createdAt: new Date('01.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('01.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION2.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c2',
|
||||
verified: true,
|
||||
createdAt: new Date('02.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('02.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION3.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c3',
|
||||
verified: true,
|
||||
createdAt: new Date('03.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('03.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txSEND1to2.id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txRECEIVE2From1.id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
/*
|
||||
describe('with one Community of api 1_0 and not matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: 'somePubKey',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables1)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs not matching publicKeys', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Federation: received not matching publicKey:',
|
||||
'somePubKey',
|
||||
expect.stringMatching('11111111111111111111111111111111'),
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with one Community of api 1_0 and matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: '11111111111111111111111111111111',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables1)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs community pubKey verified', () => {
|
||||
expect(logger.info).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'Federation: verified community with',
|
||||
'http//localhost:5001/api/',
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with two Communities of api 1_0 and 1_1', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: '11111111111111111111111111111111',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables2 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_1',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables2)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
it('logs two communities found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 2 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with three Communities of api 1_0, 1_1 and 2_0', () => {
|
||||
let dbCom: DbFederatedCommunity
|
||||
beforeEach(async () => {
|
||||
const variables3 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '2_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables3)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
dbCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion },
|
||||
})
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
it('logs three community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 3 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
it('logs unsupported api for community with api 2_0 ', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
'2_0',
|
||||
)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
})
|
||||
@ -2,9 +2,9 @@ import { TransactionLink as dbTransactionLink } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { validate, version } from 'uuid'
|
||||
|
||||
import { Decay } from '@model/Decay'
|
||||
import { Decay } from 'core'
|
||||
|
||||
import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { transactionLinkSummary } from '@/graphql/resolver/util/transactionLinkSummary'
|
||||
|
||||
import { calculateDecay } from 'shared'
|
||||
|
||||
@ -2,7 +2,7 @@ import { Transaction as dbTransaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { RemoveOptions, SaveOptions } from 'typeorm'
|
||||
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { TransactionTypeId } from 'core'
|
||||
import { Transaction } from '@model/Transaction'
|
||||
import { User } from '@model/User'
|
||||
|
||||
|
||||
9
bun.lock
9
bun.lock
@ -184,14 +184,22 @@
|
||||
"dependencies": {
|
||||
"database": "*",
|
||||
"esbuild": "^0.25.2",
|
||||
"i18n": "^0.15.1",
|
||||
"jose": "^4.14.4",
|
||||
"log4js": "^6.9.1",
|
||||
"shared": "*",
|
||||
"sodium-native": "^3.4.1",
|
||||
"zod": "^3.25.61",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/i18n": "^0.13.4",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/sodium-native": "^2.3.5",
|
||||
"config-schema": "*",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"graphql-request": "5.0.0",
|
||||
"jest": "27.2.4",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typescript": "^4.9.5",
|
||||
},
|
||||
@ -228,6 +236,7 @@
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/node": "^18.7.14",
|
||||
"await-semaphore": "^0.1.3",
|
||||
"crypto-random-bigint": "^2.1.1",
|
||||
"jest": "27.2.4",
|
||||
"ts-jest": "27.0.5",
|
||||
|
||||
@ -24,20 +24,28 @@
|
||||
"lint:fix": "biome check --error-on-warnings . --write",
|
||||
"clear": "rm -rf node_modules && rm -rf build && rm -rf .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"database": "*",
|
||||
"esbuild": "^0.25.2",
|
||||
"i18n": "^0.15.1",
|
||||
"jose": "^4.14.4",
|
||||
"log4js": "^6.9.1",
|
||||
"shared": "*",
|
||||
"sodium-native": "^3.4.1",
|
||||
"zod": "^3.25.61"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/i18n": "^0.13.4",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/sodium-native": "^2.3.5",
|
||||
"config-schema": "*",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"graphql-request": "5.0.0",
|
||||
"jest": "27.2.4",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
|
||||
27
core/src/config/index.ts
Normal file
27
core/src/config/index.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
|
||||
import { LogLevel, validate } from 'config-schema'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { schema } from './schema'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
|
||||
const federation = {
|
||||
FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API ?? '1_0',
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED:
|
||||
process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false,
|
||||
// default value for community-uuid is equal uuid of stage-3
|
||||
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID:
|
||||
process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc',
|
||||
FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS: parseInt(
|
||||
process.env.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS ?? '3',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
export const CONFIG = {
|
||||
...federation,
|
||||
}
|
||||
validate(schema, CONFIG)
|
||||
29
core/src/config/schema.ts
Normal file
29
core/src/config/schema.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import Joi from 'joi'
|
||||
|
||||
export const schema = Joi.object({
|
||||
FEDERATION_BACKEND_SEND_ON_API: Joi.string()
|
||||
.pattern(/^\d+_\d+$/)
|
||||
.default('1_0')
|
||||
.description('API Version of sending requests to another communities, e.g., "1_0"')
|
||||
.required(),
|
||||
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED: Joi.boolean()
|
||||
.default(false)
|
||||
.description('Enable or disable the federation send coins feature')
|
||||
.optional(),
|
||||
|
||||
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: Joi.string()
|
||||
.uuid()
|
||||
.default('56a55482-909e-46a4-bfa2-cd025e894ebc')
|
||||
.description(
|
||||
'UUID of the receiver community for federation cross-community transactions if the receiver is unknown',
|
||||
)
|
||||
.required(),
|
||||
|
||||
FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS: Joi.number()
|
||||
.integer()
|
||||
.min(0)
|
||||
.default(3)
|
||||
.description('Maximum number of retries for reverting send coins transactions')
|
||||
.required(),
|
||||
})
|
||||
@ -1,16 +1,15 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { ensureUrlEndsWithSlash } from '../../../util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../../config/const'
|
||||
import { revertSendCoins as revertSendCoinsQuery } from './query/revertSendCoins'
|
||||
import { revertSettledSendCoins as revertSettledSendCoinsQuery } from './query/revertSettledSendCoins'
|
||||
import { settleSendCoins as settleSendCoinsQuery } from './query/settleSendCoins'
|
||||
import { voteForSendCoins as voteForSendCoinsQuery } from './query/voteForSendCoins'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { EncryptedTransferArgs } from '../../../graphql/model/EncryptedTransferArgs'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.SendCoinsClient`)
|
||||
|
||||
@ -83,7 +82,9 @@ export class SendCoinsClient {
|
||||
logger.debug(`settleSendCoins successful from endpoint=${this.endpoint}`)
|
||||
return true
|
||||
} catch (err) {
|
||||
throw new LogError(`settleSendCoins failed for endpoint=${this.endpoint}`, err)
|
||||
const errmsg = `settleSendCoins failed for endpoint=${this.endpoint}, err=${err}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +104,9 @@ export class SendCoinsClient {
|
||||
logger.debug(`revertSettledSendCoins successful from endpoint=${this.endpoint}`)
|
||||
return true
|
||||
} catch (err) {
|
||||
throw new LogError(`revertSettledSendCoins failed for endpoint=${this.endpoint}`, err)
|
||||
const errmsg = `revertSettledSendCoins failed for endpoint=${this.endpoint}, err=${err}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { AbstractLoggingView } from 'database'
|
||||
|
||||
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
|
||||
import { SendCoinsArgs } from '../model/SendCoinsArgs'
|
||||
|
||||
export class SendCoinsArgsLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: SendCoinsArgs) {
|
||||
@ -1,6 +1,6 @@
|
||||
import { AbstractLoggingView } from 'database'
|
||||
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { SendCoinsResult } from '../model/SendCoinsResult'
|
||||
|
||||
export class SendCoinsResultLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: SendCoinsResult) {
|
||||
@ -5,13 +5,4 @@ export const voteForSendCoins = gql`
|
||||
voteForSendCoins(data: $args)
|
||||
}
|
||||
`
|
||||
/*
|
||||
{
|
||||
vote
|
||||
recipGradidoID
|
||||
recipFirstName
|
||||
recipLastName
|
||||
recipAlias
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
3
core/src/federation/client/1_1/SendCoinsClient.ts
Normal file
3
core/src/federation/client/1_1/SendCoinsClient.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '../1_0/SendCoinsClient'
|
||||
|
||||
export class SendCoinsClient extends V1_0_SendCoinsClient {}
|
||||
@ -1,9 +1,8 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
|
||||
|
||||
import { SendCoinsClient as V1_1_SendCoinsClient } from '@/federation/client/1_1/SendCoinsClient'
|
||||
import { ApiVersionType } from '@/federation/enum/apiVersionType'
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from './1_0/SendCoinsClient'
|
||||
import { SendCoinsClient as V1_1_SendCoinsClient } from './1_1/SendCoinsClient'
|
||||
import { ApiVersionType } from '../enum/apiVersionType'
|
||||
|
||||
type SendCoinsClient = V1_0_SendCoinsClient | V1_1_SendCoinsClient
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { AbstractLoggingView } from 'database'
|
||||
|
||||
import { Decay } from '@/graphql/model/Decay'
|
||||
import { Decay } from '../model/Decay'
|
||||
import type { Decay as DecayInterface } from 'shared'
|
||||
|
||||
export class DecayLoggingView extends AbstractLoggingView {
|
||||
@ -6,10 +6,10 @@ import { CommunityLoggingView, getHomeCommunity } from 'database'
|
||||
import { verifyAndDecrypt } from 'shared'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.interpretEncryptedTransferArgs`)
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.interpretEncryptedTransferArgs`)
|
||||
|
||||
export const interpretEncryptedTransferArgs = async (args: EncryptedTransferArgs): Promise<JwtPayloadType | null> => {
|
||||
const methodLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.interpretEncryptedTransferArgs-method`)
|
||||
const methodLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.interpretEncryptedTransferArgs-method`)
|
||||
methodLogger.addContext('handshakeID', args.handshakeID)
|
||||
methodLogger.debug('interpretEncryptedTransferArgs()... args:', args)
|
||||
// first find with args.publicKey the community 'requestingCom', which starts the request
|
||||
|
||||
@ -1,37 +1,160 @@
|
||||
import {
|
||||
CommunityLoggingView,
|
||||
countOpenPendingTransactions,
|
||||
Community as DbCommunity,
|
||||
FederatedCommunity as DbFederatedCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
User as dbUser,
|
||||
findTransactionLinkByCode,
|
||||
findUserByIdentifier,
|
||||
getCommunityByUuid,
|
||||
PendingTransactionLoggingView,
|
||||
CommunityLoggingView,
|
||||
UserLoggingView,
|
||||
countOpenPendingTransactions,
|
||||
UserLoggingView
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { CONFIG as CONFIG_CORE } from '../../config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'
|
||||
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
|
||||
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory'
|
||||
import { SendCoinsClient as V1_0_SendCoinsClient } from '../../federation/client/1_0/SendCoinsClient'
|
||||
import { SendCoinsResult } from '../../federation/client/1_0/model/SendCoinsResult'
|
||||
import { SendCoinsClientFactory } from '../../federation/client/SendCoinsClientFactory'
|
||||
import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId'
|
||||
import { encryptAndSign, PendingTransactionState, SendCoinsJwtPayloadType, SendCoinsResponseJwtPayloadType, verifyAndDecrypt } from 'shared'
|
||||
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { calculateSenderBalance } from '@/util/calculateSenderBalance'
|
||||
import { fullName } from '@/util/utilities'
|
||||
// import { LogError } from '@server/LogError'
|
||||
import { calculateSenderBalance } from '../../util/calculateSenderBalance'
|
||||
import { fullName } from '../../util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { settlePendingSenderTransaction } from './settlePendingSenderTransaction'
|
||||
import { SendCoinsArgsLoggingView } from '@/federation/client/1_0/logging/SendCoinsArgsLogging.view'
|
||||
import { SendCoinsResultLoggingView } from '@/federation/client/1_0/logging/SendCoinsResultLogging.view'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { SendCoinsResultLoggingView } from '../../federation/client/1_0/logging/SendCoinsResultLogging.view'
|
||||
import { EncryptedTransferArgs } from '../../graphql/model/EncryptedTransferArgs'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
import { settlePendingSenderTransaction } from './settlePendingSenderTransaction'
|
||||
import { storeForeignUser } from './storeForeignUser'
|
||||
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.processXComSendCoins.${method}`)
|
||||
|
||||
export async function processXComCompleteTransaction(
|
||||
senderCommunityUuid: string,
|
||||
senderGradidoId: string,
|
||||
recipientCommunityUuid: string,
|
||||
recipientGradidoId: string,
|
||||
amount: string,
|
||||
memo: string,
|
||||
code?: string,
|
||||
recipientFirstName?: string,
|
||||
recipientAlias?: string,
|
||||
creationDate?: Date,
|
||||
): Promise<boolean> {
|
||||
const methodLogger = createLogger(`processXComCompleteTransaction`)
|
||||
// processing a x-community sendCoins
|
||||
methodLogger.info('processing a x-community transaction...')
|
||||
if (!CONFIG_CORE.FEDERATION_XCOM_SENDCOINS_ENABLED) {
|
||||
const errmsg = 'X-Community sendCoins disabled per configuration!'
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
const senderCom = await getCommunityByUuid(senderCommunityUuid)
|
||||
methodLogger.debug('sender community: ', senderCom?.id)
|
||||
if (senderCom === null) {
|
||||
const errmsg = `no sender community found for identifier: ${senderCommunityUuid}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
const senderUser = await findUserByIdentifier(senderGradidoId, senderCommunityUuid)
|
||||
if (senderUser === null) {
|
||||
const errmsg = `no sender user found for identifier: ${senderCommunityUuid}:${senderGradidoId}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
const recipientCom = await getCommunityByUuid(recipientCommunityUuid)
|
||||
methodLogger.debug('recipient community: ', recipientCom?.id)
|
||||
if (recipientCom === null) {
|
||||
const errmsg = `no recipient community found for identifier: ${recipientCommunityUuid}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if (recipientCom !== null && recipientCom.authenticatedAt === null) {
|
||||
const errmsg = 'recipient community is connected, but still not authenticated yet!'
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if(code !== undefined) {
|
||||
try {
|
||||
const dbTransactionLink = await findTransactionLinkByCode(code)
|
||||
if (dbTransactionLink && dbTransactionLink.validUntil < new Date()) {
|
||||
const errmsg = `TransactionLink ${code} is expired!`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
} catch (_err) {
|
||||
const errmsg = `TransactionLink ${code} not found any more!`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
if(creationDate === undefined) {
|
||||
creationDate = new Date()
|
||||
}
|
||||
let pendingResult: SendCoinsResponseJwtPayloadType | null = null
|
||||
let committingResult: SendCoinsResult
|
||||
|
||||
try {
|
||||
pendingResult = await processXComPendingSendCoins(
|
||||
recipientCom,
|
||||
senderCom,
|
||||
creationDate,
|
||||
new Decimal(amount),
|
||||
memo,
|
||||
senderUser,
|
||||
recipientGradidoId,
|
||||
)
|
||||
methodLogger.debug('processXComPendingSendCoins result: ', pendingResult)
|
||||
if (pendingResult && pendingResult.vote && pendingResult.recipGradidoID) {
|
||||
methodLogger.debug('vor processXComCommittingSendCoins... ')
|
||||
committingResult = await processXComCommittingSendCoins(
|
||||
recipientCom,
|
||||
senderCom,
|
||||
creationDate,
|
||||
new Decimal(amount),
|
||||
memo,
|
||||
senderUser,
|
||||
pendingResult,
|
||||
)
|
||||
methodLogger.debug('processXComCommittingSendCoins result: ', committingResult)
|
||||
if (!committingResult.vote) {
|
||||
methodLogger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult)
|
||||
throw new Error(
|
||||
'FATAL ERROR: on processXComCommittingSendCoins with ' +
|
||||
recipientCom.communityUuid +
|
||||
recipientGradidoId +
|
||||
amount.toString() +
|
||||
memo,
|
||||
)
|
||||
}
|
||||
// after successful x-com-tx store the recipient as foreign user
|
||||
methodLogger.debug('store recipient as foreign user...')
|
||||
if (await storeForeignUser(recipientCom, committingResult)) {
|
||||
methodLogger.info(
|
||||
'X-Com: new foreign user inserted successfully...',
|
||||
recipientCom.communityUuid,
|
||||
committingResult.recipGradidoID,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const errmsg = `ERROR: on processXComCommittingSendCoins with ` +
|
||||
recipientCommunityUuid +
|
||||
recipientGradidoId +
|
||||
amount.toString() +
|
||||
memo +
|
||||
err
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export async function processXComPendingSendCoins(
|
||||
receiverCom: DbCommunity,
|
||||
senderCom: DbCommunity,
|
||||
@ -60,7 +183,7 @@ export async function processXComPendingSendCoins(
|
||||
if (await countOpenPendingTransactions([sender.gradidoID, recipientIdentifier]) > 0) {
|
||||
const errmsg = `There exist still ongoing 'Pending-Transactions' for the involved users on sender-side!`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
const handshakeID = randombytes_random().toString()
|
||||
methodLogger.addContext('handshakeID', handshakeID)
|
||||
@ -69,7 +192,7 @@ export async function processXComPendingSendCoins(
|
||||
if (!senderBalance) {
|
||||
const errmsg = `User has not enough GDD or amount is < 0`
|
||||
methodLogger.error(errmsg)
|
||||
throw new LogError(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug(`calculated senderBalance = ${JSON.stringify(senderBalance, null, 2)}`)
|
||||
@ -78,7 +201,7 @@ export async function processXComPendingSendCoins(
|
||||
const receiverFCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: {
|
||||
publicKey: Buffer.from(receiverCom.publicKey),
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
apiVersion: CONFIG_CORE.FEDERATION_BACKEND_SEND_ON_API,
|
||||
},
|
||||
})
|
||||
const client = SendCoinsClientFactory.getInstance(receiverFCom)
|
||||
@ -173,7 +296,7 @@ export async function processXComPendingSendCoins(
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
} while (CONFIG.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
} while (CONFIG_CORE.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
const errmsg = `Error in reverting receiver pending transaction even after revertCount=${revertCount}` + JSON.stringify(err, null, 2)
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
@ -228,7 +351,7 @@ export async function processXComCommittingSendCoins(
|
||||
userGradidoID: sender.gradidoID,
|
||||
userName: fullName(sender.firstName, sender.lastName),
|
||||
linkedUserCommunityUuid:
|
||||
receiverCom.communityUuid ?? CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID,
|
||||
receiverCom.communityUuid ?? CONFIG_CORE.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID,
|
||||
linkedUserGradidoID: recipient.recipGradidoID ? recipient.recipGradidoID : undefined,
|
||||
typeId: TransactionTypeId.SEND,
|
||||
state: PendingTransactionState.NEW,
|
||||
@ -242,7 +365,7 @@ export async function processXComCommittingSendCoins(
|
||||
const receiverFCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: {
|
||||
publicKey: Buffer.from(receiverCom.publicKey),
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
apiVersion: CONFIG_CORE.FEDERATION_BACKEND_SEND_ON_API,
|
||||
},
|
||||
})
|
||||
const client = SendCoinsClientFactory.getInstance(receiverFCom)
|
||||
@ -252,7 +375,7 @@ export async function processXComCommittingSendCoins(
|
||||
handshakeID,
|
||||
pendingTx.linkedUserCommunityUuid
|
||||
? pendingTx.linkedUserCommunityUuid
|
||||
: CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID,
|
||||
: CONFIG_CORE.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID,
|
||||
pendingTx.linkedUserGradidoID!,
|
||||
pendingTx.balanceDate.toISOString(),
|
||||
pendingTx.amount.mul(-1),
|
||||
@ -264,7 +387,7 @@ export async function processXComCommittingSendCoins(
|
||||
)
|
||||
payload.recipientCommunityUuid = pendingTx.linkedUserCommunityUuid
|
||||
? pendingTx.linkedUserCommunityUuid
|
||||
: CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID
|
||||
: CONFIG_CORE.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID
|
||||
if (pendingTx.linkedUserGradidoID) {
|
||||
payload.recipientUserIdentifier = pendingTx.linkedUserGradidoID
|
||||
}
|
||||
@ -305,20 +428,21 @@ export async function processXComCommittingSendCoins(
|
||||
methodLogger.error(`Error in writing sender pending transaction: ${JSON.stringify(err, null, 2)}`)
|
||||
// revert the existing pending transaction on receiver side
|
||||
let revertCount = 0
|
||||
methodLogger.debug('first try to revertSetteledSendCoins of receiver')
|
||||
methodLogger.debug('first try to revertSettledSendCoins of receiver')
|
||||
do {
|
||||
if (await client.revertSettledSendCoins(args)) {
|
||||
methodLogger.debug(
|
||||
`revertSettledSendCoins()-1_0... successfull after revertCount=${revertCount}`,
|
||||
)
|
||||
// treat revertingSettledSendCoins as an error of the whole sendCoins-process
|
||||
throw new LogError('Error in settle sender pending transaction: ', err)
|
||||
const errmsg = `Error in settle sender pending transaction: ${JSON.stringify(err, null, 2)}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
} while (CONFIG.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
throw new LogError(
|
||||
`Error in reverting receiver pending transaction even after revertCount=${revertCount}`,
|
||||
err,
|
||||
)
|
||||
} while (CONFIG_CORE.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
const errmsg = `Error in reverting receiver pending transaction even after revertCount=${revertCount}`
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,17 +10,16 @@ import {
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'
|
||||
import { PendingTransactionState } from 'shared'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { calculateSenderBalance } from '@/util/calculateSenderBalance'
|
||||
// import { LogError } from '@/server/LogError'
|
||||
import { calculateSenderBalance } from 'core'
|
||||
import { TRANSACTIONS_LOCK, getLastTransaction } from 'database'
|
||||
import { getLogger } from 'log4js'
|
||||
import { getLastTransaction } from './getLastTransaction'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.settlePendingSenderTransaction`,
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.settlePendingSenderTransaction`,
|
||||
)
|
||||
|
||||
export async function settlePendingSenderTransaction(
|
||||
@ -53,15 +52,17 @@ export async function settlePendingSenderTransaction(
|
||||
],
|
||||
})
|
||||
if (openSenderPendingTx > 1 || openReceiverPendingTx > 1) {
|
||||
throw new LogError('There are more than 1 pending Transactions for Sender and/or Recipient')
|
||||
const errmsg = `There are more than 1 pending Transactions for Sender and/or Recipient`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
|
||||
const lastTransaction = await getLastTransaction(senderUser.id)
|
||||
|
||||
if (lastTransaction?.id !== pendingTx.previous) {
|
||||
throw new LogError(
|
||||
`X-Com: missmatching transaction order! lastTransationId=${lastTransaction?.id} != pendingTx.previous=${pendingTx.previous}`,
|
||||
)
|
||||
const errmsg = `X-Com: missmatching transaction order! lastTransationId=${lastTransaction?.id} != pendingTx.previous=${pendingTx.previous}`
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
|
||||
// transfer the pendingTx to the transactions table
|
||||
@ -82,7 +83,9 @@ export async function settlePendingSenderTransaction(
|
||||
pendingTx.balanceDate,
|
||||
)
|
||||
if (!sendBalance) {
|
||||
throw new LogError(`Sender has not enough GDD or amount is < 0', sendBalance`)
|
||||
const errmsg = 'Sender has not enough GDD or amount is < 0'
|
||||
logger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
transactionSend.balance = sendBalance?.balance ?? new Decimal(0)
|
||||
transactionSend.balanceDate = pendingTx.balanceDate
|
||||
@ -114,7 +117,8 @@ export async function settlePendingSenderTransaction(
|
||||
// void sendTransactionsToDltConnector()
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('X-Com: send Transaction was not successful', e)
|
||||
logger.error('X-Com: send Transaction was not successful', e)
|
||||
throw new Error('X-Com: send Transaction was not successful')
|
||||
} finally {
|
||||
await queryRunner.release()
|
||||
releaseLock()
|
||||
@ -1,10 +1,10 @@
|
||||
import { Community as DbCommunity, User as DbUser } from 'database'
|
||||
import { Community as DbCommunity, User as DbUser, findForeignUserByUuids } from 'database'
|
||||
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { SendCoinsResult } from '../../federation/client/1_0/model/SendCoinsResult'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.storeForeignUser`)
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.logic.storeForeignUser`)
|
||||
|
||||
export async function storeForeignUser(
|
||||
recipCom: DbCommunity,
|
||||
@ -12,13 +12,7 @@ export async function storeForeignUser(
|
||||
): Promise<boolean> {
|
||||
if (recipCom.communityUuid !== null && committingResult.recipGradidoID !== null) {
|
||||
try {
|
||||
const user = await DbUser.findOne({
|
||||
where: {
|
||||
foreign: true,
|
||||
communityUuid: recipCom.communityUuid,
|
||||
gradidoID: committingResult.recipGradidoID,
|
||||
},
|
||||
})
|
||||
const user = await findForeignUserByUuids(recipCom.communityUuid, committingResult.recipGradidoID)
|
||||
if (!user) {
|
||||
logger.debug(
|
||||
'no foreignUser found for:',
|
||||
@ -1,3 +1,25 @@
|
||||
export * from './validation/user'
|
||||
export {SendCoinsClient as V1_0_SendCoinsClient} from './federation/client/1_0/SendCoinsClient'
|
||||
export * from './federation/client/1_0/logging/SendCoinsArgsLogging.view'
|
||||
export * from './federation/client/1_0/logging/SendCoinsResultLogging.view'
|
||||
export * from './federation/client/1_0/model/SendCoinsArgs'
|
||||
export * from './federation/client/1_0/model/SendCoinsResult'
|
||||
export * from './federation/client/1_0/query/revertSendCoins'
|
||||
export * from './federation/client/1_0/query/revertSettledSendCoins'
|
||||
export * from './federation/client/1_0/query/settleSendCoins'
|
||||
export * from './federation/client/1_0/query/voteForSendCoins'
|
||||
export {SendCoinsClient as V1_1_SendCoinsClient} from './federation/client/1_1/SendCoinsClient'
|
||||
export * from './federation/client/SendCoinsClientFactory'
|
||||
export * from './federation/enum/apiVersionType'
|
||||
export * from './graphql/enum/TransactionTypeId'
|
||||
export * from './graphql/logging/DecayLogging.view'
|
||||
export * from './graphql/logic/interpretEncryptedTransferArgs'
|
||||
export * from './graphql/logic/processXComSendCoins'
|
||||
export * from './graphql/logic/settlePendingSenderTransaction'
|
||||
export * from './graphql/logic/storeForeignUser'
|
||||
export * from './graphql/model/Decay'
|
||||
export * from './graphql/model/EncryptedTransferArgs'
|
||||
export * from './util/calculateSenderBalance'
|
||||
export * from './util/utilities'
|
||||
export * from './validation/user'
|
||||
export * from './config/index'
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { Decay } from '@model/Decay'
|
||||
import { Decay } from '../graphql/model/Decay'
|
||||
|
||||
import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction'
|
||||
import { getLastTransaction } from 'database'
|
||||
|
||||
import { calculateDecay } from 'shared'
|
||||
|
||||
@ -44,8 +44,8 @@
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": ".", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "baseUrl": ".", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": { }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": ["bun-types"], /* Type declaration files to be included in compilation. */
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/node": "^18.7.14",
|
||||
"await-semaphore": "^0.1.3",
|
||||
"crypto-random-bigint": "^2.1.1",
|
||||
"jest": "27.2.4",
|
||||
"ts-jest": "27.0.5",
|
||||
|
||||
@ -60,4 +60,5 @@ export const entities = [
|
||||
export { latestDbVersion }
|
||||
export * from './logging'
|
||||
export * from './queries'
|
||||
export * from './util'
|
||||
export { AppDatabase } from './AppDatabase'
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Community as DbCommunity } from '../entity/Community'
|
||||
import { Community as DbCommunity } from '../entity'
|
||||
|
||||
/**
|
||||
* Retrieves the home community, i.e., a community that is not foreign.
|
||||
@ -8,4 +8,10 @@ export async function getHomeCommunity(): Promise<DbCommunity | null> {
|
||||
return await DbCommunity.findOne({
|
||||
where: { foreign: false },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCommunityByUuid(communityUuid: string): Promise<DbCommunity | null> {
|
||||
return await DbCommunity.findOne({
|
||||
where: [{ communityUuid }],
|
||||
})
|
||||
}
|
||||
|
||||
@ -3,5 +3,7 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
|
||||
export * from './user'
|
||||
export * from './communities'
|
||||
export * from './pendingTransactions'
|
||||
export * from './transactions'
|
||||
export * from './transactionLinks'
|
||||
|
||||
export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries`
|
||||
|
||||
8
database/src/queries/transactionLinks.ts
Normal file
8
database/src/queries/transactionLinks.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { TransactionLink as DbTransactionLink } from "../entity"
|
||||
|
||||
export async function findTransactionLinkByCode(code: string): Promise<DbTransactionLink> {
|
||||
return await DbTransactionLink.findOneOrFail({
|
||||
where: { code },
|
||||
withDeleted: true,
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { Transaction as DbTransaction } from 'database'
|
||||
import { Transaction as DbTransaction } from '../entity'
|
||||
|
||||
export const getLastTransaction = async (
|
||||
userId: number,
|
||||
@ -61,3 +61,12 @@ export const findUserByIdentifier = async (
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function findForeignUserByUuids(
|
||||
communityUuid: string,
|
||||
gradidoID: string,
|
||||
): Promise<DbUser | null> {
|
||||
return DbUser.findOne({
|
||||
where: { foreign: true, communityUuid, gradidoID },
|
||||
})
|
||||
}
|
||||
|
||||
2
database/src/util/index.ts
Normal file
2
database/src/util/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './TRANSACTIONS_LOCK'
|
||||
export * from './TRANSACTION_LINK_LOCK'
|
||||
@ -35,8 +35,8 @@ export default defineConfig({
|
||||
excludeSpecPattern: '*.js',
|
||||
baseUrl: 'http://127.0.0.1:3000',
|
||||
chromeWebSecurity: false,
|
||||
defaultCommandTimeout: 25000,
|
||||
pageLoadTimeout: 24000,
|
||||
defaultCommandTimeout: 50000,
|
||||
pageLoadTimeout: 32000,
|
||||
supportFile: 'cypress/support/index.ts',
|
||||
viewportHeight: 720,
|
||||
viewportWidth: 1280,
|
||||
|
||||
@ -3,7 +3,7 @@ import { GraphQLClient } from 'graphql-request'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { EncryptedTransferArgs } from 'core/src/graphql/model/EncryptedTransferArgs'
|
||||
import { EncryptedTransferArgs } from 'core'
|
||||
import { authenticate } from './query/authenticate'
|
||||
import { openConnectionCallback } from './query/openConnectionCallback'
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from './1_0/AuthenticationClient'
|
||||
|
||||
import { AuthenticationClient as V1_1_AuthenticationClient } from './1_1/AuthenticationClient'
|
||||
import { ApiVersionType } from './enum/ApiVersionType'
|
||||
import { ApiVersionType } from 'core'
|
||||
|
||||
type AuthenticationClient = V1_0_AuthenticationClient | V1_1_AuthenticationClient
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
export enum ApiVersionType {
|
||||
V1_0 = '1_0',
|
||||
V1_1 = '1_1',
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
import { getLogger } from "log4js"
|
||||
import { Arg, Mutation, Resolver } from "type-graphql"
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from "@/config/const"
|
||||
import { interpretEncryptedTransferArgs } from "core"
|
||||
import { EncryptedTransferArgs } from "core"
|
||||
import { DisburseJwtPayloadType } from "shared"
|
||||
import { processXComCompleteTransaction } from "core"
|
||||
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.DisbursementResolver.${method}`)
|
||||
|
||||
@Resolver()
|
||||
export class DisbursementResolver {
|
||||
@Mutation(() => String)
|
||||
async processDisburseJwtOnSenderCommunity(
|
||||
@Arg('data')
|
||||
args: EncryptedTransferArgs,
|
||||
): Promise<string> {
|
||||
const methodLogger = createLogger(`processDisburseJwtOnSenderCommunity`)
|
||||
methodLogger.addContext('handshakeID', args.handshakeID)
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug(`processDisburseJwtOnSenderCommunity() via apiVersion=1_0 ...`, args)
|
||||
}
|
||||
const authArgs = await interpretEncryptedTransferArgs(args) as DisburseJwtPayloadType
|
||||
if (!authArgs) {
|
||||
const errmsg = `invalid disbursement payload of requesting community with publicKey` + args.publicKey
|
||||
methodLogger.error(errmsg)
|
||||
throw new Error(errmsg)
|
||||
}
|
||||
if(methodLogger.isDebugEnabled()) {
|
||||
methodLogger.debug(`processDisburseJwtOnSenderCommunity() via apiVersion=1_0 ...`, authArgs)
|
||||
}
|
||||
let result = 'Disbursement of Redeem-Link failed!'
|
||||
try {
|
||||
if(await processXComCompleteTransaction(
|
||||
authArgs.sendercommunityuuid,
|
||||
authArgs.sendergradidoid,
|
||||
authArgs.recipientcommunityuuid,
|
||||
authArgs.recipientgradidoid,
|
||||
authArgs.amount,
|
||||
authArgs.memo,
|
||||
authArgs.code,
|
||||
authArgs.recipientfirstname,
|
||||
authArgs.recipientalias,
|
||||
)) {
|
||||
result = 'Disbursement of Redeem-Link successfull!'
|
||||
}
|
||||
} catch (err) {
|
||||
result = `Error in Disbursement of Redeem-Link: ` + err
|
||||
methodLogger.error(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
import { NonEmptyArray } from 'type-graphql'
|
||||
import { AuthenticationResolver } from './resolver/AuthenticationResolver'
|
||||
import { DisbursementResolver } from './resolver/DisbursementResolver'
|
||||
import { PublicCommunityInfoResolver } from './resolver/PublicCommunityInfoResolver'
|
||||
import { PublicKeyResolver } from './resolver/PublicKeyResolver'
|
||||
import { SendCoinsResolver } from './resolver/SendCoinsResolver'
|
||||
|
||||
export const getApiResolvers = (): NonEmptyArray<Function> => {
|
||||
return [AuthenticationResolver, PublicCommunityInfoResolver, PublicKeyResolver, SendCoinsResolver]
|
||||
return [AuthenticationResolver, DisbursementResolver, PublicCommunityInfoResolver, PublicKeyResolver, SendCoinsResolver]
|
||||
}
|
||||
|
||||
@ -340,10 +340,7 @@ function setRedeemJwtLinkInformation() {
|
||||
if (deepCopy) {
|
||||
// recipientUser is only set if the user is logged in
|
||||
if (store.state.gradidoID !== null) {
|
||||
// console.log(
|
||||
// 'TransactionLink.setRedeemJwtLinkInformation... gradidoID=',
|
||||
// store.state.gradidoID,
|
||||
// )
|
||||
// console.log('TransactionLink.setRedeemJwtLinkInformation... gradidoID=', store.state.gradidoID)
|
||||
deepCopy.queryTransactionLink.recipientUser = {
|
||||
__typename: 'User',
|
||||
gradidoID: store.state.gradidoID,
|
||||
@ -362,11 +359,23 @@ function setRedeemJwtLinkInformation() {
|
||||
|
||||
async function mutationLink(amount) {
|
||||
// console.log('TransactionLink.mutationLink... params=', params)
|
||||
// console.log('TransactionLink.mutationLink... linkData.value=', linkData.value)
|
||||
// console.log('TransactionLink.mutationLink... linkData=', linkData)
|
||||
if (isRedeemJwtLink.value) {
|
||||
// console.log('TransactionLink.mutationLink... trigger disbursement from recipient-community')
|
||||
try {
|
||||
await disburseMutate({
|
||||
code: params.code,
|
||||
senderCommunityUuid: linkData.value.senderCommunity.uuid,
|
||||
senderGradidoId: linkData.value.senderUser.gradidoID,
|
||||
recipientCommunityUuid: linkData.value.recipientCommunity.uuid,
|
||||
recipientCommunityName: linkData.value.recipientCommunity.name,
|
||||
recipientGradidoId: linkData.value.recipientUser.gradidoID,
|
||||
recipientFirstName: linkData.value.recipientUser.firstName,
|
||||
code: linkData.value.code,
|
||||
amount: linkData.value.amount,
|
||||
memo: linkData.value.memo,
|
||||
validUntil: linkData.value.validUntil,
|
||||
recipientAlias: linkData.value.recipientUser.alias,
|
||||
})
|
||||
toastSuccess(t('gdd_per_link.disbured', { n: amount }))
|
||||
await router.push('/overview')
|
||||
|
||||
@ -12,4 +12,5 @@ export * from './jwt/payloadtypes/OpenConnectionCallbackJwtPayloadType'
|
||||
export * from './jwt/payloadtypes/RedeemJwtPayloadType'
|
||||
export * from './jwt/payloadtypes/SendCoinsJwtPayloadType'
|
||||
export * from './jwt/payloadtypes/SendCoinsResponseJwtPayloadType'
|
||||
export * from './jwt/payloadtypes/SignedTransferPayloadType'
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ export class DisburseJwtPayloadType extends JwtPayloadType {
|
||||
recipientalias: string
|
||||
|
||||
constructor(
|
||||
handshakeID: string,
|
||||
senderCommunityUuid: string,
|
||||
senderGradidoId: string,
|
||||
recipientCommunityUuid: string,
|
||||
@ -29,9 +30,7 @@ export class DisburseJwtPayloadType extends JwtPayloadType {
|
||||
validUntil: string,
|
||||
recipientAlias: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super('handshakeID')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
super(handshakeID)
|
||||
this.tokentype = DisburseJwtPayloadType.DISBURSE_ACTIVATION_TYPE
|
||||
this.sendercommunityuuid = senderCommunityUuid
|
||||
this.sendergradidoid = senderGradidoId
|
||||
|
||||
@ -9,9 +9,7 @@ export class EncryptedJWEJwtPayloadType extends JwtPayloadType {
|
||||
handshakeID: string,
|
||||
jwe: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super(handshakeID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = EncryptedJWEJwtPayloadType.ENCRYPTED_JWE_TYPE
|
||||
this.jwe = jwe
|
||||
}
|
||||
|
||||
@ -11,9 +11,7 @@ export class OpenConnectionCallbackJwtPayloadType extends JwtPayloadType {
|
||||
oneTimeCode: string,
|
||||
url: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super(handshakeID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = OpenConnectionCallbackJwtPayloadType.OPEN_CONNECTION_CALLBACK_TYPE
|
||||
this.oneTimeCode = oneTimeCode
|
||||
this.url = url
|
||||
|
||||
@ -9,9 +9,7 @@ export class OpenConnectionJwtPayloadType extends JwtPayloadType {
|
||||
handshakeID: string,
|
||||
url: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super(handshakeID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = OpenConnectionJwtPayloadType.OPEN_CONNECTION_TYPE
|
||||
this.url = url
|
||||
}
|
||||
|
||||
@ -20,9 +20,7 @@ export class RedeemJwtPayloadType extends JwtPayloadType {
|
||||
memo: string,
|
||||
validUntil: string,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super('handshakeID')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = RedeemJwtPayloadType.REDEEM_ACTIVATION_TYPE
|
||||
this.sendercommunityuuid = senderCom
|
||||
this.sendergradidoid = senderUser
|
||||
|
||||
@ -26,9 +26,7 @@ export class SendCoinsJwtPayloadType extends JwtPayloadType {
|
||||
senderUserName: string,
|
||||
senderAlias?: string | null,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super(handshakeID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = SendCoinsJwtPayloadType.SEND_COINS_TYPE
|
||||
this.recipientCommunityUuid = recipientCommunityUuid
|
||||
this.recipientUserIdentifier = recipientUserIdentifier
|
||||
|
||||
@ -17,9 +17,7 @@ export class SendCoinsResponseJwtPayloadType extends JwtPayloadType {
|
||||
recipLastName: string | null,
|
||||
recipAlias: string | null,
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
super(handshakeID)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
this.tokentype = SendCoinsResponseJwtPayloadType.SEND_COINS_RESPONSE_TYPE
|
||||
this.vote = vote
|
||||
this.recipGradidoID = recipGradidoID
|
||||
|
||||
19
shared/src/jwt/payloadtypes/SignedTransferPayloadType.ts
Normal file
19
shared/src/jwt/payloadtypes/SignedTransferPayloadType.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { JwtPayloadType } from './JwtPayloadType'
|
||||
|
||||
export class SignedTransferPayloadType extends JwtPayloadType {
|
||||
static SIGNED_TRANSFER_TYPE = 'signed-transfer'
|
||||
|
||||
publicKey: string
|
||||
jwt: string
|
||||
|
||||
constructor(
|
||||
publicKey: string,
|
||||
jwt: string,
|
||||
handshakeID: string,
|
||||
) {
|
||||
super(handshakeID)
|
||||
this.tokentype = SignedTransferPayloadType.SIGNED_TRANSFER_TYPE
|
||||
this.publicKey = publicKey
|
||||
this.jwt = jwt
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user