From b9e138939b1140f2ebca59d7efaa625ac19f8aed Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 3 May 2024 15:20:59 +0200 Subject: [PATCH] refactor, add feature for register address --- dlt-connector/src/data/User.repository.ts | 8 +++ .../src/data/proto/3_3/RegisterAddress.ts | 20 ++++++ .../src/data/proto/3_3/TransactionBody.ts | 15 ++++- .../src/data/proto/TransactionBody.builder.ts | 11 +++- .../src/data/proto/transactionBody.logic.ts | 59 +++++++++++++++++ .../src/graphql/resolver/AccountsResolver.ts | 60 ++++++++++++++++++ .../graphql/resolver/TransactionsResolver.ts | 2 +- .../account/RegisterAddress.context.ts | 63 +++++++++++++++++++ .../community/HomeCommunity.role.ts | 6 +- .../AbstractTransactionRecipeRole.ts | 15 +++++ ...> BalanceChangingTransactionRecipeRole.ts} | 17 +---- .../CommunityRootTransaction.role.ts | 8 +-- .../CreateTransactionRecipe.context.test.ts | 2 +- ....ts => CreateTransactionRecipe.context.ts} | 38 ++++++++--- .../RegisterAddressTransaction.role.ts | 29 +++++++++ .../TransmitToIota.context.test.ts | 2 +- 16 files changed, 318 insertions(+), 37 deletions(-) create mode 100644 dlt-connector/src/data/proto/transactionBody.logic.ts create mode 100644 dlt-connector/src/graphql/resolver/AccountsResolver.ts create mode 100644 dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts rename dlt-connector/src/interactions/backendToDb/transaction/{TransactionRecipe.role.ts => BalanceChangingTransactionRecipeRole.ts} (87%) rename dlt-connector/src/interactions/backendToDb/transaction/{CreateTransationRecipe.context.ts => CreateTransactionRecipe.context.ts} (61%) create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts diff --git a/dlt-connector/src/data/User.repository.ts b/dlt-connector/src/data/User.repository.ts index 6e5a66203..1e9e4dcef 100644 --- a/dlt-connector/src/data/User.repository.ts +++ b/dlt-connector/src/data/User.repository.ts @@ -1,5 +1,6 @@ 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' @@ -21,4 +22,11 @@ export const UserRepository = getDataSource() return account } }, + + findByGradidoId( + { uuid }: UserIdentifier, + relations?: FindOptionsRelations, + ): Promise { + return User.findOne({ where: { gradidoID: uuid }, relations }) + }, }) diff --git a/dlt-connector/src/data/proto/3_3/RegisterAddress.ts b/dlt-connector/src/data/proto/3_3/RegisterAddress.ts index 87f09afbd..08cb39868 100644 --- a/dlt-connector/src/data/proto/3_3/RegisterAddress.ts +++ b/dlt-connector/src/data/proto/3_3/RegisterAddress.ts @@ -1,15 +1,35 @@ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-unused-vars */ +import { Account } from '@entity/Account' import { Transaction } from '@entity/Transaction' +import { User } from '@entity/User' import { Field, Message } from 'protobufjs' import { AddressType } from '@/data/proto/3_3/enum/AddressType' +import { AccountType } from '@/graphql/enum/AccountType' +import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' +import { accountTypeToAddressType } from '@/utils/typeConverter' import { AbstractTransaction } from '../AbstractTransaction' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class RegisterAddress extends Message implements AbstractTransaction { + constructor(transaction?: UserAccountDraft, account?: Account) { + if (transaction) { + super({ addressType: accountTypeToAddressType(transaction.accountType) }) + if (account) { + this.derivationIndex = account.derivationIndex + this.accountPubkey = account.derive2Pubkey + if (account.user) { + this.userPubkey = account.user.derive1Pubkey + } + } + } else { + super() + } + } + @Field.d(1, 'bytes') public userPubkey: Buffer diff --git a/dlt-connector/src/data/proto/3_3/TransactionBody.ts b/dlt-connector/src/data/proto/3_3/TransactionBody.ts index 39d5602ec..934e05cfc 100644 --- a/dlt-connector/src/data/proto/3_3/TransactionBody.ts +++ b/dlt-connector/src/data/proto/3_3/TransactionBody.ts @@ -4,12 +4,14 @@ import { Field, Message, OneOf } from 'protobufjs' 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 { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' import { timestampToDate } from '@/utils/typeConverter' import { AbstractTransaction } from '../AbstractTransaction' +import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic' import { CommunityRoot } from './CommunityRoot' import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const' @@ -25,14 +27,21 @@ import { Timestamp } from './Timestamp' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class TransactionBody extends Message { - public constructor(transaction?: TransactionDraft | CommunityDraft) { + public constructor(transaction?: TransactionDraft | CommunityDraft | UserAccountDraft) { if (transaction) { + let type = CrossGroupType.LOCAL + let otherGroup = '' + if (transaction instanceof TransactionDraft) { + type = determineCrossGroupType(transaction) + otherGroup = determineOtherGroup(type, transaction) + } + super({ memo: 'Not implemented yet', createdAt: new Timestamp(new Date(transaction.createdAt)), versionNumber: PROTO_TRANSACTION_BODY_VERSION_NUMBER, - type: CrossGroupType.LOCAL, - otherGroup: '', + type, + otherGroup, }) } else { super() diff --git a/dlt-connector/src/data/proto/TransactionBody.builder.ts b/dlt-connector/src/data/proto/TransactionBody.builder.ts index 22d943d48..cfc1e808b 100644 --- a/dlt-connector/src/data/proto/TransactionBody.builder.ts +++ b/dlt-connector/src/data/proto/TransactionBody.builder.ts @@ -4,12 +4,14 @@ import { Community } from '@entity/Community' import { InputTransactionType } from '@/graphql/enum/InputTransactionType' import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { UserAccountDraft } from '@/graphql/input/UserAccountDraft' import { LogError } from '@/server/LogError' import { CommunityRoot } from './3_3/CommunityRoot' import { CrossGroupType } from './3_3/enum/CrossGroupType' import { GradidoCreation } from './3_3/GradidoCreation' import { GradidoTransfer } from './3_3/GradidoTransfer' +import { RegisterAddress } from './3_3/RegisterAddress' import { TransactionBody } from './3_3/TransactionBody' export class TransactionBodyBuilder { @@ -99,9 +101,16 @@ export class TransactionBodyBuilder { return this } + public fromUserAccountDraft(userAccountDraft: UserAccountDraft, account: Account): this { + this.body = new TransactionBody(userAccountDraft) + this.body.registerAddress = new RegisterAddress(userAccountDraft, account) + this.body.data = 'registerAddress' + return this + } + public fromTransactionDraft(transactionDraft: TransactionDraft): TransactionBodyBuilder { this.body = new TransactionBody(transactionDraft) - // TODO: load pubkeys for sender and recipient user from db + // TODO: load public keys for sender and recipient user from db switch (transactionDraft.type) { case InputTransactionType.CREATION: if (!this.recipientAccount) { diff --git a/dlt-connector/src/data/proto/transactionBody.logic.ts b/dlt-connector/src/data/proto/transactionBody.logic.ts new file mode 100644 index 000000000..69d7e565e --- /dev/null +++ b/dlt-connector/src/data/proto/transactionBody.logic.ts @@ -0,0 +1,59 @@ +import { InputTransactionType } from '@/graphql/enum/InputTransactionType' +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 = ({ + user, + linkedUser, + type, +}: TransactionDraft): CrossGroupType => { + if ( + !linkedUser.communityUuid || + !user.communityUuid || + linkedUser.communityUuid === '' || + user.communityUuid === '' || + user.communityUuid === linkedUser.communityUuid || + type === InputTransactionType.CREATION + ) { + return CrossGroupType.LOCAL + } else if (type === InputTransactionType.SEND) { + return CrossGroupType.INBOUND + } else if (type === InputTransactionType.RECEIVE) { + return CrossGroupType.OUTBOUND + } + throw new TransactionError( + TransactionErrorType.NOT_IMPLEMENTED_YET, + 'cannot determine CrossGroupType', + ) +} + +export const determineOtherGroup = ( + type: CrossGroupType, + { user, linkedUser }: TransactionDraft, +): string => { + switch (type) { + case CrossGroupType.LOCAL: + return '' + case CrossGroupType.INBOUND: + if (!linkedUser.communityUuid || linkedUser.communityUuid === '') { + throw new TransactionError( + TransactionErrorType.MISSING_PARAMETER, + 'missing linkedUser community id for cross group transaction', + ) + } + return linkedUser.communityUuid + case CrossGroupType.OUTBOUND: + if (!user.communityUuid || user.communityUuid === '') { + throw new TransactionError( + TransactionErrorType.MISSING_PARAMETER, + 'missing user community id for cross group transaction', + ) + } + return user.communityUuid + case CrossGroupType.CROSS: + throw new TransactionError(TransactionErrorType.NOT_IMPLEMENTED_YET, 'not implemented yet') + } +} diff --git a/dlt-connector/src/graphql/resolver/AccountsResolver.ts b/dlt-connector/src/graphql/resolver/AccountsResolver.ts new file mode 100644 index 000000000..91ac7bd73 --- /dev/null +++ b/dlt-connector/src/graphql/resolver/AccountsResolver.ts @@ -0,0 +1,60 @@ +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 { logger } from '@/logging/logger' +import { TransactionLoggingView } from '@/logging/TransactionLogging.view' +import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' +import { getDataSource } from '@/typeorm/DataSource' + +import { TransactionErrorType } from '../enum/TransactionErrorType' +import { UserAccountDraft } from '../input/UserAccountDraft' +import { UserIdentifier } from '../input/UserIdentifier' +import { TransactionError } from '../model/TransactionError' +import { TransactionResult } from '../model/TransactionResult' + +@Resolver() +export class AccountResolver { + @Query(() => Boolean) + async isAccountExist(@Arg('data') userIdentifier: UserIdentifier): Promise { + logger.info('isAccountExist', userIdentifier) + return !!(await UserRepository.findAccountByUserIdentifier(userIdentifier)) + } + + @Mutation(() => TransactionResult) + async registerAddress( + @Arg('data') + userAccountDraft: UserAccountDraft, + ): Promise { + const registerAddressContext = new RegisterAddressContext(userAccountDraft) + try { + const { transaction, account } = await registerAddressContext.run() + 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)) + } 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) { + return new TransactionResult(err) + } else { + logger.error('error in register address: ', err) + throw err + } + } + } +} diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts index 7cc619400..3f34584a9 100755 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts @@ -4,7 +4,7 @@ import { TransactionDraft } from '@input/TransactionDraft' import { TRANSMIT_TO_IOTA_INTERRUPTIVE_SLEEP_KEY } from '@/data/const' import { TransactionRepository } from '@/data/Transaction.repository' -import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransationRecipe.context' +import { CreateTransactionRecipeContext } from '@/interactions/backendToDb/transaction/CreateTransactionRecipe.context' import { BackendTransactionLoggingView } from '@/logging/BackendTransactionLogging.view' import { logger } from '@/logging/logger' import { TransactionLoggingView } from '@/logging/TransactionLogging.view' diff --git a/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts b/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts new file mode 100644 index 000000000..196c0daf4 --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/account/RegisterAddress.context.ts @@ -0,0 +1,63 @@ +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 communityKeyPair = await CommunityRepository.loadHomeCommunityKeyPair() + 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, { + 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/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index 5d7bec94c..ad259b372 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -15,7 +15,7 @@ import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' import { LogError } from '@/server/LogError' import { getDataSource } from '@/typeorm/DataSource' -import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' +import { CreateTransactionRecipeContext } from '../transaction/CreateTransactionRecipe.context' import { CommunityRole } from './Community.role' @@ -44,7 +44,9 @@ export class HomeCommunityRole extends CommunityRole { this.self.aufAccount = AccountFactory.createAufAccount(keyPair, this.self.createdAt) this.self.gmwAccount = AccountFactory.createGmwAccount(keyPair, this.self.createdAt) - const transactionRecipeContext = new CreateTransactionRecipeContext(communityDraft, this.self) + const transactionRecipeContext = new CreateTransactionRecipeContext(communityDraft, { + community: this.self, + }) await transactionRecipeContext.run() this.transactionRecipe = transactionRecipeContext.getTransactionRecipe() } diff --git a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts new file mode 100644 index 000000000..812f6a7f0 --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransactionRecipeRole.ts @@ -0,0 +1,15 @@ +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/TransactionRecipe.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts similarity index 87% rename from dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts rename to dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts index f1be50c75..a4145cd44 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/BalanceChangingTransactionRecipeRole.ts @@ -1,29 +1,22 @@ import { Community } from '@entity/Community' -import { Transaction } from '@entity/Transaction' import { AccountLogic } from '@/data/Account.logic' import { KeyPair } from '@/data/KeyPair' import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType' import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' -import { TransactionBuilder } from '@/data/Transaction.builder' import { UserRepository } from '@/data/User.repository' import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { TransactionError } from '@/graphql/model/TransactionError' import { AbstractTransactionRole } from './AbstractTransaction.role' +import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' -export class TransactionRecipeRole { - protected transactionBuilder: TransactionBuilder - - public constructor() { - this.transactionBuilder = new TransactionBuilder() - } - +export class BalanceChangingTransactionRecipeRole extends AbstractTransactionRecipeRole { public async create( transactionDraft: TransactionDraft, transactionTypeRole: AbstractTransactionRole, - ): Promise { + ): Promise { const signingUser = transactionTypeRole.getSigningUser() const recipientUser = transactionTypeRole.getRecipientUser() @@ -82,8 +75,4 @@ export class TransactionRecipeRole { } return this.transactionBuilder.getCommunity() } - - public getTransaction(): Transaction { - return this.transactionBuilder.getTransaction() - } } diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts index 75b885f2f..34d56cce0 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts @@ -4,13 +4,13 @@ import { KeyPair } from '@/data/KeyPair' import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionRecipeRole } from './TransactionRecipe.role' +import { AbstractTransactionRecipeRole } from './AbstractTransactionRecipeRole' -export class CommunityRootTransactionRole extends TransactionRecipeRole { - public createFromCommunityRoot( +export class CommunityRootTransactionRole extends AbstractTransactionRecipeRole { + public create( communityDraft: CommunityDraft, community: Community, - ): CommunityRootTransactionRole { + ): AbstractTransactionRecipeRole { // create proto transaction body const transactionBody = new TransactionBodyBuilder() .fromCommunityDraft(communityDraft, community) diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts index e5535f3f7..117645bd1 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.test.ts @@ -15,7 +15,7 @@ import { InputTransactionType } from '@/graphql/enum/InputTransactionType' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' -import { CreateTransactionRecipeContext } from './CreateTransationRecipe.context' +import { CreateTransactionRecipeContext } from './CreateTransactionRecipe.context' // eslint-disable-next-line import/order import { communitySeed } from '@test/seeding/Community.seed' diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts similarity index 61% rename from dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts rename to dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts index 8fa3dc443..42e633a18 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransactionRecipe.context.ts @@ -1,3 +1,4 @@ +import { Account } from '@entity/Account' import { Community } from '@entity/Community' import { Transaction } from '@entity/Transaction' @@ -5,30 +6,38 @@ 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 { ReceiveTransactionRole } from './ReceiveTransaction.role' +import { RegisterAddressTransactionRole } from './RegisterAddressTransaction.role' import { SendTransactionRole } from './SendTransaction.role' -import { TransactionRecipeRole } from './TransactionRecipe.role' /** * @DCI-Context * Context for create and add Transaction Recipe to DB */ +export interface AdditionalData { + community?: Community + account?: Account +} + export class CreateTransactionRecipeContext { - private transactionRecipeRole: TransactionRecipeRole + private transactionRecipe: AbstractTransactionRecipeRole // eslint-disable-next-line no-useless-constructor public constructor( - private draft: CommunityDraft | TransactionDraft, - private community?: Community, + private draft: CommunityDraft | TransactionDraft | UserAccountDraft, + private data?: AdditionalData, ) {} public getTransactionRecipe(): Transaction { - return this.transactionRecipeRole.getTransaction() + return this.transactionRecipe.getTransaction() } /** @@ -36,7 +45,7 @@ export class CreateTransactionRecipeContext { */ public async run(): Promise { if (this.draft instanceof TransactionDraft) { - this.transactionRecipeRole = new TransactionRecipeRole() + const transactionRecipeRole = new BalanceChangingTransactionRecipeRole() // contain logic for translation from backend to dlt-connector format let transactionTypeRole: AbstractTransactionRole switch (this.draft.type) { @@ -50,15 +59,24 @@ export class CreateTransactionRecipeContext { transactionTypeRole = new ReceiveTransactionRole(this.draft) break } - await this.transactionRecipeRole.create(this.draft, transactionTypeRole) + await transactionRecipeRole.create(this.draft, transactionTypeRole) return true } else if (this.draft instanceof CommunityDraft) { - if (!this.community) { + if (!this.data?.community) { throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'community was not set') } - this.transactionRecipeRole = new CommunityRootTransactionRole().createFromCommunityRoot( + this.transactionRecipe = new CommunityRootTransactionRole().create( this.draft, - this.community, + 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') + } + this.transactionRecipe = await new RegisterAddressTransactionRole().create( + this.draft, + this.data.account, ) return true } diff --git a/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts new file mode 100644 index 000000000..84de6b5df --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/RegisterAddressTransaction.role.ts @@ -0,0 +1,29 @@ +import { Account } from '@entity/Account' + +import { AccountLogic } from '@/data/Account.logic' +import { CommunityRepository } from '@/data/Community.repository' +import { TransactionBodyBuilder } from '@/data/proto/TransactionBody.builder' +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, + ): Promise { + const bodyBuilder = new TransactionBodyBuilder() + 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") + } + this.transactionBuilder + .fromTransactionBodyBuilder(bodyBuilder.fromUserAccountDraft(userAccountDraft, account)) + .setSignature(signingKeyPair.sign(this.transactionBuilder.getTransaction().bodyBytes)) + .setSigningAccount(account) + return this + } +} diff --git a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts b/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts index 94a8e4f9d..520c4e143 100644 --- a/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts +++ b/dlt-connector/src/interactions/transmitToIota/TransmitToIota.context.test.ts @@ -14,7 +14,7 @@ import { InputTransactionType } from '@/graphql/enum/InputTransactionType' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { logger } from '@/logging/logger' -import { CreateTransactionRecipeContext } from '../backendToDb/transaction/CreateTransationRecipe.context' +import { CreateTransactionRecipeContext } from '../backendToDb/transaction/CreateTransactionRecipe.context' import { TransmitToIotaContext } from './TransmitToIota.context'