mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
refactor for deferred transfer
This commit is contained in:
parent
a2c3b82b0f
commit
eb424fc6c5
@ -10,11 +10,9 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { DltConnectorClient } from './DltConnectorClient'
|
||||
import { TransactionDraft } from './model/TransactionDraft'
|
||||
|
||||
let con: Connection
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ export enum TransactionType {
|
||||
GROUP_FRIENDS_UPDATE = 'GROUP_FRIENDS_UPDATE',
|
||||
REGISTER_ADDRESS = 'REGISTER_ADDRESS',
|
||||
GRADIDO_DEFERRED_TRANSFER = 'GRADIDO_DEFERRED_TRANSFER',
|
||||
GRADIDO_REDEEM_DEFERRED_TRANSFER = 'GRADIDO_REDEEM_DEFERRED_TRANSFER',
|
||||
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ export class TransactionLinkDeleteToDltRole extends AbstractTransactionToDltRole
|
||||
)
|
||||
.andWhere('TransactionLink.deletedAt IS NOT NULL')
|
||||
.withDeleted()
|
||||
/*
|
||||
const queryBuilder2 = TransactionLink.createQueryBuilder()
|
||||
.leftJoinAndSelect('TransactionLink.user', 'user')
|
||||
.where('TransactionLink.deletedAt IS NOT NULL')
|
||||
@ -41,6 +42,7 @@ export class TransactionLinkDeleteToDltRole extends AbstractTransactionToDltRole
|
||||
.withDeleted()
|
||||
// eslint-disable-next-line camelcase
|
||||
.orderBy({ TransactionLink_deletedAt: 'ASC', User_id: 'ASC' })
|
||||
*/
|
||||
// console.log('query: ', queryBuilder.getSql())
|
||||
this.self = await queryBuilder.getOne()
|
||||
return this
|
||||
@ -67,9 +69,9 @@ export class TransactionLinkDeleteToDltRole extends AbstractTransactionToDltRole
|
||||
draft.amount = this.self.amount.abs().toString()
|
||||
const user = this.self.user
|
||||
draft.user = new UserIdentifier(user.communityUuid, new IdentifierSeed(this.self.code))
|
||||
draft.linkedUser = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID))
|
||||
draft.linkedUser = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID, 1))
|
||||
draft.createdAt = this.self.deletedAt.toISOString()
|
||||
draft.type = TransactionType.GRADIDO_TRANSFER
|
||||
draft.type = TransactionType.GRADIDO_REDEEM_DEFERRED_TRANSFER
|
||||
return draft
|
||||
}
|
||||
|
||||
|
||||
@ -40,10 +40,11 @@ export class TransactionLinkToDltRole extends AbstractTransactionToDltRole<Trans
|
||||
const draft = new TransactionDraft()
|
||||
draft.amount = this.self.amount.abs().toString()
|
||||
const user = this.self.user
|
||||
draft.user = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID))
|
||||
draft.user = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID, 1))
|
||||
draft.linkedUser = new UserIdentifier(user.communityUuid, new IdentifierSeed(this.self.code))
|
||||
draft.createdAt = this.self.createdAt.toISOString()
|
||||
draft.timeoutDate = this.self.validUntil.toISOString()
|
||||
draft.timeoutDuration = (this.self.validUntil.getTime() - this.self.createdAt.getTime()) / 1000
|
||||
draft.memo = this.self.memo
|
||||
draft.type = TransactionType.GRADIDO_DEFERRED_TRANSFER
|
||||
return draft
|
||||
}
|
||||
|
||||
@ -81,13 +81,14 @@ export class TransactionToDltRole extends AbstractTransactionToDltRole<Transacti
|
||||
} else {
|
||||
draft.user = new UserIdentifier(
|
||||
this.self.userCommunityUuid,
|
||||
new CommunityUser(this.self.userGradidoID),
|
||||
new CommunityUser(this.self.userGradidoID, 1),
|
||||
)
|
||||
}
|
||||
draft.linkedUser = new UserIdentifier(
|
||||
this.self.linkedUserCommunityUuid,
|
||||
new CommunityUser(this.self.linkedUserGradidoID),
|
||||
new CommunityUser(this.self.linkedUserGradidoID, 1),
|
||||
)
|
||||
draft.memo = this.self.memo
|
||||
draft.createdAt = this.self.balanceDate.toISOString()
|
||||
draft.targetDate = this.self.creationDate?.toISOString()
|
||||
return draft
|
||||
|
||||
@ -10,12 +10,13 @@ export class TransactionDraft {
|
||||
linkedUser?: UserIdentifier
|
||||
// not used for register address
|
||||
amount?: string
|
||||
memo?: string
|
||||
type: TransactionType
|
||||
createdAt: string
|
||||
// only for creation transaction
|
||||
targetDate?: string
|
||||
// only for deferred transaction
|
||||
timeoutDate?: string
|
||||
timeoutDuration?: number
|
||||
// only for register address
|
||||
accountType?: AccountType
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"dotenv": "10.0.0",
|
||||
"express": "4.17.1",
|
||||
"express-slow-down": "^2.0.1",
|
||||
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#master",
|
||||
"gradido-blockchain-js": "git+https://github.com/gradido/gradido-blockchain-js#1c75576",
|
||||
"graphql": "^16.7.1",
|
||||
"graphql-request": "^6.1.0",
|
||||
"graphql-scalars": "^1.22.2",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { AddressType, ConfirmedTransaction, stringToAddressType } from 'gradido-blockchain-js'
|
||||
import { AddressType, ConfirmedTransaction, MemoryBlock, stringToAddressType } from 'gradido-blockchain-js'
|
||||
import JsonRpcClient from 'jsonrpc-ts-client'
|
||||
import { JsonRpcEitherResponse } from 'jsonrpc-ts-client/dist/types/utils/jsonrpc'
|
||||
|
||||
@ -121,4 +121,36 @@ async function getAddressType(pubkey: Buffer, iotaTopic: string): Promise<Addres
|
||||
)
|
||||
}
|
||||
|
||||
export { getTransaction, getLastTransaction, getTransactions, getAddressType }
|
||||
async function getTransactionsForAccount(
|
||||
pubkey: MemoryBlock,
|
||||
iotaTopic: string,
|
||||
maxResultCount = 0,
|
||||
firstTransactionNr = 1,
|
||||
): Promise<ConfirmedTransaction[] | undefined> {
|
||||
const parameter = {
|
||||
pubkey: pubkey.convertToHex(),
|
||||
format: 'base64',
|
||||
firstTransactionNr,
|
||||
maxResultCount,
|
||||
communityId: iotaTopic,
|
||||
}
|
||||
logger.info('call listtransactionsforaddress on Node Server via jsonrpc 2.0', parameter)
|
||||
const response = await client.exec<ConfirmedTransactionList>(
|
||||
'listtransactionsforaddress',
|
||||
parameter,
|
||||
)
|
||||
return resolveResponse(response, (result: ConfirmedTransactionList) => {
|
||||
logger.debug('GradidoNode used time', result.timeUsed)
|
||||
return result.transactions.map((transactionBase64) =>
|
||||
confirmedTransactionFromBase64(transactionBase64),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
getTransaction,
|
||||
getLastTransaction,
|
||||
getTransactions,
|
||||
getAddressType,
|
||||
getTransactionsForAccount,
|
||||
}
|
||||
|
||||
72
dlt-connector/src/data/KeyPairIdentifier.ts
Normal file
72
dlt-connector/src/data/KeyPairIdentifier.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4sToMemoryBlock } from '@/utils/typeConverter'
|
||||
|
||||
export class KeyPairIdentifier {
|
||||
// used for community key pair if it is only parameter or for user key pair
|
||||
communityUuid?: string
|
||||
// if set calculate key pair from seed, ignore all other parameter
|
||||
seed?: string
|
||||
// used for user key pair and account key pair, need also communityUuid
|
||||
userUuid?: string
|
||||
// used for account key pair, need also userUuid
|
||||
accountNr?: number
|
||||
|
||||
public constructor(input: UserIdentifier | string | undefined = undefined) {
|
||||
if (input instanceof UserIdentifier) {
|
||||
if (input.seed !== undefined) {
|
||||
this.seed = input.seed.seed
|
||||
} else {
|
||||
this.communityUuid = input.communityUuid
|
||||
this.userUuid = input.communityUser?.uuid
|
||||
this.accountNr = input.communityUser?.accountNr
|
||||
}
|
||||
} else if (typeof input === 'string') {
|
||||
this.communityUuid = input
|
||||
}
|
||||
}
|
||||
|
||||
isCommunityKeyPair(): boolean {
|
||||
return this.communityUuid !== undefined && this.userUuid === undefined
|
||||
}
|
||||
|
||||
isSeedKeyPair(): boolean {
|
||||
return this.seed !== undefined
|
||||
}
|
||||
|
||||
isUserKeyPair(): boolean {
|
||||
return (
|
||||
this.communityUuid !== undefined &&
|
||||
this.userUuid !== undefined &&
|
||||
this.accountNr === undefined
|
||||
)
|
||||
}
|
||||
|
||||
isAccountKeyPair(): boolean {
|
||||
return (
|
||||
this.communityUuid !== undefined &&
|
||||
this.userUuid !== undefined &&
|
||||
this.accountNr !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
getKey(): string {
|
||||
if (this.seed && this.isSeedKeyPair()) {
|
||||
return this.seed
|
||||
} else if (this.communityUuid && this.isCommunityKeyPair()) {
|
||||
return this.communityUuid
|
||||
}
|
||||
if (this.userUuid && this.communityUuid) {
|
||||
const communityUserHash = uuid4sToMemoryBlock([this.userUuid, this.communityUuid])
|
||||
.calculateHash()
|
||||
.convertToHex()
|
||||
if (this.isUserKeyPair()) {
|
||||
return communityUserHash
|
||||
}
|
||||
if (this.accountNr && this.isAccountKeyPair()) {
|
||||
return communityUserHash + this.accountNr.toString()
|
||||
}
|
||||
}
|
||||
throw new LogError('KeyPairIdentifier: unhandled input type', this)
|
||||
}
|
||||
}
|
||||
2
dlt-connector/src/graphql/const.ts
Normal file
2
dlt-connector/src/graphql/const.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const MEMO_MAX_CHARS = 255
|
||||
export const MEMO_MIN_CHARS = 5
|
||||
@ -8,6 +8,7 @@ export enum InputTransactionType {
|
||||
GROUP_FRIENDS_UPDATE = 'GROUP_FRIENDS_UPDATE',
|
||||
REGISTER_ADDRESS = 'REGISTER_ADDRESS',
|
||||
GRADIDO_DEFERRED_TRANSFER = 'GRADIDO_DEFERRED_TRANSFER',
|
||||
GRADIDO_REDEEM_DEFERRED_TRANSFER = 'GRADIDO_REDEEM_DEFERRED_TRANSFER',
|
||||
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import { registerEnumType } from 'type-graphql'
|
||||
export enum TransactionErrorType {
|
||||
NOT_IMPLEMENTED_YET = 'Not Implemented yet',
|
||||
MISSING_PARAMETER = 'Missing parameter',
|
||||
INVALID_PARAMETER = 'Invalid parameter',
|
||||
ALREADY_EXIST = 'Already exist',
|
||||
DB_ERROR = 'DB Error',
|
||||
PROTO_DECODE_ERROR = 'Proto Decode Error',
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
import { InputTransactionType } from '@enum/InputTransactionType'
|
||||
import { isValidDateString, isValidNumberString } from '@validator/DateString'
|
||||
import { IsEnum, IsObject, ValidateNested } from 'class-validator'
|
||||
import { IsEnum, IsObject, IsPositive, MaxLength, MinLength, ValidateNested } from 'class-validator'
|
||||
import { InputType, Field } from 'type-graphql'
|
||||
|
||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from '@/graphql//const'
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
|
||||
import { UserIdentifier } from './UserIdentifier'
|
||||
@ -26,6 +27,11 @@ export class TransactionDraft {
|
||||
@isValidNumberString()
|
||||
amount?: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@MaxLength(MEMO_MAX_CHARS)
|
||||
@MinLength(MEMO_MIN_CHARS)
|
||||
memo?: string
|
||||
|
||||
@Field(() => InputTransactionType)
|
||||
@IsEnum(InputTransactionType)
|
||||
type: InputTransactionType
|
||||
@ -40,9 +46,10 @@ export class TransactionDraft {
|
||||
targetDate?: string
|
||||
|
||||
// only for deferred transaction
|
||||
@Field(() => String, { nullable: true })
|
||||
@isValidDateString()
|
||||
timeoutDate?: string
|
||||
// duration in seconds
|
||||
@Field(() => Number, { nullable: true })
|
||||
@IsPositive()
|
||||
timeoutDuration?: number
|
||||
|
||||
// only for register address
|
||||
@Field(() => AccountType, { nullable: true })
|
||||
|
||||
@ -3,9 +3,9 @@ import { AddressType_NONE } from 'gradido-blockchain-js'
|
||||
import { Arg, Query, Resolver } from 'type-graphql'
|
||||
|
||||
import { getAddressType } from '@/client/GradidoNode'
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { KeyPairCalculation } from '@/interactions/keyPairCalculation/KeyPairCalculation.context'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { TransactionErrorType } from '../enum/TransactionErrorType'
|
||||
@ -17,7 +17,7 @@ import { TransactionResult } from '../model/TransactionResult'
|
||||
export class AccountResolver {
|
||||
@Query(() => Boolean)
|
||||
async isAccountExist(@Arg('data') userIdentifier: UserIdentifier): Promise<boolean> {
|
||||
const accountKeyPair = await KeyPairCalculation(userIdentifier)
|
||||
const accountKeyPair = await KeyPairCalculation(new KeyPairIdentifier(userIdentifier))
|
||||
const publicKey = accountKeyPair.getPublicKey()
|
||||
if (!publicKey) {
|
||||
throw new TransactionResult(
|
||||
|
||||
@ -26,6 +26,6 @@ export class ForeignCommunityKeyPairRole extends AbstractRemoteKeyPairRole {
|
||||
if (!communityRoot) {
|
||||
throw new LogError('invalid confirmed transaction')
|
||||
}
|
||||
return new KeyPairEd25519(communityRoot.getPubkey())
|
||||
return new KeyPairEd25519(communityRoot.getPublicKey())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { AccountKeyPairRole } from './AccountKeyPair.role'
|
||||
import { ForeignCommunityKeyPairRole } from './ForeignCommunityKeyPair.role'
|
||||
@ -15,7 +16,7 @@ import { UserKeyPairRole } from './UserKeyPair.role'
|
||||
* @DCI-Context
|
||||
* Context for calculating key pair for signing transactions
|
||||
*/
|
||||
export async function KeyPairCalculation(input: UserIdentifier | string): Promise<KeyPairEd25519> {
|
||||
export async function KeyPairCalculation(input: KeyPairIdentifier): Promise<KeyPairEd25519> {
|
||||
const cache = KeyPairCacheManager.getInstance()
|
||||
|
||||
// Try cache lookup first
|
||||
@ -24,33 +25,49 @@ export async function KeyPairCalculation(input: UserIdentifier | string): Promis
|
||||
return keyPair
|
||||
}
|
||||
|
||||
const retrieveKeyPair = async (input: UserIdentifier | string): Promise<KeyPairEd25519> => {
|
||||
if (input instanceof UserIdentifier && input.seed) {
|
||||
return new LinkedTransactionKeyPairRole(input.seed.seed).generateKeyPair()
|
||||
const retrieveKeyPair = async (input: KeyPairIdentifier): Promise<KeyPairEd25519> => {
|
||||
if (input.isSeedKeyPair() && input.seed) {
|
||||
return new LinkedTransactionKeyPairRole(input.seed).generateKeyPair()
|
||||
}
|
||||
if (!input.communityUuid) {
|
||||
throw new LogError('missing community id')
|
||||
}
|
||||
|
||||
const communityUUID = input instanceof UserIdentifier ? input.communityUuid : input
|
||||
|
||||
// If input does not belong to the home community, handle as remote key pair
|
||||
if (cache.getHomeCommunityUUID() !== communityUUID) {
|
||||
if (cache.getHomeCommunityUUID() !== input.communityUuid) {
|
||||
const role =
|
||||
input instanceof UserIdentifier
|
||||
? new RemoteAccountKeyPairRole(input)
|
||||
: new ForeignCommunityKeyPairRole(input)
|
||||
: new ForeignCommunityKeyPairRole(input.communityUuid)
|
||||
return await role.retrieveKeyPair()
|
||||
}
|
||||
|
||||
let communityKeyPair = cache.findKeyPair(communityUUID)
|
||||
let communityKeyPair = cache.findKeyPair(input)
|
||||
if (!communityKeyPair) {
|
||||
communityKeyPair = new HomeCommunityKeyPairRole().generateKeyPair()
|
||||
cache.addKeyPair(communityUUID, communityKeyPair)
|
||||
}
|
||||
if (input instanceof UserIdentifier) {
|
||||
const userKeyPair = new UserKeyPairRole(input, communityKeyPair).generateKeyPair()
|
||||
const accountNr = input.communityUser?.accountNr ?? 1
|
||||
return new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
|
||||
if (input.isCommunityKeyPair()) {
|
||||
return communityKeyPair
|
||||
}
|
||||
return communityKeyPair
|
||||
const userKeyPairIdentifier = new KeyPairIdentifier()
|
||||
userKeyPairIdentifier.communityUuid = input.communityUuid
|
||||
userKeyPairIdentifier.userUuid = input.userUuid
|
||||
|
||||
let userKeyPair = cache.findKeyPair(userKeyPairIdentifier)
|
||||
if (!userKeyPair && userKeyPairIdentifier.userUuid) {
|
||||
userKeyPair = new UserKeyPairRole(
|
||||
userKeyPairIdentifier.userUuid,
|
||||
communityKeyPair,
|
||||
).generateKeyPair()
|
||||
}
|
||||
if (!userKeyPair) {
|
||||
throw new LogError("couldn't generate user key pair")
|
||||
}
|
||||
if (input.isUserKeyPair()) {
|
||||
return userKeyPair
|
||||
}
|
||||
|
||||
const accountNr = input.accountNr ?? 1
|
||||
return new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
|
||||
}
|
||||
|
||||
keyPair = await retrieveKeyPair(input)
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { uuid4ToBuffer } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractKeyPairRole } from './AbstractKeyPair.role'
|
||||
|
||||
export class UserKeyPairRole extends AbstractKeyPairRole {
|
||||
public constructor(private user: UserIdentifier, private communityKeys: KeyPairEd25519) {
|
||||
public constructor(private userUuid: string, private communityKeys: KeyPairEd25519) {
|
||||
super()
|
||||
}
|
||||
|
||||
public generateKeyPair(): KeyPairEd25519 {
|
||||
// example gradido id: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16 =>
|
||||
// wholeHex: '03857ac19cc2483e8a91e5b10f5b8d16']
|
||||
if (!this.user.communityUser) {
|
||||
throw new LogError('missing community user')
|
||||
}
|
||||
const wholeHex = uuid4ToBuffer(this.user.communityUser.uuid)
|
||||
|
||||
const wholeHex = uuid4ToBuffer(this.userUuid)
|
||||
const parts = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
|
||||
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { CommunityDraft } from '@/graphql/input/CommunityDraft'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import {
|
||||
@ -27,7 +28,7 @@ export class CommunityRootTransactionRole extends AbstractTransactionRole {
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const communityKeyPair = await KeyPairCalculation(this.self.uuid)
|
||||
const communityKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.uuid))
|
||||
const gmwKeyPair = communityKeyPair.deriveChild(
|
||||
hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX),
|
||||
)
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
|
||||
import {
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoUnit,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
@ -40,15 +48,25 @@ export class CreationTransactionRole extends AbstractTransactionRole {
|
||||
if (!this.self.amount) {
|
||||
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'creation: amount missing')
|
||||
}
|
||||
if (!this.self.memo) {
|
||||
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'creation: memo missing')
|
||||
}
|
||||
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const recipientKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const signerKeyPair = await KeyPairCalculation(this.self.linkedUser)
|
||||
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
|
||||
const signerKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.linkedUser))
|
||||
const homeCommunityKeyPair = await KeyPairCalculation(
|
||||
new KeyPairIdentifier(KeyPairCacheManager.getInstance().getHomeCommunityUUID()),
|
||||
)
|
||||
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setMemo('dummy memo for creation')
|
||||
.addMemo(new EncryptedMemo(this.self.memo, new AuthenticatedEncryption(homeCommunityKeyPair)))
|
||||
.setTransactionCreation(
|
||||
new TransferAmount(recipientKeyPair.getPublicKey(), this.self.amount.toString()),
|
||||
new TransferAmount(
|
||||
recipientKeyPair.getPublicKey(),
|
||||
GradidoUnit.fromString(this.self.amount),
|
||||
),
|
||||
new Date(this.self.targetDate),
|
||||
)
|
||||
.sign(signerKeyPair)
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { GradidoTransactionBuilder, GradidoTransfer, TransferAmount } from 'gradido-blockchain-js'
|
||||
import {
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
@ -37,27 +45,42 @@ export class DeferredTransferTransactionRole extends AbstractTransactionRole {
|
||||
'deferred transfer: amount missing',
|
||||
)
|
||||
}
|
||||
if (!this.self.timeoutDate) {
|
||||
if (!this.self.memo) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.MISSING_PARAMETER,
|
||||
'deferred transfer: timeout date missing',
|
||||
'deferred transfer: memo missing',
|
||||
)
|
||||
}
|
||||
if (!this.self.timeoutDuration) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.MISSING_PARAMETER,
|
||||
'deferred transfer: timeout duration missing',
|
||||
)
|
||||
}
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const senderKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)
|
||||
const senderKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
|
||||
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.linkedUser))
|
||||
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setMemo('dummy memo for transfer')
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
this.self.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setDeferredTransfer(
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), this.self.amount.toString()),
|
||||
new TransferAmount(
|
||||
senderKeyPair.getPublicKey(),
|
||||
GradidoUnit.fromString(this.self.amount),
|
||||
),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
),
|
||||
new Date(this.self.timeoutDate),
|
||||
this.self.timeoutDuration,
|
||||
)
|
||||
builder.sign(senderKeyPair)
|
||||
.sign(senderKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
import {
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
|
||||
import { getTransactionsForAccount } from '@/client/GradidoNode'
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
import { communityUuidToTopic, uuid4ToHash } from '@/utils/typeConverter'
|
||||
|
||||
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
|
||||
export class RedeemDeferredTransferTransactionRole extends AbstractTransactionRole {
|
||||
private linkedUser: UserIdentifier
|
||||
constructor(protected self: TransactionDraft) {
|
||||
super()
|
||||
if (!this.self.linkedUser) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.MISSING_PARAMETER,
|
||||
'transfer: linked user missing',
|
||||
)
|
||||
}
|
||||
this.linkedUser = this.self.linkedUser
|
||||
}
|
||||
|
||||
getSenderCommunityUuid(): string {
|
||||
return this.self.user.communityUuid
|
||||
}
|
||||
|
||||
getRecipientCommunityUuid(): string {
|
||||
return this.linkedUser.communityUuid
|
||||
}
|
||||
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
if (!this.self.amount) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.MISSING_PARAMETER,
|
||||
'redeem deferred transfer: amount missing',
|
||||
)
|
||||
}
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const senderKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
|
||||
const senderPublicKey = senderKeyPair.getPublicKey()
|
||||
if (!senderPublicKey) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.INVALID_PARAMETER,
|
||||
"redeem deferred transfer: couldn't calculate sender public key",
|
||||
)
|
||||
}
|
||||
// load deferred transfer transaction from gradido node
|
||||
const transactions = await getTransactionsForAccount(
|
||||
senderPublicKey,
|
||||
communityUuidToTopic(this.getSenderCommunityUuid()),
|
||||
)
|
||||
if (!transactions || transactions.length !== 1) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.NOT_FOUND,
|
||||
"redeem deferred transfer: couldn't find deferred transfer on Gradido Node",
|
||||
)
|
||||
}
|
||||
const deferredTransfer = transactions[0]
|
||||
const deferredTransferBody = deferredTransfer.getGradidoTransaction()?.getTransactionBody()
|
||||
if (!deferredTransferBody) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.NOT_FOUND,
|
||||
"redeem deferred transfer: couldn't deserialize deferred transfer from Gradido Node",
|
||||
)
|
||||
}
|
||||
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.linkedUser))
|
||||
|
||||
// TODO: fix getMemos in gradido-blockchain-js to return correct data
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.addMemo(deferredTransferBody.getMemos()[0])
|
||||
.setRedeemDeferredTransfer(
|
||||
deferredTransfer.getId(),
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(
|
||||
senderKeyPair.getPublicKey(),
|
||||
GradidoUnit.fromString(this.self.amount),
|
||||
),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
),
|
||||
)
|
||||
const senderCommunity = this.self.user.communityUuid
|
||||
const recipientCommunity = this.linkedUser.communityUuid
|
||||
if (senderCommunity !== recipientCommunity) {
|
||||
// we have a cross group transaction
|
||||
builder
|
||||
.setSenderCommunity(uuid4ToHash(senderCommunity).convertToHex())
|
||||
.setRecipientCommunity(uuid4ToHash(recipientCommunity).convertToHex())
|
||||
}
|
||||
builder.sign(senderKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
|
||||
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { TransactionError } from '@/graphql/model/TransactionError'
|
||||
@ -40,17 +41,25 @@ export class RegisterAddressTransactionRole extends AbstractTransactionRole {
|
||||
}
|
||||
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const communityKeyPair = await KeyPairCalculation(this.self.user.communityUuid)
|
||||
const accountKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const communityKeyPair = await KeyPairCalculation(
|
||||
new KeyPairIdentifier(this.self.user.communityUuid),
|
||||
)
|
||||
const keyPairIdentifer = new KeyPairIdentifier(this.self.user)
|
||||
const accountKeyPair = await KeyPairCalculation(keyPairIdentifer)
|
||||
// unsetting accountNr change identifier from account key pair to user key pair
|
||||
keyPairIdentifer.accountNr = undefined
|
||||
const userKeyPair = await KeyPairCalculation(keyPairIdentifer)
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setRegisterAddress(
|
||||
accountKeyPair.getPublicKey(),
|
||||
userKeyPair.getPublicKey(),
|
||||
accountTypeToAddressType(this.self.accountType),
|
||||
uuid4ToHash(this.self.user.communityUser.uuid),
|
||||
accountKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(communityKeyPair)
|
||||
.sign(accountKeyPair)
|
||||
.sign(userKeyPair)
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,12 +17,13 @@ import { TransactionRecipe } from '@/graphql/model/TransactionRecipe'
|
||||
import { TransactionResult } from '@/graphql/model/TransactionResult'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { uuid4ToHash } from '@/utils/typeConverter'
|
||||
import { communityUuidToTopic } from '@/utils/typeConverter'
|
||||
|
||||
import { AbstractTransactionRole } from './AbstractTransaction.role'
|
||||
import { CommunityRootTransactionRole } from './CommunityRootTransaction.role'
|
||||
import { CreationTransactionRole } from './CreationTransaction.role'
|
||||
import { DeferredTransferTransactionRole } from './DeferredTransferTransaction.role'
|
||||
import { RedeemDeferredTransferTransactionRole } from './RedeemDeferredTransferTransaction.role'
|
||||
import { RegisterAddressTransactionRole } from './RegisterAddressTransaction.role'
|
||||
import { TransferTransactionRole } from './TransferTransaction.role'
|
||||
|
||||
@ -87,6 +88,9 @@ export async function SendToIotaContext(
|
||||
case InputTransactionType.GRADIDO_DEFERRED_TRANSFER:
|
||||
role = new DeferredTransferTransactionRole(input)
|
||||
break
|
||||
case InputTransactionType.GRADIDO_REDEEM_DEFERRED_TRANSFER:
|
||||
role = new RedeemDeferredTransferTransactionRole(input)
|
||||
break
|
||||
default:
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.NOT_IMPLEMENTED_YET,
|
||||
@ -104,22 +108,19 @@ export async function SendToIotaContext(
|
||||
validate(outboundTransaction)
|
||||
const outboundIotaMessageId = await sendViaIota(
|
||||
outboundTransaction,
|
||||
uuid4ToHash(role.getSenderCommunityUuid()).convertToHex(),
|
||||
communityUuidToTopic(role.getSenderCommunityUuid()),
|
||||
)
|
||||
builder.setParentMessageId(outboundIotaMessageId)
|
||||
const inboundTransaction = builder.buildInbound()
|
||||
validate(inboundTransaction)
|
||||
await sendViaIota(
|
||||
inboundTransaction,
|
||||
uuid4ToHash(role.getRecipientCommunityUuid()).convertToHex(),
|
||||
)
|
||||
await sendViaIota(inboundTransaction, communityUuidToTopic(role.getRecipientCommunityUuid()))
|
||||
return new TransactionResult(new TransactionRecipe(outboundTransaction, outboundIotaMessageId))
|
||||
} else {
|
||||
const transaction = builder.build()
|
||||
validate(transaction)
|
||||
const iotaMessageId = await sendViaIota(
|
||||
transaction,
|
||||
uuid4ToHash(role.getSenderCommunityUuid()).convertToHex(),
|
||||
communityUuidToTopic(role.getSenderCommunityUuid()),
|
||||
)
|
||||
return new TransactionResult(new TransactionRecipe(transaction, iotaMessageId))
|
||||
}
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
|
||||
import {
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoUnit,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
@ -35,14 +42,27 @@ export class TransferTransactionRole extends AbstractTransactionRole {
|
||||
if (!this.self.amount) {
|
||||
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'transfer: amount missing')
|
||||
}
|
||||
if (!this.self.memo) {
|
||||
throw new TransactionError(
|
||||
TransactionErrorType.MISSING_PARAMETER,
|
||||
'deferred transfer: memo missing',
|
||||
)
|
||||
}
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const senderKeyPair = await KeyPairCalculation(this.self.user)
|
||||
const recipientKeyPair = await KeyPairCalculation(this.linkedUser)
|
||||
const senderKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.self.user))
|
||||
const recipientKeyPair = await KeyPairCalculation(new KeyPairIdentifier(this.linkedUser))
|
||||
|
||||
builder
|
||||
.setCreatedAt(new Date(this.self.createdAt))
|
||||
.setMemo('dummy memo for transfer')
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
this.self.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), this.self.amount.toString()),
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), GradidoUnit.fromString(this.self.amount)),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
const senderCommunity = this.self.user.communityUuid
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { KeyPairIdentifier } from '@/data/KeyPairIdentifier'
|
||||
import { logger } from '@/logging/logger'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
||||
// and ../federation/client/FederationClientFactory.ts
|
||||
@ -45,30 +44,19 @@ export class KeyPairCacheManager {
|
||||
return this.homeCommunityUUID
|
||||
}
|
||||
|
||||
public findKeyPair(input: UserIdentifier | string): KeyPairEd25519 | undefined {
|
||||
return this.cache.get(this.getKey(input))
|
||||
public findKeyPair(input: KeyPairIdentifier): KeyPairEd25519 | undefined {
|
||||
return this.cache.get(input.getKey())
|
||||
}
|
||||
|
||||
public addKeyPair(input: UserIdentifier | string, keyPair: KeyPairEd25519): void {
|
||||
const key = this.getKey(input)
|
||||
public addKeyPair(input: KeyPairIdentifier, keyPair: KeyPairEd25519): void {
|
||||
const key = input.getKey()
|
||||
if (this.cache.has(key)) {
|
||||
logger.warn('key already exist, cannot add', key)
|
||||
logger.warn('key already exist, cannot add', {
|
||||
key,
|
||||
publicKey: keyPair.getPublicKey()?.convertToHex(),
|
||||
})
|
||||
return
|
||||
}
|
||||
this.cache.set(key, keyPair)
|
||||
}
|
||||
|
||||
protected getKey(input: UserIdentifier | string): string {
|
||||
if (input instanceof UserIdentifier) {
|
||||
if (input.communityUser) {
|
||||
return input.communityUser.uuid
|
||||
} else if (input.seed) {
|
||||
return input.seed.seed
|
||||
}
|
||||
throw new LogError('unhandled branch')
|
||||
} else if (typeof input === 'string') {
|
||||
return input
|
||||
}
|
||||
throw new LogError('unhandled input type')
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,6 +32,14 @@ export const uuid4ToMemoryBlock = (uuid: string): MemoryBlock => {
|
||||
return MemoryBlock.fromHex(uuid.replace(/-/g, ''))
|
||||
}
|
||||
|
||||
export const uuid4sToMemoryBlock = (uuid: string[]): MemoryBlock => {
|
||||
let resultHexString = ''
|
||||
for (let i = 0; i < uuid.length; i++) {
|
||||
resultHexString += uuid[i].replace(/-/g, '')
|
||||
}
|
||||
return MemoryBlock.fromHex(resultHexString)
|
||||
}
|
||||
|
||||
export const uuid4ToHash = (communityUUID: string): MemoryBlock => {
|
||||
return uuid4ToMemoryBlock(communityUUID).calculateHash()
|
||||
}
|
||||
@ -40,6 +48,10 @@ export const base64ToBuffer = (base64: string): Buffer => {
|
||||
return Buffer.from(base64, 'base64')
|
||||
}
|
||||
|
||||
export const communityUuidToTopic = (communityUUID: string): string => {
|
||||
return uuid4ToHash(communityUUID).convertToHex()
|
||||
}
|
||||
|
||||
export function getEnumValue<T extends Record<string, unknown>>(
|
||||
enumType: T,
|
||||
value: number | string,
|
||||
|
||||
@ -3277,9 +3277,9 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
|
||||
"gradido-blockchain-js@git+https://github.com/gradido/gradido-blockchain-js#master":
|
||||
"gradido-blockchain-js@git+https://github.com/gradido/gradido-blockchain-js#1c75576":
|
||||
version "0.0.1"
|
||||
resolved "git+https://github.com/gradido/gradido-blockchain-js#5e7bc50af82d30ef0fdbe48414b1f916c592b6f4"
|
||||
resolved "git+https://github.com/gradido/gradido-blockchain-js#1c755763b7f3f71c2ee9f396da5e9512fa666ee4"
|
||||
dependencies:
|
||||
bindings "^1.5.0"
|
||||
nan "^2.20.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user