diff --git a/database/migration/migrations/0097-fix_production_data_for_blockchain2.ts b/database/migration/migrations/0097-fix_production_data_for_blockchain2.ts index f7c99561d..9f67e267a 100644 --- a/database/migration/migrations/0097-fix_production_data_for_blockchain2.ts +++ b/database/migration/migrations/0097-fix_production_data_for_blockchain2.ts @@ -251,6 +251,18 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis WHERE u.created_at >= t.first_date; ;`) + // linked user also, but we need to use gradido_id as index, because on cross group transactions linked_user_id is empty + await queryFn(` + UPDATE users u + LEFT JOIN ( + SELECT linked_user_gradido_id , MIN(balance_date) AS first_date + FROM transactions + GROUP BY linked_user_gradido_id + ) t ON t.linked_user_gradido_id = u.gradido_id + SET u.created_at = DATE_SUB(t.first_date, INTERVAL 1 SECOND) + WHERE u.created_at >= t.first_date; + ;`) + /** * Fix 4: Ensure all transaction memos meet the minimum length requirement. * diff --git a/dlt-connector/src/interactions/sendToHiero/CreationTransaction.role.ts b/dlt-connector/src/interactions/sendToHiero/CreationTransaction.role.ts index 4b0f7aefd..4f4d653a6 100644 --- a/dlt-connector/src/interactions/sendToHiero/CreationTransaction.role.ts +++ b/dlt-connector/src/interactions/sendToHiero/CreationTransaction.role.ts @@ -36,7 +36,7 @@ export class CreationTransactionRole extends AbstractTransactionRole { } getRecipientCommunityTopicId(): HieroId { - throw new Error('creation: cannot be used as cross group transaction') + return this.creationTransaction.user.communityTopicId } public async getGradidoTransactionBuilder(): Promise { diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/blockchain.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/blockchain.ts index 5091a122b..fd70dcca6 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/blockchain.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/blockchain.ts @@ -1,7 +1,7 @@ import { AccountBalances, Filter, - GradidoTransactionBuilder, + GradidoTransaction, HieroAccountId, InMemoryBlockchain, LedgerAnchor, @@ -11,12 +11,12 @@ import { NotEnoughGradidoBalanceError } from './errors' export const defaultHieroAccount = new HieroAccountId(0, 0, 2) export function addToBlockchain( - builder: GradidoTransactionBuilder, + transaction: GradidoTransaction, blockchain: InMemoryBlockchain, ledgerAnchor: LedgerAnchor, accountBalances: AccountBalances, ): boolean { - const transaction = builder.build() + try { const result = blockchain.createAndAddConfirmedTransactionExtern( transaction, @@ -33,7 +33,7 @@ export function addToBlockchain( throw new NotEnoughGradidoBalanceError(needed, exist) } } - const lastTransaction = blockchain.findOne(Filter.LAST_TRANSACTION) + const lastTransaction = blockchain.findOne(Filter.LAST_TRANSACTION) throw new Error(`Transaction ${transaction.toJson(true)} not added: ${error}, last transaction was: ${lastTransaction?.getConfirmedTransaction()?.toJson(true)}`) } } diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/bootstrap.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/bootstrap.ts index 9bc6f6a57..c1f5bc1ae 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/bootstrap.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/bootstrap.ts @@ -1,5 +1,5 @@ import { randomBytes } from 'node:crypto' -import { AccountBalances, GradidoTransactionBuilder, InMemoryBlockchainProvider, LedgerAnchor } from 'gradido-blockchain-js' +import { Abstract, AccountBalances, GradidoTransactionBuilder, InMemoryBlockchain, InMemoryBlockchainProvider, LedgerAnchor } from 'gradido-blockchain-js' import * as v from 'valibot' import { CONFIG } from '../../config' import { deriveFromSeed } from '../../data/deriveKeyPair' @@ -27,15 +27,13 @@ async function bootstrapCommunities(context: Context): Promise() for (const communityDb of communitiesDb) { - let alias = communityDb.name - if (communityNames.has(communityDb.name)) { + let alias = communityDb.name.toLowerCase() + if (communityNames.has(alias)) { alias = communityDb.communityUuid } else { - communityNames.add(communityDb.name) + communityNames.add(alias) } - const blockchain = InMemoryBlockchainProvider.getInstance().findBlockchain( - alias, - ) + const blockchain = InMemoryBlockchainProvider.getInstance().findBlockchain(alias) if (!blockchain) { throw new Error(`Couldn't create Blockchain for community ${alias}`) } @@ -66,22 +64,24 @@ async function bootstrapCommunities(context: Context): Promise [index('user_id').on(table.userId)], diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/AbstractSync.role.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/AbstractSync.role.ts index adb84e0c9..443c68b4a 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/AbstractSync.role.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/AbstractSync.role.ts @@ -29,7 +29,8 @@ export abstract class AbstractSyncRole { }) } - getLastBalanceForUser(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, communityId: string = ''): Balance { + getLastBalanceForUser(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, communityId: string + ): Balance { if (publicKey.isEmpty()) { throw new Error('publicKey is empty') } @@ -45,7 +46,7 @@ export abstract class AbstractSyncRole { if (!senderLastAccountBalance) { return new Balance(publicKey, communityId) } - return Balance.fromAccountBalance(senderLastAccountBalance, lastConfirmedTransaction.getConfirmedAt().getDate()) + return Balance.fromAccountBalance(senderLastAccountBalance, lastConfirmedTransaction.getConfirmedAt().getDate(), communityId) } logLastBalanceChangingTransactions(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, transactionCount: number = 5) { diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/CreationsSync.role.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/CreationsSync.role.ts index a509a15ba..9546c07b9 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/CreationsSync.role.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/CreationsSync.role.ts @@ -95,9 +95,10 @@ export class CreationsSyncRole extends AbstractSyncRole { ), ) .setTransactionCreation( - new TransferAmount(recipientKeyPair.getPublicKey(), item.amount), + new TransferAmount(recipientKeyPair.getPublicKey(), item.amount, communityContext.communityId), item.contributionDate, ) + .setRecipientCommunity(communityContext.communityId) .sign(signerKeyPair) } @@ -107,7 +108,7 @@ export class CreationsSyncRole extends AbstractSyncRole { recipientPublicKey: MemoryBlockPtr ): AccountBalances { const accountBalances = new AccountBalances() - const balance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain) + const balance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain, communityContext.communityId) // calculate decay since last balance with legacy calculation method balance.updateLegacyDecay(item.amount, item.confirmedAt) @@ -136,7 +137,7 @@ export class CreationsSyncRole extends AbstractSyncRole { try { addToBlockchain( - this.buildTransaction(item, communityContext, recipientKeyPair, signerKeyPair), + this.buildTransaction(item, communityContext, recipientKeyPair, signerKeyPair).build(), blockchain, new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_CONTRIBUTION_ID), this.calculateAccountBalances(item, communityContext, recipientPublicKey), diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/DeletedTransactionLinksSync.role.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/DeletedTransactionLinksSync.role.ts index e772509f9..a1e6d046e 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/DeletedTransactionLinksSync.role.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/DeletedTransactionLinksSync.role.ts @@ -74,6 +74,7 @@ export class DeletedTransactionLinksSyncRole extends AbstractSyncRole { } buildTransaction( + communityContext: CommunityContext, item: TransactionDb, senderKeyPair: KeyPairEd25519, recipientKeyPair: KeyPairEd25519, @@ -94,9 +93,10 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole { ), ) .setTransactionTransfer( - new TransferAmount(senderKeyPair.getPublicKey(), item.amount), + new TransferAmount(senderKeyPair.getPublicKey(), item.amount, communityContext.communityId), recipientKeyPair.getPublicKey(), ) + .setSenderCommunity(communityContext.communityId) .sign(senderKeyPair) } @@ -108,8 +108,8 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole { ): AccountBalances { const accountBalances = new AccountBalances() - const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain) - const recipientLastBalance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain) + const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain, communityContext.communityId) + const recipientLastBalance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain, communityContext.communityId) try { senderLastBalance.updateLegacyDecay(item.amount.negated(), item.balanceDate) @@ -145,7 +145,7 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole { try { addToBlockchain( - this.buildTransaction(item, senderKeyPair, recipientKeyPair), + this.buildTransaction(communityContext, item, senderKeyPair, recipientKeyPair).build(), blockchain, new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_ID), this.calculateBalances(item, communityContext, senderPublicKey, recipientPublicKey), diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/RedeemTransactionLinksSync.role.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/RedeemTransactionLinksSync.role.ts index 139be9259..0f472a109 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/RedeemTransactionLinksSync.role.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/RedeemTransactionLinksSync.role.ts @@ -82,6 +82,7 @@ export class RedeemTransactionLinksSyncRole extends AbstractSyncRole { + getDate(): Date { + return this.peek().balanceDate + } + + getLastIndex(): IndexType { + const lastItem = this.peekLast() + return { date: lastItem.balanceDate, id: lastItem.id } + } + + itemTypeName(): string { + return 'remoteTransactions' + } + + async loadFromDb(lastIndex: IndexType, count: number): Promise { + const linkedUsers = alias(usersTable, 'linkedUser') + const result = await this.context.db + .select({ + transaction: transactionsTable, + user: usersTable, + linkedUser: linkedUsers, + }) + .from(transactionsTable) + .where( + and( + inArray(transactionsTable.typeId, [TransactionTypeId.RECEIVE, TransactionTypeId.SEND]), + isNull(transactionsTable.transactionLinkId), + ne(usersTable.communityUuid, linkedUsers.communityUuid), + or( + gt(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)), + and( + eq(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)), + gt(transactionsTable.id, lastIndex.id) + ) + ) + ) + ) + .innerJoin(usersTable, eq(transactionsTable.userId, usersTable.id)) + .innerJoin(linkedUsers, eq(transactionsTable.linkedUserGradidoId, linkedUsers.gradidoId)) + .orderBy(asc(transactionsTable.balanceDate), asc(transactionsTable.id)) + .limit(count) + + return result.map((row) => { + const item = { + ...row.transaction, + user: row.user, + linkedUser: row.linkedUser, + } + if (item.typeId === TransactionTypeId.SEND && item.amount) { + item.amount = new Decimal(item.amount).neg().toString() + } + try { + return v.parse(transactionDbSchema, item) + } catch (e) { + throw new DatabaseError('loadRemoteTransferTransactions', item, e as Error) + } + }) + } + + buildTransaction( + item: TransactionDb, + senderKeyPair: KeyPairEd25519, + recipientKeyPair: KeyPairEd25519, + senderCommunityId: string, + recipientCommunityId: string, + ): GradidoTransactionBuilder { + const builder = new GradidoTransactionBuilder() + .setCreatedAt(item.balanceDate) + .addMemo(new EncryptedMemo( + item.memo, + new AuthenticatedEncryption(senderKeyPair), + new AuthenticatedEncryption(recipientKeyPair), + ), + ) + .setTransactionTransfer( + new TransferAmount(senderKeyPair.getPublicKey(), item.amount, senderCommunityId), + recipientKeyPair.getPublicKey(), + ) + .setSenderCommunity(senderCommunityId) + .setRecipientCommunity(recipientCommunityId) + .sign(senderKeyPair) + return builder + } + + calculateBalances( + item: TransactionDb, + communityContext: CommunityContext, + amount: GradidoUnit, + publicKey: MemoryBlockPtr, + ): AccountBalances { + const accountBalances = new AccountBalances() + if (communityContext.foreign) { + accountBalances.add(new AccountBalance(publicKey, GradidoUnit.zero(), communityContext.communityId)) + return accountBalances + } else { + const lastBalance = this.getLastBalanceForUser(publicKey, communityContext.blockchain, communityContext.communityId) + + try { + lastBalance.updateLegacyDecay(amount, item.balanceDate) + } catch(e) { + if (e instanceof NegativeBalanceError) { + this.logLastBalanceChangingTransactions(publicKey, communityContext.blockchain, 10) + throw e + } + } + accountBalances.add(lastBalance.getAccountBalance()) + return accountBalances + } + } + + getUser(item: TransactionDb): { senderUser: UserDb, recipientUser: UserDb } { + return ( + item.typeId === TransactionTypeId.RECEIVE + ? { senderUser: item.linkedUser, recipientUser: item.user } + : { senderUser: item.user, recipientUser: item.linkedUser } + ) + } + + pushToBlockchain(item: TransactionDb): void { + const { senderUser, recipientUser } = this.getUser(item) + const ledgerAnchor = new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_ID) + + if (senderUser.communityUuid === recipientUser.communityUuid) { + throw new Error(`transfer between user from same community: ${JSON.stringify(item, null, 2)}, check db query`) + } + const senderCommunityContext = this.context.getCommunityContextByUuid(senderUser.communityUuid) + const recipientCommunityContext = this.context.getCommunityContextByUuid(recipientUser.communityUuid) + const senderBlockchain = senderCommunityContext.blockchain + const recipientBlockchain = recipientCommunityContext.blockchain + + // I use the received transaction so user and linked user are swapped and user is recipient and linkedUser ist sender + const senderKeyPair = this.getAccountKeyPair(senderCommunityContext, senderUser.gradidoId) + const senderPublicKey = senderKeyPair.getPublicKey() + const recipientKeyPair = this.getAccountKeyPair(recipientCommunityContext, recipientUser.gradidoId) + const recipientPublicKey = recipientKeyPair.getPublicKey() + + if (!senderKeyPair || !senderPublicKey || !recipientKeyPair || !recipientPublicKey) { + throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`) + } + const transactionBuilder = this.buildTransaction( + item, + senderKeyPair, + recipientKeyPair, + senderCommunityContext.communityId, + recipientCommunityContext.communityId + ) + const outboundTransaction = transactionBuilder.buildOutbound() + + try { + addToBlockchain( + outboundTransaction, + senderBlockchain, + ledgerAnchor, + this.calculateBalances(item, senderCommunityContext, item.amount.negated(), senderPublicKey), + ) + } catch(e) { + if (e instanceof NotEnoughGradidoBalanceError) { + this.logLastBalanceChangingTransactions(senderPublicKey, senderBlockchain) + } + throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error) + } + transactionBuilder.setParentLedgerAnchor(ledgerAnchor) + const inboundTransaction = transactionBuilder.buildInbound() + try { + addToBlockchain( + inboundTransaction, + recipientBlockchain, + ledgerAnchor, + this.calculateBalances(item, recipientCommunityContext, item.amount, recipientPublicKey), + ) + } catch(e) { + if (e instanceof NotEnoughGradidoBalanceError) { + this.logLastBalanceChangingTransactions(recipientPublicKey, recipientBlockchain) + } + throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error) + } + } +} diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/TransactionLinkFundingsSync.role.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/TransactionLinkFundingsSync.role.ts index dc4032f68..bc568df61 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/TransactionLinkFundingsSync.role.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/TransactionLinkFundingsSync.role.ts @@ -1,4 +1,4 @@ -import { asc, eq, or, gt, and, isNull } from 'drizzle-orm' +import { asc, eq, or, gt, and } from 'drizzle-orm' import { AccountBalance, AccountBalances, @@ -20,7 +20,7 @@ import { BlockchainError, DatabaseError, NegativeBalanceError } from '../../erro import { CommunityContext, TransactionLinkDb, transactionLinkDbSchema } from '../../valibot.schema' import { AbstractSyncRole, IndexType } from './AbstractSync.role' import { deriveFromCode } from '../../../../data/deriveKeyPair' -import { calculateEffectiveSeconds, reverseLegacyDecay, toMysqlDateTime } from '../../utils' +import { reverseLegacyDecay, toMysqlDateTime } from '../../utils' import Decimal from 'decimal.js-light' export class TransactionLinkFundingsSyncRole extends AbstractSyncRole { @@ -66,6 +66,7 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole { .select() .from(usersTable) .where(and( - eq(usersTable.foreign, 0), or( gt(usersTable.createdAt, toMysqlDateTime(lastIndex.date)), and( @@ -61,6 +60,7 @@ export class UsersSyncRole extends AbstractSyncRole { } buildTransaction( + communityContext: CommunityContext, item: UserDb, communityKeyPair: KeyPairEd25519, accountKeyPair: KeyPairEd25519, @@ -74,14 +74,15 @@ export class UsersSyncRole extends AbstractSyncRole { new Uuidv4Hash(item.gradidoId).getAsMemoryBlock(), accountKeyPair.getPublicKey(), ) + .setSenderCommunity(communityContext.communityId) .sign(communityKeyPair) .sign(accountKeyPair) .sign(userKeyPair) } - calculateAccountBalances(accountPublicKey: MemoryBlockPtr): AccountBalances { + calculateAccountBalances(accountPublicKey: MemoryBlockPtr, communityContext: CommunityContext,): AccountBalances { const accountBalances = new AccountBalances() - accountBalances.add(new AccountBalance(accountPublicKey, GradidoUnit.zero(), '')) + accountBalances.add(new AccountBalance(accountPublicKey, GradidoUnit.zero(), communityContext.communityId)) return accountBalances } @@ -96,10 +97,10 @@ export class UsersSyncRole extends AbstractSyncRole { try { addToBlockchain( - this.buildTransaction(item, communityContext.keyPair, accountKeyPair, userKeyPair), + this.buildTransaction(communityContext, item, communityContext.keyPair, accountKeyPair, userKeyPair).build(), communityContext.blockchain, new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_USER_ID), - this.calculateAccountBalances(accountPublicKey), + this.calculateAccountBalances(accountPublicKey, communityContext), ) } catch (e) { throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error) diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/syncDbWithBlockchain.context.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/syncDbWithBlockchain.context.ts index 37ab78373..8bc3b5a0b 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/syncDbWithBlockchain.context.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/interaction/syncDbWithBlockchain/syncDbWithBlockchain.context.ts @@ -7,6 +7,7 @@ import { TransactionLinkFundingsSyncRole } from './TransactionLinkFundingsSync.r import { RedeemTransactionLinksSyncRole } from './RedeemTransactionLinksSync.role' import { ContributionLinkTransactionSyncRole } from './ContributionLinkTransactionSync.role' import { DeletedTransactionLinksSyncRole } from './DeletedTransactionLinksSync.role' +import { RemoteTransactionsSyncRole } from './RemoteTransactionsSync.role' export async function syncDbWithBlockchainContext(context: Context, batchSize: number) { const timeUsedDB = new Profiler() @@ -20,6 +21,7 @@ export async function syncDbWithBlockchainContext(context: Context, batchSize: n new RedeemTransactionLinksSyncRole(context), new ContributionLinkTransactionSyncRole(context), new DeletedTransactionLinksSyncRole(context), + new RemoteTransactionsSyncRole(context), ] let transactionsCount = 0 let transactionsCountSinceLastLog = 0 diff --git a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/valibot.schema.ts b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/valibot.schema.ts index 207b401ed..904be605d 100644 --- a/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/valibot.schema.ts +++ b/dlt-connector/src/migrations/db-v2.7.0_to_blockchain-v3.7/valibot.schema.ts @@ -1,4 +1,4 @@ -import { InMemoryBlockchain, KeyPairEd25519 } from 'gradido-blockchain-js' +import { GradidoUnit, InMemoryBlockchain, KeyPairEd25519 } from 'gradido-blockchain-js' import * as v from 'valibot' import { booleanSchema, dateSchema } from '../../schemas/typeConverter.schema' import { @@ -9,6 +9,7 @@ import { } from '../../schemas/typeGuard.schema' import { Balance } from './data/Balance' import { TransactionTypeId } from './data/TransactionTypeId' +import Decimal from 'decimal.js-light' const positiveNumberSchema = v.pipe(v.number(), v.minValue(1)) @@ -18,7 +19,31 @@ export const userDbSchema = v.object({ communityUuid: uuidv4Schema, createdAt: dateSchema, }) +/* +declare const validLegacyAmount: unique symbol +export type LegacyAmount = string & { [validLegacyAmount]: true } +export const legacyAmountSchema = v.pipe( + v.string(), + v.regex(/^-?[0-9]+(\.[0-9]+)?$/), + v.transform((input: string) => input as LegacyAmount), +) + +declare const validGradidoAmount: unique symbol +export type GradidoAmount = GradidoUnit & { [validGradidoAmount]: true } + +export const gradidoAmountSchema = v.pipe( + v.union([legacyAmountSchema, v.instance(GradidoUnit, 'expect GradidoUnit type')]), + v.transform((input: LegacyAmount | GradidoUnit) => { + if (input instanceof GradidoUnit) { + return input as GradidoAmount + } + // round floor with decimal js beforehand + const rounded = new Decimal(input).toDecimalPlaces(4, Decimal.ROUND_FLOOR).toString() + return GradidoUnit.fromString(rounded) as GradidoAmount + }), +) +*/ export const transactionBaseSchema = v.object({ id: positiveNumberSchema, amount: gradidoAmountSchema, @@ -26,6 +51,7 @@ export const transactionBaseSchema = v.object({ user: userDbSchema, }) + export const transactionDbSchema = v.pipe(v.object({ ...transactionBaseSchema.entries, typeId: v.enum(TransactionTypeId), @@ -107,6 +133,7 @@ export const communityDbSchema = v.object({ export const communityContextSchema = v.object({ communityId: v.string(), + foreign: booleanSchema, blockchain: v.instance(InMemoryBlockchain, 'expect InMemoryBlockchain type'), keyPair: v.instance(KeyPairEd25519), folder: v.pipe(