diff --git a/dlt-connector/src/data/Account.factory.ts b/dlt-connector/src/data/Account.factory.ts deleted file mode 100644 index fc20a0acc..000000000 --- a/dlt-connector/src/data/Account.factory.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable camelcase */ -import { Account } from '@entity/Account' -import Decimal from 'decimal.js-light' -import { - AddressType, - AddressType_COMMUNITY_AUF, - AddressType_COMMUNITY_GMW, -} from 'gradido-blockchain-js' - -import { KeyPair } from '@/data/KeyPair' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { hardenDerivationIndex } from '@/utils/derivationHelper' -import { accountTypeToAddressType } from '@/utils/typeConverter' - -const GMW_ACCOUNT_DERIVATION_INDEX = 1 -const AUF_ACCOUNT_DERIVATION_INDEX = 2 - -export class AccountFactory { - public static createAccount( - createdAt: Date, - derivationIndex: number, - type: AddressType, - parentKeyPair: KeyPair, - ): Account { - const account = Account.create() - account.derivationIndex = derivationIndex - account.derive2Pubkey = parentKeyPair.derive([derivationIndex]).publicKey - account.type = type.valueOf() - account.createdAt = createdAt - account.balanceOnConfirmation = new Decimal(0) - account.balanceOnCreation = new Decimal(0) - account.balanceCreatedAt = createdAt - return account - } - - public static createAccountFromUserAccountDraft( - { createdAt, accountType, user }: UserAccountDraft, - parentKeyPair: KeyPair, - ): Account { - return AccountFactory.createAccount( - new Date(createdAt), - user.accountNr ?? 1, - accountTypeToAddressType(accountType), - parentKeyPair, - ) - } - - public static createGmwAccount(keyPair: KeyPair, createdAt: Date): Account { - return AccountFactory.createAccount( - createdAt, - hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX), - AddressType_COMMUNITY_GMW, - keyPair, - ) - } - - public static createAufAccount(keyPair: KeyPair, createdAt: Date): Account { - return AccountFactory.createAccount( - createdAt, - hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX), - AddressType_COMMUNITY_AUF, - keyPair, - ) - } -} diff --git a/dlt-connector/src/data/Account.logic.ts b/dlt-connector/src/data/Account.logic.ts deleted file mode 100644 index 9cff66070..000000000 --- a/dlt-connector/src/data/Account.logic.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Account } from '@entity/Account' - -import { LogError } from '@/server/LogError' - -import { KeyPair } from './KeyPair' -import { UserLogic } from './User.logic' - -export class AccountLogic { - // eslint-disable-next-line no-useless-constructor - public constructor(private self: Account) {} - - /** - * calculate account key pair starting from community key pair => derive user key pair => derive account key pair - * @param communityKeyPair - */ - public calculateKeyPair(communityKeyPair: KeyPair): KeyPair { - if (!this.self.user) { - throw new LogError('missing user') - } - const userLogic = new UserLogic(this.self.user) - const accountKeyPair = userLogic - .calculateKeyPair(communityKeyPair) - .derive([this.self.derivationIndex]) - - if ( - this.self.derive2Pubkey && - this.self.derive2Pubkey.compare(accountKeyPair.publicKey) !== 0 - ) { - throw new LogError( - 'The freshly derived public key does not correspond to the stored public key', - ) - } - return accountKeyPair - } -} diff --git a/dlt-connector/src/data/Account.repository.ts b/dlt-connector/src/data/Account.repository.ts deleted file mode 100644 index 6931e6ea6..000000000 --- a/dlt-connector/src/data/Account.repository.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Account } from '@entity/Account' -import { User } from '@entity/User' -import { In } from 'typeorm' - -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { getDataSource } from '@/typeorm/DataSource' - -export const AccountRepository = getDataSource() - .getRepository(Account) - .extend({ - findAccountsByPublicKeys(publicKeys: Buffer[]): Promise { - return this.findBy({ derive2Pubkey: In(publicKeys) }) - }, - - async findAccountByPublicKey(publicKey: Buffer | undefined): Promise { - if (!publicKey) return undefined - return (await this.findOneBy({ derive2Pubkey: Buffer.from(publicKey) })) ?? undefined - }, - - async findAccountByUserIdentifier({ - uuid, - accountNr, - }: UserIdentifier): Promise { - const user = await User.findOne({ - where: { gradidoID: uuid, accounts: { derivationIndex: accountNr ?? 1 } }, - relations: { accounts: true }, - }) - if (user && user.accounts?.length === 1) { - const account = user.accounts[0] - account.user = user - return account - } - }, - }) diff --git a/dlt-connector/src/data/Account.test.ts b/dlt-connector/src/data/Account.test.ts deleted file mode 100644 index 130908de6..000000000 --- a/dlt-connector/src/data/Account.test.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* eslint-disable camelcase */ -import 'reflect-metadata' -import { Decimal } from 'decimal.js-light' - -import { TestDB } from '@test/TestDB' - -import { - AddressType_COMMUNITY_AUF, - AddressType_COMMUNITY_GMW, - AddressType_COMMUNITY_HUMAN, -} from 'gradido-blockchain-js' - -import { AccountType } from '@/graphql/enum/AccountType' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { UserIdentifier } from '@/graphql/input/UserIdentifier' - -import { AccountFactory } from './Account.factory' -import { AccountRepository } from './Account.repository' -import { KeyPair } from './KeyPair' -import { Mnemonic } from './Mnemonic' -import { UserFactory } from './User.factory' -import { UserLogic } from './User.logic' - -const con = TestDB.instance - -jest.mock('@typeorm/DataSource', () => ({ - getDataSource: jest.fn(() => TestDB.instance.dbConnect), -})) - -describe('data/Account test factory and repository', () => { - const now = new Date() - const keyPair1 = new KeyPair(new Mnemonic('62ef251edc2416f162cd24ab1711982b')) - const keyPair2 = new KeyPair(new Mnemonic('000a0000000002000000000003000070')) - const keyPair3 = new KeyPair(new Mnemonic('00ba541a1000020000000000300bda70')) - const userGradidoID = '6be949ab-8198-4acf-ba63-740089081d61' - - describe('test factory methods', () => { - beforeAll(async () => { - await con.setupTestDB() - }) - afterAll(async () => { - await con.teardownTestDB() - }) - - it('test createAccount', () => { - const account = AccountFactory.createAccount(now, 1, AddressType_COMMUNITY_HUMAN, keyPair1) - expect(account).toMatchObject({ - derivationIndex: 1, - derive2Pubkey: Buffer.from( - 'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994', - 'hex', - ), - type: AddressType_COMMUNITY_HUMAN, - createdAt: now, - balanceCreatedAt: now, - balanceOnConfirmation: new Decimal(0), - balanceOnCreation: new Decimal(0), - }) - }) - - it('test createAccountFromUserAccountDraft', () => { - const userAccountDraft = new UserAccountDraft() - userAccountDraft.createdAt = now.toISOString() - userAccountDraft.accountType = AccountType.COMMUNITY_HUMAN - userAccountDraft.user = new UserIdentifier() - userAccountDraft.user.accountNr = 1 - const account = AccountFactory.createAccountFromUserAccountDraft(userAccountDraft, keyPair1) - expect(account).toMatchObject({ - derivationIndex: 1, - derive2Pubkey: Buffer.from( - 'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994', - 'hex', - ), - type: AddressType_COMMUNITY_HUMAN, - createdAt: now, - balanceCreatedAt: now, - balanceOnConfirmation: new Decimal(0), - balanceOnCreation: new Decimal(0), - }) - }) - - it('test createGmwAccount', () => { - const account = AccountFactory.createGmwAccount(keyPair1, now) - expect(account).toMatchObject({ - derivationIndex: 2147483649, - derive2Pubkey: Buffer.from( - '05f0060357bb73bd290283870fc47a10b3764f02ca26938479ed853f46145366', - 'hex', - ), - type: AddressType_COMMUNITY_GMW, - createdAt: now, - balanceCreatedAt: now, - balanceOnConfirmation: new Decimal(0), - balanceOnCreation: new Decimal(0), - }) - }) - - it('test createAufAccount', () => { - const account = AccountFactory.createAufAccount(keyPair1, now) - expect(account).toMatchObject({ - derivationIndex: 2147483650, - derive2Pubkey: Buffer.from( - '6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', - 'hex', - ), - type: AddressType_COMMUNITY_AUF, - createdAt: now, - balanceCreatedAt: now, - balanceOnConfirmation: new Decimal(0), - balanceOnCreation: new Decimal(0), - }) - }) - }) - - describe('test repository functions', () => { - beforeAll(async () => { - await con.setupTestDB() - await Promise.all([ - AccountFactory.createAufAccount(keyPair1, now).save(), - AccountFactory.createGmwAccount(keyPair1, now).save(), - AccountFactory.createAufAccount(keyPair2, now).save(), - AccountFactory.createGmwAccount(keyPair2, now).save(), - AccountFactory.createAufAccount(keyPair3, now).save(), - AccountFactory.createGmwAccount(keyPair3, now).save(), - ]) - const userAccountDraft = new UserAccountDraft() - userAccountDraft.accountType = AccountType.COMMUNITY_HUMAN - userAccountDraft.createdAt = now.toString() - userAccountDraft.user = new UserIdentifier() - userAccountDraft.user.accountNr = 1 - userAccountDraft.user.uuid = userGradidoID - const user = UserFactory.create(userAccountDraft, keyPair1) - const userLogic = new UserLogic(user) - const account = AccountFactory.createAccountFromUserAccountDraft( - userAccountDraft, - userLogic.calculateKeyPair(keyPair1), - ) - account.user = user - // user is set to cascade: ['insert'] will be saved together with account - await account.save() - }) - afterAll(async () => { - await con.teardownTestDB() - }) - it('test findAccountsByPublicKeys', async () => { - const accounts = await AccountRepository.findAccountsByPublicKeys([ - Buffer.from('6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', 'hex'), - Buffer.from('0fa996b73b624592fe326b8500cb1e3f10026112b374d84c87d097f4d489c019', 'hex'), - Buffer.from('0ffa996b73b624592f26b850b0cb1e3f1026112b374d84c87d017f4d489c0197', 'hex'), // invalid - ]) - expect(accounts).toHaveLength(2) - expect(accounts).toMatchObject( - expect.arrayContaining([ - expect.objectContaining({ - derivationIndex: 2147483649, - derive2Pubkey: Buffer.from( - '0fa996b73b624592fe326b8500cb1e3f10026112b374d84c87d097f4d489c019', - 'hex', - ), - type: AddressType_COMMUNITY_GMW, - }), - expect.objectContaining({ - derivationIndex: 2147483650, - derive2Pubkey: Buffer.from( - '6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', - 'hex', - ), - type: AddressType_COMMUNITY_AUF, - }), - ]), - ) - }) - - it('test findAccountByPublicKey', async () => { - expect( - await AccountRepository.findAccountByPublicKey( - Buffer.from('6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', 'hex'), - ), - ).toMatchObject({ - derivationIndex: 2147483650, - derive2Pubkey: Buffer.from( - '6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', - 'hex', - ), - type: AddressType_COMMUNITY_AUF, - }) - }) - - it('test findAccountByUserIdentifier', async () => { - const userIdentifier = new UserIdentifier() - userIdentifier.accountNr = 1 - userIdentifier.uuid = userGradidoID - expect(await AccountRepository.findAccountByUserIdentifier(userIdentifier)).toMatchObject({ - derivationIndex: 1, - derive2Pubkey: Buffer.from( - '2099c004a26e5387c9fbbc9bb0f552a9642d3fd7c710ae5802b775d24ff36f93', - 'hex', - ), - type: AddressType_COMMUNITY_HUMAN, - }) - }) - }) -}) diff --git a/dlt-connector/src/data/Community.repository.ts b/dlt-connector/src/data/Community.repository.ts deleted file mode 100644 index eb52fb38c..000000000 --- a/dlt-connector/src/data/Community.repository.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Community } from '@entity/Community' -import { FindOptionsSelect, In, IsNull, Not } from 'typeorm' - -import { CommunityArg } from '@/graphql/arg/CommunityArg' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { TransactionError } from '@/graphql/model/TransactionError' -import { LogError } from '@/server/LogError' -import { getDataSource } from '@/typeorm/DataSource' -import { uuid4ToHash } from '@/utils/typeConverter' - -import { KeyPair } from './KeyPair' - -export const CommunityRepository = getDataSource() - .getRepository(Community) - .extend({ - async isExist(community: CommunityDraft | string): Promise { - const iotaTopic = - community instanceof CommunityDraft ? uuid4ToHash(community.uuid) : community - const result = await this.find({ - where: { iotaTopic }, - }) - return result.length > 0 - }, - - async findByCommunityArg({ uuid, foreign, confirmed }: CommunityArg): Promise { - return await this.find({ - where: { - ...(uuid && { iotaTopic: uuid4ToHash(uuid) }), - ...(foreign && { foreign }), - ...(confirmed && { confirmedAt: Not(IsNull()) }), - }, - }) - }, - - async findByCommunityUuid(communityUuid: string): Promise { - return await this.findOneBy({ iotaTopic: uuid4ToHash(communityUuid) }) - }, - - async findByIotaTopic(iotaTopic: string): Promise { - return await this.findOneBy({ iotaTopic }) - }, - - findCommunitiesByTopics(topics: string[]): Promise { - return this.findBy({ iotaTopic: In(topics) }) - }, - - async getCommunityForUserIdentifier( - identifier: UserIdentifier, - ): Promise { - if (!identifier.communityUuid) { - throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'community uuid not set') - } - return ( - (await this.findOneBy({ - iotaTopic: uuid4ToHash(identifier.communityUuid), - })) ?? undefined - ) - }, - - findAll(select: FindOptionsSelect): Promise { - return this.find({ select }) - }, - - async loadHomeCommunityKeyPair(): Promise { - const community = await this.findOneOrFail({ - where: { foreign: false }, - select: { rootChaincode: true, rootPubkey: true, rootEncryptedPrivkey: true }, - }) - if (!community.rootChaincode || !community.rootEncryptedPrivkey) { - throw new LogError('Missing chaincode or private key for home community') - } - return new KeyPair(community) - }, - - async loadHomeCommunity(): Promise { - return await this.findOneOrFail({ - where: { foreign: false }, - }) - }, - }) diff --git a/dlt-connector/src/data/KeyPair.ts b/dlt-connector/src/data/KeyPair.ts deleted file mode 100644 index 61207ee7e..000000000 --- a/dlt-connector/src/data/KeyPair.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Community } from '@entity/Community' -// https://www.npmjs.com/package/bip32-ed25519 -import { - KeyPairEd25519, - MemoryBlock, - Passphrase, - SecretKeyCryptography, - SignaturePair, -} from 'gradido-blockchain-js' - -import { CONFIG } from '@/config' -import { LogError } from '@/server/LogError' - -/** - * Class Managing Key Pair and also generate, sign and verify signature with it - */ -export class KeyPair { - private _ed25519KeyPair: KeyPairEd25519 - /** - * @param input: KeyPairEd25519 = already loaded KeyPairEd25519 - * @param input: Passphrase = Passphrase which work as seed for generating algorithms - * @param input: MemoryBlock = a seed at least 32 byte - * @param input: Community = community entity with keys loaded from db - */ - public constructor(input: KeyPairEd25519 | Passphrase | MemoryBlock | Community) { - let keyPair: KeyPairEd25519 | null = null - if (input instanceof KeyPairEd25519) { - keyPair = input - } else if (input instanceof Passphrase) { - keyPair = KeyPairEd25519.create(input) - } else if (input instanceof MemoryBlock) { - keyPair = KeyPairEd25519.create(input) - } else if (input instanceof Community) { - if (!input.rootEncryptedPrivkey || !input.rootChaincode || !input.rootPubkey) { - throw new LogError( - 'missing encrypted private key or chaincode or public key in commmunity entity', - ) - } - const secretBox = this.createSecretBox(input.iotaTopic) - keyPair = new KeyPairEd25519( - new MemoryBlock(input.rootPubkey), - secretBox.decrypt(new MemoryBlock(input.rootEncryptedPrivkey)), - new MemoryBlock(input.rootChaincode), - ) - } - if (!keyPair) { - throw new LogError("couldn't create KeyPairEd25519 from input") - } - this._ed25519KeyPair = keyPair - } - - /** - * copy keys to community entity - * @param community - */ - public fillInCommunityKeys(community: Community) { - const secretBox = this.createSecretBox(community.iotaTopic) - community.rootPubkey = this._ed25519KeyPair.getPublicKey()?.data() - community.rootEncryptedPrivkey = this._ed25519KeyPair.getCryptedPrivKey(secretBox).data() - community.rootChaincode = this._ed25519KeyPair.getChainCode()?.data() - } - - public get publicKey(): Buffer { - const publicKey = this._ed25519KeyPair.getPublicKey() - if (!publicKey) { - throw new LogError('invalid key pair, get empty public key') - } - return publicKey.data() - } - - public get keyPair(): KeyPairEd25519 { - return this._ed25519KeyPair - } - - public derive(path: number[]): KeyPair { - return new KeyPair( - path.reduce( - (keyPair: KeyPairEd25519, node: number) => keyPair.deriveChild(node), - this._ed25519KeyPair, - ), - ) - } - - public sign(message: Buffer): Buffer { - return this._ed25519KeyPair.sign(new MemoryBlock(message)).data() - } - - public static verify(message: Buffer, signaturePair: SignaturePair): boolean { - const publicKeyPair = new KeyPairEd25519(signaturePair.getPubkey()) - const signature = signaturePair.getSignature() - if (!signature) { - throw new LogError('missing signature') - } - return publicKeyPair.verify(new MemoryBlock(message), signature) - } - - private createSecretBox(salt: string): SecretKeyCryptography { - if (!CONFIG.GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD) { - throw new LogError( - 'missing GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD in env or config', - ) - } - const secretBox = new SecretKeyCryptography() - secretBox.createKey(salt, CONFIG.GRADIDO_BLOCKCHAIN_PRIVATE_KEY_ENCRYPTION_PASSWORD) - return secretBox - } -} diff --git a/dlt-connector/src/data/Transaction.builder.ts b/dlt-connector/src/data/Transaction.builder.ts deleted file mode 100644 index a07ed8589..000000000 --- a/dlt-connector/src/data/Transaction.builder.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { Account } from '@entity/Account' -import { Community } from '@entity/Community' -import { Transaction } from '@entity/Transaction' -import { - GradidoTransaction, - InteractionSerialize, - InteractionToJson, - TransactionBody, -} from 'gradido-blockchain-js' - -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { LogError } from '@/server/LogError' - -import { CommunityRepository } from './Community.repository' - -export class TransactionBuilder { - private transaction: Transaction - - // https://refactoring.guru/design-patterns/builder/typescript/example - /** - * A fresh builder instance should contain a blank product object, which is - * used in further assembly. - */ - constructor() { - this.reset() - } - - public reset(): void { - this.transaction = Transaction.create() - } - - /** - * Concrete Builders are supposed to provide their own methods for - * retrieving results. That's because various types of builders may create - * entirely different products that don't follow the same interface. - * Therefore, such methods cannot be declared in the base Builder interface - * (at least in a statically typed programming language). - * - * Usually, after returning the end result to the client, a builder instance - * is expected to be ready to start producing another product. That's why - * it's a usual practice to call the reset method at the end of the - * `getProduct` method body. However, this behavior is not mandatory, and - * you can make your builders wait for an explicit reset call from the - * client code before disposing of the previous result. - */ - public build(): Transaction { - const result = this.transaction - this.reset() - return result - } - - // return transaction without calling reset - public getTransaction(): Transaction { - return this.transaction - } - - public getCommunity(): Community { - return this.transaction.community - } - - public getOtherCommunity(): Community | undefined { - return this.transaction.otherCommunity - } - - public setSigningAccount(signingAccount: Account): TransactionBuilder { - this.transaction.signingAccount = signingAccount - return this - } - - public setRecipientAccount(recipientAccount: Account): TransactionBuilder { - this.transaction.recipientAccount = recipientAccount - return this - } - - public setCommunity(community: Community): TransactionBuilder { - this.transaction.community = community - return this - } - - public setOtherCommunity(otherCommunity?: Community): TransactionBuilder { - if (!this.transaction.community) { - throw new LogError('Please set community first!') - } - - this.transaction.otherCommunity = - otherCommunity && - this.transaction.community && - this.transaction.community.id !== otherCommunity.id - ? otherCommunity - : undefined - return this - } - - public setSignature(signature: Buffer): TransactionBuilder { - this.transaction.signature = signature - return this - } - - public async setCommunityFromUser(user: UserIdentifier): Promise { - // get sender community - const community = await CommunityRepository.getCommunityForUserIdentifier(user) - if (!community) { - throw new LogError("couldn't find community for transaction") - } - return this.setCommunity(community) - } - - public async setOtherCommunityFromUser(user: UserIdentifier): Promise { - // get recipient community - const otherCommunity = await CommunityRepository.getCommunityForUserIdentifier(user) - return this.setOtherCommunity(otherCommunity) - } - - public fromGradidoTransaction(transaction: GradidoTransaction): TransactionBuilder { - const body = transaction.getTransactionBody() - if (!body) { - throw new LogError('missing transaction body on Gradido Transaction') - } - // set first signature - const firstSignature = transaction.getSignatureMap().getSignaturePairs().get(0).getSignature() - if (!firstSignature) { - throw new LogError('error missing first signature') - } - this.transaction.signature = firstSignature.data() - return this.fromTransactionBody(body, transaction.getBodyBytes()?.data()) - } - - public fromTransactionBody( - transactionBody: TransactionBody, - bodyBytes: Buffer | null | undefined, - ): TransactionBuilder { - if (!bodyBytes) { - bodyBytes = new InteractionSerialize(transactionBody).run()?.data() - } - if (!bodyBytes) { - throw new LogError( - 'cannot serialize TransactionBody', - JSON.parse(new InteractionToJson(transactionBody).run()), - ) - } - this.transaction.type = transactionBody.getTransactionType() - this.transaction.createdAt = new Date(transactionBody.getCreatedAt().getDate()) - this.transaction.protocolVersion = transactionBody.getVersionNumber() - - const transferAmount = transactionBody.getTransferAmount() - this.transaction.amount = transferAmount - ? transferAmount.getAmount().getGradidoCent() - : undefined - - this.transaction.bodyBytes ??= bodyBytes - return this - } -} diff --git a/dlt-connector/src/data/Transaction.repository.ts b/dlt-connector/src/data/Transaction.repository.ts deleted file mode 100644 index 6ba622c9c..000000000 --- a/dlt-connector/src/data/Transaction.repository.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Transaction } from '@entity/Transaction' -import { IsNull } from 'typeorm' - -import { getDataSource } from '@/typeorm/DataSource' - -// https://www.artima.com/articles/the-dci-architecture-a-new-vision-of-object-oriented-programming -export const TransactionRepository = getDataSource() - .getRepository(Transaction) - .extend({ - findBySignature(signature: Buffer): Promise { - return this.findOneBy({ signature: Buffer.from(signature) }) - }, - findByMessageId(iotaMessageId: string): Promise { - return this.findOneBy({ iotaMessageId: Buffer.from(iotaMessageId, 'hex') }) - }, - async getNextPendingTransaction(): Promise { - return await this.findOne({ - where: { iotaMessageId: IsNull() }, - order: { createdAt: 'ASC' }, - relations: { signingAccount: true }, - }) - }, - findExistingTransactionAndMissingMessageIds(messageIDsHex: string[]): Promise { - return this.createQueryBuilder('Transaction') - .where('HEX(Transaction.iota_message_id) IN (:...messageIDs)', { - messageIDs: messageIDsHex, - }) - .leftJoinAndSelect('Transaction.community', 'Community') - .leftJoinAndSelect('Transaction.otherCommunity', 'OtherCommunity') - .leftJoinAndSelect('Transaction.recipientAccount', 'RecipientAccount') - .leftJoinAndSelect('Transaction.backendTransactions', 'BackendTransactions') - .leftJoinAndSelect('RecipientAccount.user', 'RecipientUser') - .leftJoinAndSelect('Transaction.signingAccount', 'SigningAccount') - .leftJoinAndSelect('SigningAccount.user', 'SigningUser') - .getMany() - }, - removeConfirmedTransaction(transactions: Transaction[]): Transaction[] { - return transactions.filter( - (transaction: Transaction) => - transaction.runningHash === undefined || transaction.runningHash.length === 0, - ) - }, - }) diff --git a/dlt-connector/src/data/User.factory.ts b/dlt-connector/src/data/User.factory.ts deleted file mode 100644 index a8c7f0e71..000000000 --- a/dlt-connector/src/data/User.factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { User } from '@entity/User' - -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' - -import { KeyPair } from './KeyPair' -import { UserLogic } from './User.logic' - -export class UserFactory { - static create(userAccountDraft: UserAccountDraft, parentKeys: KeyPair): User { - const user = User.create() - user.createdAt = new Date(userAccountDraft.createdAt) - user.gradidoID = userAccountDraft.user.uuid - const userLogic = new UserLogic(user) - // store generated pubkey into entity - userLogic.calculateKeyPair(parentKeys) - return user - } -} diff --git a/dlt-connector/src/data/User.logic.ts b/dlt-connector/src/data/User.logic.ts deleted file mode 100644 index 8bffe326e..000000000 --- a/dlt-connector/src/data/User.logic.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { User } from '@entity/User' - -import { LogError } from '@/server/LogError' -import { hardenDerivationIndex } from '@/utils/derivationHelper' -import { uuid4ToBuffer } from '@/utils/typeConverter' - -import { KeyPair } from './KeyPair' - -export class UserLogic { - // eslint-disable-next-line no-useless-constructor - constructor(private user: User) {} - - /** - * - * @param parentKeys from home community for own user - * @returns - */ - - calculateKeyPair = (parentKeys: KeyPair): KeyPair => { - if (!this.user.gradidoID) { - throw new LogError('missing GradidoID for user.', { id: this.user.id }) - } - // example gradido id: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16 => - // wholeHex: '03857ac19cc2483e8a91e5b10f5b8d16'] - const wholeHex = uuid4ToBuffer(this.user.gradidoID) - const parts = [] - for (let i = 0; i < 4; i++) { - parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE()) - } - // parts: [2206563009, 2629978174, 2324817329, 2405141782] - const keyPair = parentKeys.derive(parts) - if (this.user.derive1Pubkey && this.user.derive1Pubkey.compare(keyPair.publicKey) !== 0) { - throw new LogError( - 'The freshly derived public key does not correspond to the stored public key', - ) - } - if (!this.user.derive1Pubkey) { - this.user.derive1Pubkey = keyPair.publicKey - } - return keyPair - } -} diff --git a/dlt-connector/src/data/User.repository.ts b/dlt-connector/src/data/User.repository.ts deleted file mode 100644 index 1e9e4dcef..000000000 --- a/dlt-connector/src/data/User.repository.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Account } from '@entity/Account' -import { User } from '@entity/User' -import { FindOptionsRelations } from 'typeorm' - -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { getDataSource } from '@/typeorm/DataSource' - -export const UserRepository = getDataSource() - .getRepository(User) - .extend({ - async findAccountByUserIdentifier({ - uuid, - accountNr, - }: UserIdentifier): Promise { - const user = await this.findOne({ - where: { gradidoID: uuid, accounts: { derivationIndex: accountNr ?? 1 } }, - relations: { accounts: true }, - }) - if (user && user.accounts?.length === 1) { - const account = user.accounts[0] - account.user = user - return account - } - }, - - findByGradidoId( - { uuid }: UserIdentifier, - relations?: FindOptionsRelations, - ): Promise { - return User.findOne({ where: { gradidoID: uuid }, relations }) - }, - }) diff --git a/dlt-connector/src/data/const.ts b/dlt-connector/src/data/const.ts deleted file mode 100644 index 82470e8d4..000000000 --- a/dlt-connector/src/data/const.ts +++ /dev/null @@ -1 +0,0 @@ -export const TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY = 'transmitToIota' diff --git a/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts b/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts deleted file mode 100644 index 07bf5c393..000000000 --- a/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * based on TransactionBody data oneOf - * https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/transaction_body.proto - * for storing type in db as number - */ -export enum TransactionType { - GRADIDO_CREATION = 1, - GRADIDO_TRANSFER = 2, - GROUP_FRIENDS_UPDATE = 3, - REGISTER_ADDRESS = 4, - GRADIDO_DEFERRED_TRANSFER = 5, - COMMUNITY_ROOT = 6, -} diff --git a/dlt-connector/src/graphql/input/TransactionDraft.ts b/dlt-connector/src/graphql/input/TransactionDraft.ts index 541797565..59e1ecafd 100755 --- a/dlt-connector/src/graphql/input/TransactionDraft.ts +++ b/dlt-connector/src/graphql/input/TransactionDraft.ts @@ -1,11 +1,8 @@ // https://www.npmjs.com/package/@apollo/protobufjs -import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator' -import { Decimal } from 'decimal.js-light' -import { InputType, Field, Int } from 'type-graphql' - import { InputTransactionType } from '@enum/InputTransactionType' -import { isValidDateString } from '@validator/DateString' -import { IsPositiveDecimal } from '@validator/Decimal' +import { isValidDateString, isValidNumberString } from '@validator/DateString' +import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator' +import { InputType, Field, Int } from 'type-graphql' import { UserIdentifier } from './UserIdentifier' @@ -25,9 +22,9 @@ export class TransactionDraft { @IsPositive() backendTransactionId: number - @Field(() => Decimal) - @IsPositiveDecimal() - amount: Decimal + @Field(() => String) + @isValidNumberString() + amount: string @Field(() => InputTransactionType) @IsEnum(InputTransactionType) diff --git a/dlt-connector/src/graphql/input/UserAccountDraft.ts b/dlt-connector/src/graphql/input/UserAccountDraft.ts index 9ae544e32..e10be9574 100644 --- a/dlt-connector/src/graphql/input/UserAccountDraft.ts +++ b/dlt-connector/src/graphql/input/UserAccountDraft.ts @@ -1,10 +1,9 @@ // https://www.npmjs.com/package/@apollo/protobufjs +import { isValidDateString } from '@validator/DateString' import { IsEnum, IsObject, ValidateNested } from 'class-validator' import { InputType, Field } from 'type-graphql' -import { isValidDateString } from '@validator/DateString' - import { AccountType } from '@/graphql/enum/AccountType' import { UserIdentifier } from './UserIdentifier' diff --git a/dlt-connector/src/graphql/resolver/AccountsResolver.ts b/dlt-connector/src/graphql/resolver/AccountsResolver.ts index 7f782af89..a7f264764 100644 --- a/dlt-connector/src/graphql/resolver/AccountsResolver.ts +++ b/dlt-connector/src/graphql/resolver/AccountsResolver.ts @@ -1,16 +1,12 @@ +/* eslint-disable camelcase */ +import { AddressType_NONE } from 'gradido-blockchain-js' import { Arg, Mutation, Query, Resolver } from 'type-graphql' -import { QueryFailedError } from 'typeorm' -import { TransactionRecipe } from '@model/TransactionRecipe' - -import { TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/data/const' -import { UserRepository } from '@/data/User.repository' -import { RegisterAddressContext } from '@/interactions/backendToDb/account/RegisterAddress.context' -import { AccountLoggingView } from '@/logging/AccountLogging.view' +import { getAddressType } from '@/client/GradidoNode' +import { KeyPairCalculation } from '@/interactions/keyPairCalculation/KeyPairCalculation.context' +import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context' import { logger } from '@/logging/logger' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' -import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' -import { getDataSource } from '@/typeorm/DataSource' +import { uuid4ToHash } from '@/utils/typeConverter' import { TransactionErrorType } from '../enum/TransactionErrorType' import { UserAccountDraft } from '../input/UserAccountDraft' @@ -22,8 +18,20 @@ import { TransactionResult } from '../model/TransactionResult' export class AccountResolver { @Query(() => Boolean) async isAccountExist(@Arg('data') userIdentifier: UserIdentifier): Promise { + const accountKeyPair = await KeyPairCalculation(userIdentifier) + const publicKey = accountKeyPair.getPublicKey() + if (!publicKey) { + throw new TransactionResult( + new TransactionError(TransactionErrorType.NOT_FOUND, 'cannot get user public key'), + ) + } + // ask gradido node server for account type, if type !== NONE account exist + const addressType = await getAddressType( + publicKey.data(), + uuid4ToHash(userIdentifier.communityUuid).convertToHex(), + ) logger.info('isAccountExist', userIdentifier) - return !!(await UserRepository.findAccountByUserIdentifier(userIdentifier)) + return addressType !== AddressType_NONE } @Mutation(() => TransactionResult) @@ -31,30 +39,10 @@ export class AccountResolver { @Arg('data') userAccountDraft: UserAccountDraft, ): Promise { - const registerAddressContext = new RegisterAddressContext(userAccountDraft) try { - const { transaction, account } = await registerAddressContext.run() - logger.info('register address', { - account: new AccountLoggingView(account), - transaction: new TransactionLoggingView(transaction), - }) - await getDataSource().transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.save(account) - await transactionalEntityManager.save(transaction) - logger.debug('store register address transaction', new TransactionLoggingView(transaction)) - }) - InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY) - return new TransactionResult(new TransactionRecipe(transaction)) + return await SendToIotaContext(userAccountDraft) } catch (err) { - if (err instanceof QueryFailedError) { - logger.error('error saving user or new account or transaction into db: %s', err) - return new TransactionResult( - new TransactionError( - TransactionErrorType.DB_ERROR, - 'error saving user or new account or transaction into db', - ), - ) - } else if (err instanceof TransactionError) { + if (err instanceof TransactionError) { return new TransactionResult(err) } else { logger.error('error in register address: ', err) diff --git a/dlt-connector/src/graphql/resolver/CommunityResolver.ts b/dlt-connector/src/graphql/resolver/CommunityResolver.ts deleted file mode 100644 index 848dd0733..000000000 --- a/dlt-connector/src/graphql/resolver/CommunityResolver.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { CommunityArg } from '@arg/CommunityArg' -import { TransactionErrorType } from '@enum/TransactionErrorType' -import { CommunityDraft } from '@input/CommunityDraft' -import { Community } from '@model/Community' -import { TransactionError } from '@model/TransactionError' -import { TransactionResult } from '@model/TransactionResult' -import { Resolver, Query, Arg, Mutation, Args } from 'type-graphql' - -import { CommunityRepository } from '@/data/Community.repository' -import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context' -import { logger } from '@/logging/logger' -import { LogError } from '@/server/LogError' -import { uuid4ToHash } from '@/utils/typeConverter' - -@Resolver() -export class CommunityResolver { - @Query(() => Community) - async community(@Args() communityArg: CommunityArg): Promise { - logger.info('community', communityArg) - const result = await CommunityRepository.findByCommunityArg(communityArg) - if (result.length === 0) { - throw new LogError('cannot find community') - } else if (result.length === 1) { - return new Community(result[0]) - } else { - throw new LogError('find multiple communities') - } - } - - @Query(() => Boolean) - async isCommunityExist(@Args() communityArg: CommunityArg): Promise { - logger.info('isCommunity', communityArg) - return (await CommunityRepository.findByCommunityArg(communityArg)).length === 1 - } - - @Query(() => [Community]) - async communities(@Args() communityArg: CommunityArg): Promise { - logger.info('communities', communityArg) - const result = await CommunityRepository.findByCommunityArg(communityArg) - return result.map((communityEntity) => new Community(communityEntity)) - } - - @Mutation(() => TransactionResult) - async addCommunity( - @Arg('data') - communityDraft: CommunityDraft, - ): Promise { - logger.info('addCommunity', communityDraft) - const topic = uuid4ToHash(communityDraft.uuid) - // check if community was already written to db - if (await CommunityRepository.isExist(topic)) { - return new TransactionResult( - new TransactionError(TransactionErrorType.ALREADY_EXIST, 'community already exist!'), - ) - } - // prepare context for interaction - // shouldn't throw at all - // TODO: write tests to make sure that it doesn't throw - const addCommunityContext = new AddCommunityContext(communityDraft, topic) - try { - // actually run interaction, create community, accounts for foreign community and transactionRecipe - await addCommunityContext.run() - return new TransactionResult() - } catch (error) { - if (error instanceof TransactionError) { - return new TransactionResult(error) - } else { - throw error - } - } - } -} diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts index 8302a872f..50636dee3 100755 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts @@ -1,17 +1,9 @@ +import { TransactionDraft } from '@input/TransactionDraft' import { Resolver, Arg, Mutation } from 'type-graphql' -import { TransactionDraft } from '@input/TransactionDraft' +import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context' -import { TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/data/const' -import { TransactionRepository } from '@/data/Transaction.repository' -import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransactionRecipe.context' -import { logger } from '@/logging/logger' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' -import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' - -import { TransactionErrorType } from '../enum/TransactionErrorType' import { TransactionError } from '../model/TransactionError' -import { TransactionRecipe } from '../model/TransactionRecipe' import { TransactionResult } from '../model/TransactionResult' @Resolver() @@ -21,36 +13,8 @@ export class TransactionResolver { @Arg('data') transactionDraft: TransactionDraft, ): Promise { - const createTransactionRecipeContext = new CreateTransactionRecipeContext(transactionDraft) try { - const result = await createTransactionRecipeContext.run() - if (!result) { - return new TransactionResult( - new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'cannot work with this parameters', - ), - ) - } - const transactionRecipe = createTransactionRecipeContext.getTransactionRecipe() - // check if a transaction with this signature already exist - const existingRecipe = await TransactionRepository.findBySignature( - transactionRecipe.signature, - ) - if (existingRecipe) { - return new TransactionResult( - new TransactionError( - TransactionErrorType.ALREADY_EXIST, - 'Transaction with same signature already exist', - ), - ) - } else { - logger.debug('store transaction recipe', new TransactionLoggingView(transactionRecipe)) - // we store the transaction - await transactionRecipe.save() - } - InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY) - return new TransactionResult(new TransactionRecipe(transactionRecipe)) + return await SendToIotaContext(transactionDraft) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { if (error instanceof TransactionError) { diff --git a/dlt-connector/src/graphql/scalar/Decimal.ts b/dlt-connector/src/graphql/scalar/Decimal.ts deleted file mode 100755 index b343f383a..000000000 --- a/dlt-connector/src/graphql/scalar/Decimal.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { Decimal } from 'decimal.js-light' -import { GraphQLScalarType, Kind, ValueNode } from 'graphql' - -export const DecimalScalar = new GraphQLScalarType({ - name: 'Decimal', - description: 'The `Decimal` scalar type to represent currency values', - - serialize(value: unknown): string { - if (!(value instanceof Decimal)) { - throw new TypeError(`Value is not a Decimal: ${value}`) - } - return value.toString() - }, - - parseValue(value: unknown): Decimal { - if (typeof value !== 'string') { - throw new TypeError('Decimal values must be strings') - } - return new Decimal(value) - }, - - parseLiteral(ast: ValueNode): Decimal { - if (ast.kind !== Kind.STRING) { - throw new TypeError(`${String(ast)} is not a valid decimal value.`) - } - - return new Decimal(ast.value) - }, -}) diff --git a/dlt-connector/src/graphql/schema.ts b/dlt-connector/src/graphql/schema.ts index a756b1ac9..f0e2f7198 100755 --- a/dlt-connector/src/graphql/schema.ts +++ b/dlt-connector/src/graphql/schema.ts @@ -1,16 +1,12 @@ -import { Decimal } from 'decimal.js-light' import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' import { AccountResolver } from './resolver/AccountsResolver' -import { CommunityResolver } from './resolver/CommunityResolver' import { TransactionResolver } from './resolver/TransactionsResolver' -import { DecimalScalar } from './scalar/Decimal' export const schema = async (): Promise => { return buildSchema({ - resolvers: [TransactionResolver, CommunityResolver, AccountResolver], - scalarsMap: [{ type: Decimal, scalar: DecimalScalar }], + resolvers: [TransactionResolver, AccountResolver], validate: { validationError: { target: false }, skipMissingProperties: true, diff --git a/dlt-connector/src/graphql/validator/DateString.ts b/dlt-connector/src/graphql/validator/DateString.ts index 3f46d89ec..2be057194 100644 --- a/dlt-connector/src/graphql/validator/DateString.ts +++ b/dlt-connector/src/graphql/validator/DateString.ts @@ -19,3 +19,23 @@ export function isValidDateString(validationOptions?: ValidationOptions) { }) } } + +export function isValidNumberString(validationOptions?: ValidationOptions) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isValidNumberString', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: string): boolean { + return !isNaN(parseFloat(value)) + }, + defaultMessage(): string { + return `${propertyName} must be a valid number string` + }, + }, + }) + } +} \ No newline at end of file diff --git a/dlt-connector/src/graphql/validator/Decimal.ts b/dlt-connector/src/graphql/validator/Decimal.ts deleted file mode 100644 index fd2604514..000000000 --- a/dlt-connector/src/graphql/validator/Decimal.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator' -import { Decimal } from 'decimal.js-light' - -export function IsPositiveDecimal(validationOptions?: ValidationOptions) { - // eslint-disable-next-line @typescript-eslint/ban-types - return function (object: Object, propertyName: string) { - registerDecorator({ - name: 'isPositiveDecimal', - target: object.constructor, - propertyName, - options: validationOptions, - validator: { - validate(value: Decimal): boolean { - return value.greaterThan(0) - }, - defaultMessage(args: ValidationArguments): string { - return `The ${propertyName} must be a positive value ${args.property}` - }, - }, - }) - } -} diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index b61157dfb..2dd21b66b 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -7,12 +7,13 @@ import { CONFIG } from '@/config' import { BackendClient } from './client/BackendClient' import { CommunityDraft } from './graphql/input/CommunityDraft' -import { AddCommunityContext } from './interactions/backendToDb/community/AddCommunity.context' import { logger } from './logging/logger' import { KeyPairCacheManager } from './manager/KeyPairCacheManager' import createServer from './server/createServer' import { LogError } from './server/LogError' -import { stopTransmitToIota, transmitToIota } from './tasks/transmitToIota' +import { getTransaction } from './client/GradidoNode' +import { uuid4ToHash } from './utils/typeConverter' +import { SendToIotaContext } from './interactions/sendToIota/SendToIota.context' async function waitForServer( backend: BackendClient, @@ -70,11 +71,12 @@ async function main() { const communityDraft = await backend.getHomeCommunityDraft() KeyPairCacheManager.getInstance().setHomeCommunityUUID(communityDraft.uuid) - const addCommunityContext = new AddCommunityContext(communityDraft) - await addCommunityContext.run() - - // loop run all the time, check for new transaction for sending to iota - void transmitToIota() + // ask gradido node if community blockchain was created + const firstTransaction = await getTransaction(1, uuid4ToHash(communityDraft.uuid).convertToHex()) + if (!firstTransaction) { + // if not exist, create community root transaction + await SendToIotaContext(communityDraft) + } app.listen(CONFIG.DLT_CONNECTOR_PORT, () => { // eslint-disable-next-line no-console console.log(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`) @@ -82,7 +84,6 @@ async function main() { process.on('exit', () => { // Add shutdown logic here. - stopTransmitToIota() }) } diff --git a/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts b/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts deleted file mode 100644 index c84cb0149..000000000 --- a/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Account } from '@entity/Account' -import { Transaction } from '@entity/Transaction' -import { User } from '@entity/User' - -import { AccountFactory } from '@/data/Account.factory' -import { CommunityRepository } from '@/data/Community.repository' -import { KeyPair } from '@/data/KeyPair' -import { UserFactory } from '@/data/User.factory' -import { UserLogic } from '@/data/User.logic' -import { UserRepository } from '@/data/User.repository' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { TransactionError } from '@/graphql/model/TransactionError' -import { logger } from '@/logging/logger' - -import { CreateTransactionRecipeContext } from '../transaction/CreateTransactionRecipe.context' - -export interface TransactionWithAccount { - transaction: Transaction - account: Account -} - -export class RegisterAddressContext { - // eslint-disable-next-line no-useless-constructor - public constructor(private userAccountDraft: UserAccountDraft) {} - - public async run(): Promise { - const community = await CommunityRepository.loadHomeCommunity() - const communityKeyPair = new KeyPair(community) - const user = await this.loadOrCreateUser(communityKeyPair) - if (this.isAccountAlreadyExistOnUser(user)) { - throw new TransactionError( - TransactionErrorType.ALREADY_EXIST, - 'account for this user already exist!', - ) - } - logger.info('add user and account', this.userAccountDraft) - const account = this.createAccount(new UserLogic(user).calculateKeyPair(communityKeyPair)) - account.user = user - const createTransactionContext = new CreateTransactionRecipeContext(this.userAccountDraft, { - community, - account, - }) - await createTransactionContext.run() - return { transaction: createTransactionContext.getTransactionRecipe(), account } - } - - public isAccountAlreadyExistOnUser(user: User): boolean { - return !!user.accounts?.find( - (value) => value.derivationIndex === this.userAccountDraft.user.accountNr, - ) - } - - public async loadOrCreateUser(communityKeyPair: KeyPair): Promise { - let user = await UserRepository.findByGradidoId(this.userAccountDraft.user, { accounts: true }) - if (!user) { - user = UserFactory.create(this.userAccountDraft, communityKeyPair) - } - return user - } - - public createAccount(userKeyPair: KeyPair): Account { - return AccountFactory.createAccountFromUserAccountDraft(this.userAccountDraft, userKeyPair) - } -} diff --git a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.test.ts b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.test.ts deleted file mode 100644 index d7ec4e9c6..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import 'reflect-metadata' -import { Community } from '@entity/Community' - -import { TestDB } from '@test/TestDB' - -import { CONFIG } from '@/config' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' - -import { AddCommunityContext } from './AddCommunity.context' - -CONFIG.IOTA_HOME_COMMUNITY_SEED = '034b0229a2ba4e98e1cc5e8767dca886279b484303ffa73546bd5f5bf0b71285' - -jest.mock('@typeorm/DataSource', () => ({ - getDataSource: jest.fn(() => TestDB.instance.dbConnect), -})) - -describe('interactions/backendToDb/community/AddCommunity Context Test', () => { - beforeAll(async () => { - await TestDB.instance.setupTestDB() - }) - - afterAll(async () => { - await TestDB.instance.teardownTestDB() - }) - - const homeCommunityDraft = new CommunityDraft() - homeCommunityDraft.uuid = 'a2fd0fee-f3ba-4bef-a62a-10a34b0e2754' - homeCommunityDraft.foreign = false - homeCommunityDraft.createdAt = '2024-01-25T13:09:55.339Z' - // calculated from a2fd0fee-f3ba-4bef-a62a-10a34b0e2754 with iotaTopicFromCommunityUUID - const iotaTopic = '7be2ad83f279a3aaf6d62371cb6be301e2e3c7a3efda9c89984e8f6a7865d9ce' - - const foreignCommunityDraft = new CommunityDraft() - foreignCommunityDraft.uuid = '70df8de5-0fb7-4153-a124-4ff86965be9a' - foreignCommunityDraft.foreign = true - foreignCommunityDraft.createdAt = '2024-01-25T13:34:28.020Z' - - it('with home community, without iota topic', async () => { - const context = new AddCommunityContext(homeCommunityDraft) - await context.run() - const homeCommunity = await Community.findOneOrFail({ where: { iotaTopic } }) - expect(homeCommunity).toMatchObject({ - id: 1, - iotaTopic, - foreign: 0, - rootPubkey: Buffer.from( - '07cbf56d4b6b7b188c5f6250c0f4a01d0e44e1d422db1935eb375319ad9f9af0', - 'hex', - ), - createdAt: new Date('2024-01-25T13:09:55.339Z'), - }) - }) - - it('with foreign community', async () => { - const context = new AddCommunityContext(foreignCommunityDraft, 'randomTopic') - await context.run() - const foreignCommunity = await Community.findOneOrFail({ where: { foreign: true } }) - expect(foreignCommunity).toMatchObject({ - id: 2, - iotaTopic: 'randomTopic', - foreign: 1, - createdAt: new Date('2024-01-25T13:34:28.020Z'), - }) - }) -}) diff --git a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts deleted file mode 100644 index 16bfd8513..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { uuid4ToHash } from '@/utils/typeConverter' - -import { CommunityRole } from './Community.role' -import { ForeignCommunityRole } from './ForeignCommunity.role' -import { HomeCommunityRole } from './HomeCommunity.role' - -/** - * @DCI-Context - * Context for adding community to DB - * using roles to distinct between foreign and home communities - */ -export class AddCommunityContext { - private communityRole: CommunityRole - private iotaTopic: string - public constructor(private communityDraft: CommunityDraft, iotaTopic?: string) { - if (!iotaTopic) { - this.iotaTopic = uuid4ToHash(this.communityDraft.uuid) - } else { - this.iotaTopic = iotaTopic - } - this.communityRole = communityDraft.foreign - ? new ForeignCommunityRole() - : new HomeCommunityRole() - } - - public async run(): Promise { - await this.communityRole.create(this.communityDraft, this.iotaTopic) - await this.communityRole.store() - } -} diff --git a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts b/dlt-connector/src/interactions/backendToDb/community/Community.role.ts deleted file mode 100644 index 2b1514ef2..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Community } from '@entity/Community' - -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionError } from '@/graphql/model/TransactionError' -import { CommunityLoggingView } from '@/logging/CommunityLogging.view' -import { logger } from '@/logging/logger' - -export abstract class CommunityRole { - protected self: Community - public constructor() { - this.self = Community.create() - } - - public async create(communityDraft: CommunityDraft, topic: string): Promise { - this.self.iotaTopic = topic - this.self.createdAt = new Date(communityDraft.createdAt) - this.self.foreign = communityDraft.foreign - } - - public async store(): Promise { - try { - const community = await this.self.save() - logger.debug('store community', new CommunityLoggingView(community)) - return community - } catch (error) { - logger.error('error saving new community into db: %s', error) - throw new TransactionError(TransactionErrorType.DB_ERROR, 'error saving community into db') - } - } -} diff --git a/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts deleted file mode 100644 index cf93deaa5..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { CommunityRole } from './Community.role' - -// same as base class -export class ForeignCommunityRole extends CommunityRole {} diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts deleted file mode 100644 index 050b245e3..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Community } from '@entity/Community' -import { Transaction } from '@entity/Transaction' - -import { CONFIG } from '@/config' -import { AccountFactory } from '@/data/Account.factory' -import { TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/data/const' -import { KeyPair } from '@/data/KeyPair' -import { Mnemonic } from '@/data/Mnemonic' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionError } from '@/graphql/model/TransactionError' -import { CommunityLoggingView } from '@/logging/CommunityLogging.view' -import { logger } from '@/logging/logger' -import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' -import { LogError } from '@/server/LogError' -import { getDataSource } from '@/typeorm/DataSource' - -import { CreateTransactionRecipeContext } from '../transaction/CreateTransactionRecipe.context' - -import { CommunityRole } from './Community.role' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' - -export class HomeCommunityRole extends CommunityRole { - private transactionRecipe: Transaction - - public async create(communityDraft: CommunityDraft, topic: string): Promise { - super.create(communityDraft, topic) - // generate key pair for signing transactions and deriving all keys for community - let mnemonic: Mnemonic - try { - mnemonic = new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined) - } catch (e) { - throw new LogError( - 'error creating mnemonic for home community, please fill IOTA_HOME_COMMUNITY_SEED in .env', - { - IOTA_HOME_COMMUNITY_SEED: CONFIG.IOTA_HOME_COMMUNITY_SEED, - error: e, - }, - ) - } - const keyPair = new KeyPair(mnemonic) - keyPair.fillInCommunityKeys(this.self) - - // create auf account and gmw account - this.self.aufAccount = AccountFactory.createAufAccount(keyPair, this.self.createdAt) - this.self.gmwAccount = AccountFactory.createGmwAccount(keyPair, this.self.createdAt) - - const transactionRecipeContext = new CreateTransactionRecipeContext(communityDraft, { - community: this.self, - }) - await transactionRecipeContext.run() - this.transactionRecipe = transactionRecipeContext.getTransactionRecipe() - } - - public async store(): Promise { - try { - const community = await getDataSource().transaction(async (transactionalEntityManager) => { - const community = await transactionalEntityManager.save(this.self) - await transactionalEntityManager.save(this.transactionRecipe) - logger.debug('store home community', new CommunityLoggingView(community)) - return community - }) - InterruptiveSleepManager.getInstance().interrupt(TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY) - return community - } catch (error) { - logger.error('error saving home community into db: %s', error) - throw new TransactionError( - TransactionErrorType.DB_ERROR, - 'error saving home community into db', - ) - } - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts deleted file mode 100644 index 2b815cd7a..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable camelcase */ -import { Account } from '@entity/Account' -import { GradidoTransactionBuilder } from 'gradido-blockchain-js' - -import { UserRepository } from '@/data/User.repository' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { TransactionError } from '@/graphql/model/TransactionError' - -export abstract class AbstractTransactionRole { - // eslint-disable-next-line no-useless-constructor - public constructor(protected self: TransactionDraft) {} - - abstract getSigningUser(): UserIdentifier - abstract getRecipientUser(): UserIdentifier - abstract getGradidoTransactionBuilder(): Promise - - public isCrossGroupTransaction(): boolean { - return ( - this.self.user.communityUuid !== this.self.linkedUser.communityUuid && - this.self.linkedUser.communityUuid !== '' - ) - } - - public async loadUser(user: UserIdentifier): Promise { - const account = await UserRepository.findAccountByUserIdentifier(user) - if (!account) { - throw new TransactionError( - TransactionErrorType.NOT_FOUND, - "couldn't found user account in db", - ) - } - return account - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts deleted file mode 100644 index 812f6a7f0..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Transaction } from '@entity/Transaction' - -import { TransactionBuilder } from '@/data/Transaction.builder' - -export class AbstractTransactionRecipeRole { - protected transactionBuilder: TransactionBuilder - - public constructor() { - this.transactionBuilder = new TransactionBuilder() - } - - public getTransaction(): Transaction { - return this.transactionBuilder.getTransaction() - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts b/dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts deleted file mode 100644 index 659c62c22..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable camelcase */ -import { AccountLogic } from '@/data/Account.logic' -import { KeyPair } from '@/data/KeyPair' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' - -import { AbstractTransactionRole } from './AbstractTransaction.role' -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' - -export class BalanceChangingTransactionRecipeRole extends AbstractTransactionRecipeRole { - public async create( - transactionDraft: TransactionDraft, - transactionTypeRole: AbstractTransactionRole, - ): Promise { - // loading signing and recipient account - const signingAccount = await transactionTypeRole.loadUser(transactionTypeRole.getSigningUser()) - const recipientAccount = await transactionTypeRole.loadUser( - transactionTypeRole.getRecipientUser(), - ) - const accountLogic = new AccountLogic(signingAccount) - await this.transactionBuilder.setCommunityFromUser(transactionDraft.user) - const communityKeyPair = new KeyPair(this.transactionBuilder.getCommunity()) - - const gradidoTransactionBuilder = await transactionTypeRole.getGradidoTransactionBuilder() - const transaction = gradidoTransactionBuilder - .setCreatedAt(new Date(transactionDraft.createdAt)) - .sign(accountLogic.calculateKeyPair(communityKeyPair).keyPair) - .build() - - // build transaction entity - this.transactionBuilder - .fromGradidoTransaction(transaction) - .setRecipientAccount(recipientAccount) - .setSigningAccount(signingAccount) - - if (transactionTypeRole.isCrossGroupTransaction()) { - await this.transactionBuilder.setOtherCommunityFromUser(transactionDraft.linkedUser) - } - return this - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts deleted file mode 100644 index cdd953d4c..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Community } from '@entity/Community' -// eslint-disable-next-line camelcase -import { MemoryBlock, GradidoTransactionBuilder } from 'gradido-blockchain-js' - -import { KeyPair } from '@/data/KeyPair' -// import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' - -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' - -export class CommunityRootTransactionRole extends AbstractTransactionRecipeRole { - public create( - communityDraft: CommunityDraft, - community: Community, - ): AbstractTransactionRecipeRole { - if ( - !community.rootPubkey || - !community.gmwAccount?.derive2Pubkey || - !community.aufAccount?.derive2Pubkey - ) { - throw new Error('missing one of the public keys for community') - } - // create proto transaction body - const transaction = new GradidoTransactionBuilder() - .setCommunityRoot( - new MemoryBlock(community.rootPubkey), - new MemoryBlock(community.gmwAccount?.derive2Pubkey), - new MemoryBlock(community.aufAccount?.derive2Pubkey), - ) - .setCreatedAt(new Date(communityDraft.createdAt)) - .sign(new KeyPair(community).keyPair) - .build() - - // build transaction entity - this.transactionBuilder.fromGradidoTransaction(transaction).setCommunity(community) - return this - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts deleted file mode 100644 index 6671e6e78..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts +++ /dev/null @@ -1,420 +0,0 @@ -/* eslint-disable camelcase */ -import 'reflect-metadata' -import { Account } from '@entity/Account' -import { Community } from '@entity/Community' -import { - AddressType_COMMUNITY_HUMAN, - CrossGroupType_INBOUND, - CrossGroupType_LOCAL, - CrossGroupType_OUTBOUND, - InteractionDeserialize, - MemoryBlock, - TransactionType_CREATION, -} from 'gradido-blockchain-js' -import { v4 } from 'uuid' - -import { TestDB } from '@test/TestDB' - -import { CONFIG } from '@/config' -import { KeyPair } from '@/data/KeyPair' -import { TransactionType } from '@/data/proto/3_3/enum/TransactionType' -import { AccountType } from '@/graphql/enum/AccountType' -import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { uuid4ToHash } from '@/utils/typeConverter' - -import { CreateTransactionRecipeContext } from './CreateTransactionRecipe.context' - -// eslint-disable-next-line import/order -import { communitySeed } from '@test/seeding/Community.seed' -// eslint-disable-next-line import/order -import { createUserSet, UserSet } from '@test/seeding/UserSet.seed' - -jest.mock('@typeorm/DataSource', () => ({ - getDataSource: jest.fn(() => TestDB.instance.dbConnect), -})) - -CONFIG.IOTA_HOME_COMMUNITY_SEED = '034b0229a2ba4e98e1cc5e8767dca886279b484303ffa73546bd5f5bf0b71285' -const homeCommunityUuid = v4() -const foreignCommunityUuid = v4() - -const keyPair = new KeyPair(MemoryBlock.fromHex(CONFIG.IOTA_HOME_COMMUNITY_SEED)) -const foreignKeyPair = new KeyPair( - MemoryBlock.fromHex('5d4e163c078cc6b51f5c88f8422bc8f21d1d59a284515ab1ea79e1c176ebec50'), -) - -let moderator: UserSet -let firstUser: UserSet -let secondUser: UserSet -let foreignUser: UserSet -let homeCommunity: Community - -const topic = uuid4ToHash(homeCommunityUuid) -const foreignTopic = uuid4ToHash(foreignCommunityUuid) - -describe('interactions/backendToDb/transaction/Create Transaction Recipe Context Test', () => { - beforeAll(async () => { - await TestDB.instance.setupTestDB() - homeCommunity = await communitySeed(homeCommunityUuid, false) - await communitySeed(foreignCommunityUuid, true, foreignKeyPair) - - moderator = createUserSet(homeCommunityUuid, keyPair) - firstUser = createUserSet(homeCommunityUuid, keyPair) - secondUser = createUserSet(homeCommunityUuid, keyPair) - foreignUser = createUserSet(foreignCommunityUuid, foreignKeyPair) - - await Account.save([ - moderator.account, - firstUser.account, - secondUser.account, - foreignUser.account, - ]) - }) - - afterAll(async () => { - await TestDB.instance.teardownTestDB() - }) - - it('register address transaction', async () => { - const userAccountDraft = new UserAccountDraft() - userAccountDraft.accountType = AccountType.COMMUNITY_HUMAN - userAccountDraft.createdAt = new Date().toISOString() - userAccountDraft.user = firstUser.identifier - const context = new CreateTransactionRecipeContext(userAccountDraft, { - account: firstUser.account, - community: homeCommunity, - }) - await context.run() - const transaction = context.getTransactionRecipe() - expect(transaction).toMatchObject({ - type: TransactionType.REGISTER_ADDRESS, - protocolVersion: '3.3', - community: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - signingAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - }) - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - expect(body?.isRegisterAddress()).toBeTruthy() - - expect(body).toMatchObject({ - type: CrossGroupType_LOCAL, - registerAddress: { - derivationIndex: 1, - addressType: AddressType_COMMUNITY_HUMAN, - }, - }) - }) - - it('creation transaction', async () => { - const creationTransactionDraft = new TransactionDraft() - creationTransactionDraft.amount = new Decimal('2000') - creationTransactionDraft.backendTransactionId = 1 - creationTransactionDraft.createdAt = new Date().toISOString() - creationTransactionDraft.linkedUser = moderator.identifier - creationTransactionDraft.user = firstUser.identifier - creationTransactionDraft.type = InputTransactionType.CREATION - creationTransactionDraft.targetDate = new Date().toISOString() - const context = new CreateTransactionRecipeContext(creationTransactionDraft) - await context.run() - const transaction = context.getTransactionRecipe() - - expect(transaction).toMatchObject({ - type: TransactionType_CREATION, - protocolVersion: '3.3', - community: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - signingAccount: { - derive2Pubkey: moderator.account.derive2Pubkey, - }, - recipientAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - amount: new Decimal(2000), - backendTransactions: [ - { - typeId: InputTransactionType.CREATION, - }, - ], - }) - - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - // console.log(new TransactionBodyLoggingView(body)) - expect(body?.isCreation()).toBeTruthy() - - expect( - body - ?.getCreation() - ?.getRecipient() - .getPubkey() - ?.equal(new MemoryBlock(firstUser.account.derive2Pubkey)), - ).toBeTruthy() - - expect(body).toMatchObject({ - type: CrossGroupType_LOCAL, - creation: { - recipient: { - amount: '2000', - }, - }, - }) - }) - - it('local send transaction', async () => { - const sendTransactionDraft = new TransactionDraft() - sendTransactionDraft.amount = new Decimal('100') - sendTransactionDraft.backendTransactionId = 2 - sendTransactionDraft.createdAt = new Date().toISOString() - sendTransactionDraft.linkedUser = secondUser.identifier - sendTransactionDraft.user = firstUser.identifier - sendTransactionDraft.type = InputTransactionType.SEND - const context = new CreateTransactionRecipeContext(sendTransactionDraft) - await context.run() - const transaction = context.getTransactionRecipe() - - // console.log(new TransactionLoggingView(transaction)) - expect(transaction).toMatchObject({ - type: TransactionType.GRADIDO_TRANSFER, - protocolVersion: '3.3', - community: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - signingAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - recipientAccount: { - derive2Pubkey: secondUser.account.derive2Pubkey, - }, - amount: new Decimal(100), - backendTransactions: [ - { - typeId: InputTransactionType.SEND, - }, - ], - }) - - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - // console.log(new TransactionBodyLoggingView(body)) - expect(body?.isTransfer()).toBeTruthy() - const transfer = body?.getTransfer() - expect(transfer).not.toBeNull() - expect( - transfer?.getRecipient()?.equal(new MemoryBlock(secondUser.account.derive2Pubkey)), - ).toBeTruthy() - expect( - transfer?.getSender().getPubkey()?.equal(new MemoryBlock(firstUser.account.derive2Pubkey)), - ).toBeTruthy() - - expect(body).toMatchObject({ - type: CrossGroupType_LOCAL, - transfer: { - sender: { - amount: '100', - }, - }, - }) - }) - - it('local recv transaction', async () => { - const recvTransactionDraft = new TransactionDraft() - recvTransactionDraft.amount = new Decimal('100') - recvTransactionDraft.backendTransactionId = 3 - recvTransactionDraft.createdAt = new Date().toISOString() - recvTransactionDraft.linkedUser = firstUser.identifier - recvTransactionDraft.user = secondUser.identifier - recvTransactionDraft.type = InputTransactionType.RECEIVE - const context = new CreateTransactionRecipeContext(recvTransactionDraft) - await context.run() - const transaction = context.getTransactionRecipe() - // console.log(new TransactionLoggingView(transaction)) - expect(transaction).toMatchObject({ - type: TransactionType.GRADIDO_TRANSFER, - protocolVersion: '3.3', - community: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - signingAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - recipientAccount: { - derive2Pubkey: secondUser.account.derive2Pubkey, - }, - amount: new Decimal(100), - backendTransactions: [ - { - typeId: InputTransactionType.RECEIVE, - }, - ], - }) - - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - expect(body?.isTransfer()).toBeTruthy() - const transfer = body?.getTransfer() - expect(transfer).not.toBeNull() - expect( - transfer?.getRecipient()?.equal(new MemoryBlock(secondUser.account.derive2Pubkey)), - ).toBeTruthy() - expect( - transfer?.getSender().getPubkey()?.equal(new MemoryBlock(firstUser.account.derive2Pubkey)), - ).toBeTruthy() - - expect(body).toMatchObject({ - type: CrossGroupType_LOCAL, - transfer: { - sender: { - amount: '100', - }, - }, - }) - }) - - it('cross group send transaction', async () => { - const crossGroupSendTransactionDraft = new TransactionDraft() - crossGroupSendTransactionDraft.amount = new Decimal('100') - crossGroupSendTransactionDraft.backendTransactionId = 4 - crossGroupSendTransactionDraft.createdAt = new Date().toISOString() - crossGroupSendTransactionDraft.linkedUser = foreignUser.identifier - crossGroupSendTransactionDraft.user = firstUser.identifier - crossGroupSendTransactionDraft.type = InputTransactionType.SEND - const context = new CreateTransactionRecipeContext(crossGroupSendTransactionDraft) - await context.run() - const transaction = context.getTransactionRecipe() - // console.log(new TransactionLoggingView(transaction)) - expect(transaction).toMatchObject({ - type: TransactionType.GRADIDO_TRANSFER, - protocolVersion: '3.3', - community: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - otherCommunity: { - rootPubkey: foreignKeyPair.publicKey, - foreign: 1, - iotaTopic: foreignTopic, - }, - signingAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - recipientAccount: { - derive2Pubkey: foreignUser.account.derive2Pubkey, - }, - amount: new Decimal(100), - backendTransactions: [ - { - typeId: InputTransactionType.SEND, - }, - ], - }) - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - // console.log(new TransactionBodyLoggingView(body)) - expect(body?.isTransfer()).toBeTruthy() - const transfer = body?.getTransfer() - expect(transfer).not.toBeNull() - expect( - transfer?.getRecipient()?.equal(new MemoryBlock(foreignUser.account.derive2Pubkey)), - ).toBeTruthy() - expect( - transfer?.getSender().getPubkey()?.equal(new MemoryBlock(firstUser.account.derive2Pubkey)), - ).toBeTruthy() - expect(body).toMatchObject({ - type: CrossGroupType_OUTBOUND, - otherGroup: foreignTopic, - transfer: { - sender: { - amount: '100', - }, - }, - }) - }) - - it('cross group recv transaction', async () => { - const crossGroupRecvTransactionDraft = new TransactionDraft() - crossGroupRecvTransactionDraft.amount = new Decimal('100') - crossGroupRecvTransactionDraft.backendTransactionId = 5 - crossGroupRecvTransactionDraft.createdAt = new Date().toISOString() - crossGroupRecvTransactionDraft.linkedUser = firstUser.identifier - crossGroupRecvTransactionDraft.user = foreignUser.identifier - crossGroupRecvTransactionDraft.type = InputTransactionType.RECEIVE - const context = new CreateTransactionRecipeContext(crossGroupRecvTransactionDraft) - await context.run() - const transaction = context.getTransactionRecipe() - // console.log(new TransactionLoggingView(transaction)) - expect(transaction).toMatchObject({ - type: TransactionType.GRADIDO_TRANSFER, - protocolVersion: '3.3', - community: { - rootPubkey: foreignKeyPair.publicKey, - foreign: 1, - iotaTopic: foreignTopic, - }, - otherCommunity: { - rootPubkey: keyPair.publicKey, - foreign: 0, - iotaTopic: topic, - }, - signingAccount: { - derive2Pubkey: firstUser.account.derive2Pubkey, - }, - recipientAccount: { - derive2Pubkey: foreignUser.account.derive2Pubkey, - }, - amount: new Decimal(100), - backendTransactions: [ - { - typeId: InputTransactionType.RECEIVE, - }, - ], - }) - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - // console.log(new TransactionBodyLoggingView(body)) - expect(body?.isTransfer()).toBeTruthy() - const transfer = body?.getTransfer() - expect(transfer).not.toBeNull() - expect( - transfer?.getRecipient()?.equal(new MemoryBlock(foreignUser.account.derive2Pubkey)), - ).toBeTruthy() - expect( - transfer?.getSender().getPubkey()?.equal(new MemoryBlock(firstUser.account.derive2Pubkey)), - ).toBeTruthy() - expect(body).toMatchObject({ - type: CrossGroupType_INBOUND, - otherGroup: topic, - transfer: { - sender: { - amount: '100', - }, - }, - }) - }) -}) diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts deleted file mode 100644 index 10bb3f4f6..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Account } from '@entity/Account' -import { Community } from '@entity/Community' -import { Transaction } from '@entity/Transaction' - -import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { TransactionError } from '@/graphql/model/TransactionError' - -import { AbstractTransactionRole } from './AbstractTransaction.role' -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' -import { BalanceChangingTransactionRecipeRole } from './BalanceChangingTransactionRecipeRole' -import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' -import { CreationTransactionRole } from './CreationTransaction.role' -import { RegisterAddressTransactionRole } from './RegisterAddressTransaction.role' -import { SendTransactionRole } from './SendTransaction.role' - -/** - * @DCI-Context - * Context for create and add Transaction Recipe to DB - */ - -export interface AdditionalData { - community?: Community - account?: Account -} - -export class CreateTransactionRecipeContext { - private transactionRecipe: AbstractTransactionRecipeRole - // eslint-disable-next-line no-useless-constructor - public constructor( - private draft: CommunityDraft | TransactionDraft | UserAccountDraft, - private data?: AdditionalData, - ) {} - - public getTransactionRecipe(): Transaction { - return this.transactionRecipe.getTransaction() - } - - /** - * @returns true if a transaction recipe was created and false if it wasn't necessary - */ - public async run(): Promise { - if (this.draft instanceof TransactionDraft) { - // contain logic for translation from backend to dlt-connector format - let transactionTypeRole: AbstractTransactionRole - switch (this.draft.type) { - case InputTransactionType.CREATION: - transactionTypeRole = new CreationTransactionRole(this.draft) - break - case InputTransactionType.SEND: - transactionTypeRole = new SendTransactionRole(this.draft) - break - case InputTransactionType.RECEIVE: - return false - } - this.transactionRecipe = await new BalanceChangingTransactionRecipeRole().create( - this.draft, - transactionTypeRole, - ) - return true - } else if (this.draft instanceof CommunityDraft) { - if (!this.data?.community) { - throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'community was not set') - } - this.transactionRecipe = new CommunityRootTransactionRole().create( - this.draft, - this.data.community, - ) - return true - } else if (this.draft instanceof UserAccountDraft) { - if (!this.data?.account) { - throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'account was not set') - } - if (!this.data.community) { - throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'community was not set') - } - this.transactionRecipe = await new RegisterAddressTransactionRole().create( - this.draft, - this.data.account, - this.data.community, - ) - return true - } - return false - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts deleted file mode 100644 index 40648b566..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* eslint-disable camelcase */ -import { Community } from '@entity/Community' -import { MemoryBlock, GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js' - -import { CommunityRepository } from '@/data/Community.repository' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { TransactionError } from '@/graphql/model/TransactionError' -import { logger } from '@/logging/logger' -import { UserIdentifierLoggingView } from '@/logging/UserIdentifierLogging.view' - -import { AbstractTransactionRole } from './AbstractTransaction.role' - -export class CreationTransactionRole extends AbstractTransactionRole { - public getSigningUser(): UserIdentifier { - return this.self.linkedUser - } - - public getRecipientUser(): UserIdentifier { - return this.self.user - } - - public async getGradidoTransactionBuilder(): Promise { - const builder = new GradidoTransactionBuilder() - const recipientUser = await this.loadUser(this.self.user) - if (!this.self.targetDate) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing targetDate for contribution', - ) - } - return builder - .setTransactionCreation( - new TransferAmount( - new MemoryBlock(recipientUser.derive2Pubkey), - this.self.amount.toString(), - ), - new Date(this.self.targetDate), - ) - .setMemo('dummy memo for creation') - } - - public async getCommunity(): Promise { - if (this.self.user.communityUuid !== this.self.linkedUser.communityUuid) { - throw new TransactionError( - TransactionErrorType.LOGIC_ERROR, - 'mismatch community uuids on contribution', - ) - } - const community = await CommunityRepository.getCommunityForUserIdentifier(this.self.user) - if (!community) { - logger.error( - 'missing community for user identifier', - new UserIdentifierLoggingView(this.self.user), - ) - throw new TransactionError(TransactionErrorType.NOT_FOUND, "couldn't find community for user") - } - return community - } - - public async getOtherCommunity(): Promise { - return null - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts deleted file mode 100644 index f2a41a72f..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Account } from '@entity/Account' -import { Community } from '@entity/Community' -import { - // eslint-disable-next-line camelcase - AddressType_COMMUNITY_HUMAN, - MemoryBlock, - GradidoTransactionBuilder, -} from 'gradido-blockchain-js' - -import { AccountLogic } from '@/data/Account.logic' -import { CommunityRepository } from '@/data/Community.repository' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' -import { TransactionError } from '@/graphql/model/TransactionError' - -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' - -export class RegisterAddressTransactionRole extends AbstractTransactionRecipeRole { - async create( - userAccountDraft: UserAccountDraft, - account: Account, - community: Community, - ): Promise { - const user = account.user - if (!user) { - throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'missing user for account') - } - const gradidoTransactionBuilder = new GradidoTransactionBuilder() - const communityKeyPair = await CommunityRepository.loadHomeCommunityKeyPair() - const signingKeyPair = new AccountLogic(account).calculateKeyPair(communityKeyPair) - if (!signingKeyPair) { - throw new TransactionError(TransactionErrorType.NOT_FOUND, "couldn't found signing key pair") - } - const transaction = gradidoTransactionBuilder - .setRegisterAddress( - new MemoryBlock(user.derive1Pubkey), - AddressType_COMMUNITY_HUMAN, - null, - new MemoryBlock(account.derive2Pubkey), - ) - .setCreatedAt(new Date(userAccountDraft.createdAt)) - .sign(signingKeyPair.keyPair) - .sign(communityKeyPair.keyPair) - .build() - - this.transactionBuilder - .fromGradidoTransaction(transaction) - .setCommunity(community) - .setSigningAccount(account) - return this - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts deleted file mode 100644 index 874656cda..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable camelcase */ -import { - CrossGroupType, - CrossGroupType_LOCAL, - CrossGroupType_OUTBOUND, - MemoryBlock, - GradidoTransactionBuilder, - TransferAmount, -} from 'gradido-blockchain-js' - -import { UserIdentifier } from '@/graphql/input/UserIdentifier' - -import { AbstractTransactionRole } from './AbstractTransaction.role' - -export class SendTransactionRole extends AbstractTransactionRole { - public getSigningUser(): UserIdentifier { - return this.self.user - } - - public getRecipientUser(): UserIdentifier { - return this.self.linkedUser - } - - public async getGradidoTransactionBuilder(): Promise { - const builder = new GradidoTransactionBuilder() - const signingUser = await this.loadUser(this.self.user) - const recipientUser = await this.loadUser(this.self.linkedUser) - return builder - .setTransactionTransfer( - new TransferAmount(new MemoryBlock(signingUser.derive2Pubkey), this.self.amount.toString()), - new MemoryBlock(recipientUser.derive2Pubkey), - ) - .setMemo('dummy memo for transfer') - } -} diff --git a/dlt-connector/src/interactions/sendToIota/RegisterAddressTransaction.role.ts b/dlt-connector/src/interactions/sendToIota/RegisterAddressTransaction.role.ts index 498e71bf7..dc80d7c02 100644 --- a/dlt-connector/src/interactions/sendToIota/RegisterAddressTransaction.role.ts +++ b/dlt-connector/src/interactions/sendToIota/RegisterAddressTransaction.role.ts @@ -1,9 +1,9 @@ /* eslint-disable camelcase */ -import { AddressType_COMMUNITY_HUMAN, GradidoTransactionBuilder } from 'gradido-blockchain-js' +import { GradidoTransactionBuilder } from 'gradido-blockchain-js' import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' import { LogError } from '@/server/LogError' -import { uuid4ToHash } from '@/utils/typeConverter' +import { accountTypeToAddressType, uuid4ToHash } from '@/utils/typeConverter' import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context' @@ -30,7 +30,7 @@ export class RegisterAddressTransactionRole extends AbstractTransactionRole { .setCreatedAt(new Date(this.self.createdAt)) .setRegisterAddress( accountKeyPair.getPublicKey(), - AddressType_COMMUNITY_HUMAN, + accountTypeToAddressType(this.self.accountType), uuid4ToHash(this.self.user.uuid), ) .sign(communityKeyPair) diff --git a/dlt-connector/src/interactions/transmitToIota/AbstractTransactionRecipe.role.ts b/dlt-connector/src/interactions/transmitToIota/AbstractTransactionRecipe.role.ts deleted file mode 100644 index e8730f8e3..000000000 --- a/dlt-connector/src/interactions/transmitToIota/AbstractTransactionRecipe.role.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* eslint-disable camelcase */ -import { Transaction } from '@entity/Transaction' -import { - GradidoTransaction, - GradidoTransactionBuilder, - InteractionSerialize, - InteractionValidate, - MemoryBlock, - TransactionType_COMMUNITY_ROOT, - ValidateType_SINGLE, -} from 'gradido-blockchain-js' - -import { sendMessage as iotaSendMessage } from '@/client/IotaClient' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { TransactionError } from '@/graphql/model/TransactionError' -import { logger } from '@/logging/logger' - -export abstract class AbstractTransactionRecipeRole { - // eslint-disable-next-line no-useless-constructor - public constructor(protected self: Transaction) {} - - public abstract transmitToIota(): Promise - public abstract getCrossGroupTypeName(): string - - public validate(transactionBuilder: GradidoTransactionBuilder): GradidoTransaction { - const transaction = transactionBuilder.build() - try { - // throw an exception when something is wrong - const validator = new InteractionValidate(transaction) - validator.run(ValidateType_SINGLE) - } catch (e) { - if (e instanceof Error) { - throw new TransactionError(TransactionErrorType.VALIDATION_ERROR, e.message) - } else if (typeof e === 'string') { - throw new TransactionError(TransactionErrorType.VALIDATION_ERROR, e) - } else { - throw e - } - } - return transaction - } - - protected getGradidoTransactionBuilder(): GradidoTransactionBuilder { - if (!this.self.signature) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing signature in transaction recipe', - ) - } - let publicKey: Buffer | undefined - if (this.self.type === TransactionType_COMMUNITY_ROOT) { - publicKey = this.self.community.rootPubkey - if (!publicKey) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing community public key for community root transaction', - ) - } - } else if (this.self.signingAccount) { - publicKey = this.self.signingAccount.derive2Pubkey - if (!publicKey) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing signing account public key for transaction', - ) - } - } else { - throw new TransactionError( - TransactionErrorType.NOT_FOUND, - "signingAccount not exist and it isn't a community root transaction", - ) - } - return new GradidoTransactionBuilder() - .setTransactionBody(new MemoryBlock(this.self.bodyBytes)) - .addSignaturePair(new MemoryBlock(publicKey), new MemoryBlock(this.self.signature)) - } - - /** - * - * @param gradidoTransaction - * @param topic - * @return iota message id - */ - protected async sendViaIota( - gradidoTransaction: GradidoTransaction, - topic: string, - ): Promise { - // protobuf serializing function - const serialized = new InteractionSerialize(gradidoTransaction).run() - if (!serialized) { - throw new TransactionError( - TransactionErrorType.PROTO_ENCODE_ERROR, - 'cannot serialize transaction', - ) - } - const resultMessage = await iotaSendMessage( - Uint8Array.from(serialized.data()), - Uint8Array.from(Buffer.from(topic, 'hex')), - ) - logger.info('transmitted Gradido Transaction to Iota', { - id: this.self.id, - messageId: resultMessage.messageId, - }) - return Buffer.from(resultMessage.messageId, 'hex') - } -} diff --git a/dlt-connector/src/interactions/transmitToIota/InboundTransactionRecipe.role.ts b/dlt-connector/src/interactions/transmitToIota/InboundTransactionRecipe.role.ts deleted file mode 100644 index afa171a37..000000000 --- a/dlt-connector/src/interactions/transmitToIota/InboundTransactionRecipe.role.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Transaction } from '@entity/Transaction' -import { MemoryBlock } from 'gradido-blockchain-js' - -import { logger } from '@/logging/logger' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' -import { LogError } from '@/server/LogError' - -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipe.role' - -/** - * Inbound Transaction on recipient community, mark the gradidos as received from another community - * need to set gradido id from OUTBOUND transaction! - */ -export class InboundTransactionRecipeRole extends AbstractTransactionRecipeRole { - public getCrossGroupTypeName(): string { - return 'INBOUND' - } - - public async transmitToIota(): Promise { - logger.debug('transmit INBOUND transaction to iota', new TransactionLoggingView(this.self)) - const builder = this.getGradidoTransactionBuilder() - const pairingTransaction = await new TransactionLogic(this.self).findPairTransaction() - if (!pairingTransaction.iotaMessageId || pairingTransaction.iotaMessageId.length !== 32) { - throw new LogError( - 'missing iota message id in pairing transaction, was it already send?', - new TransactionLoggingView(pairingTransaction), - ) - } - builder.setParentMessageId(new MemoryBlock(pairingTransaction.iotaMessageId)) - this.self.pairingTransactionId = pairingTransaction.id - this.self.pairingTransaction = pairingTransaction - pairingTransaction.pairingTransactionId = this.self.id - - if (!this.self.otherCommunity) { - throw new LogError('missing other community') - } - - this.self.iotaMessageId = await this.sendViaIota( - this.validate(builder), - this.self.otherCommunity.iotaTopic, - ) - return this.self - } -} diff --git a/dlt-connector/src/interactions/transmitToIota/LocalTransactionRecipe.role.ts b/dlt-connector/src/interactions/transmitToIota/LocalTransactionRecipe.role.ts deleted file mode 100644 index 10f862d9a..000000000 --- a/dlt-connector/src/interactions/transmitToIota/LocalTransactionRecipe.role.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Transaction } from '@entity/Transaction' - -import { logger } from '@/logging/logger' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' - -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipe.role' - -export class LocalTransactionRecipeRole extends AbstractTransactionRecipeRole { - public getCrossGroupTypeName(): string { - return 'LOCAL' - } - - public async transmitToIota(): Promise { - logger.debug( - `transmit ${this.getCrossGroupTypeName()} transaction to iota`, - new TransactionLoggingView(this.self), - ) - this.self.iotaMessageId = await this.sendViaIota( - this.validate(this.getGradidoTransactionBuilder()), - this.self.community.iotaTopic, - ) - return this.self - } -} diff --git a/dlt-connector/src/interactions/transmitToIota/OutboundTransactionRecipeRole.ts b/dlt-connector/src/interactions/transmitToIota/OutboundTransactionRecipeRole.ts deleted file mode 100644 index d3b0b67c4..000000000 --- a/dlt-connector/src/interactions/transmitToIota/OutboundTransactionRecipeRole.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { LocalTransactionRecipeRole } from './LocalTransactionRecipe.role' - -/** - * Outbound Transaction on sender community, mark the gradidos as sended out of community - */ -export class OutboundTransactionRecipeRole extends LocalTransactionRecipeRole { - public getCrossGroupTypeName(): string { - return 'OUTBOUND' - } -} diff --git a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts b/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts deleted file mode 100644 index f366be2b4..000000000 --- a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -import 'reflect-metadata' -import { Account } from '@entity/Account' -import { Decimal } from 'decimal.js-light' -import { CrossGroupType_INBOUND, CrossGroupType_OUTBOUND, InteractionDeserialize, InteractionToJson, InteractionValidate, MemoryBlock } from 'gradido-blockchain-js' -import { v4 } from 'uuid' - -import { TestDB } from '@test/TestDB' - -import { CONFIG } from '@/config' -import { KeyPair } from '@/data/KeyPair' -import { Mnemonic } from '@/data/Mnemonic' -import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { logger } from '@/logging/logger' - -import { CreateTransactionRecipeContext } from '../backendToDb/transaction/CreateTransactionRecipe.context' - -import { TransmitToIotaContext } from './TransmitToIota.context' - -// eslint-disable-next-line import/order -import { communitySeed } from '@test/seeding/Community.seed' -// eslint-disable-next-line import/order -import { createUserSet, UserSet } from '@test/seeding/UserSet.seed' - -jest.mock('@typeorm/DataSource', () => ({ - getDataSource: jest.fn(() => TestDB.instance.dbConnect), -})) - -jest.mock('@/client/IotaClient', () => { - return { - sendMessage: jest.fn().mockReturnValue({ - messageId: '5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', - }), - } -}) - -CONFIG.IOTA_HOME_COMMUNITY_SEED = '034b0229a2ba4e98e1cc5e8767dca886279b484303ffa73546bd5f5bf0b71285' -const homeCommunityUuid = v4() -const foreignCommunityUuid = v4() - -const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED)) -const foreignKeyPair = new KeyPair( - new Mnemonic('5d4e163c078cc6b51f5c88f8422bc8f21d1d59a284515ab1ea79e1c176ebec50'), -) - -let moderator: UserSet -let firstUser: UserSet -let secondUser: UserSet -let foreignUser: UserSet - -const now = new Date() - -describe('interactions/transmitToIota/TransmitToIotaContext', () => { - beforeAll(async () => { - await TestDB.instance.setupTestDB() - await communitySeed(homeCommunityUuid, false) - await communitySeed(foreignCommunityUuid, true, foreignKeyPair) - - moderator = createUserSet(homeCommunityUuid, keyPair) - firstUser = createUserSet(homeCommunityUuid, keyPair) - secondUser = createUserSet(homeCommunityUuid, keyPair) - foreignUser = createUserSet(foreignCommunityUuid, foreignKeyPair) - - await Account.save([ - moderator.account, - firstUser.account, - secondUser.account, - foreignUser.account, - ]) - }) - - afterAll(async () => { - await TestDB.instance.teardownTestDB() - }) - - it('LOCAL transaction', async () => { - const creationTransactionDraft = new TransactionDraft() - creationTransactionDraft.amount = new Decimal('1000') - creationTransactionDraft.backendTransactionId = 1 - creationTransactionDraft.createdAt = new Date().toISOString() - creationTransactionDraft.linkedUser = moderator.identifier - creationTransactionDraft.user = firstUser.identifier - creationTransactionDraft.type = InputTransactionType.CREATION - creationTransactionDraft.targetDate = new Date().toISOString() - const transactionRecipeContext = new CreateTransactionRecipeContext(creationTransactionDraft) - await transactionRecipeContext.run() - const transaction = transactionRecipeContext.getTransactionRecipe() - - const context = new TransmitToIotaContext(transaction) - const debugSpy = jest.spyOn(logger, 'debug') - await context.run() - expect( - transaction.iotaMessageId?.compare( - Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'), - ), - ).toBe(0) - expect(debugSpy).toHaveBeenNthCalledWith( - 3, - expect.stringContaining('transmit LOCAL transaction to iota'), - expect.objectContaining({}), - ) - }) - - it('OUTBOUND transaction', async () => { - const crossGroupSendTransactionDraft = new TransactionDraft() - crossGroupSendTransactionDraft.amount = new Decimal('100') - crossGroupSendTransactionDraft.backendTransactionId = 4 - crossGroupSendTransactionDraft.createdAt = now.toISOString() - crossGroupSendTransactionDraft.linkedUser = foreignUser.identifier - crossGroupSendTransactionDraft.user = firstUser.identifier - crossGroupSendTransactionDraft.type = InputTransactionType.SEND - const transactionRecipeContext = new CreateTransactionRecipeContext( - crossGroupSendTransactionDraft, - ) - await transactionRecipeContext.run() - const transaction = transactionRecipeContext.getTransactionRecipe() - await transaction.save() - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body).not.toBeNull() - expect(body?.getType()).toEqual(CrossGroupType_OUTBOUND) - const context = new TransmitToIotaContext(transaction) - const debugSpy = jest.spyOn(logger, 'debug') - await context.run() - expect( - transaction.iotaMessageId?.compare( - Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'), - ), - ).toBe(0) - expect(debugSpy).toHaveBeenNthCalledWith( - 5, - expect.stringContaining('transmit OUTBOUND transaction to iota'), - expect.objectContaining({}), - ) - }) - - it('INBOUND transaction', async () => { - const crossGroupRecvTransactionDraft = new TransactionDraft() - crossGroupRecvTransactionDraft.amount = new Decimal('100') - crossGroupRecvTransactionDraft.backendTransactionId = 5 - crossGroupRecvTransactionDraft.createdAt = now.toISOString() - crossGroupRecvTransactionDraft.linkedUser = firstUser.identifier - crossGroupRecvTransactionDraft.user = foreignUser.identifier - crossGroupRecvTransactionDraft.type = InputTransactionType.RECEIVE - const transactionRecipeContext = new CreateTransactionRecipeContext( - crossGroupRecvTransactionDraft, - ) - await transactionRecipeContext.run() - const transaction = transactionRecipeContext.getTransactionRecipe() - await transaction.save() - // console.log(new TransactionLoggingView(transaction)) - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const body = deserializer.getTransactionBody() - expect(body?.getType()).toEqual(CrossGroupType_INBOUND) - - const context = new TransmitToIotaContext(transaction) - const debugSpy = jest.spyOn(logger, 'debug') - await context.run() - expect( - transaction.iotaMessageId?.compare( - Buffer.from('5498130bc3918e1a7143969ce05805502417e3e1bd596d3c44d6a0adeea22710', 'hex'), - ), - ).toBe(0) - expect(debugSpy).toHaveBeenNthCalledWith( - 7, - expect.stringContaining('transmit INBOUND transaction to iota'), - expect.objectContaining({}), - ) - }) -}) diff --git a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.ts b/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.ts deleted file mode 100644 index 69c9ade3f..000000000 --- a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable camelcase */ -import { Transaction } from '@entity/Transaction' -import { - CrossGroupType_INBOUND, - CrossGroupType_LOCAL, - CrossGroupType_OUTBOUND, - InteractionDeserialize, - MemoryBlock, -} from 'gradido-blockchain-js' - -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { TransactionError } from '@/graphql/model/TransactionError' -import { logger } from '@/logging/logger' -import { TransactionLoggingView } from '@/logging/TransactionLogging.view' -import { LogError } from '@/server/LogError' -import { getDataSource } from '@/typeorm/DataSource' - -import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipe.role' -import { InboundTransactionRecipeRole } from './InboundTransactionRecipe.role' -import { LocalTransactionRecipeRole } from './LocalTransactionRecipe.role' -import { OutboundTransactionRecipeRole } from './OutboundTransactionRecipeRole' - -/** - * @DCI-Context - * Context for sending transaction recipe to iota - * send every transaction only once to iota! - */ -export class TransmitToIotaContext { - private transactionRecipeRole: AbstractTransactionRecipeRole - - public constructor(transaction: Transaction) { - const deserializer = new InteractionDeserialize(new MemoryBlock(transaction.bodyBytes)) - deserializer.run() - const transactionBody = deserializer.getTransactionBody() - if (!transactionBody) { - throw new TransactionError( - TransactionErrorType.PROTO_DECODE_ERROR, - 'error decoding body bytes', - ) - } - switch (transactionBody.getType()) { - case CrossGroupType_LOCAL: - this.transactionRecipeRole = new LocalTransactionRecipeRole(transaction) - break - case CrossGroupType_INBOUND: - this.transactionRecipeRole = new InboundTransactionRecipeRole(transaction) - break - case CrossGroupType_OUTBOUND: - this.transactionRecipeRole = new OutboundTransactionRecipeRole(transaction) - break - default: - throw new LogError('unknown cross group type', transactionBody.getType()) - } - } - - public async run(): Promise { - const transaction = await this.transactionRecipeRole.transmitToIota() - logger.debug('transaction sended via iota', new TransactionLoggingView(transaction)) - // store changes in db - // prevent endless loop - const pairingTransaction = transaction.pairingTransaction - if (pairingTransaction) { - transaction.pairingTransaction = undefined - await getDataSource().transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.save(transaction) - await transactionalEntityManager.save(pairingTransaction) - }) - } else { - await transaction.save() - } - logger.info('sended transaction successfully updated in db') - } -} diff --git a/dlt-connector/src/logging/AbstractLogging.view.ts b/dlt-connector/src/logging/AbstractLogging.view.ts index e5f439b5d..ddb1cb6ed 100644 --- a/dlt-connector/src/logging/AbstractLogging.view.ts +++ b/dlt-connector/src/logging/AbstractLogging.view.ts @@ -1,6 +1,5 @@ import util from 'util' -import { Decimal } from 'decimal.js-light' import { Timestamp, TimestampSeconds } from 'gradido-blockchain-js' export abstract class AbstractLoggingView { @@ -25,13 +24,6 @@ export abstract class AbstractLoggingView { return undefined } - protected decimalToString(number: Decimal | undefined | null): string | undefined { - if (number) { - return number.toString() - } - return undefined - } - protected timestampSecondsToDateString(timestamp: TimestampSeconds): string | undefined { if (timestamp && timestamp.getSeconds()) { return timestamp.getDate().toISOString() diff --git a/dlt-connector/src/logging/AccountLogging.view.ts b/dlt-connector/src/logging/AccountLogging.view.ts deleted file mode 100644 index f1a7abe20..000000000 --- a/dlt-connector/src/logging/AccountLogging.view.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Account } from '@entity/Account' -import { addressTypeToString } from 'gradido-blockchain-js' - -import { AccountType } from '@/graphql/enum/AccountType' -import { accountTypeToAddressType } from '@/utils/typeConverter' - -import { AbstractLoggingView } from './AbstractLogging.view' -import { UserLoggingView } from './UserLogging.view' - -export class AccountLoggingView extends AbstractLoggingView { - public constructor(private account: Account) { - super() - } - - public toJSON() { - return { - id: this.account.id, - user: this.account.user ? new UserLoggingView(this.account.user).toJSON() : null, - derivationIndex: this.account.derivationIndex, - derive2Pubkey: this.account.derive2Pubkey.toString(this.bufferStringFormat), - type: addressTypeToString( - accountTypeToAddressType(this.account.type as unknown as AccountType), - ), - createdAt: this.dateToString(this.account.createdAt), - confirmedAt: this.dateToString(this.account.confirmedAt), - balanceOnConfirmation: this.decimalToString(this.account.balanceOnConfirmation), - balanceConfirmedAt: this.dateToString(this.account.balanceConfirmedAt), - balanceOnCreation: this.decimalToString(this.account.balanceOnCreation), - balanceCreatedAt: this.dateToString(this.account.balanceCreatedAt), - } - } -} diff --git a/dlt-connector/src/logging/BackendTransactionLogging.view.ts b/dlt-connector/src/logging/BackendTransactionLogging.view.ts deleted file mode 100644 index d21c765aa..000000000 --- a/dlt-connector/src/logging/BackendTransactionLogging.view.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { BackendTransaction } from '@entity/BackendTransaction' - -import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { getEnumValue } from '@/utils/typeConverter' - -import { AbstractLoggingView } from './AbstractLogging.view' -import { TransactionLoggingView } from './TransactionLogging.view' - -export class BackendTransactionLoggingView extends AbstractLoggingView { - public constructor(private self: BackendTransaction) { - super() - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public toJSON(showTransaction = true): any { - return { - id: this.self.id, - backendTransactionId: this.self.backendTransactionId, - transaction: - showTransaction && this.self.transaction - ? new TransactionLoggingView(this.self.transaction).toJSON(false) - : undefined, - type: getEnumValue(InputTransactionType, this.self.typeId), - balance: this.decimalToString(this.self.balance), - createdAt: this.dateToString(this.self.createdAt), - confirmedAt: this.dateToString(this.self.confirmedAt), - verifiedOnBackend: this.self.verifiedOnBackend, - } - } -} diff --git a/dlt-connector/src/logging/CommunityLogging.view.ts b/dlt-connector/src/logging/CommunityLogging.view.ts deleted file mode 100644 index 22f0a4597..000000000 --- a/dlt-connector/src/logging/CommunityLogging.view.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Community } from '@entity/Community' - -import { AbstractLoggingView } from './AbstractLogging.view' -import { AccountLoggingView } from './AccountLogging.view' - -export class CommunityLoggingView extends AbstractLoggingView { - public constructor(private self: Community) { - super() - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public toJSON(): any { - return { - id: this.self.id, - iotaTopic: this.self.iotaTopic, - foreign: this.self.foreign, - publicKey: this.self.rootPubkey?.toString(this.bufferStringFormat), - createdAt: this.dateToString(this.self.createdAt), - confirmedAt: this.dateToString(this.self.confirmedAt), - aufAccount: this.self.aufAccount ? new AccountLoggingView(this.self.aufAccount) : undefined, - gmwAccount: this.self.gmwAccount ? new AccountLoggingView(this.self.gmwAccount) : undefined, - } - } -} diff --git a/dlt-connector/src/logging/TransactionDraftLogging.view.ts b/dlt-connector/src/logging/TransactionDraftLogging.view.ts index 5e86822ec..655a9ab9e 100644 --- a/dlt-connector/src/logging/TransactionDraftLogging.view.ts +++ b/dlt-connector/src/logging/TransactionDraftLogging.view.ts @@ -16,7 +16,7 @@ export class TransactionDraftLoggingView extends AbstractLoggingView { user: new UserIdentifierLoggingView(this.self.user).toJSON(), linkedUser: new UserIdentifierLoggingView(this.self.linkedUser).toJSON(), backendTransactionId: this.self.backendTransactionId, - amount: this.decimalToString(this.self.amount), + amount: Number(this.self.amount), type: getEnumValue(InputTransactionType, this.self.type), createdAt: this.self.createdAt, targetDate: this.self.targetDate, diff --git a/dlt-connector/src/logging/TransactionLogging.view.ts b/dlt-connector/src/logging/TransactionLogging.view.ts deleted file mode 100644 index 1bb59cc55..000000000 --- a/dlt-connector/src/logging/TransactionLogging.view.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Transaction } from '@entity/Transaction' - -import { TransactionType } from '@/data/proto/3_3/enum/TransactionType' -import { LogError } from '@/server/LogError' -import { getEnumValue } from '@/utils/typeConverter' - -import { AbstractLoggingView } from './AbstractLogging.view' -import { AccountLoggingView } from './AccountLogging.view' -import { BackendTransactionLoggingView } from './BackendTransactionLogging.view' -import { CommunityLoggingView } from './CommunityLogging.view' - -export class TransactionLoggingView extends AbstractLoggingView { - public constructor(private self: Transaction) { - super() - if (this.self.community === undefined) { - throw new LogError('sender community is zero') - } - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public toJSON(showBackendTransactions = true, deep = 1): any { - return { - id: this.self.id, - nr: this.self.nr, - bodyBytesLength: this.self.bodyBytes.length, - createdAt: this.dateToString(this.self.createdAt), - confirmedAt: this.dateToString(this.self.confirmedAt), - protocolVersion: this.self.protocolVersion, - type: getEnumValue(TransactionType, this.self.type), - signature: this.self.signature.subarray(0, 31).toString(this.bufferStringFormat) + '..', - community: new CommunityLoggingView(this.self.community).toJSON(), - otherCommunity: this.self.otherCommunity - ? new CommunityLoggingView(this.self.otherCommunity) - : { id: this.self.otherCommunityId }, - iotaMessageId: this.self.iotaMessageId - ? this.self.iotaMessageId.toString(this.bufferStringFormat) - : undefined, - signingAccount: this.self.signingAccount - ? new AccountLoggingView(this.self.signingAccount) - : { id: this.self.signingAccountId }, - recipientAccount: this.self.recipientAccount - ? new AccountLoggingView(this.self.recipientAccount) - : { id: this.self.recipientAccountId }, - pairingTransaction: - this.self.pairingTransaction && deep === 1 - ? new TransactionLoggingView(this.self.pairingTransaction).toJSON( - showBackendTransactions, - deep + 1, - ) - : { id: this.self.pairingTransaction }, - amount: this.decimalToString(this.self.amount), - accountBalanceOnCreation: this.decimalToString(this.self.accountBalanceOnCreation), - accountBalanceOnConfirmation: this.decimalToString(this.self.accountBalanceOnConfirmation), - runningHash: this.self.runningHash - ? this.self.runningHash.toString(this.bufferStringFormat) - : undefined, - iotaMilestone: this.self.iotaMilestone, - backendTransactions: - showBackendTransactions && this.self.backendTransactions - ? this.self.backendTransactions.map((backendTransaction) => - new BackendTransactionLoggingView(backendTransaction).toJSON(false), - ) - : undefined, - } - } -} diff --git a/dlt-connector/src/logging/UserLogging.view.ts b/dlt-connector/src/logging/UserLogging.view.ts deleted file mode 100644 index a3cbd66bc..000000000 --- a/dlt-connector/src/logging/UserLogging.view.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { User } from '@entity/User' - -import { AbstractLoggingView } from './AbstractLogging.view' - -export class UserLoggingView extends AbstractLoggingView { - public constructor(private user: User) { - super() - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public toJSON(): any { - return { - id: this.user.id, - gradidoId: this.user.gradidoID, - derive1Pubkey: this.user.derive1Pubkey.toString(this.bufferStringFormat), - createdAt: this.dateToString(this.user.createdAt), - confirmedAt: this.dateToString(this.user.confirmedAt), - } - } -} diff --git a/dlt-connector/src/manager/InterruptiveSleepManager.ts b/dlt-connector/src/manager/InterruptiveSleepManager.ts deleted file mode 100644 index 7827c8fe9..000000000 --- a/dlt-connector/src/manager/InterruptiveSleepManager.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { LogError } from '@/server/LogError' - -import { InterruptiveSleep } from '../utils/InterruptiveSleep' - -// Source: https://refactoring.guru/design-patterns/singleton/typescript/example -// and ../federation/client/FederationClientFactory.ts -/** - * A Singleton class defines the `getInstance` method that lets clients access - * the unique singleton instance. - */ -// eslint-disable-next-line @typescript-eslint/no-extraneous-class -export class InterruptiveSleepManager { - // eslint-disable-next-line no-use-before-define - private static instance: InterruptiveSleepManager - private interruptiveSleep: Map = new Map() - private stepSizeMilliseconds = 10 - - /** - * The Singleton's constructor should always be private to prevent direct - * construction calls with the `new` operator. - */ - // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function - private constructor() {} - - /** - * 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(): InterruptiveSleepManager { - if (!InterruptiveSleepManager.instance) { - InterruptiveSleepManager.instance = new InterruptiveSleepManager() - } - return InterruptiveSleepManager.instance - } - - /** - * only for new created InterruptiveSleepManager Entries! - * @param step size in ms in which new! InterruptiveSleepManager check if they where triggered - */ - public setStepSize(ms: number) { - this.stepSizeMilliseconds = ms - } - - public interrupt(key: string): void { - const interruptiveSleep = this.interruptiveSleep.get(key) - if (interruptiveSleep) { - interruptiveSleep.interrupt() - } - } - - public sleep(key: string, ms: number): Promise { - if (!this.interruptiveSleep.has(key)) { - this.interruptiveSleep.set(key, new InterruptiveSleep(this.stepSizeMilliseconds)) - } - const interruptiveSleep = this.interruptiveSleep.get(key) - if (!interruptiveSleep) { - throw new LogError('map entry not exist after setting it') - } - return interruptiveSleep.sleep(ms) - } -} diff --git a/dlt-connector/src/tasks/transmitToIota.ts b/dlt-connector/src/tasks/transmitToIota.ts deleted file mode 100644 index 89236586e..000000000 --- a/dlt-connector/src/tasks/transmitToIota.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/data/const' -import { TransactionRepository } from '@/data/Transaction.repository' -import { TransmitToIotaContext } from '@/interactions/transmitToIota/TransmitToIota.context' -import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' - -import { logger } from '../logging/logger' - -function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms) - }) -} - -let running = true - -export const stopTransmitToIota = (): void => { - running = false -} -/** - * check for pending transactions: - * - if one found call TransmitToIotaContext - * - if not, wait 1000 ms and try again - * if a new transaction was added, the sleep will be interrupted - */ -export const transmitToIota = async (): Promise => { - logger.info('start iota message transmitter') - // eslint-disable-next-line no-unmodified-loop-condition - while (running) { - try { - while (true) { - const recipe = await TransactionRepository.getNextPendingTransaction() - if (!recipe) break - const transmitToIotaContext = new TransmitToIotaContext(recipe) - await transmitToIotaContext.run() - } - - await InterruptiveSleepManager.getInstance().sleep( - TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY, - 1000, - ) - } catch (error) { - logger.error('error while transmitting to iota, retry in 10 seconds ', error) - await sleep(10000) - } - } - logger.error( - 'end iota message transmitter, no further transaction will be transmitted. !!! Please restart Server !!!', - ) -} diff --git a/dlt-connector/src/utils/InterruptiveSleep.ts b/dlt-connector/src/utils/InterruptiveSleep.ts deleted file mode 100644 index c21e57db9..000000000 --- a/dlt-connector/src/utils/InterruptiveSleep.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Sleep, that can be interrupted - * call sleep only for msSteps and than check if interrupt was called - */ -export class InterruptiveSleep { - private interruptSleep = false - private msSteps = 10 - - constructor(msSteps: number) { - this.msSteps = msSteps - } - - public interrupt(): void { - this.interruptSleep = true - } - - private static _sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms) - }) - } - - public async sleep(ms: number): Promise { - let waited = 0 - this.interruptSleep = false - while (waited < ms && !this.interruptSleep) { - await InterruptiveSleep._sleep(this.msSteps) - waited += this.msSteps - } - } -}