trigger timeout transactions directly from typeScript

This commit is contained in:
einhornimmond 2026-03-06 08:39:17 +01:00
parent 9f4fa9f5b2
commit aff9e5f816
12 changed files with 112 additions and 20 deletions

View File

@ -1,6 +1,5 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "gradido",
@ -8,7 +7,7 @@
"auto-changelog": "^2.4.0",
"cross-env": "^7.0.3",
"jose": "^4.14.4",
"turbo": "^2.5.0",
"turbo": "^2.8.12",
"uuid": "^8.3.2",
},
"devDependencies": {
@ -19,7 +18,7 @@
},
"admin": {
"name": "admin",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"@iconify/json": "^2.2.228",
"@popperjs/core": "^2.11.8",
@ -89,7 +88,7 @@
},
"backend": {
"name": "backend",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"cross-env": "^7.0.3",
"email-templates": "^10.0.1",
@ -166,7 +165,7 @@
},
"config-schema": {
"name": "config-schema",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"esbuild": "^0.25.2",
"joi": "17.13.3",
@ -184,7 +183,7 @@
},
"core": {
"name": "core",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"database": "*",
"email-templates": "^10.0.1",
@ -221,7 +220,7 @@
},
"database": {
"name": "database",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"@types/uuid": "^8.3.4",
"cross-env": "^7.0.3",
@ -257,7 +256,7 @@
},
"dht-node": {
"name": "dht-node",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"cross-env": "^7.0.3",
"dht-rpc": "6.18.1",
@ -295,7 +294,7 @@
},
"federation": {
"name": "federation",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"cross-env": "^7.0.3",
"email-templates": "^10.0.1",
@ -356,7 +355,7 @@
},
"frontend": {
"name": "frontend",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"@morev/vue-transitions": "^3.0.2",
"@types/leaflet": "^1.9.12",
@ -452,7 +451,7 @@
},
"shared": {
"name": "shared",
"version": "2.7.3",
"version": "2.7.4",
"dependencies": {
"decimal.js-light": "^2.5.1",
"esbuild": "^0.25.2",
@ -3358,19 +3357,19 @@
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
"turbo": ["turbo@2.6.1", "", { "optionalDependencies": { "turbo-darwin-64": "2.6.1", "turbo-darwin-arm64": "2.6.1", "turbo-linux-64": "2.6.1", "turbo-linux-arm64": "2.6.1", "turbo-windows-64": "2.6.1", "turbo-windows-arm64": "2.6.1" }, "bin": { "turbo": "bin/turbo" } }, "sha512-qBwXXuDT3rA53kbNafGbT5r++BrhRgx3sAo0cHoDAeG9g1ItTmUMgltz3Hy7Hazy1ODqNpR+C7QwqL6DYB52yA=="],
"turbo": ["turbo@2.8.12", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.12", "turbo-darwin-arm64": "2.8.12", "turbo-linux-64": "2.8.12", "turbo-linux-arm64": "2.8.12", "turbo-windows-64": "2.8.12", "turbo-windows-arm64": "2.8.12" }, "bin": { "turbo": "bin/turbo" } }, "sha512-auUAMLmi0eJhxDhQrxzvuhfEbICnVt0CTiYQYY8WyRJ5nwCDZxD0JG8bCSxT4nusI2CwJzmZAay5BfF6LmK7Hw=="],
"turbo-darwin-64": ["turbo-darwin-64@2.6.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ=="],
"turbo-darwin-64": ["turbo-darwin-64@2.8.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-EiHJmW2MeQQx+21x8hjMHw/uPhXt9PIxvDrxzOtyVwrXzL0tQmsxtO4qHf2l7uA+K6PUJ4+TjY1MHZDuCvWXrw=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.6.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cbqqGN0vd7ly2TeuaM8k9AK9u1CABO4kBA5KPSqovTiLL3sORccn/mZzJSbvQf0EsYRfU34MgW5FotfwW3kx8Q=="],
"turbo-linux-64": ["turbo-linux-64@2.6.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw=="],
"turbo-linux-64": ["turbo-linux-64@2.8.12", "", { "os": "linux", "cpu": "x64" }, "sha512-jXKw9j4r4q6s0goSXuKI3aKbQK2qiNeP25lGGEnq018TM6SWRW1CCpPMxyG91aCKrub7wDm/K45sGNT4ZFBcFQ=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.6.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-BRJCMdyXjyBoL0GYpvj9d2WNfMHwc3tKmJG5ATn2Efvil9LsiOsd/93/NxDqW0jACtHFNVOPnd/CBwXRPiRbwA=="],
"turbo-windows-64": ["turbo-windows-64@2.6.1", "", { "os": "win32", "cpu": "x64" }, "sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ=="],
"turbo-windows-64": ["turbo-windows-64@2.8.12", "", { "os": "win32", "cpu": "x64" }, "sha512-vyFOlpFFzQFkikvSVhVkESEfzIopgs2J7J1rYvtSwSHQ4zmHxkC95Q8Kjkus8gg+8X2mZyP1GS5jirmaypGiPw=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.6.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-9nRnlw5DF0LkJClkIws1evaIF36dmmMEO84J5Uj4oQ8C0QTHwlH7DNe5Kq2Jdmu8GXESCNDNuUYG8Cx6W/vm3g=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],

View File

@ -101,4 +101,13 @@ export class Balance {
getAccountBalance(): AccountBalance {
return new AccountBalance(this.publicKey, this.balance, this.communityId)
}
toString(): string {
return JSON.stringify({
balance: this.balance.toString(),
date: this.date,
publicKey: this.publicKey.convertToHex(),
communityId: this.communityId
}, null, 2)
}
}

View File

@ -109,6 +109,7 @@ export abstract class AbstractSyncRole<ItemType> {
abstract loadFromDb(lastIndex: IndexType, count: number): Promise<ItemType[]>
abstract pushToBlockchain(item: ItemType): void
abstract itemTypeName(): string
abstract getCommunityUuids(): Uuidv4[]
// return count of new loaded items
async ensureFilled(batchSize: number): Promise<number> {

View File

@ -26,6 +26,7 @@ import {
creationTransactionDbSchema,
} from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
constructor(context: Context) {
@ -36,6 +37,9 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
getDate(): Date {
return this.peek().confirmedAt
}
getCommunityUuids(): Uuidv4[] {
return [ this.peek().user.communityUuid ]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()

View File

@ -26,6 +26,7 @@ import {
deletedTransactionLinKDbSchema,
} from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class DeletedTransactionLinksSyncRole extends AbstractSyncRole<DeletedTransactionLinkDb> {
constructor(context: Context) {
@ -36,6 +37,9 @@ export class DeletedTransactionLinksSyncRole extends AbstractSyncRole<DeletedTra
getDate(): Date {
return this.peek().deletedAt
}
getCommunityUuids(): Uuidv4[] {
return [this.peek().user.communityUuid]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()

View File

@ -24,6 +24,7 @@ import {
import { toMysqlDateTime } from '../../utils'
import { CommunityContext, TransactionDb, transactionDbSchema } from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
constructor(context: Context) {
@ -35,6 +36,10 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
return this.peek().balanceDate
}
getCommunityUuids(): Uuidv4[] {
return [this.peek().user.communityUuid]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()
return { date: lastItem.balanceDate, id: lastItem.id }

View File

@ -28,6 +28,7 @@ import {
redeemedTransactionLinkDbSchema,
} from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTransactionLinkDb> {
constructor(context: Context) {
@ -39,6 +40,10 @@ export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTra
return this.peek().redeemedAt
}
getCommunityUuids(): Uuidv4[] {
return [this.peek().user.communityUuid]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()
return { date: lastItem.redeemedAt, id: lastItem.id }

View File

@ -27,6 +27,7 @@ import {
import { toMysqlDateTime } from '../../utils'
import { CommunityContext, TransactionDb, transactionDbSchema, UserDb } from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
constructor(context: Context) {
@ -38,6 +39,11 @@ export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb>
return this.peek().balanceDate
}
getCommunityUuids(): Uuidv4[] {
const currentItem = this.peek()
return [ currentItem.user.communityUuid, currentItem.linkedUser.communityUuid ]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()
return { date: lastItem.balanceDate, id: lastItem.id }

View File

@ -6,12 +6,15 @@ import {
AuthenticatedEncryption,
DurationSeconds,
EncryptedMemo,
Filter,
GradidoTransactionBuilder,
GradidoTransfer,
GradidoUnit,
KeyPairEd25519,
LedgerAnchor,
MemoryBlockPtr,
SearchDirection_DESC,
transactionTypeToString,
TransferAmount,
} from 'gradido-blockchain-js'
import * as v from 'valibot'
@ -23,6 +26,7 @@ import { BlockchainError, DatabaseError, NegativeBalanceError } from '../../erro
import { reverseLegacyDecay, toMysqlDateTime } from '../../utils'
import { CommunityContext, TransactionLinkDb, transactionLinkDbSchema } from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<TransactionLinkDb> {
constructor(context: Context) {
@ -33,6 +37,10 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
return this.peek().createdAt
}
getCommunityUuids(): Uuidv4[] {
return [this.peek().user.communityUuid]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()
return { date: lastItem.createdAt, id: lastItem.id }
@ -111,6 +119,7 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
senderPublicKey: MemoryBlockPtr,
recipientPublicKey: MemoryBlockPtr,
): AccountBalances {
this.accountBalances.clear()
const senderLastBalance = this.getLastBalanceForUser(
senderPublicKey,

View File

@ -19,6 +19,7 @@ import { BlockchainError, DatabaseError } from '../../errors'
import { toMysqlDateTime } from '../../utils'
import { CommunityContext, UserDb, userDbSchema } from '../../valibot.schema'
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
import { Uuidv4 } from '../../../../schemas/typeGuard.schema'
export class UsersSyncRole extends AbstractSyncRole<UserDb> {
constructor(context: Context) {
@ -29,6 +30,10 @@ export class UsersSyncRole extends AbstractSyncRole<UserDb> {
return this.peek().createdAt
}
getCommunityUuids(): Uuidv4[] {
return [this.peek().communityUuid]
}
getLastIndex(): IndexType {
const lastItem = this.peekLast()
return { date: lastItem.createdAt, id: lastItem.id }

View File

@ -1,4 +1,4 @@
import { Profiler } from 'gradido-blockchain-js'
import { Filter, Profiler, Timestamp, InteractionCreateTransactionByEvent, LedgerAnchor, Abstract } from 'gradido-blockchain-js'
import { callTime } from '../../blockchain'
import { Context } from '../../Context'
import { nanosBalanceForUser } from './AbstractSync.role'
@ -10,6 +10,46 @@ import { RedeemTransactionLinksSyncRole } from './RedeemTransactionLinksSync.rol
import { RemoteTransactionsSyncRole } from './RemoteTransactionsSync.role'
import { TransactionLinkFundingsSyncRole } from './TransactionLinkFundingsSync.role'
import { UsersSyncRole } from './UsersSync.role'
import { CommunityContext } from '../../valibot.schema'
import { Logger } from 'log4js'
function processTransactionTrigger(context: CommunityContext, endDate: Date, logger: Logger) {
while(true) {
const lastTx = context.blockchain.findOne(Filter.LAST_TRANSACTION)
let confirmedAt: Timestamp | undefined = undefined
if (!lastTx) {
// no transaction, no triggers
return
} else {
const confirmedTx = lastTx.getConfirmedTransaction()
if (!confirmedTx) {
throw new Error("missing confirmed tx in transaction entry")
}
confirmedAt = confirmedTx.getConfirmedAt()
}
const triggerEvent = context.blockchain.findNextTransactionTriggerEventInRange(confirmedAt, new Timestamp(endDate))
if (!triggerEvent) {
// no trigger, we can exit here
return
}
context.blockchain.removeTransactionTriggerEvent(triggerEvent)
try {
// InMemoryBlockchain extend Abstract, but between C++ -> Swig -> TypeScript it seems the info is gone, so I need to cheat a bit here
const createTransactionByEvent = new InteractionCreateTransactionByEvent(context.blockchain as unknown as Abstract)
if (!context.blockchain.createAndAddConfirmedTransaction(
createTransactionByEvent.run(triggerEvent),
new LedgerAnchor(triggerEvent.getLinkedTransactionId(), LedgerAnchor.Type_NODE_TRIGGER_TRANSACTION_ID),
triggerEvent.getTargetDate()
)) {
throw new Error('Adding trigger created Transaction Failed')
}
} catch(e) {
context.blockchain.addTransactionTriggerEvent(triggerEvent)
logger.error(`Error processing transaction trigger event for transaction: ${triggerEvent.getLinkedTransactionId()}`)
throw e
}
}
}
export async function syncDbWithBlockchainContext(context: Context, batchSize: number) {
const timeUsedDB = new Profiler()
@ -53,6 +93,11 @@ export async function syncDbWithBlockchainContext(context: Context, batchSize: n
available.sort((a, b) => a.getDate().getTime() - b.getDate().getTime())
// context.logger.debug(`sorted ${available.length} containers in ${sortTime.string()}`)
}
const communityUuids = available[0].getCommunityUuids()
for (let i = 0; i < communityUuids.length; ++i) {
processTransactionTrigger(context.getCommunityContextByUuid(communityUuids[i]), available[0].getDate(), context.logger)
}
available[0].toBlockchain()
transactionsCount++
if (isDebug) {

View File

@ -34,7 +34,7 @@
"auto-changelog": "^2.4.0",
"cross-env": "^7.0.3",
"jose": "^4.14.4",
"turbo": "^2.5.0",
"turbo": "^2.8.12",
"uuid": "^8.3.2"
},
"devDependencies": {