diff --git a/backend/src/apis/DltConnectorClient.test.ts b/backend/src/apis/DltConnectorClient.test.ts index 56fa3d13f..aa7cff2fc 100644 --- a/backend/src/apis/DltConnectorClient.test.ts +++ b/backend/src/apis/DltConnectorClient.test.ts @@ -134,7 +134,11 @@ describe('transmitTransaction', () => { const localTransaction = new DbTransaction() localTransaction.typeId = 12 try { - await DltConnectorClient.getInstance()?.transmitTransaction(localTransaction) + await DltConnectorClient.getInstance()?.transmitTransaction( + localTransaction, + 'senderCommunityUUid', + 'recipientCommunity', + ) } catch (e) { expect(e).toMatchObject( new LogError('invalid transaction type id: ' + localTransaction.typeId.toString()), diff --git a/backend/src/apis/DltConnectorClient.ts b/backend/src/apis/DltConnectorClient.ts index 593072eef..852454220 100644 --- a/backend/src/apis/DltConnectorClient.ts +++ b/backend/src/apis/DltConnectorClient.ts @@ -78,27 +78,35 @@ export class DltConnectorClient { * transmit transaction via dlt-connector to iota * and update dltTransactionId of transaction in db with iota message id */ - public async transmitTransaction(transaction?: DbTransaction | null): Promise { - if (transaction) { - const typeString = getTransactionTypeString(transaction.typeId) - const secondsSinceEpoch = Math.round(transaction.balanceDate.getTime() / 1000) - const amountString = transaction.amount.toString() - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { data } = await this.client.rawRequest(sendTransaction, { - input: { - type: typeString, - amount: amountString, - createdAt: secondsSinceEpoch, + public async transmitTransaction( + transaction: DbTransaction, + senderCommunityUuid: string, + recipientCommunityUuid = '', + ): Promise { + const typeString = getTransactionTypeString(transaction.typeId) + const milliSecondsSinceEpoch = Math.round(transaction.balanceDate.getTime()) + const amountString = transaction.amount.toString() + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { data } = await this.client.rawRequest(sendTransaction, { + input: { + senderUser: { + uuid: transaction.userGradidoID, + communityUuid: senderCommunityUuid, }, - }) - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access - return data.sendTransaction.dltTransactionIdHex - } catch (e) { - throw new LogError('Error send sending transaction to dlt-connector: ', e) - } - } else { - throw new LogError('parameter transaction not set...') + recipientUser: { + uuid: transaction.linkedUserGradidoID, + communityUuid: recipientCommunityUuid, + }, + amount: amountString, + type: typeString, + createdAt: milliSecondsSinceEpoch, + }, + }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access + return data.sendTransaction.dltTransactionIdHex + } catch (e) { + throw new LogError('Error send sending transaction to dlt-connector: ', e) } } } diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts index 871c31a89..0e0f0a810 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts @@ -360,7 +360,7 @@ describe('create and send Transactions to DltConnector', () => { txCREATION3 = await createTxCREATION3(false) CONFIG.DLT_CONNECTOR = false - await sendTransactionsToDltConnector() + await sendTransactionsToDltConnector('senderCommunityUuid') expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...') // Find the previous created transactions of sendCoin mutation @@ -429,7 +429,7 @@ describe('create and send Transactions to DltConnector', () => { } as Response }) - await sendTransactionsToDltConnector() + await sendTransactionsToDltConnector('senderCommunityUuid') expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...') @@ -507,7 +507,7 @@ describe('create and send Transactions to DltConnector', () => { } as Response }) - await sendTransactionsToDltConnector() + await sendTransactionsToDltConnector('senderCommunityUuid') expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...') diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts index 98ea255c1..2e3211378 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts @@ -6,7 +6,10 @@ import { DltConnectorClient } from '@/apis/DltConnectorClient' import { backendLogger as logger } from '@/server/logger' import { Monitor, MonitorNames } from '@/util/Monitor' -export async function sendTransactionsToDltConnector(): Promise { +export async function sendTransactionsToDltConnector( + senderCommunityUuid: string, + recipientCommunityUuid = '', +): Promise { logger.info('sendTransactionsToDltConnector...') // check if this logic is still occupied, no concurrecy allowed if (!Monitor.isLocked(MonitorNames.SEND_DLT_TRANSACTIONS)) { @@ -24,8 +27,15 @@ export async function sendTransactionsToDltConnector(): Promise { order: { createdAt: 'ASC', id: 'ASC' }, }) for (const dltTx of dltTransactions) { + if (!dltTx.transaction) { + continue + } try { - const messageId = await dltConnector.transmitTransaction(dltTx.transaction) + const messageId = await dltConnector.transmitTransaction( + dltTx.transaction, + senderCommunityUuid, + recipientCommunityUuid, + ) const dltMessageId = Buffer.from(messageId, 'hex') if (dltMessageId.length !== 32) { logger.error( diff --git a/common/src/graphql/validator/DateString.ts b/common/src/graphql/validator/DateString.ts new file mode 100644 index 000000000..4ee23b51a --- /dev/null +++ b/common/src/graphql/validator/DateString.ts @@ -0,0 +1,21 @@ +import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator' + +export function isValidDateString(validationOptions?: ValidationOptions) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isValidDateString', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: string) { + return new Date(value).toString() !== 'Invalid Date' + }, + defaultMessage(args: ValidationArguments) { + return `${propertyName} must be a valid date string, ${args.property}` + }, + }, + }) + } +} diff --git a/common/src/graphql/validator/Decimal.ts b/common/src/graphql/validator/Decimal.ts new file mode 100644 index 000000000..09e8fb4bd --- /dev/null +++ b/common/src/graphql/validator/Decimal.ts @@ -0,0 +1,22 @@ +import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator' +import { Decimal } from 'decimal.js-light' + +export function IsPositiveDecimal(validationOptions?: ValidationOptions) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isPositiveDecimal', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: Decimal) { + return value.greaterThan(0) + }, + defaultMessage(args: ValidationArguments) { + return `The ${propertyName} must be a positive value ${args.property}` + }, + }, + }) + } +} diff --git a/common/src/graphql/validator/validator b/common/src/graphql/validator/validator new file mode 120000 index 000000000..cc34f673a --- /dev/null +++ b/common/src/graphql/validator/validator @@ -0,0 +1 @@ +validator \ No newline at end of file diff --git a/dlt-connector/src/controller/Community.ts b/dlt-connector/src/controller/Community.ts new file mode 100644 index 000000000..ff6d43012 --- /dev/null +++ b/dlt-connector/src/controller/Community.ts @@ -0,0 +1,3 @@ +export class Community { + +} \ No newline at end of file diff --git a/dlt-connector/src/controller/GradidoTransaction.ts b/dlt-connector/src/controller/GradidoTransaction.ts new file mode 100644 index 000000000..671f3f57a --- /dev/null +++ b/dlt-connector/src/controller/GradidoTransaction.ts @@ -0,0 +1,10 @@ +import { GradidoTransaction } from '@/proto/3_3/GradidoTransaction' +import { TransactionBody } from '@/proto/3_3/TransactionBody' + +export const create = (body: TransactionBody): GradidoTransaction => { + const transaction = new GradidoTransaction({ + bodyBytes: Buffer.from(TransactionBody.encode(body).finish()), + }) + // TODO: add correct signature(s) + return transaction +} diff --git a/dlt-connector/src/controller/TransactionBase.ts b/dlt-connector/src/controller/TransactionBase.ts new file mode 100644 index 000000000..9833226a9 --- /dev/null +++ b/dlt-connector/src/controller/TransactionBase.ts @@ -0,0 +1,6 @@ +import { TransactionValidationLevel } from '@/graphql/enum/TransactionValidationLevel' + +export abstract class TransactionBase { + // validate if transaction is valid, maybe expensive because depending on level several transactions will be fetched from db + public abstract validate(level: TransactionValidationLevel): boolean +} diff --git a/dlt-connector/src/controller/TransactionBody.ts b/dlt-connector/src/controller/TransactionBody.ts new file mode 100644 index 000000000..e5141277d --- /dev/null +++ b/dlt-connector/src/controller/TransactionBody.ts @@ -0,0 +1,74 @@ +import { CrossGroupType } from '@/graphql/enum/CrossGroupType' +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { TransactionType } from '@/graphql/enum/TransactionType' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { TransactionError } from '@/graphql/model/TransactionError' +import { GradidoCreation } from '@/proto/3_3/GradidoCreation' +import { GradidoTransfer } from '@/proto/3_3/GradidoTransfer' +import { TransactionBody } from '@/proto/3_3/TransactionBody' + +export const create = (transaction: TransactionDraft): TransactionBody => { + const body = new TransactionBody(transaction) + // TODO: load pubkeys for sender and recipient user from db + switch (transaction.type) { + case TransactionType.CREATION: + body.creation = new GradidoCreation(transaction) + body.data = 'gradidoCreation' + break + case TransactionType.SEND: + body.transfer = new GradidoTransfer(transaction) + body.data = 'gradidoTransfer' + break + case TransactionType.RECEIVE: + body.transfer = new GradidoTransfer(transaction) + body.data = 'gradidoTransfer' + break + default: + throw new TransactionError( + TransactionErrorType.NOT_IMPLEMENTED_YET, + 'transaction type unknown', + ) + } + return body +} + +export const determineCrossGroupType = ({ + senderUser, + recipientUser, + type, +}: TransactionDraft): CrossGroupType => { + if ( + recipientUser.communityUuid === '' || + senderUser.communityUuid === recipientUser.communityUuid || + type === TransactionType.CREATION + ) { + return CrossGroupType.LOCAL + } else if (type === TransactionType.SEND) { + return CrossGroupType.INBOUND + } else if (type === TransactionType.RECEIVE) { + return CrossGroupType.OUTBOUND + } + throw new TransactionError( + TransactionErrorType.NOT_IMPLEMENTED_YET, + 'cannot determine CrossGroupType', + ) +} + +export const determineOtherGroup = ( + type: CrossGroupType, + { senderUser, recipientUser }: TransactionDraft, +): string => { + switch (type) { + case CrossGroupType.LOCAL: + return '' + case CrossGroupType.INBOUND: + return recipientUser.communityUuid + case CrossGroupType.OUTBOUND: + return senderUser.communityUuid + default: + throw new TransactionError( + TransactionErrorType.NOT_IMPLEMENTED_YET, + type.toString() + ' for enum CrossGroupType not implemented yet', + ) + } +} diff --git a/dlt-connector/src/graphql/arg/User.ts b/dlt-connector/src/graphql/arg/User.ts new file mode 100644 index 000000000..5175e3d78 --- /dev/null +++ b/dlt-connector/src/graphql/arg/User.ts @@ -0,0 +1,19 @@ +// https://www.npmjs.com/package/@apollo/protobufjs + +import { IsPositive, IsUUID } from 'class-validator' +import { Field, Int, ArgsType } from 'type-graphql' + +@ArgsType() +export class User { + @Field(() => String) + @IsUUID('4') + uuid: string + + @Field(() => String) + @IsUUID('4') + communityUuid: string + + @Field(() => Int, { defaultValue: 1 }) + @IsPositive() + accountNr: number +} diff --git a/dlt-connector/src/graphql/enum/AddressType.ts b/dlt-connector/src/graphql/enum/AddressType.ts index d75ad9988..26efd2825 100644 --- a/dlt-connector/src/graphql/enum/AddressType.ts +++ b/dlt-connector/src/graphql/enum/AddressType.ts @@ -1,8 +1,9 @@ export enum AddressType { NONE = 0, // if no address was found - HUMAN = 1, - PROJECT = 2, // no creations allowed - SUBACCOUNT = 3, // no creations allowed - CRYPTO_ACCOUNT = 4, // user control his keys, no creations - COMMUNITY_ACCOUNT = 5, // community control keys, creations allowed + COMMUNITY_HUMAN = 1, // creation account for human + COMMUNITY_GMW = 2, // community public budget account + COMMUNITY_AUF = 3, // community compensation and environment founds account + COMMUNITY_PROJECT = 4, // no creations allowed + SUBACCOUNT = 5, // no creations allowed + CRYPTO_ACCOUNT = 6, // user control his keys, no creations } diff --git a/dlt-connector/src/graphql/enum/TransactionErrorType.ts b/dlt-connector/src/graphql/enum/TransactionErrorType.ts new file mode 100644 index 000000000..6977c34a8 --- /dev/null +++ b/dlt-connector/src/graphql/enum/TransactionErrorType.ts @@ -0,0 +1,10 @@ +import { registerEnumType } from 'type-graphql' + +export enum TransactionErrorType { + NOT_IMPLEMENTED_YET = 'Not Implemented yet', +} + +registerEnumType(TransactionErrorType, { + name: 'TransactionErrorType', + description: 'Transaction Error Type', +}) diff --git a/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts b/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts new file mode 100644 index 000000000..9462dd8a8 --- /dev/null +++ b/dlt-connector/src/graphql/enum/TransactionValidationLevel.ts @@ -0,0 +1,15 @@ +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/AddCommunityDraft.ts b/dlt-connector/src/graphql/input/AddCommunityDraft.ts new file mode 100644 index 000000000..f967019b3 --- /dev/null +++ b/dlt-connector/src/graphql/input/AddCommunityDraft.ts @@ -0,0 +1,20 @@ +// https://www.npmjs.com/package/@apollo/protobufjs +import { InputType, Field } from 'type-graphql' + +import { isValidDateString } from '../validator/DateString' +import { IsBoolean, IsUUID } from 'class-validator' + +@InputType() +export class AddCommunityDraft { + @Field(() => String) + @IsUUID('4') + communityUuid: string + + @Field(() => String) + @isValidDateString() + createdAt: string + + @Field(() => Boolean) + @IsBoolean() + foreign: boolean +} diff --git a/dlt-connector/src/graphql/input/AddUserDraft.ts b/dlt-connector/src/graphql/input/AddUserDraft.ts new file mode 100644 index 000000000..5c6705375 --- /dev/null +++ b/dlt-connector/src/graphql/input/AddUserDraft.ts @@ -0,0 +1,20 @@ +// https://www.npmjs.com/package/@apollo/protobufjs + +import { IsUUID } from 'class-validator' +import { isValidDateString } from '../validator/DateString' +import { InputType, Field } from 'type-graphql' + +@InputType() +export class AddUserDraft { + @Field(() => String) + @IsUUID('4') + uuid: string + + @Field(() => String) + @IsUUID('4') + communityUuid: string + + @Field(() => String) + @isValidDateString() + createdAt: string +} diff --git a/dlt-connector/src/graphql/input/TransactionDraft.ts b/dlt-connector/src/graphql/input/TransactionDraft.ts new file mode 100755 index 000000000..22da79d4a --- /dev/null +++ b/dlt-connector/src/graphql/input/TransactionDraft.ts @@ -0,0 +1,40 @@ +// https://www.npmjs.com/package/@apollo/protobufjs + +import { Decimal } from 'decimal.js-light' +import { TransactionType } from '@enum/TransactionType' +import { InputType, Field } from 'type-graphql' +import { User } from '@arg/User' +import { isValidDateString } from '../validator/DateString' +import { IsPositiveDecimal } from '../validator/Decimal' +import { IsEnum, IsObject, ValidateNested, IsNumber, Min } from 'class-validator' + +@InputType() +export class TransactionDraft { + @Field(() => User) + @IsObject() + @ValidateNested() + senderUser: User + + @Field(() => User) + @IsObject() + @ValidateNested() + recipientUser: User + + @Field(() => Decimal) + @IsPositiveDecimal() + amount: Decimal + + @Field(() => TransactionType) + @IsEnum(TransactionType) + type: TransactionType + + @Field(() => Number) + @IsNumber() + @Min(9783072000000) // 01.01.2001 + createdAt: number // in milliseconds + + // only for creation transactions + @Field(() => String, { nullable: true }) + @isValidDateString() + targetDate?: string +} diff --git a/dlt-connector/src/graphql/input/TransactionInput.ts b/dlt-connector/src/graphql/input/TransactionInput.ts deleted file mode 100755 index 02eba916e..000000000 --- a/dlt-connector/src/graphql/input/TransactionInput.ts +++ /dev/null @@ -1,21 +0,0 @@ -// https://www.npmjs.com/package/@apollo/protobufjs - -import { Decimal } from 'decimal.js-light' -import { TransactionType } from '../enum/TransactionType' -import { InputType, Field } from 'type-graphql' - -@InputType() -export class TransactionInput { - @Field(() => TransactionType) - type: TransactionType - - @Field(() => Decimal) - amount: Decimal - - @Field(() => Number) - createdAt: number - - // @protoField.d(4, 'string') - // @Field(() => Decimal) - // communitySum: Decimal -} diff --git a/dlt-connector/src/graphql/model/TransactionError.ts b/dlt-connector/src/graphql/model/TransactionError.ts new file mode 100644 index 000000000..be2743239 --- /dev/null +++ b/dlt-connector/src/graphql/model/TransactionError.ts @@ -0,0 +1,16 @@ +import { ObjectType, Field } from 'type-graphql' +import { TransactionErrorType } from '../enum/TransactionErrorType' + +@ObjectType() +export class TransactionError { + constructor(type: TransactionErrorType, message: string) { + this.type = type + this.message = message + } + + @Field(() => TransactionErrorType) + type: TransactionErrorType + + @Field(() => String) + message: string +} diff --git a/dlt-connector/src/graphql/model/TransactionResult.ts b/dlt-connector/src/graphql/model/TransactionResult.ts new file mode 100644 index 000000000..5aeb7f441 --- /dev/null +++ b/dlt-connector/src/graphql/model/TransactionResult.ts @@ -0,0 +1,21 @@ +import { ObjectType, Field } from 'type-graphql' +import { TransactionError } from './TransactionError' + +@ObjectType() +export class TransactionResult { + constructor(content: TransactionError | Buffer) { + if (content instanceof TransactionError) { + this.error = content + } else if (content instanceof Buffer) { + this.messageId = content.toString('hex') + } + } + + // the error if one happened + @Field(() => TransactionError, { nullable: true }) + error?: TransactionError + + // if no error happend, the message id of the iota transaction + @Field(() => String, { nullable: true }) + messageId?: string +} diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts index df50322fb..b798465d0 100755 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.ts @@ -1,9 +1,12 @@ import { Resolver, Query, Arg, Mutation } from 'type-graphql' -import { TransactionInput } from '@input/TransactionInput' -import { TransactionBody } from '@proto/TransactionBody' +import { TransactionDraft } from '@input/TransactionDraft' + +import { create as createTransactionBody } from '@controller/TransactionBody' +import { create as createGradidoTransaction } from '@controller/GradidoTransaction' import { sendMessage as iotaSendMessage } from '@/client/IotaClient' +import { GradidoTransaction } from '@/proto/3_3/GradidoTransaction' @Resolver() export class TransactionResolver { @@ -21,10 +24,11 @@ export class TransactionResolver { @Mutation(() => String) async sendTransaction( @Arg('data') - transaction: TransactionInput, + transaction: TransactionDraft, ): Promise { - const message = TransactionBody.fromObject(transaction) - const messageBuffer = TransactionBody.encode(message).finish() + const body = createTransactionBody(transaction) + const message = createGradidoTransaction(body) + const messageBuffer = GradidoTransaction.encode(message).finish() const resultMessage = await iotaSendMessage(messageBuffer) return resultMessage.messageId } diff --git a/dlt-connector/src/graphql/validator b/dlt-connector/src/graphql/validator new file mode 120000 index 000000000..7268ccb05 --- /dev/null +++ b/dlt-connector/src/graphql/validator @@ -0,0 +1 @@ +../../../common/src/graphql/validator \ No newline at end of file diff --git a/dlt-connector/src/proto/3_3/GradidoConfirmedTransaction.ts b/dlt-connector/src/proto/3_3/GradidoConfirmedTransaction.ts new file mode 100644 index 000000000..7f0a58109 --- /dev/null +++ b/dlt-connector/src/proto/3_3/GradidoConfirmedTransaction.ts @@ -0,0 +1,34 @@ +import { Field, Message } from '@apollo/protobufjs' +import { GradidoTransaction } from './GradidoTransaction' +import { TimestampSeconds } from './TimestampSeconds' + +/* + id will be set by Node server + running_hash will be also set by Node server, + calculated from previous transaction running_hash and this id, transaction and received +*/ + +// https://www.npmjs.com/package/@apollo/protobufjs +// eslint-disable-next-line no-use-before-define +export class GradidoConfirmedTransaction extends Message { + @Field.d(1, 'uint64') + id: number + + @Field.d(2, 'GradidoTransaction') + transaction: GradidoTransaction + + @Field.d(3, 'TimestampSeconds') + confirmedAt: TimestampSeconds + + @Field.d(4, 'string') + versionNumber: string + + @Field.d(5, 'bytes') + runningHash: Buffer + + @Field.d(6, 'bytes') + messageId: Buffer + + @Field.d(7, 'string') + accountBalance: string +} diff --git a/dlt-connector/src/proto/GradidoCreation.ts b/dlt-connector/src/proto/3_3/GradidoCreation.ts similarity index 67% rename from dlt-connector/src/proto/GradidoCreation.ts rename to dlt-connector/src/proto/3_3/GradidoCreation.ts index 90ca85806..a54cb6169 100644 --- a/dlt-connector/src/proto/GradidoCreation.ts +++ b/dlt-connector/src/proto/3_3/GradidoCreation.ts @@ -2,12 +2,20 @@ import { Field, Message } from '@apollo/protobufjs' import { TimestampSeconds } from './TimestampSeconds' import { TransferAmount } from './TransferAmount' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' // need signature from group admin or // percent of group users another than the receiver // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class GradidoCreation extends Message { + constructor(transaction: TransactionDraft) { + super({ + recipient: new TransferAmount({ amount: transaction.amount.toString() }), + targetDate: new TimestampSeconds(), + }) + } + @Field.d(1, TransferAmount) public recipient: TransferAmount diff --git a/dlt-connector/src/proto/GradidoDeferredTransfer.ts b/dlt-connector/src/proto/3_3/GradidoDeferredTransfer.ts similarity index 92% rename from dlt-connector/src/proto/GradidoDeferredTransfer.ts rename to dlt-connector/src/proto/3_3/GradidoDeferredTransfer.ts index a97cc22a7..7b27c064a 100644 --- a/dlt-connector/src/proto/GradidoDeferredTransfer.ts +++ b/dlt-connector/src/proto/3_3/GradidoDeferredTransfer.ts @@ -1,7 +1,7 @@ import { Field, Message } from '@apollo/protobufjs' import { GradidoTransfer } from './GradidoTransfer' -import { Timestamp } from './Timestamp' +import { TimestampSeconds } from './TimestampSeconds' // transaction type for chargeable transactions // for transaction for people which haven't a account already @@ -23,8 +23,8 @@ export class GradidoDeferredTransfer extends Message { // the decay for amount and the seconds until timeout is lost no matter what happened // consider is as fee for this service // rest decay could be transferred back as separate transaction - @Field.d(2, 'Timestamp') - public timeout: Timestamp + @Field.d(2, 'TimestampSeconds') + public timeout: TimestampSeconds // split for n recipient // max gradido per recipient? or per transaction with cool down? diff --git a/dlt-connector/src/proto/GradidoTransaction.ts b/dlt-connector/src/proto/3_3/GradidoTransaction.ts similarity index 100% rename from dlt-connector/src/proto/GradidoTransaction.ts rename to dlt-connector/src/proto/3_3/GradidoTransaction.ts diff --git a/dlt-connector/src/proto/GradidoTransfer.ts b/dlt-connector/src/proto/3_3/GradidoTransfer.ts similarity index 57% rename from dlt-connector/src/proto/GradidoTransfer.ts rename to dlt-connector/src/proto/3_3/GradidoTransfer.ts index 203b281ad..215ffc60f 100644 --- a/dlt-connector/src/proto/GradidoTransfer.ts +++ b/dlt-connector/src/proto/3_3/GradidoTransfer.ts @@ -1,10 +1,20 @@ import { Field, Message } from '@apollo/protobufjs' import { TransferAmount } from './TransferAmount' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class GradidoTransfer extends Message { + constructor(transaction: TransactionDraft, coinOrigin?: string) { + super({ + sender: new TransferAmount({ + amount: transaction.amount.toString(), + communityId: coinOrigin, + }), + }) + } + @Field.d(1, TransferAmount) public sender: TransferAmount diff --git a/dlt-connector/src/proto/GroupFriendsUpdate.ts b/dlt-connector/src/proto/3_3/GroupFriendsUpdate.ts similarity index 100% rename from dlt-connector/src/proto/GroupFriendsUpdate.ts rename to dlt-connector/src/proto/3_3/GroupFriendsUpdate.ts diff --git a/dlt-connector/src/proto/RegisterAddress.ts b/dlt-connector/src/proto/3_3/RegisterAddress.ts similarity index 100% rename from dlt-connector/src/proto/RegisterAddress.ts rename to dlt-connector/src/proto/3_3/RegisterAddress.ts diff --git a/dlt-connector/src/proto/SignatureMap.ts b/dlt-connector/src/proto/3_3/SignatureMap.ts similarity index 100% rename from dlt-connector/src/proto/SignatureMap.ts rename to dlt-connector/src/proto/3_3/SignatureMap.ts diff --git a/dlt-connector/src/proto/SignaturePair.ts b/dlt-connector/src/proto/3_3/SignaturePair.ts similarity index 100% rename from dlt-connector/src/proto/SignaturePair.ts rename to dlt-connector/src/proto/3_3/SignaturePair.ts diff --git a/dlt-connector/src/proto/3_3/Timestamp.ts b/dlt-connector/src/proto/3_3/Timestamp.ts new file mode 100644 index 000000000..ab060a9bc --- /dev/null +++ b/dlt-connector/src/proto/3_3/Timestamp.ts @@ -0,0 +1,27 @@ +import { Field, Message } from '@apollo/protobufjs' + +// https://www.npmjs.com/package/@apollo/protobufjs +// eslint-disable-next-line no-use-before-define +export class Timestamp extends Message { + public constructor(input?: Date | number) { + let seconds = 0 + let nanoSeconds = 0 + if (input instanceof Date) { + seconds = Math.floor(input.getTime() / 1000) + nanoSeconds = (input.getTime() % 1000) * 1000000 // Convert milliseconds to nanoseconds + } else if (typeof input === 'number') { + // Calculate seconds and nanoseconds from milliseconds + seconds = Math.floor(input / 1000) + nanoSeconds = (input % 1000) * 1000000 + } + super({ seconds, nanoSeconds }) + } + + // Number of complete seconds since the start of the epoch + @Field.d(1, 'int64') + public seconds: number + + // Number of nanoseconds since the start of the last second + @Field.d(2, 'int32') + public nanoSeconds: number +} diff --git a/dlt-connector/src/proto/TimestampSeconds.ts b/dlt-connector/src/proto/3_3/TimestampSeconds.ts similarity index 51% rename from dlt-connector/src/proto/TimestampSeconds.ts rename to dlt-connector/src/proto/3_3/TimestampSeconds.ts index 9a8c5b9de..055094c6d 100644 --- a/dlt-connector/src/proto/TimestampSeconds.ts +++ b/dlt-connector/src/proto/3_3/TimestampSeconds.ts @@ -3,6 +3,17 @@ import { Field, Message } from '@apollo/protobufjs' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class TimestampSeconds extends Message { + public constructor(input?: Date | number) { + let seconds = 0 + // Calculate seconds from milliseconds + if (input instanceof Date) { + seconds = Math.floor(input.getTime() / 1000) + } else if (typeof input === 'number') { + seconds = Math.floor(input / 1000) + } + super({ seconds }) + } + // Number of complete seconds since the start of the epoch @Field.d(1, 'int64') public seconds: number diff --git a/dlt-connector/src/proto/TransactionBody.test.ts b/dlt-connector/src/proto/3_3/TransactionBody.test.ts similarity index 91% rename from dlt-connector/src/proto/TransactionBody.test.ts rename to dlt-connector/src/proto/3_3/TransactionBody.test.ts index c9315ba81..cfed31a53 100644 --- a/dlt-connector/src/proto/TransactionBody.test.ts +++ b/dlt-connector/src/proto/3_3/TransactionBody.test.ts @@ -1,6 +1,6 @@ import 'reflect-metadata' import { TransactionType } from '@enum/TransactionType' -import { TransactionInput } from '@input/TransactionInput' +// import { TransactionInput } from '@input/TransactionInput' import Decimal from 'decimal.js-light' import { TransactionBody } from './TransactionBody' import { TimestampSeconds } from './TimestampSeconds' @@ -15,7 +15,7 @@ describe('proto/TransactionBodyTest', () => { // init both objects // graphql input object - const transactionInput = new TransactionInput() + /* const transactionInput = new TransactionInput() transactionInput.type = type transactionInput.amount = amount transactionInput.createdAt = createdAt.seconds @@ -34,5 +34,6 @@ describe('proto/TransactionBodyTest', () => { // compare expect(messageBuffer).toStrictEqual(messageBuffer2) + */ }) }) diff --git a/dlt-connector/src/proto/TransactionBody.ts b/dlt-connector/src/proto/3_3/TransactionBody.ts similarity index 69% rename from dlt-connector/src/proto/TransactionBody.ts rename to dlt-connector/src/proto/3_3/TransactionBody.ts index bb90f788a..ec15d3474 100644 --- a/dlt-connector/src/proto/TransactionBody.ts +++ b/dlt-connector/src/proto/3_3/TransactionBody.ts @@ -2,28 +2,34 @@ import { Field, Message, OneOf } from '@apollo/protobufjs' import { CrossGroupType } from '@/graphql/enum/CrossGroupType' -import { TimestampSeconds } from './TimestampSeconds' +import { Timestamp } from './Timestamp' import { GradidoTransfer } from './GradidoTransfer' import { GradidoCreation } from './GradidoCreation' import { GradidoDeferredTransfer } from './GradidoDeferredTransfer' import { GroupFriendsUpdate } from './GroupFriendsUpdate' import { RegisterAddress } from './RegisterAddress' - -/*interface OneofExample { - result: - | { oneofKind: 'value'; value: number } - | { oneofKind: 'error'; error: string } - | { oneofKind: undefined } -}*/ +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { determineCrossGroupType, determineOtherGroup } from '@/controller/TransactionBody' // https://www.npmjs.com/package/@apollo/protobufjs // eslint-disable-next-line no-use-before-define export class TransactionBody extends Message { + public constructor(transaction: TransactionDraft) { + const type = determineCrossGroupType(transaction) + super({ + memo: 'Not implemented yet', + createdAt: new Timestamp(transaction.createdAt), + versionNumber: '3.3', + type, + otherGroup: determineOtherGroup(type, transaction), + }) + } + @Field.d(1, 'string') public memo: string - @Field.d(2, TimestampSeconds) - public createdAt: TimestampSeconds + @Field.d(2, Timestamp) + public createdAt: Timestamp @Field.d(3, 'string') public versionNumber: string diff --git a/dlt-connector/src/proto/TransferAmount.ts b/dlt-connector/src/proto/3_3/TransferAmount.ts similarity index 86% rename from dlt-connector/src/proto/TransferAmount.ts rename to dlt-connector/src/proto/3_3/TransferAmount.ts index f6adc47ff..2abe87322 100644 --- a/dlt-connector/src/proto/TransferAmount.ts +++ b/dlt-connector/src/proto/3_3/TransferAmount.ts @@ -1,3 +1,4 @@ +import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { Field, Message } from '@apollo/protobufjs' // https://www.npmjs.com/package/@apollo/protobufjs diff --git a/dlt-connector/src/proto/GradidoConfirmedTransaction.ts b/dlt-connector/src/proto/GradidoConfirmedTransaction.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/dlt-connector/src/proto/Timestamp.ts b/dlt-connector/src/proto/Timestamp.ts deleted file mode 100644 index 2d3335272..000000000 --- a/dlt-connector/src/proto/Timestamp.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Field, Message } from '@apollo/protobufjs' - -// https://www.npmjs.com/package/@apollo/protobufjs -// eslint-disable-next-line no-use-before-define -export class Timestamp extends Message { - // Number of complete seconds since the start of the epoch - @Field.d(1, 'int64') - public seconds: number - - // Number of nanoseconds since the start of the last second - @Field.d(2, 'int32') - public nanoSeconds: number -} diff --git a/dlt-connector/tsconfig.json b/dlt-connector/tsconfig.json index bdc5e3a9b..c82548df9 100644 --- a/dlt-connector/tsconfig.json +++ b/dlt-connector/tsconfig.json @@ -51,10 +51,12 @@ "@arg/*": ["src/graphql/arg/*"], "@enum/*": ["src/graphql/enum/*"], "@input/*": ["src/graphql/input/*"], + "@model/*": ["src/graphql/model/*"], "@resolver/*": ["src/graphql/resolver/*"], "@scalar/*": ["src/graphql/scalar/*"], "@test/*": ["test/*"], "@proto/*" : ["src/proto/*"], + "@controller/*": ["src/controller/*"], /* external */ }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */