From 0567a3ddf0ccadde280cacaf80c761a96b6ba456 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Tue, 19 Dec 2023 15:07:53 +0100 Subject: [PATCH] add lint rule for sorted import like in backend, sort imports, refactor enums, remove KeyManager --- dlt-connector/.eslintrc.js | 48 +++---- dlt-connector/src/data/Account.factory.ts | 17 ++- dlt-connector/src/data/Account.repository.ts | 5 +- dlt-connector/src/data/Account.test.ts | 37 ++++-- .../src/data/Community.repository.ts | 8 +- dlt-connector/src/data/KeyPair.ts | 81 +++++++++--- dlt-connector/src/data/Mnemonic.ts | 25 ++++ dlt-connector/src/data/Transaction.builder.ts | 18 +-- .../src/data/Transaction.repository.ts | 3 +- dlt-connector/src/data/User.factory.ts | 8 +- dlt-connector/src/data/User.logic.ts | 11 +- dlt-connector/src/data/User.repository.ts | 5 +- .../src/data/proto/3_3/CommunityRoot.ts | 11 +- .../data/proto/3_3/ConfirmedTransaction.ts | 4 +- .../data/proto/3_3/GradidoCreation.test.ts | 8 +- .../src/data/proto/3_3/GradidoCreation.ts | 26 ++-- .../data/proto/3_3/GradidoDeferredTransfer.ts | 13 +- .../src/data/proto/3_3/GradidoTransaction.ts | 7 +- .../src/data/proto/3_3/GradidoTransfer.ts | 25 ++-- .../src/data/proto/3_3/GroupFriendsUpdate.ts | 10 +- .../src/data/proto/3_3/RegisterAddress.ts | 8 +- .../src/data/proto/3_3/TransactionBody.ts | 35 ++++-- .../src/data/proto/3_3/enum/AddressType.ts | 15 +-- .../src/data/proto/3_3/enum/CrossGroupType.ts | 19 ++- .../data/proto/3_3/enum/TransactionType.ts | 13 ++ .../src/data/proto/AbstractTransaction.ts | 4 - .../src/data/proto/TransactionBody.builder.ts | 14 ++- .../src/data/proto/transactionBody.logic.ts | 9 +- dlt-connector/src/graphql/enum/AccountType.ts | 7 +- .../src/graphql/enum/InputTransactionType.ts | 14 +-- .../src/graphql/enum/TransactionErrorType.ts | 2 + .../src/graphql/enum/TransactionType.ts | 15 --- .../enum/TransactionValidationLevel.ts | 15 --- .../src/graphql/input/CommunityDraft.ts | 1 + .../src/graphql/input/TransactionDraft.ts | 9 +- .../src/graphql/input/UserAccountDraft.ts | 9 +- dlt-connector/src/graphql/model/Community.ts | 2 +- .../src/graphql/model/TransactionError.ts | 1 + .../src/graphql/model/TransactionRecipe.ts | 17 ++- .../src/graphql/model/TransactionResult.ts | 1 + .../resolver/CommunityResolver.test.ts | 12 +- .../src/graphql/resolver/CommunityResolver.ts | 18 +-- .../resolver/TransactionsResolver.test.ts | 52 ++++---- .../graphql/resolver/TransactionsResolver.ts | 11 +- dlt-connector/src/graphql/schema.ts | 4 +- dlt-connector/src/index.ts | 3 +- .../community/AddCommunity.context.ts | 5 +- .../backendToDb/community/Community.role.ts | 3 +- .../community/HomeCommunity.role.ts | 25 ++-- .../CommunityRootTransaction.role.ts | 11 +- .../CreateTransationRecipe.context.ts | 12 +- .../transaction/TransactionRecipe.role.ts | 8 +- dlt-connector/src/manager/KeyManager.test.ts | 22 ---- dlt-connector/src/manager/KeyManager.ts | 118 ------------------ dlt-connector/src/server/createServer.ts | 10 +- dlt-connector/src/server/logger.ts | 4 +- dlt-connector/src/typeorm/DataSource.ts | 4 +- dlt-connector/src/utils/cryptoHelper.ts | 23 ---- .../src/utils/derivationHelper.test.ts | 1 + dlt-connector/src/utils/typeConverter.test.ts | 1 + dlt-connector/src/utils/typeConverter.ts | 80 ++++++------ dlt-connector/test/ApolloServerMock.ts | 1 + dlt-connector/test/TestDB.ts | 3 +- 63 files changed, 487 insertions(+), 524 deletions(-) create mode 100644 dlt-connector/src/data/Mnemonic.ts create mode 100644 dlt-connector/src/data/proto/3_3/enum/TransactionType.ts delete mode 100644 dlt-connector/src/graphql/enum/TransactionType.ts delete mode 100644 dlt-connector/src/graphql/enum/TransactionValidationLevel.ts delete mode 100644 dlt-connector/src/manager/KeyManager.test.ts delete mode 100644 dlt-connector/src/manager/KeyManager.ts delete mode 100644 dlt-connector/src/utils/cryptoHelper.ts diff --git a/dlt-connector/.eslintrc.js b/dlt-connector/.eslintrc.js index b2bc4047e..fa43a5f1a 100644 --- a/dlt-connector/.eslintrc.js +++ b/dlt-connector/.eslintrc.js @@ -77,30 +77,30 @@ module.exports = { // 'import/no-named-default': 'error', // 'import/no-namespace': 'error', // 'import/no-unassigned-import': 'error', - // 'import/order': [ - // 'error', - // { - // groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], - // 'newlines-between': 'always', - // pathGroups: [ - // { - // pattern: '@?*/**', - // group: 'external', - // position: 'after', - // }, - // { - // pattern: '@/**', - // group: 'external', - // position: 'after', - // }, - // ], - // alphabetize: { - // order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, - // caseInsensitive: true /* ignore case. Options: [true, false] */, - // }, - // distinctGroup: true, - // }, - // ], + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroups: [ + { + pattern: '@?*/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/**', + group: 'external', + position: 'after', + }, + ], + alphabetize: { + order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, + caseInsensitive: true /* ignore case. Options: [true, false] */, + }, + distinctGroup: true, + }, + ], // 'import/prefer-default-export': 'off', // n 'n/handle-callback-err': 'error', diff --git a/dlt-connector/src/data/Account.factory.ts b/dlt-connector/src/data/Account.factory.ts index 493e394bb..e318cb104 100644 --- a/dlt-connector/src/data/Account.factory.ts +++ b/dlt-connector/src/data/Account.factory.ts @@ -1,10 +1,10 @@ -import { KeyManager } from '@/manager/KeyManager' -import { KeyPair } from '@/data/KeyPair' -import { AddressType } from '@/data/proto/3_3/enum/AddressType' -import { hardenDerivationIndex } from '@/utils/derivationHelper' import { Account } from '@entity/Account' import Decimal from 'decimal.js-light' + +import { KeyPair } from '@/data/KeyPair' +import { AddressType } from '@/data/proto/3_3/enum/AddressType' import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' +import { hardenDerivationIndex } from '@/utils/derivationHelper' import { accountTypeToAddressType } from '@/utils/typeConverter' const GMW_ACCOUNT_DERIVATION_INDEX = 1 @@ -15,14 +15,11 @@ export class AccountFactory { createdAt: Date, derivationIndex: number, type: AddressType, - parentKeyPair?: KeyPair, + parentKeyPair: KeyPair, ): Account { const account = Account.create() account.derivationIndex = derivationIndex - account.derive2Pubkey = KeyManager.getInstance().derive( - [derivationIndex], - parentKeyPair, - ).publicKey + account.derive2Pubkey = parentKeyPair.derive([derivationIndex]).publicKey account.type = type.valueOf() account.createdAt = createdAt account.balanceConfirmedAt = new Decimal(0) @@ -33,7 +30,7 @@ export class AccountFactory { public static createAccountFromUserAccountDraft( { createdAt, accountType, user }: UserAccountDraft, - parentKeyPair?: KeyPair, + parentKeyPair: KeyPair, ): Account { return AccountFactory.createAccount( new Date(createdAt), diff --git a/dlt-connector/src/data/Account.repository.ts b/dlt-connector/src/data/Account.repository.ts index ea97080fd..6931e6ea6 100644 --- a/dlt-connector/src/data/Account.repository.ts +++ b/dlt-connector/src/data/Account.repository.ts @@ -1,9 +1,10 @@ -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { getDataSource } from '@/typeorm/DataSource' 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({ diff --git a/dlt-connector/src/data/Account.test.ts b/dlt-connector/src/data/Account.test.ts index 62625a66f..596b51dc2 100644 --- a/dlt-connector/src/data/Account.test.ts +++ b/dlt-connector/src/data/Account.test.ts @@ -1,13 +1,17 @@ import 'reflect-metadata' -import { TestDB } from '@test/TestDB' -import { AccountFactory } from './Account.factory' -import { AddressType } from './proto/3_3/enum/AddressType' -import { generateKeyPair, generateMnemonic } from '@/utils/cryptoHelper' import { Decimal } from 'decimal.js-light' -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' + +import { TestDB } from '@test/TestDB' + 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 { AddressType } from './proto/3_3/enum/AddressType' import { UserFactory } from './User.factory' import { UserLogic } from './User.logic' @@ -19,9 +23,9 @@ jest.mock('@typeorm/DataSource', () => ({ describe('data/Account test factory and repository', () => { const now = new Date() - const keyPair1 = generateKeyPair(generateMnemonic('62ef251edc2416f162cd24ab1711982b')) - const keyPair2 = generateKeyPair(generateMnemonic('000a0000000002000000000003000070')) - const keyPair3 = generateKeyPair(generateMnemonic('00ba541a1000020000000000300bda70')) + 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', () => { @@ -36,6 +40,10 @@ describe('data/Account test factory and repository', () => { 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, balanceCreatedAtDate: now, @@ -53,6 +61,10 @@ describe('data/Account test factory and repository', () => { const account = AccountFactory.createAccountFromUserAccountDraft(userAccountDraft, keyPair1) expect(account).toMatchObject({ derivationIndex: 1, + derive2Pubkey: Buffer.from( + 'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994', + 'hex', + ), type: AddressType.COMMUNITY_HUMAN, createdAt: now, balanceCreatedAtDate: now, @@ -65,6 +77,10 @@ describe('data/Account test factory and repository', () => { const account = AccountFactory.createGmwAccount(keyPair1, now) expect(account).toMatchObject({ derivationIndex: 2147483649, + derive2Pubkey: Buffer.from( + '05f0060357bb73bd290283870fc47a10b3764f02ca26938479ed853f46145366', + 'hex', + ), type: AddressType.COMMUNITY_GMW, createdAt: now, balanceCreatedAtDate: now, @@ -77,6 +93,10 @@ describe('data/Account test factory and repository', () => { const account = AccountFactory.createAufAccount(keyPair1, now) expect(account).toMatchObject({ derivationIndex: 2147483650, + derive2Pubkey: Buffer.from( + '6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', + 'hex', + ), type: AddressType.COMMUNITY_AUF, createdAt: now, balanceCreatedAtDate: now, @@ -89,7 +109,6 @@ describe('data/Account test factory and repository', () => { describe('test repository functions', () => { beforeAll(async () => { await con.setupTestDB() - await Promise.all([ AccountFactory.createAufAccount(keyPair1, now).save(), AccountFactory.createGmwAccount(keyPair1, now).save(), diff --git a/dlt-connector/src/data/Community.repository.ts b/dlt-connector/src/data/Community.repository.ts index 848d7b962..78023b15e 100644 --- a/dlt-connector/src/data/Community.repository.ts +++ b/dlt-connector/src/data/Community.repository.ts @@ -1,14 +1,16 @@ +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 { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' -import { Community } from '@entity/Community' -import { FindOptionsSelect, In, IsNull, Not } from 'typeorm' + import { KeyPair } from './KeyPair' -import { LogError } from '@/server/LogError' export const CommunityRepository = getDataSource() .getRepository(Community) diff --git a/dlt-connector/src/data/KeyPair.ts b/dlt-connector/src/data/KeyPair.ts index 3487c1d95..59e9a5066 100644 --- a/dlt-connector/src/data/KeyPair.ts +++ b/dlt-connector/src/data/KeyPair.ts @@ -1,38 +1,87 @@ -// https://www.npmjs.com/package/bip32-ed25519?activeTab=code -import { toPublic } from 'bip32-ed25519' import { Community } from '@entity/Community' + +// https://www.npmjs.com/package/bip32-ed25519 import { LogError } from '@/server/LogError' +import { toPublic, derivePrivate, sign, verify, generateFromSeed } from 'bip32-ed25519' + +import { Mnemonic } from './Mnemonic' + +/** + * Class Managing Key Pair and also generate, sign and verify signature with it + */ export class KeyPair { + private _publicKey: Buffer + private _chainCode: Buffer + private _privateKey: Buffer + /** - * @param input: Buffer = extended private key, returned from bip32-ed25519 generateFromSeed + * @param input: Mnemonic = Mnemonic or Passphrase which work as seed for generating algorithms + * @param input: Buffer = extended private key, returned from bip32-ed25519 generateFromSeed or from derivePrivate * @param input: Community = community entity with keys loaded from db * */ - public constructor(input: Buffer | Community) { - if (input instanceof Buffer) { - this.privateKey = input.subarray(0, 64) - this.chainCode = input.subarray(64, 96) - this.publicKey = toPublic(input).subarray(0, 32) + public constructor(input: Mnemonic | Buffer | Community) { + if (input instanceof Mnemonic) { + this.loadFromExtendedPrivateKey(generateFromSeed(input.seed)) + } else if (input instanceof Buffer) { + this.loadFromExtendedPrivateKey(input) } else if (input instanceof Community) { if (!input.rootPrivkey || !input.rootChaincode || !input.rootPubkey) { throw new LogError('missing private key or chaincode or public key in commmunity entity') } - this.privateKey = input.rootPrivkey - this.publicKey = input.rootPubkey - this.chainCode = input.rootChaincode + this._privateKey = input.rootPrivkey + this._publicKey = input.rootPubkey + this._chainCode = input.rootChaincode } } + /** + * copy keys to community entity + * @param community + */ + public fillInCommunityKeys(community: Community) { + community.rootPubkey = this._publicKey + community.rootPrivkey = this._privateKey + community.rootChaincode = this._chainCode + } + + private loadFromExtendedPrivateKey(extendedPrivateKey: Buffer) { + if (extendedPrivateKey.length !== 96) { + throw new LogError('invalid extended private key') + } + this._privateKey = extendedPrivateKey.subarray(0, 64) + this._chainCode = extendedPrivateKey.subarray(64, 96) + this._publicKey = toPublic(extendedPrivateKey).subarray(0, 32) + } + public getExtendPrivateKey(): Buffer { - return Buffer.concat([this.privateKey, this.chainCode]) + return Buffer.concat([this._privateKey, this._chainCode]) } public getExtendPublicKey(): Buffer { - return Buffer.concat([this.publicKey, this.chainCode]) + return Buffer.concat([this._publicKey, this._chainCode]) } - publicKey: Buffer - chainCode: Buffer - privateKey: Buffer + public get publicKey(): Buffer { + return this._publicKey + } + + public derive(path: number[]): KeyPair { + const extendedPrivateKey = this.getExtendPrivateKey() + return new KeyPair( + path.reduce( + (extendPrivateKey: Buffer, node: number) => derivePrivate(extendPrivateKey, node), + extendedPrivateKey, + ), + ) + } + + public sign(message: Buffer): Buffer { + return sign(message, this.getExtendPrivateKey()) + } + + public verify(message: Buffer, signature: Buffer): boolean { + return verify(message, signature, this.getExtendPublicKey()) + } } diff --git a/dlt-connector/src/data/Mnemonic.ts b/dlt-connector/src/data/Mnemonic.ts new file mode 100644 index 000000000..8f15c1046 --- /dev/null +++ b/dlt-connector/src/data/Mnemonic.ts @@ -0,0 +1,25 @@ +// https://www.npmjs.com/package/bip39 +import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39' +// eslint-disable-next-line camelcase +import { randombytes_buf } from 'sodium-native' + +export class Mnemonic { + private _passphrase = '' + public constructor(seed?: Buffer | string) { + if (seed) { + this._passphrase = entropyToMnemonic(seed) + return + } + const entropy = Buffer.alloc(256) + randombytes_buf(entropy) + this._passphrase = entropyToMnemonic(entropy) + } + + public get passphrase(): string { + return this._passphrase + } + + public get seed(): Buffer { + return mnemonicToSeedSync(this._passphrase) + } +} diff --git a/dlt-connector/src/data/Transaction.builder.ts b/dlt-connector/src/data/Transaction.builder.ts index 23820bb4d..7cbcedac5 100644 --- a/dlt-connector/src/data/Transaction.builder.ts +++ b/dlt-connector/src/data/Transaction.builder.ts @@ -1,13 +1,15 @@ -import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction' -import { TransactionBody } from '@/data/proto/3_3/TransactionBody' -import { bodyBytesToTransactionBody, transactionBodyToBodyBytes } from '@/utils/typeConverter' -import { Transaction } from '@entity/Transaction' -import { AccountRepository } from './Account.repository' -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { CommunityRepository } from './Community.repository' -import { LogError } from '@/server/LogError' import { Account } from '@entity/Account' import { Community } from '@entity/Community' +import { Transaction } from '@entity/Transaction' + +import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction' +import { TransactionBody } from '@/data/proto/3_3/TransactionBody' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' +import { LogError } from '@/server/LogError' +import { bodyBytesToTransactionBody, transactionBodyToBodyBytes } from '@/utils/typeConverter' + +import { AccountRepository } from './Account.repository' +import { CommunityRepository } from './Community.repository' import { TransactionBodyBuilder } from './proto/TransactionBody.builder' export class TransactionBuilder { diff --git a/dlt-connector/src/data/Transaction.repository.ts b/dlt-connector/src/data/Transaction.repository.ts index 8a1eac02e..9c21476ad 100644 --- a/dlt-connector/src/data/Transaction.repository.ts +++ b/dlt-connector/src/data/Transaction.repository.ts @@ -1,7 +1,8 @@ -import { getDataSource } from '@/typeorm/DataSource' 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) diff --git a/dlt-connector/src/data/User.factory.ts b/dlt-connector/src/data/User.factory.ts index 1844cff64..a8c7f0e71 100644 --- a/dlt-connector/src/data/User.factory.ts +++ b/dlt-connector/src/data/User.factory.ts @@ -1,10 +1,12 @@ -import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' import { User } from '@entity/User' -import { UserLogic } from './User.logic' + +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 { + static create(userAccountDraft: UserAccountDraft, parentKeys: KeyPair): User { const user = User.create() user.createdAt = new Date(userAccountDraft.createdAt) user.gradidoID = userAccountDraft.user.uuid diff --git a/dlt-connector/src/data/User.logic.ts b/dlt-connector/src/data/User.logic.ts index 58b441561..0a906682d 100644 --- a/dlt-connector/src/data/User.logic.ts +++ b/dlt-connector/src/data/User.logic.ts @@ -1,9 +1,10 @@ import { User } from '@entity/User' -import { KeyPair } from './KeyPair' + import { LogError } from '@/server/LogError' -import { uuid4ToBuffer } from '@/utils/typeConverter' import { hardenDerivationIndex } from '@/utils/derivationHelper' -import { KeyManager } from '@/manager/KeyManager' +import { uuid4ToBuffer } from '@/utils/typeConverter' + +import { KeyPair } from './KeyPair' export class UserLogic { // eslint-disable-next-line no-useless-constructor @@ -15,7 +16,7 @@ export class UserLogic { * @returns */ - calculateKeyPair = (parentKeys?: KeyPair): KeyPair => { + calculateKeyPair = (parentKeys: KeyPair): KeyPair => { if (!this.user.gradidoID) { throw new LogError('missing GradidoID for user.', { id: this.user.id }) } @@ -27,7 +28,7 @@ export class UserLogic { parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE()) } // parts: [2206563009, 2629978174, 2324817329, 2405141782] - const keyPair = KeyManager.getInstance().derive(parts, parentKeys) + 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', diff --git a/dlt-connector/src/data/User.repository.ts b/dlt-connector/src/data/User.repository.ts index abd64ac46..6e5a66203 100644 --- a/dlt-connector/src/data/User.repository.ts +++ b/dlt-connector/src/data/User.repository.ts @@ -1,8 +1,9 @@ -import { UserIdentifier } from '@/graphql/input/UserIdentifier' -import { getDataSource } from '@/typeorm/DataSource' import { Account } from '@entity/Account' import { User } from '@entity/User' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' +import { getDataSource } from '@/typeorm/DataSource' + export const UserRepository = getDataSource() .getRepository(User) .extend({ diff --git a/dlt-connector/src/data/proto/3_3/CommunityRoot.ts b/dlt-connector/src/data/proto/3_3/CommunityRoot.ts index 442716c3b..c03460741 100644 --- a/dlt-connector/src/data/proto/3_3/CommunityRoot.ts +++ b/dlt-connector/src/data/proto/3_3/CommunityRoot.ts @@ -1,8 +1,8 @@ -import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' -import { Field, Message } from 'protobufjs' import { Community } from '@entity/Community' import { Transaction } from '@entity/Transaction' +import { Field, Message } from 'protobufjs' + +import { AbstractTransaction } from '../AbstractTransaction' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define @@ -30,11 +30,6 @@ export class CommunityRoot extends Message implements AbstractTra @Field.d(3, 'bytes') public aufPubkey: Buffer - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public validate(_level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars public fillTransactionRecipe(recipe: Transaction): void {} } diff --git a/dlt-connector/src/data/proto/3_3/ConfirmedTransaction.ts b/dlt-connector/src/data/proto/3_3/ConfirmedTransaction.ts index 9c6491128..d59b991e8 100644 --- a/dlt-connector/src/data/proto/3_3/ConfirmedTransaction.ts +++ b/dlt-connector/src/data/proto/3_3/ConfirmedTransaction.ts @@ -1,7 +1,9 @@ import { Field, Message } from 'protobufjs' + +import { base64ToBuffer } from '@/utils/typeConverter' + import { GradidoTransaction } from './GradidoTransaction' import { TimestampSeconds } from './TimestampSeconds' -import { base64ToBuffer } from '@/utils/typeConverter' /* id will be set by Node server diff --git a/dlt-connector/src/data/proto/3_3/GradidoCreation.test.ts b/dlt-connector/src/data/proto/3_3/GradidoCreation.test.ts index 8b3fd1b3f..06011838c 100644 --- a/dlt-connector/src/data/proto/3_3/GradidoCreation.test.ts +++ b/dlt-connector/src/data/proto/3_3/GradidoCreation.test.ts @@ -1,9 +1,11 @@ import 'reflect-metadata' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { GradidoCreation } from './GradidoCreation' -import { TransactionError } from '@/graphql/model/TransactionError' import { TransactionErrorType } from '@enum/TransactionErrorType' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { TransactionError } from '@/graphql/model/TransactionError' + +import { GradidoCreation } from './GradidoCreation' + describe('proto/3.3/GradidoCreation', () => { it('test with missing targetDate', () => { const transactionDraft = new TransactionDraft() diff --git a/dlt-connector/src/data/proto/3_3/GradidoCreation.ts b/dlt-connector/src/data/proto/3_3/GradidoCreation.ts index 31ac08e26..0fa08eff5 100644 --- a/dlt-connector/src/data/proto/3_3/GradidoCreation.ts +++ b/dlt-connector/src/data/proto/3_3/GradidoCreation.ts @@ -1,15 +1,16 @@ +import { Account } from '@entity/Account' +import { Transaction } from '@entity/Transaction' +import { Decimal } from 'decimal.js-light' import { Field, Message } from 'protobufjs' +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { TransactionError } from '@/graphql/model/TransactionError' + +import { AbstractTransaction } from '../AbstractTransaction' + import { TimestampSeconds } from './TimestampSeconds' import { TransferAmount } from './TransferAmount' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { TransactionError } from '@/graphql/model/TransactionError' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' -import { Transaction } from '@entity/Transaction' -import Decimal from 'decimal.js-light' -import { Account } from '@entity/Account' // need signature from group admin or // percent of group users another than the receiver @@ -36,17 +37,16 @@ export class GradidoCreation extends Message implements Abstrac } } + // recipient: TransferAmount contain + // - recipient public key + // - amount + // - communityId // only set if not the same as recipient community @Field.d(1, TransferAmount) public recipient: TransferAmount @Field.d(3, 'TimestampSeconds') public targetDate: TimestampSeconds - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public validate(level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - public fillTransactionRecipe(recipe: Transaction): void { recipe.amount = new Decimal(this.recipient.amount ?? 0) } diff --git a/dlt-connector/src/data/proto/3_3/GradidoDeferredTransfer.ts b/dlt-connector/src/data/proto/3_3/GradidoDeferredTransfer.ts index ebe407d0d..f48719b16 100644 --- a/dlt-connector/src/data/proto/3_3/GradidoDeferredTransfer.ts +++ b/dlt-connector/src/data/proto/3_3/GradidoDeferredTransfer.ts @@ -1,11 +1,11 @@ +import { Transaction } from '@entity/Transaction' +import Decimal from 'decimal.js-light' import { Field, Message } from 'protobufjs' +import { AbstractTransaction } from '../AbstractTransaction' + import { GradidoTransfer } from './GradidoTransfer' import { TimestampSeconds } from './TimestampSeconds' -import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' -import { Transaction } from '@entity/Transaction' -import Decimal from 'decimal.js-light' // transaction type for chargeable transactions // for transaction for people which haven't a account already @@ -36,11 +36,6 @@ export class GradidoDeferredTransfer // split for n recipient // max gradido per recipient? or per transaction with cool down? - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public validate(level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - public fillTransactionRecipe(recipe: Transaction): void { recipe.amount = new Decimal(this.transfer.sender.amount ?? 0) } diff --git a/dlt-connector/src/data/proto/3_3/GradidoTransaction.ts b/dlt-connector/src/data/proto/3_3/GradidoTransaction.ts index d4eea4836..4aaa3e25c 100644 --- a/dlt-connector/src/data/proto/3_3/GradidoTransaction.ts +++ b/dlt-connector/src/data/proto/3_3/GradidoTransaction.ts @@ -1,10 +1,11 @@ import { Field, Message } from 'protobufjs' -import { SignatureMap } from './SignatureMap' -import { TransactionBody } from './TransactionBody' -import { SignaturePair } from './SignaturePair' import { LogError } from '@/server/LogError' +import { SignatureMap } from './SignatureMap' +import { SignaturePair } from './SignaturePair' +import { TransactionBody } from './TransactionBody' + // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class GradidoTransaction extends Message { diff --git a/dlt-connector/src/data/proto/3_3/GradidoTransfer.ts b/dlt-connector/src/data/proto/3_3/GradidoTransfer.ts index 31cb25ae9..7e9da40bd 100644 --- a/dlt-connector/src/data/proto/3_3/GradidoTransfer.ts +++ b/dlt-connector/src/data/proto/3_3/GradidoTransfer.ts @@ -1,12 +1,13 @@ -import { Field, Message } from 'protobufjs' - -import { TransferAmount } from './TransferAmount' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' +import { Account } from '@entity/Account' import { Transaction } from '@entity/Transaction' import Decimal from 'decimal.js-light' -import { Account } from '@entity/Account' +import { Field, Message } from 'protobufjs' + +import { TransactionDraft } from '@/graphql/input/TransactionDraft' + +import { AbstractTransaction } from '../AbstractTransaction' + +import { TransferAmount } from './TransferAmount' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define @@ -31,17 +32,17 @@ export class GradidoTransfer extends Message implements Abstrac } } + // sender: TransferAmount contain + // - sender public key + // - amount + // - communityId // only set if not the same as sender and recipient community @Field.d(1, TransferAmount) public sender: TransferAmount + // the recipient public key @Field.d(2, 'bytes') public recipient: Buffer - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public validate(level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - public fillTransactionRecipe(recipe: Transaction): void { recipe.amount = new Decimal(this.sender?.amount ?? 0) } diff --git a/dlt-connector/src/data/proto/3_3/GroupFriendsUpdate.ts b/dlt-connector/src/data/proto/3_3/GroupFriendsUpdate.ts index 52d676ffe..b64e80a73 100644 --- a/dlt-connector/src/data/proto/3_3/GroupFriendsUpdate.ts +++ b/dlt-connector/src/data/proto/3_3/GroupFriendsUpdate.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' -import { Field, Message } from 'protobufjs' import { Transaction } from '@entity/Transaction' +import { Field, Message } from 'protobufjs' + +import { AbstractTransaction } from '../AbstractTransaction' // connect group together // only CrossGroupType CROSS (in TransactionBody) @@ -17,10 +17,6 @@ export class GroupFriendsUpdate extends Message implements A @Field.d(1, 'bool') public colorFusion: boolean - public validate(level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - public fillTransactionRecipe(recipe: Transaction): void { throw new Error('Method not implemented.') } diff --git a/dlt-connector/src/data/proto/3_3/RegisterAddress.ts b/dlt-connector/src/data/proto/3_3/RegisterAddress.ts index 494d077ed..87f09afbd 100644 --- a/dlt-connector/src/data/proto/3_3/RegisterAddress.ts +++ b/dlt-connector/src/data/proto/3_3/RegisterAddress.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { Transaction } from '@entity/Transaction' import { Field, Message } from 'protobufjs' import { AddressType } from '@/data/proto/3_3/enum/AddressType' + import { AbstractTransaction } from '../AbstractTransaction' -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' -import { Transaction } from '@entity/Transaction' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define @@ -25,9 +25,5 @@ export class RegisterAddress extends Message implements Abstrac @Field.d(5, 'uint32') public derivationIndex?: number - public validate(level: TransactionValidationLevel): boolean { - throw new Error('Method not implemented.') - } - public fillTransactionRecipe(_recipe: Transaction): void {} } diff --git a/dlt-connector/src/data/proto/3_3/TransactionBody.ts b/dlt-connector/src/data/proto/3_3/TransactionBody.ts index dceb03314..70199c03c 100644 --- a/dlt-connector/src/data/proto/3_3/TransactionBody.ts +++ b/dlt-connector/src/data/proto/3_3/TransactionBody.ts @@ -1,23 +1,24 @@ +import { Transaction } from '@entity/Transaction' import { Field, Message, OneOf } from 'protobufjs' -import { CrossGroupType } from './enum/CrossGroupType' +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { LogError } from '@/server/LogError' +import { timestampToDate } from '@/utils/typeConverter' -import { Timestamp } from './Timestamp' -import { GradidoTransfer } from './GradidoTransfer' +import { AbstractTransaction } from '../AbstractTransaction' +import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic' + +import { CommunityRoot } from './CommunityRoot' +import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const' +import { CrossGroupType } from './enum/CrossGroupType' +import { TransactionType } from './enum/TransactionType' import { GradidoCreation } from './GradidoCreation' import { GradidoDeferredTransfer } from './GradidoDeferredTransfer' +import { GradidoTransfer } from './GradidoTransfer' import { GroupFriendsUpdate } from './GroupFriendsUpdate' import { RegisterAddress } from './RegisterAddress' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic' -import { CommunityRoot } from './CommunityRoot' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionType } from '@/graphql/enum/TransactionType' -import { AbstractTransaction } from '../AbstractTransaction' -import { Transaction } from '@entity/Transaction' -import { timestampToDate } from '@/utils/typeConverter' -import { LogError } from '@/server/LogError' -import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const' +import { Timestamp } from './Timestamp' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define @@ -95,6 +96,14 @@ export class TransactionBody extends Message { else if (this.communityRoot) return TransactionType.COMMUNITY_ROOT } + // The `TransactionBody` class utilizes Protobuf's `OneOf` field structure which, according to Protobuf documentation + // (https://protobuf.dev/programming-guides/proto3/#oneof), allows only one field within the group to be set at a time. + // Therefore, accessing the `getTransactionDetails()` method returns the first initialized value among the defined fields, + // each of which should be of type AbstractTransaction. It's important to note that due to the nature of Protobuf's `OneOf`, + // only one type from the defined options can be set within the object obtained from Protobuf. + // + // If multiple fields are set in a single object, the method `getTransactionDetails()` will return the first defined value + // based on the order of checks. Developers should handle this behavior according to the expected Protobuf structure. public getTransactionDetails(): AbstractTransaction | undefined { if (this.transfer) return this.transfer if (this.creation) return this.creation diff --git a/dlt-connector/src/data/proto/3_3/enum/AddressType.ts b/dlt-connector/src/data/proto/3_3/enum/AddressType.ts index 49fcf0c4e..eace1e022 100644 --- a/dlt-connector/src/data/proto/3_3/enum/AddressType.ts +++ b/dlt-connector/src/data/proto/3_3/enum/AddressType.ts @@ -1,3 +1,8 @@ +/** + * Enum for protobuf + * used from RegisterAddress to determine account type + * master implementation: https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/register_address.proto + */ export enum AddressType { NONE = 0, // if no address was found COMMUNITY_HUMAN = 1, // creation account for human @@ -7,13 +12,3 @@ export enum AddressType { SUBACCOUNT = 5, // no creations allowed CRYPTO_ACCOUNT = 6, // user control his keys, no creations } - -export function getAddressTypeEnumValue(typeString: string): AddressType | undefined { - // Iterate through all enum values - for (const key in AddressType) { - if (AddressType[key] === typeString) { - return AddressType[key] as unknown as AddressType - } - } - return undefined // If the string is not found -} diff --git a/dlt-connector/src/data/proto/3_3/enum/CrossGroupType.ts b/dlt-connector/src/data/proto/3_3/enum/CrossGroupType.ts index 13e968509..fee592e57 100644 --- a/dlt-connector/src/data/proto/3_3/enum/CrossGroupType.ts +++ b/dlt-connector/src/data/proto/3_3/enum/CrossGroupType.ts @@ -1,7 +1,22 @@ +/** + * Enum for protobuf + * Determine Cross Group type of Transactions + * LOCAL: no cross group transactions, sender and recipient community are the same, only one transaction + * INBOUND: cross group transaction, Inbound part. On recipient community chain. Recipient side by Transfer Transactions + * OUTBOUND: cross group transaction, Outbound part. On sender community chain. Sender side by Transfer Transactions + * CROSS: for cross group transaction which haven't a direction like group friend update + * master implementation: https://github.com/gradido/gradido_protocol/blob/master/proto/gradido/transaction_body.proto + * + * Transaction Handling differ from database focused backend + * In Backend for each transfer transaction there are always two entries in db, + * on for sender user and one for recipient user despite storing basically the same data two times + * In Blockchain Implementation there only two transactions on cross group transactions, one for + * the sender community chain, one for the recipient community chain + * if the transaction stay in the community there is only one transaction + */ export enum CrossGroupType { LOCAL = 0, INBOUND = 1, OUTBOUND = 2, - // for cross group transaction which haven't a direction like group friend update - // CROSS = 3, + CROSS = 3, } diff --git a/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts b/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts new file mode 100644 index 000000000..c50f33bec --- /dev/null +++ b/dlt-connector/src/data/proto/3_3/enum/TransactionType.ts @@ -0,0 +1,13 @@ +/** + * 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_TRANSFER = 1, + GRADIDO_CREATION = 2, + GROUP_FRIENDS_UPDATE = 3, + REGISTER_ADDRESS = 4, + GRADIDO_DEFERRED_TRANSFER = 5, + COMMUNITY_ROOT = 6, +} diff --git a/dlt-connector/src/data/proto/AbstractTransaction.ts b/dlt-connector/src/data/proto/AbstractTransaction.ts index 82c50dcb0..ac089b096 100644 --- a/dlt-connector/src/data/proto/AbstractTransaction.ts +++ b/dlt-connector/src/data/proto/AbstractTransaction.ts @@ -1,9 +1,5 @@ -import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' import { Transaction } from '@entity/Transaction' export abstract class AbstractTransaction { - // validate if transaction is valid, maybe expensive because depending on level several transactions will be fetched from db - public abstract validate(level: TransactionValidationLevel): boolean - public abstract fillTransactionRecipe(recipe: Transaction): void } diff --git a/dlt-connector/src/data/proto/TransactionBody.builder.ts b/dlt-connector/src/data/proto/TransactionBody.builder.ts index b0e0a9f79..cf9479015 100644 --- a/dlt-connector/src/data/proto/TransactionBody.builder.ts +++ b/dlt-connector/src/data/proto/TransactionBody.builder.ts @@ -1,13 +1,15 @@ -import { TransactionBody } from './3_3/TransactionBody' import { Account } from '@entity/Account' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { Community } from '@entity/Community' + import { InputTransactionType } from '@/graphql/enum/InputTransactionType' +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { LogError } from '@/server/LogError' + +import { CommunityRoot } from './3_3/CommunityRoot' import { GradidoCreation } from './3_3/GradidoCreation' import { GradidoTransfer } from './3_3/GradidoTransfer' -import { Community } from '@entity/Community' -import { CommunityRoot } from './3_3/CommunityRoot' -import { LogError } from '@/server/LogError' +import { TransactionBody } from './3_3/TransactionBody' export class TransactionBodyBuilder { private signingAccount?: Account diff --git a/dlt-connector/src/data/proto/transactionBody.logic.ts b/dlt-connector/src/data/proto/transactionBody.logic.ts index 872172aac..99dbc2ed6 100644 --- a/dlt-connector/src/data/proto/transactionBody.logic.ts +++ b/dlt-connector/src/data/proto/transactionBody.logic.ts @@ -1,8 +1,9 @@ -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { CrossGroupType } from './3_3/enum/CrossGroupType' import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { TransactionError } from '@/graphql/model/TransactionError' import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { TransactionError } from '@/graphql/model/TransactionError' + +import { CrossGroupType } from './3_3/enum/CrossGroupType' export const determineCrossGroupType = ({ senderUser, @@ -50,5 +51,7 @@ export const determineOtherGroup = ( ) } return senderUser.communityUuid + case CrossGroupType.CROSS: + throw new TransactionError(TransactionErrorType.NOT_IMPLEMENTED_YET, 'not implemented yet') } } diff --git a/dlt-connector/src/graphql/enum/AccountType.ts b/dlt-connector/src/graphql/enum/AccountType.ts index a6946275b..810c89044 100644 --- a/dlt-connector/src/graphql/enum/AccountType.ts +++ b/dlt-connector/src/graphql/enum/AccountType.ts @@ -1,7 +1,12 @@ import { registerEnumType } from 'type-graphql' +/** + * enum for graphql + * describe input account type in UserAccountDraft + * should have the same entries like enum AddressType from proto/enum folder + */ export enum AccountType { - NONE = 'none', // if no address was found + NONE = 'NONE', // if no address was found COMMUNITY_HUMAN = 'COMMUNITY_HUMAN', // creation account for human COMMUNITY_GMW = 'COMMUNITY_GMW', // community public budget account COMMUNITY_AUF = 'COMMUNITY_AUF', // community compensation and environment founds account diff --git a/dlt-connector/src/graphql/enum/InputTransactionType.ts b/dlt-connector/src/graphql/enum/InputTransactionType.ts index f35a26658..41eeac6cb 100755 --- a/dlt-connector/src/graphql/enum/InputTransactionType.ts +++ b/dlt-connector/src/graphql/enum/InputTransactionType.ts @@ -1,6 +1,7 @@ -import { LogError } from '@/server/LogError' import { registerEnumType } from 'type-graphql' +// enum for graphql but with int because it is the same in backend +// for transaction type from backend export enum InputTransactionType { CREATION = 1, SEND = 2, @@ -11,14 +12,3 @@ registerEnumType(InputTransactionType, { name: 'InputTransactionType', // this one is mandatory description: 'Type of the transaction', // this one is optional }) - -// from ChatGPT -export function getTransactionTypeString(id: InputTransactionType): string { - const key = Object.keys(InputTransactionType).find( - (key) => InputTransactionType[key as keyof typeof InputTransactionType] === id, - ) - if (key === undefined) { - throw new LogError('invalid transaction type id: ' + id.toString()) - } - return key -} diff --git a/dlt-connector/src/graphql/enum/TransactionErrorType.ts b/dlt-connector/src/graphql/enum/TransactionErrorType.ts index 8238eae08..1b01bc0da 100644 --- a/dlt-connector/src/graphql/enum/TransactionErrorType.ts +++ b/dlt-connector/src/graphql/enum/TransactionErrorType.ts @@ -1,5 +1,7 @@ import { registerEnumType } from 'type-graphql' +// enum for graphql +// error groups for resolver answers export enum TransactionErrorType { NOT_IMPLEMENTED_YET = 'Not Implemented yet', MISSING_PARAMETER = 'Missing parameter', diff --git a/dlt-connector/src/graphql/enum/TransactionType.ts b/dlt-connector/src/graphql/enum/TransactionType.ts deleted file mode 100644 index 3647b9ca8..000000000 --- a/dlt-connector/src/graphql/enum/TransactionType.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { registerEnumType } from 'type-graphql' - -export enum TransactionType { - GRADIDO_TRANSFER = 1, - GRADIDO_CREATION = 2, - GROUP_FRIENDS_UPDATE = 3, - REGISTER_ADDRESS = 4, - GRADIDO_DEFERRED_TRANSFER = 5, - COMMUNITY_ROOT = 6, -} - -registerEnumType(TransactionType, { - name: 'TransactionType', // this one is mandatory - description: 'Type of the transaction', // this one is optional -}) diff --git a/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts b/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts deleted file mode 100644 index 9462dd8a8..000000000 --- a/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { registerEnumType } from 'type-graphql' - -export enum TransactionValidationLevel { - SINGLE = 1, // check only the transaction - SINGLE_PREVIOUS = 2, // check also with previous transaction - DATE_RANGE = 3, // check all transaction from within date range by creation automatic the same month - PAIRED = 4, // check paired transaction on another group by cross group transactions - CONNECTED_GROUP = 5, // check all transactions in the group which connected with this transaction address(es) - CONNECTED_BLOCKCHAIN = 6, // check all transactions which connected with this transaction -} - -registerEnumType(TransactionValidationLevel, { - name: 'TransactionValidationLevel', - description: 'Transaction Validation Levels', -}) diff --git a/dlt-connector/src/graphql/input/CommunityDraft.ts b/dlt-connector/src/graphql/input/CommunityDraft.ts index f028ea06c..665e10b75 100644 --- a/dlt-connector/src/graphql/input/CommunityDraft.ts +++ b/dlt-connector/src/graphql/input/CommunityDraft.ts @@ -2,6 +2,7 @@ import { IsBoolean, IsUUID } from 'class-validator' import { Field, InputType } from 'type-graphql' + import { isValidDateString } from '@validator/DateString' @InputType() diff --git a/dlt-connector/src/graphql/input/TransactionDraft.ts b/dlt-connector/src/graphql/input/TransactionDraft.ts index 3cd497eec..7c43e9554 100755 --- a/dlt-connector/src/graphql/input/TransactionDraft.ts +++ b/dlt-connector/src/graphql/input/TransactionDraft.ts @@ -1,12 +1,13 @@ // https://www.npmjs.com/package/@apollo/protobufjs - +import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator' import { Decimal } from 'decimal.js-light' -import { InputTransactionType } from '@enum/InputTransactionType' import { InputType, Field, Int } from 'type-graphql' -import { UserIdentifier } from './UserIdentifier' + +import { InputTransactionType } from '@enum/InputTransactionType' import { isValidDateString } from '@validator/DateString' import { IsPositiveDecimal } from '@validator/Decimal' -import { IsEnum, IsObject, IsPositive, ValidateNested } from 'class-validator' + +import { UserIdentifier } from './UserIdentifier' @InputType() export class TransactionDraft { diff --git a/dlt-connector/src/graphql/input/UserAccountDraft.ts b/dlt-connector/src/graphql/input/UserAccountDraft.ts index bae9c0694..9ae544e32 100644 --- a/dlt-connector/src/graphql/input/UserAccountDraft.ts +++ b/dlt-connector/src/graphql/input/UserAccountDraft.ts @@ -1,11 +1,14 @@ // https://www.npmjs.com/package/@apollo/protobufjs -import { InputType, Field } from 'type-graphql' -import { UserIdentifier } from './UserIdentifier' -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' + @InputType() export class UserAccountDraft { @Field(() => UserIdentifier) diff --git a/dlt-connector/src/graphql/model/Community.ts b/dlt-connector/src/graphql/model/Community.ts index 214984f15..7a69288dc 100644 --- a/dlt-connector/src/graphql/model/Community.ts +++ b/dlt-connector/src/graphql/model/Community.ts @@ -1,5 +1,5 @@ -import { ObjectType, Field, Int } from 'type-graphql' import { Community as CommunityEntity } from '@entity/Community' +import { ObjectType, Field, Int } from 'type-graphql' @ObjectType() export class Community { diff --git a/dlt-connector/src/graphql/model/TransactionError.ts b/dlt-connector/src/graphql/model/TransactionError.ts index 891ad2a89..eee54e19c 100644 --- a/dlt-connector/src/graphql/model/TransactionError.ts +++ b/dlt-connector/src/graphql/model/TransactionError.ts @@ -1,4 +1,5 @@ import { ObjectType, Field } from 'type-graphql' + import { TransactionErrorType } from '../enum/TransactionErrorType' @ObjectType() diff --git a/dlt-connector/src/graphql/model/TransactionRecipe.ts b/dlt-connector/src/graphql/model/TransactionRecipe.ts index a4dacd9e9..263ccce4a 100644 --- a/dlt-connector/src/graphql/model/TransactionRecipe.ts +++ b/dlt-connector/src/graphql/model/TransactionRecipe.ts @@ -1,13 +1,20 @@ -import { Field, Int, ObjectType } from 'type-graphql' -import { TransactionType } from '@enum/TransactionType' import { Transaction } from '@entity/Transaction' +import { Field, Int, ObjectType } from 'type-graphql' + +import { TransactionType } from '@/data/proto/3_3/enum/TransactionType' +import { LogError } from '@/server/LogError' +import { getEnumValue } from '@/utils/typeConverter' @ObjectType() export class TransactionRecipe { public constructor({ id, createdAt, type, community }: Transaction) { + const transactionType = getEnumValue(TransactionType, type) + if (!transactionType) { + throw new LogError('invalid transaction, type is missing') + } this.id = id this.createdAt = createdAt.toString() - this.type = type + this.type = transactionType.toString() this.topic = community.iotaTopic } @@ -17,8 +24,8 @@ export class TransactionRecipe { @Field(() => String) createdAt: string - @Field(() => TransactionType) - type: TransactionType + @Field(() => String) + type: string @Field(() => String) topic: string diff --git a/dlt-connector/src/graphql/model/TransactionResult.ts b/dlt-connector/src/graphql/model/TransactionResult.ts index bc443ad1f..370c9827d 100644 --- a/dlt-connector/src/graphql/model/TransactionResult.ts +++ b/dlt-connector/src/graphql/model/TransactionResult.ts @@ -1,4 +1,5 @@ import { ObjectType, Field } from 'type-graphql' + import { TransactionError } from './TransactionError' import { TransactionRecipe } from './TransactionRecipe' diff --git a/dlt-connector/src/graphql/resolver/CommunityResolver.test.ts b/dlt-connector/src/graphql/resolver/CommunityResolver.test.ts index 2c402c537..0fa61cc48 100644 --- a/dlt-connector/src/graphql/resolver/CommunityResolver.test.ts +++ b/dlt-connector/src/graphql/resolver/CommunityResolver.test.ts @@ -1,10 +1,14 @@ import 'reflect-metadata' -import { ApolloServer } from '@apollo/server' -// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories -import { TestDB } from '@test/TestDB' -import { createApolloTestServer } from '@test/ApolloServerMock' import assert from 'assert' + +import { ApolloServer } from '@apollo/server' + +// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories +// eslint-disable-next-line import/order +import { TestDB } from '@test/TestDB' import { TransactionResult } from '@model/TransactionResult' +import { createApolloTestServer } from '@test/ApolloServerMock' + import { CONFIG } from '@/config' CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899' diff --git a/dlt-connector/src/graphql/resolver/CommunityResolver.ts b/dlt-connector/src/graphql/resolver/CommunityResolver.ts index 9b6d8f3f2..d4bbeb28e 100644 --- a/dlt-connector/src/graphql/resolver/CommunityResolver.ts +++ b/dlt-connector/src/graphql/resolver/CommunityResolver.ts @@ -1,17 +1,17 @@ import { Resolver, Query, Arg, Mutation, Args } from 'type-graphql' -import { CommunityDraft } from '@input/CommunityDraft' - -import { TransactionResult } from '@model/TransactionResult' -import { TransactionError } from '@model/TransactionError' -import { TransactionErrorType } from '@enum/TransactionErrorType' -import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' -import { Community } from '@model/Community' import { CommunityArg } from '@arg/CommunityArg' -import { LogError } from '@/server/LogError' -import { logger } from '@/server/logger' +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 { CommunityRepository } from '@/data/Community.repository' import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context' +import { LogError } from '@/server/LogError' +import { logger } from '@/server/logger' +import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' @Resolver() export class CommunityResolver { diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts index 71044805c..b5d5ee126 100644 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts @@ -1,22 +1,28 @@ import 'reflect-metadata' -import { ApolloServer } from '@apollo/server' -// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories -import { TestDB } from '@test/TestDB' -import { createApolloTestServer } from '@test/ApolloServerMock' import assert from 'assert' -import { TransactionResult } from '@model/TransactionResult' -import { AccountFactory } from '@/data/Account.factory' -import { CONFIG } from '@/config' -import { UserFactory } from '@/data/User.factory' -import { UserAccountDraft } from '../input/UserAccountDraft' -import { UserLogic } from '@/data/User.logic' -import { AccountType } from '@enum/AccountType' -import { UserIdentifier } from '../input/UserIdentifier' -import { CommunityDraft } from '../input/CommunityDraft' -import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context' -import { InputTransactionType, getTransactionTypeString } from '../enum/InputTransactionType' -CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899' +import { ApolloServer } from '@apollo/server' + +// must be imported before createApolloTestServer so that TestDB was created before createApolloTestServer imports repositories +// eslint-disable-next-line import/order +import { TestDB } from '@test/TestDB' +import { AccountType } from '@enum/AccountType' +import { TransactionResult } from '@model/TransactionResult' +import { createApolloTestServer } from '@test/ApolloServerMock' + +import { CONFIG } from '@/config' +import { AccountFactory } from '@/data/Account.factory' +import { KeyPair } from '@/data/KeyPair' +import { Mnemonic } from '@/data/Mnemonic' +import { UserFactory } from '@/data/User.factory' +import { UserLogic } from '@/data/User.logic' +import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context' +import { getEnumValue } from '@/utils/typeConverter' + +import { InputTransactionType } from '../enum/InputTransactionType' +import { CommunityDraft } from '../input/CommunityDraft' +import { UserAccountDraft } from '../input/UserAccountDraft' +import { UserIdentifier } from '../input/UserIdentifier' let apolloTestServer: ApolloServer @@ -32,7 +38,9 @@ jest.mock('@typeorm/DataSource', () => ({ getDataSource: jest.fn(() => TestDB.instance.dbConnect), })) +CONFIG.IOTA_HOME_COMMUNITY_SEED = 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899' const communityUUID = '3d813cbb-37fb-42ba-91df-831e1593ac29' +const communityKeyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED)) const createUserStoreAccount = async (uuid: string): Promise => { const userAccountDraft = new UserAccountDraft() @@ -41,11 +49,11 @@ const createUserStoreAccount = async (uuid: string): Promise => userAccountDraft.user = new UserIdentifier() userAccountDraft.user.uuid = uuid userAccountDraft.user.communityUuid = communityUUID - const user = UserFactory.create(userAccountDraft) + const user = UserFactory.create(userAccountDraft, communityKeyPair) const userLogic = new UserLogic(user) const account = AccountFactory.createAccountFromUserAccountDraft( userAccountDraft, - userLogic.calculateKeyPair(), + userLogic.calculateKeyPair(communityKeyPair), ) account.user = user // user is set to cascade: ['insert'] will be saved together with account @@ -82,7 +90,7 @@ describe('Transaction Resolver Test', () => { input: { senderUser, recipientUser, - type: getTransactionTypeString(InputTransactionType.SEND), + type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: '10', createdAt: '2012-04-17T17:12:00Z', backendTransactionId: 1, @@ -130,7 +138,7 @@ describe('Transaction Resolver Test', () => { input: { senderUser, recipientUser, - type: getTransactionTypeString(InputTransactionType.SEND), + type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: 'no number', createdAt: '2012-04-17T17:12:00Z', backendTransactionId: 1, @@ -156,7 +164,7 @@ describe('Transaction Resolver Test', () => { input: { senderUser, recipientUser, - type: getTransactionTypeString(InputTransactionType.SEND), + type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: '10', createdAt: 'not valid', backendTransactionId: 1, @@ -192,7 +200,7 @@ describe('Transaction Resolver Test', () => { input: { senderUser, recipientUser, - type: getTransactionTypeString(InputTransactionType.CREATION), + type: getEnumValue(InputTransactionType, InputTransactionType.CREATION), amount: '10', createdAt: '2012-04-17T17:12:00Z', backendTransactionId: 1, diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts index eb1cb769b..4f3f3efe2 100755 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts @@ -1,11 +1,14 @@ import { Resolver, Arg, Mutation } from 'type-graphql' + import { TransactionDraft } from '@input/TransactionDraft' -import { TransactionResult } from '../model/TransactionResult' -import { TransactionError } from '../model/TransactionError' -import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransationRecipe.context' -import { TransactionRecipe } from '../model/TransactionRecipe' + import { TransactionRepository } from '@/data/Transaction.repository' +import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransationRecipe.context' + import { TransactionErrorType } from '../enum/TransactionErrorType' +import { TransactionError } from '../model/TransactionError' +import { TransactionRecipe } from '../model/TransactionRecipe' +import { TransactionResult } from '../model/TransactionResult' @Resolver() export class TransactionResolver { diff --git a/dlt-connector/src/graphql/schema.ts b/dlt-connector/src/graphql/schema.ts index ac3119d1a..19a6d5566 100755 --- a/dlt-connector/src/graphql/schema.ts +++ b/dlt-connector/src/graphql/schema.ts @@ -2,9 +2,9 @@ import { Decimal } from 'decimal.js-light' import { GraphQLSchema } from 'graphql' import { buildSchema } from 'type-graphql' -import { DecimalScalar } from './scalar/Decimal' -import { TransactionResolver } from './resolver/TransactionsResolver' import { CommunityResolver } from './resolver/CommunityResolver' +import { TransactionResolver } from './resolver/TransactionsResolver' +import { DecimalScalar } from './scalar/Decimal' export const schema = async (): Promise => { return buildSchema({ diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index 7e1ea1adc..c72978b35 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { CONFIG } from '@/config' + import createServer from './server/createServer' -import { KeyManager } from './manager/KeyManager' async function main() { // eslint-disable-next-line no-console console.log(`DLT_CONNECTOR_PORT=${CONFIG.DLT_CONNECTOR_PORT}`) const { app } = await createServer() - await KeyManager.getInstance().init() app.listen(CONFIG.DLT_CONNECTOR_PORT, () => { // eslint-disable-next-line no-console console.log(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`) diff --git a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts index ff46de4d3..bc8f90c32 100644 --- a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts +++ b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts @@ -1,8 +1,9 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' + +import { CommunityRole } from './Community.role' import { ForeignCommunityRole } from './ForeignCommunity.role' import { HomeCommunityRole } from './HomeCommunity.role' -import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' -import { CommunityRole } from './Community.role' /** * @DCI-Context diff --git a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts b/dlt-connector/src/interactions/backendToDb/community/Community.role.ts index f08dc25a0..30d91bfed 100644 --- a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/Community.role.ts @@ -1,8 +1,9 @@ +import { Community } from '@entity/Community' + import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { TransactionError } from '@/graphql/model/TransactionError' import { logger } from '@/server/logger' -import { Community } from '@entity/Community' export abstract class CommunityRole { protected self: Community diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index 57155d8ff..0b134b97c 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -1,27 +1,28 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { Community } from '@entity/Community' -import { CommunityRole } from './Community.role' import { Transaction } from '@entity/Transaction' -import { KeyManager } from '@/manager/KeyManager' + +import { CONFIG } from '@/config' import { AccountFactory } from '@/data/Account.factory' -import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' -import { logger } from '@/server/logger' -import { TransactionError } from '@/graphql/model/TransactionError' +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 { logger } from '@/server/logger' import { getDataSource } from '@/typeorm/DataSource' +import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' + +import { CommunityRole } from './Community.role' + 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 - const keyPair = KeyManager.generateKeyPair() - this.self.rootPubkey = keyPair.publicKey - this.self.rootPrivkey = keyPair.privateKey - this.self.rootChaincode = keyPair.chainCode - // we should only have one home community per server - KeyManager.getInstance().setHomeCommunityKeyPair(keyPair) + const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined)) + keyPair.fillInCommunityKeys(this.self) // create auf account and gmw account this.self.aufAccount = AccountFactory.createAufAccount(keyPair, this.self.createdAt) diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts index 240c86dc4..75b885f2f 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts @@ -1,9 +1,10 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionRecipeRole } from './TransactionRecipe.role' import { Community } from '@entity/Community' -import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' + import { KeyPair } from '@/data/KeyPair' -import { sign } from '@/utils/cryptoHelper' +import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' +import { CommunityDraft } from '@/graphql/input/CommunityDraft' + +import { TransactionRecipeRole } from './TransactionRecipe.role' export class CommunityRootTransactionRole extends TransactionRecipeRole { public createFromCommunityRoot( @@ -18,7 +19,7 @@ export class CommunityRootTransactionRole extends TransactionRecipeRole { this.transactionBuilder.fromTransactionBody(transactionBody).setCommunity(community) const transaction = this.transactionBuilder.getTransaction() // sign - this.transactionBuilder.setSignature(sign(transaction.bodyBytes, new KeyPair(community))) + this.transactionBuilder.setSignature(new KeyPair(community).sign(transaction.bodyBytes)) return this } } diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts index a5da13dcd..078ec8e5c 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts @@ -1,11 +1,13 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { Community } from '@entity/Community' -import { TransactionRecipeRole } from './TransactionRecipe.role' -import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { Transaction } from '@entity/Transaction' -import { TransactionError } from '@/graphql/model/TransactionError' + import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { TransactionError } from '@/graphql/model/TransactionError' + +import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' +import { TransactionRecipeRole } from './TransactionRecipe.role' /** * @DCI-Context diff --git a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts index 6b59e5eaa..362d114ef 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts @@ -1,12 +1,12 @@ +import { Transaction } from '@entity/Transaction' + import { KeyPair } from '@/data/KeyPair' +import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' import { TransactionBuilder } from '@/data/Transaction.builder' import { UserRepository } from '@/data/User.repository' -import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { TransactionError } from '@/graphql/model/TransactionError' -import { sign } from '@/utils/cryptoHelper' -import { Transaction } from '@entity/Transaction' export class TransactionRecipeRole { protected transactionBuilder: TransactionBuilder @@ -52,7 +52,7 @@ export class TransactionRecipeRole { const transaction = this.transactionBuilder.getTransaction() // sign this.transactionBuilder.setSignature( - sign(transaction.bodyBytes, new KeyPair(this.transactionBuilder.getCommunity())), + new KeyPair(this.transactionBuilder.getCommunity()).sign(transaction.bodyBytes), ) return this } diff --git a/dlt-connector/src/manager/KeyManager.test.ts b/dlt-connector/src/manager/KeyManager.test.ts deleted file mode 100644 index 7f3d26932..000000000 --- a/dlt-connector/src/manager/KeyManager.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* eslint-disable camelcase */ -import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39' -import { generateFromSeed, toPublic } from 'bip32-ed25519' - -describe('controller/KeyManager', () => { - describe('test crypto lib', () => { - it('key length', () => { - const mnemonic = entropyToMnemonic( - 'aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899', - ) - expect(mnemonic).toEqual( - 'primary taxi danger target useless ancient match hammer fever crisp timber crew produce toy jeans that abandon math mimic master filter design carbon carbon', - ) - const seed = mnemonicToSeedSync(mnemonic) - // private key 64 Bytes + 32 Byte ChainCode - const extendPrivkey = generateFromSeed(seed) - expect(extendPrivkey).toHaveLength(96) - // public key 32 Bytes + 32 Bytes ChainCode - expect(toPublic(extendPrivkey)).toHaveLength(64) - }) - }) -}) diff --git a/dlt-connector/src/manager/KeyManager.ts b/dlt-connector/src/manager/KeyManager.ts deleted file mode 100644 index 2e8b615aa..000000000 --- a/dlt-connector/src/manager/KeyManager.ts +++ /dev/null @@ -1,118 +0,0 @@ -// eslint-disable-next-line camelcase -import { randombytes_buf } from 'sodium-native' -import { CONFIG } from '../config' -import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39' -// https://www.npmjs.com/package/bip32-ed25519?activeTab=code -import { generateFromSeed, derivePrivate, sign as ed25519Sign } from 'bip32-ed25519' -import { logger } from '@/server/logger' -import { LogError } from '@/server/LogError' -import { KeyPair } from '@/data/KeyPair' -import { GradidoTransaction } from '@/data/proto/3_3/GradidoTransaction' -import { CommunityRepository } from '@/data/Community.repository' -import { SignaturePair } from '@/data/proto/3_3/SignaturePair' - -// 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 KeyManager { - // eslint-disable-next-line no-use-before-define - private static instance: KeyManager - private homeCommunityRootKeys: KeyPair | null = null - - /** - * 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(): KeyManager { - if (!KeyManager.instance) { - KeyManager.instance = new KeyManager() - } - return KeyManager.instance - } - - public async init(): Promise { - try { - this.homeCommunityRootKeys = await CommunityRepository.loadHomeCommunityKeyPair() - return true - } catch (error) { - logger.error('error by init key manager', error) - return false - } - } - - public static generateKeyPair(): KeyPair { - const mnemonic = KeyManager.generateMnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined) - // logger.info('passphrase for key pair: ' + mnemonic) - const seed = mnemonicToSeedSync(mnemonic) - return new KeyPair(generateFromSeed(seed)) - } - - public setHomeCommunityKeyPair(keyPair: KeyPair) { - this.homeCommunityRootKeys = keyPair - } - - public sign(transaction: GradidoTransaction, keys?: KeyPair[]) { - let localKeys: KeyPair[] = [] - - if (!keys && this.homeCommunityRootKeys) { - localKeys.push(this.homeCommunityRootKeys) - } else if (keys) { - localKeys = keys - } - if (!localKeys.length) { - throw new LogError('no key pair for signing') - } - localKeys.forEach((keyPair: KeyPair) => { - const signature = ed25519Sign(transaction.bodyBytes, keyPair.getExtendPrivateKey()) - const sigPair = new SignaturePair({ pubKey: keyPair.publicKey, signature }) - logger.debug('sign transaction', { - signature: signature.toString('hex'), - publicKey: keyPair.publicKey.toString('hex'), - bodyBytes: transaction.bodyBytes.toString('hex'), - }) - transaction.sigMap.sigPair.push(sigPair) - }) - } - - public getHomeCommunityPublicKey(): Buffer | undefined { - if (!this.homeCommunityRootKeys) return undefined - return this.homeCommunityRootKeys.publicKey - } - - public derive(path: number[], parentKeys?: KeyPair): KeyPair { - const extendedPrivateKey = parentKeys - ? parentKeys.getExtendPrivateKey() - : this.homeCommunityRootKeys?.getExtendPrivateKey() - if (!extendedPrivateKey) { - throw new LogError('missing parent or root key pair') - } - return new KeyPair( - path.reduce( - (extendPrivateKey: Buffer, node: number) => derivePrivate(extendPrivateKey, node), - extendedPrivateKey, - ), - ) - } - - static generateMnemonic(seed?: Buffer | string): string { - if (seed) { - return entropyToMnemonic(seed) - } - const entropy = Buffer.alloc(256) - randombytes_buf(entropy) - return entropyToMnemonic(entropy) - } -} diff --git a/dlt-connector/src/server/createServer.ts b/dlt-connector/src/server/createServer.ts index f3a3b1de7..e02cc3073 100755 --- a/dlt-connector/src/server/createServer.ts +++ b/dlt-connector/src/server/createServer.ts @@ -2,16 +2,16 @@ import 'reflect-metadata' import { ApolloServer } from '@apollo/server' import { expressMiddleware } from '@apollo/server/express4' +import bodyParser from 'body-parser' +import cors from 'cors' import express, { Express } from 'express' - // graphql +import { Logger } from 'log4js' + import { schema } from '@/graphql/schema' +import { Connection } from '@/typeorm/DataSource' import { logger as dltLogger } from './logger' -import { Logger } from 'log4js' -import cors from 'cors' -import bodyParser from 'body-parser' -import { Connection } from '@/typeorm/DataSource' type ServerDef = { apollo: ApolloServer; app: Express } diff --git a/dlt-connector/src/server/logger.ts b/dlt-connector/src/server/logger.ts index 89757f656..bec2ec578 100644 --- a/dlt-connector/src/server/logger.ts +++ b/dlt-connector/src/server/logger.ts @@ -1,7 +1,9 @@ +import { readFileSync } from 'fs' + import log4js from 'log4js' + import { CONFIG } from '@/config' -import { readFileSync } from 'fs' const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8')) log4js.configure(options) diff --git a/dlt-connector/src/typeorm/DataSource.ts b/dlt-connector/src/typeorm/DataSource.ts index 441cf7eef..18be65300 100644 --- a/dlt-connector/src/typeorm/DataSource.ts +++ b/dlt-connector/src/typeorm/DataSource.ts @@ -2,11 +2,11 @@ // We cannot use our connection here, but must use the external typeorm installation import { DataSource as DBDataSource, FileLogger } from '@dbTools/typeorm' import { entities } from '@entity/index' +import { Migration } from '@entity/Migration' import { CONFIG } from '@/config' -import { logger } from '@/server/logger' -import { Migration } from '@entity/Migration' import { LogError } from '@/server/LogError' +import { logger } from '@/server/logger' // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class Connection { diff --git a/dlt-connector/src/utils/cryptoHelper.ts b/dlt-connector/src/utils/cryptoHelper.ts deleted file mode 100644 index 467a85e85..000000000 --- a/dlt-connector/src/utils/cryptoHelper.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { KeyPair } from '@/data/KeyPair' -import { sign as ed25519Sign, generateFromSeed } from 'bip32-ed25519' -import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39' -// eslint-disable-next-line camelcase -import { randombytes_buf } from 'sodium-native' - -export const sign = (message: Buffer, keyPair: KeyPair): Buffer => { - return ed25519Sign(message, keyPair.getExtendPrivateKey()) -} - -export const generateKeyPair = (mnemonic: string): KeyPair => { - const seedFromMnemonic = mnemonicToSeedSync(mnemonic) - return new KeyPair(generateFromSeed(seedFromMnemonic)) -} - -export const generateMnemonic = (seed?: Buffer | string): string => { - if (seed) { - return entropyToMnemonic(seed) - } - const entropy = Buffer.alloc(256) - randombytes_buf(entropy) - return entropyToMnemonic(entropy) -} diff --git a/dlt-connector/src/utils/derivationHelper.test.ts b/dlt-connector/src/utils/derivationHelper.test.ts index 0ad381aa8..f14b99cdf 100644 --- a/dlt-connector/src/utils/derivationHelper.test.ts +++ b/dlt-connector/src/utils/derivationHelper.test.ts @@ -1,5 +1,6 @@ import 'reflect-metadata' import { Timestamp } from '../data/proto/3_3/Timestamp' + import { hardenDerivationIndex, HARDENED_KEY_BITMASK } from './derivationHelper' import { timestampToDate } from './typeConverter' diff --git a/dlt-connector/src/utils/typeConverter.test.ts b/dlt-connector/src/utils/typeConverter.test.ts index 03863d0f2..d9b1c2356 100644 --- a/dlt-connector/src/utils/typeConverter.test.ts +++ b/dlt-connector/src/utils/typeConverter.test.ts @@ -1,5 +1,6 @@ import 'reflect-metadata' import { Timestamp } from '@/data/proto/3_3/Timestamp' + import { timestampToDate } from './typeConverter' describe('utils/typeConverter', () => { diff --git a/dlt-connector/src/utils/typeConverter.ts b/dlt-connector/src/utils/typeConverter.ts index ecf090c45..1fc46ee4b 100644 --- a/dlt-connector/src/utils/typeConverter.ts +++ b/dlt-connector/src/utils/typeConverter.ts @@ -1,14 +1,14 @@ import { crypto_generichash as cryptoHash } from 'sodium-native' +import { AddressType } from '@/data/proto/3_3/enum/AddressType' import { Timestamp } from '@/data/proto/3_3/Timestamp' import { TimestampSeconds } from '@/data/proto/3_3/TimestampSeconds' import { TransactionBody } from '@/data/proto/3_3/TransactionBody' -import { logger } from '@/server/logger' -import { TransactionError } from '@/graphql/model/TransactionError' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { AccountType } from '@/graphql/enum/AccountType' +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { TransactionError } from '@/graphql/model/TransactionError' import { LogError } from '@/server/LogError' -import { AddressType } from '@/data/proto/3_3/enum/AddressType' +import { logger } from '@/server/logger' export const uuid4ToBuffer = (uuid: string): Buffer => { // Remove dashes from the UUIDv4 string @@ -64,44 +64,44 @@ export const transactionBodyToBodyBytes = (transactionBody: TransactionBody): Bu } } -export const accountTypeToAddressType = (accountType: AccountType): AddressType => { - switch (accountType) { - case AccountType.NONE: - return AddressType.NONE - case AccountType.COMMUNITY_HUMAN: - return AddressType.COMMUNITY_HUMAN - case AccountType.COMMUNITY_GMW: - return AddressType.COMMUNITY_GMW - case AccountType.COMMUNITY_AUF: - return AddressType.COMMUNITY_AUF - case AccountType.COMMUNITY_PROJECT: - return AddressType.COMMUNITY_PROJECT - case AccountType.SUBACCOUNT: - return AddressType.SUBACCOUNT - case AccountType.CRYPTO_ACCOUNT: - return AddressType.CRYPTO_ACCOUNT - default: - throw new LogError(`Unsupported AccountType: ${accountType}`) +export function getEnumValue>( + enumType: T, + value: number | string, +): T[keyof T] | undefined { + if (typeof value === 'number' && typeof enumType === 'object') { + return enumType[value as keyof T] as T[keyof T] + } else if (typeof value === 'string') { + for (const key in enumType) { + if (enumType[key as keyof T] === value) { + return enumType[key as keyof T] as T[keyof T] + } + } } + return undefined } -export const addressTypeToAccountType = (addressType: AddressType): AccountType => { - switch (addressType) { - case AddressType.NONE: - return AccountType.NONE - case AddressType.COMMUNITY_HUMAN: - return AccountType.COMMUNITY_HUMAN - case AddressType.COMMUNITY_GMW: - return AccountType.COMMUNITY_GMW - case AddressType.COMMUNITY_AUF: - return AccountType.COMMUNITY_AUF - case AddressType.COMMUNITY_PROJECT: - return AccountType.COMMUNITY_PROJECT - case AddressType.SUBACCOUNT: - return AccountType.SUBACCOUNT - case AddressType.CRYPTO_ACCOUNT: - return AccountType.CRYPTO_ACCOUNT - default: - throw new LogError(`Unsupported AddressType: ${addressType}`) +export const accountTypeToAddressType = (type: AccountType): AddressType => { + const typeString: string = AccountType[type] + const addressType: AddressType = AddressType[typeString as keyof typeof AddressType] + + if (!addressType) { + throw new LogError("couldn't find corresponding AddressType for AccountType", { + accountType: type, + addressTypes: Object.keys(AddressType), + }) } + return addressType +} + +export const addressTypeToAccountType = (type: AddressType): AccountType => { + const typeString: string = AddressType[type] + const accountType: AccountType = AccountType[typeString as keyof typeof AccountType] + + if (!accountType) { + throw new LogError("couldn't find corresponding AccountType for AddressType", { + addressTypes: type, + accountType: Object.keys(AccountType), + }) + } + return accountType } diff --git a/dlt-connector/test/ApolloServerMock.ts b/dlt-connector/test/ApolloServerMock.ts index 6e0585862..c13df2407 100644 --- a/dlt-connector/test/ApolloServerMock.ts +++ b/dlt-connector/test/ApolloServerMock.ts @@ -1,5 +1,6 @@ import { ApolloServer } from '@apollo/server' import { addMocksToSchema } from '@graphql-tools/mock' + import { schema } from '@/graphql/schema' let apolloTestServer: ApolloServer diff --git a/dlt-connector/test/TestDB.ts b/dlt-connector/test/TestDB.ts index 75f362c65..63ce78500 100644 --- a/dlt-connector/test/TestDB.ts +++ b/dlt-connector/test/TestDB.ts @@ -1,7 +1,6 @@ import { DataSource, FileLogger } from '@dbTools/typeorm' -import { createDatabase } from 'typeorm-extension' - import { entities } from '@entity/index' +import { createDatabase } from 'typeorm-extension' import { CONFIG } from '@/config' import { LogError } from '@/server/LogError'