mirror of
https://github.com/IT4Change/gradido.git
synced 2026-04-06 01:25:28 +00:00
fix linting
This commit is contained in:
parent
4025175cea
commit
31766b83ef
@ -5,7 +5,11 @@ export class AccountIdentifier {
|
||||
account?: CommunityAccountIdentifier
|
||||
seed?: string // used for deferred transfers
|
||||
|
||||
constructor(communityTopicId: string, communityUuid: string, input: CommunityAccountIdentifier | string) {
|
||||
constructor(
|
||||
communityTopicId: string,
|
||||
communityUuid: string,
|
||||
input: CommunityAccountIdentifier | string,
|
||||
) {
|
||||
if (input instanceof CommunityAccountIdentifier) {
|
||||
this.account = input
|
||||
} else {
|
||||
|
||||
@ -133,7 +133,11 @@ export class TransactionDraft {
|
||||
sendingUser.community.communityUuid!,
|
||||
new CommunityAccountIdentifier(sendingUser.gradidoID),
|
||||
)
|
||||
draft.linkedUser = new AccountIdentifier(senderUserTopic, sendingUser.community.communityUuid!, transactionLink.code)
|
||||
draft.linkedUser = new AccountIdentifier(
|
||||
senderUserTopic,
|
||||
sendingUser.community.communityUuid!,
|
||||
transactionLink.code,
|
||||
)
|
||||
draft.type = TransactionType.GRADIDO_DEFERRED_TRANSFER
|
||||
draft.createdAt = createdAtOnlySeconds.toISOString()
|
||||
draft.amount = transactionLink.amount.toString()
|
||||
|
||||
@ -2,7 +2,12 @@ import { Paginated } from '@arg/Paginated'
|
||||
import { EditCommunityInput } from '@input/EditCommunityInput'
|
||||
import { AdminCommunityView } from '@model/AdminCommunityView'
|
||||
import { Community } from '@model/Community'
|
||||
import { Community as DbCommunity, getAuthorizedCommunities, getHomeCommunity, getReachableCommunities } from 'database'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
getAuthorizedCommunities,
|
||||
getHomeCommunity,
|
||||
getReachableCommunities,
|
||||
} from 'database'
|
||||
import { updateAllDefinedAndChanged } from 'shared'
|
||||
import { Arg, Args, Authorized, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
@ -38,9 +43,9 @@ export class CommunityResolver {
|
||||
@Query(() => [Community])
|
||||
async authorizedCommunities(): Promise<Community[]> {
|
||||
const dbCommunities: DbCommunity[] = await getAuthorizedCommunities({
|
||||
// order by
|
||||
// order by
|
||||
foreign: 'ASC', // home community first
|
||||
name: 'ASC',
|
||||
name: 'ASC',
|
||||
})
|
||||
return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
||||
}
|
||||
|
||||
@ -45,7 +45,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
|
||||
* Fix 0: Update transaction links to match holdAvailableAmount with validUntil, because the old formula lead to incorrect values
|
||||
*/
|
||||
|
||||
let sumCount = 0
|
||||
let count = 0
|
||||
let lastProcessedId = 0
|
||||
const LIMIT = 200
|
||||
@ -106,7 +105,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
|
||||
`,
|
||||
[updates.map((u) => u.id)],
|
||||
)
|
||||
sumCount += updates.length
|
||||
}
|
||||
count = rows.length
|
||||
lastProcessedId = rows[rows.length - 1].id
|
||||
|
||||
@ -103,13 +103,12 @@ export async function getNotReachableCommunities(
|
||||
// return the home community and all communities which had at least once make it through the first handshake
|
||||
export async function getAuthorizedCommunities(
|
||||
order?: FindOptionsOrder<DbCommunity>,
|
||||
): Promise<DbCommunity[]>
|
||||
{
|
||||
): Promise<DbCommunity[]> {
|
||||
return await DbCommunity.find({
|
||||
where: [
|
||||
{ authenticatedAt: Not(IsNull()) }, // or
|
||||
{ foreign: false }
|
||||
],
|
||||
order
|
||||
{ foreign: false },
|
||||
],
|
||||
order,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,7 @@ import { exportCommunities } from '../client/GradidoNode/communities'
|
||||
import { GradidoNodeProcess } from '../client/GradidoNode/GradidoNodeProcess'
|
||||
import { HieroClient } from '../client/hiero/HieroClient'
|
||||
import { CONFIG } from '../config'
|
||||
import {
|
||||
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||
LOG4JS_BASE_CATEGORY,
|
||||
} from '../config/const'
|
||||
import { GRADIDO_NODE_HOME_FOLDER_NAME, LOG4JS_BASE_CATEGORY } from '../config/const'
|
||||
import { checkFileExist, checkPathExist } from '../utils/filesystem'
|
||||
import { isPortOpen } from '../utils/network'
|
||||
import { AppContextClients } from './appContext'
|
||||
|
||||
@ -77,10 +77,7 @@ export class KeyPairCacheManager {
|
||||
return keyPair
|
||||
}
|
||||
|
||||
public getKeyPairSync(
|
||||
input: string,
|
||||
createKeyPair: () => KeyPairEd25519,
|
||||
): KeyPairEd25519 {
|
||||
public getKeyPairSync(input: string, createKeyPair: () => KeyPairEd25519): KeyPairEd25519 {
|
||||
const keyPair = this.cache.get(input)
|
||||
if (!keyPair) {
|
||||
const keyPair = createKeyPair()
|
||||
|
||||
@ -8,7 +8,7 @@ import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||
import { AddressType } from '../../data/AddressType.enum'
|
||||
import { Uuidv4Hash } from '../../data/Uuidv4Hash'
|
||||
import { addressTypeSchema, confirmedTransactionSchema } from '../../schemas/typeConverter.schema'
|
||||
import { Hex32, Hex32Input, Uuidv4, hex32Schema } from '../../schemas/typeGuard.schema'
|
||||
import { Hex32, Hex32Input, hex32Schema, Uuidv4 } from '../../schemas/typeGuard.schema'
|
||||
import { isPortOpenRetry } from '../../utils/network'
|
||||
import { GradidoNodeErrorCodes } from './GradidoNodeErrorCodes'
|
||||
import {
|
||||
@ -75,7 +75,10 @@ export class GradidoNodeClient {
|
||||
const response = await this.rpcCall<{ transaction: string }>('getTransaction', parameter)
|
||||
if (response.isSuccess()) {
|
||||
// this.logger.debug('result: ', response.result.transaction)
|
||||
return v.parse(confirmedTransactionSchema, { base64: response.result.transaction, communityId: parameter.communityId })
|
||||
return v.parse(confirmedTransactionSchema, {
|
||||
base64: response.result.transaction,
|
||||
communityId: parameter.communityId,
|
||||
})
|
||||
}
|
||||
if (response.isError()) {
|
||||
if (response.error.code === GradidoNodeErrorCodes.TRANSACTION_NOT_FOUND) {
|
||||
@ -100,7 +103,10 @@ export class GradidoNodeClient {
|
||||
}
|
||||
const response = await this.rpcCall<{ transaction: string }>('getLastTransaction', parameter)
|
||||
if (response.isSuccess()) {
|
||||
return v.parse(confirmedTransactionSchema, { base64: response.result.transaction, communityId: parameter.communityId })
|
||||
return v.parse(confirmedTransactionSchema, {
|
||||
base64: response.result.transaction,
|
||||
communityId: parameter.communityId,
|
||||
})
|
||||
}
|
||||
if (response.isError()) {
|
||||
if (response.error.code === GradidoNodeErrorCodes.GRADIDO_NODE_ERROR) {
|
||||
@ -137,7 +143,10 @@ export class GradidoNodeClient {
|
||||
parameter,
|
||||
)
|
||||
return result.transactions.map((transactionBase64) =>
|
||||
v.parse(confirmedTransactionSchema, { base64: transactionBase64, communityId: parameter.communityId }),
|
||||
v.parse(confirmedTransactionSchema, {
|
||||
base64: transactionBase64,
|
||||
communityId: parameter.communityId,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@ -163,7 +172,10 @@ export class GradidoNodeClient {
|
||||
parameter,
|
||||
)
|
||||
return response.transactions.map((transactionBase64) =>
|
||||
v.parse(confirmedTransactionSchema, { base64: transactionBase64, communityId: parameter.communityId }),
|
||||
v.parse(confirmedTransactionSchema, {
|
||||
base64: transactionBase64,
|
||||
communityId: parameter.communityId,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import path from 'node:path'
|
||||
import { Mutex } from 'async-mutex'
|
||||
import { Subprocess, spawn } from 'bun'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
@ -10,7 +11,6 @@ import {
|
||||
LOG4JS_BASE_CATEGORY,
|
||||
} from '../../config/const'
|
||||
import { delay } from '../../utils/time'
|
||||
import path from 'node:path'
|
||||
/**
|
||||
* A Singleton class defines the `getInstance` method that lets clients access
|
||||
* the unique singleton instance.
|
||||
@ -48,14 +48,7 @@ export class GradidoNodeProcess {
|
||||
const isWindows = process.platform === 'win32'
|
||||
const binaryName = isWindows ? 'GradidoNode.exe' : 'GradidoNode'
|
||||
|
||||
return path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'gradido_node',
|
||||
'bin',
|
||||
binaryName,
|
||||
)
|
||||
return path.join(__dirname, '..', '..', 'gradido_node', 'bin', binaryName)
|
||||
}
|
||||
|
||||
public start() {
|
||||
|
||||
@ -39,7 +39,6 @@ export async function ensureCommunitiesAvailable(communityTopicIds: HieroId[]):
|
||||
|
||||
export async function exportCommunities(homeFolder: string, client: BackendClient): Promise<void> {
|
||||
const communities = await client.getAuthorizedCommunities()
|
||||
console.log(`communities: ${JSON.stringify(communities, null, 2)}`)
|
||||
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||
checkPathExist(path.dirname(communitiesPath), true)
|
||||
const communitiesForDltNodeServer: CommunityForDltNodeServer[] = []
|
||||
@ -52,7 +51,7 @@ export async function exportCommunities(homeFolder: string, client: BackendClien
|
||||
hieroTopicId: com.hieroTopicId,
|
||||
alias: com.name,
|
||||
// use only alpha-numeric chars for folder name
|
||||
folder: toFolderName(com.uuid),
|
||||
folder: toFolderName(com.uuid),
|
||||
})
|
||||
}
|
||||
fs.writeFileSync(communitiesPath, JSON.stringify(communitiesForDltNodeServer, null, 2))
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { beforeAll, describe, expect, it } from 'bun:test'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import * as v from 'valibot'
|
||||
import {
|
||||
HieroTransactionIdString,
|
||||
Uuidv4,
|
||||
hieroIdSchema,
|
||||
hieroTransactionIdStringSchema,
|
||||
Uuidv4,
|
||||
uuidv4Schema,
|
||||
} from '../../schemas/typeGuard.schema'
|
||||
import { transactionIdentifierSchema } from './input.schema'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
let communityId: Uuidv4
|
||||
const uuidv4String = uuidv4()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import * as v from 'valibot'
|
||||
import { uuidv4Schema, hieroTransactionIdStringSchema } from '../../schemas/typeGuard.schema'
|
||||
import { hieroTransactionIdStringSchema, uuidv4Schema } from '../../schemas/typeGuard.schema'
|
||||
|
||||
export const transactionsRangeSchema = v.object({
|
||||
// default value is 1, from first transactions
|
||||
|
||||
@ -53,5 +53,3 @@ export const getAuthorizedCommunities = gql`
|
||||
}
|
||||
${communityFragment}
|
||||
`
|
||||
|
||||
|
||||
|
||||
@ -17,9 +17,7 @@ export function deriveFromCode(code: string): KeyPairEd25519 {
|
||||
const hash = new MemoryBlock(code).calculateHash()
|
||||
const keyPair = KeyPairEd25519.create(hash)
|
||||
if (!keyPair) {
|
||||
throw new ParameterError(
|
||||
`error creating Ed25519 KeyPair from seed: ${code.substring(0, 5)}...`,
|
||||
)
|
||||
throw new ParameterError(`error creating Ed25519 KeyPair from seed: ${code.substring(0, 5)}...`)
|
||||
}
|
||||
return keyPair
|
||||
}
|
||||
@ -31,7 +29,10 @@ export function deriveFromKeyPairAndUuid(keyPair: KeyPairEd25519, uuid: Uuidv4):
|
||||
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())
|
||||
}
|
||||
// parts: [2206563009, 2629978174, 2324817329, 2405141782]
|
||||
return parts.reduce((keyPair: KeyPairEd25519, node: number) => deriveFromKeyPairAndIndex(keyPair, node), keyPair)
|
||||
return parts.reduce(
|
||||
(keyPair: KeyPairEd25519, node: number) => deriveFromKeyPairAndIndex(keyPair, node),
|
||||
keyPair,
|
||||
)
|
||||
}
|
||||
|
||||
export function deriveFromKeyPairAndIndex(keyPair: KeyPairEd25519, index: number): KeyPairEd25519 {
|
||||
@ -42,4 +43,4 @@ export function deriveFromKeyPairAndIndex(keyPair: KeyPairEd25519, index: number
|
||||
)
|
||||
}
|
||||
return localKeyPair
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,10 +35,12 @@ export class RegisterAddressTransactionRole extends AbstractTransactionRole {
|
||||
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
const communityTopicId = this.registerAddressTransaction.user.communityTopicId
|
||||
const communityKeyPair = await ResolveKeyPair(new KeyPairIdentifierLogic({
|
||||
communityTopicId,
|
||||
communityId: this.registerAddressTransaction.user.communityId,
|
||||
}))
|
||||
const communityKeyPair = await ResolveKeyPair(
|
||||
new KeyPairIdentifierLogic({
|
||||
communityTopicId,
|
||||
communityId: this.registerAddressTransaction.user.communityId,
|
||||
}),
|
||||
)
|
||||
const keyPairIdentifier = this.registerAddressTransaction.user
|
||||
// when accountNr is 0 it is the user account
|
||||
keyPairIdentifier.account.accountNr = 0
|
||||
|
||||
@ -65,7 +65,9 @@ export async function SendToHieroContext(
|
||||
)
|
||||
|
||||
// attach Hiero transaction ID to the builder for the inbound transaction
|
||||
builder.setParentLedgerAnchor(new LedgerAnchor(new HieroTransactionId(outboundHieroTransactionIdString)))
|
||||
builder.setParentLedgerAnchor(
|
||||
new LedgerAnchor(new HieroTransactionId(outboundHieroTransactionIdString)),
|
||||
)
|
||||
|
||||
// build and validate inbound transaction
|
||||
const inboundTransaction = builder.buildInbound()
|
||||
@ -103,7 +105,7 @@ function validate(transaction: GradidoTransaction): void {
|
||||
async function sendViaHiero(
|
||||
gradidoTransaction: GradidoTransaction,
|
||||
topic: HieroId,
|
||||
communityId: Uuidv4
|
||||
communityId: Uuidv4,
|
||||
): Promise<HieroTransactionIdString> {
|
||||
const client = HieroClient.getInstance()
|
||||
const transactionId = await client.sendMessage(topic, communityId, gradidoTransaction)
|
||||
|
||||
@ -23,7 +23,12 @@ export class Context {
|
||||
public balanceFixGradidoUser: UserDb | null
|
||||
private timeUsed: Profiler
|
||||
|
||||
constructor(logger: Logger, db: MySql2Database, cache: KeyPairCacheManager, balanceFixGradidoUser: UserDb | null) {
|
||||
constructor(
|
||||
logger: Logger,
|
||||
db: MySql2Database,
|
||||
cache: KeyPairCacheManager,
|
||||
balanceFixGradidoUser: UserDb | null,
|
||||
) {
|
||||
this.logger = logger
|
||||
this.db = db
|
||||
this.cache = cache
|
||||
@ -42,23 +47,23 @@ export class Context {
|
||||
database: CONFIG.MYSQL_DATABASE,
|
||||
port: CONFIG.MYSQL_PORT,
|
||||
})
|
||||
const db = drizzle({ client: connection })
|
||||
const db = drizzle({ client: connection })
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.migrations.db-v2.7.0_to_blockchain-v3.5`)
|
||||
let balanceFixGradidoUser: UserDb | null = null
|
||||
if (process.env.MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID) {
|
||||
balanceFixGradidoUser = await loadUserByGradidoId(db, process.env.MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID)
|
||||
balanceFixGradidoUser = await loadUserByGradidoId(
|
||||
db,
|
||||
process.env.MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID,
|
||||
)
|
||||
if (!balanceFixGradidoUser) {
|
||||
logger.error(`MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID was set to ${process.env.MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID} but user not found`)
|
||||
logger.error(
|
||||
`MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID was set to ${process.env.MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID} but user not found`,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
logger.debug(`MIGRATION_ACCOUNT_BALANCE_FIX_GRADIDO_ID was not set`)
|
||||
}
|
||||
return new Context(
|
||||
logger,
|
||||
db,
|
||||
KeyPairCacheManager.getInstance(),
|
||||
balanceFixGradidoUser,
|
||||
)
|
||||
return new Context(logger, db, KeyPairCacheManager.getInstance(), balanceFixGradidoUser)
|
||||
}
|
||||
|
||||
getCommunityContextByUuid(communityUuid: Uuidv4): CommunityContext {
|
||||
|
||||
@ -43,7 +43,9 @@ export function exportCommunity(
|
||||
const isDebug = context.logger.isDebugEnabled()
|
||||
const printConsole = () => {
|
||||
if (triggeredTransactionsCount > 0) {
|
||||
process.stdout.write(`exported ${count} transactions + ${triggeredTransactionsCount} triggered from timeouted transaction links\r`)
|
||||
process.stdout.write(
|
||||
`exported ${count} transactions + ${triggeredTransactionsCount} triggered from timeouted transaction links\r`,
|
||||
)
|
||||
} else {
|
||||
process.stdout.write(`exported ${count} transactions\r`)
|
||||
}
|
||||
@ -59,7 +61,12 @@ export function exportCommunity(
|
||||
throw new Error(`invalid TransactionEntry at index: ${transactionNr} `)
|
||||
}
|
||||
hash = exportTransaction(confirmedTransaction, hash, binFilePath)
|
||||
if (confirmedTransaction?.getGradidoTransaction()?.getTransactionBody()?.isTimeoutDeferredTransfer()) {
|
||||
if (
|
||||
confirmedTransaction
|
||||
?.getGradidoTransaction()
|
||||
?.getTransactionBody()
|
||||
?.isTimeoutDeferredTransfer()
|
||||
) {
|
||||
triggeredTransactionsCount++
|
||||
} else {
|
||||
count++
|
||||
@ -77,7 +84,7 @@ export function exportCommunity(
|
||||
}
|
||||
}
|
||||
}
|
||||
f.pagination.page++
|
||||
f.pagination.page++
|
||||
} while (lastTransactionCount === batchSize)
|
||||
printConsole()
|
||||
process.stdout.write(`\n`)
|
||||
@ -86,7 +93,7 @@ export function exportCommunity(
|
||||
context.logger.info(
|
||||
`binary file for community ${communityContext.communityId} written to ${binFilePath}`,
|
||||
)
|
||||
const sumTransactionsCount = ((f.pagination.page - 2) * batchSize) + lastTransactionCount
|
||||
const sumTransactionsCount = (f.pagination.page - 2) * batchSize + lastTransactionCount
|
||||
const fileSize = fs.statSync(binFilePath).size
|
||||
context.logger.info(
|
||||
`exported ${sumTransactionsCount} transactions (${bytesString(fileSize)}) in ${timeUsed.string()}`,
|
||||
|
||||
@ -11,7 +11,7 @@ import { NotEnoughGradidoBalanceError } from './errors'
|
||||
|
||||
export const defaultHieroAccount = new HieroAccountId(0, 0, 2)
|
||||
export let callTime: number = 0
|
||||
const timeUsed = new Profiler
|
||||
const timeUsed = new Profiler()
|
||||
|
||||
export function addToBlockchain(
|
||||
transaction: GradidoTransaction,
|
||||
@ -19,7 +19,7 @@ export function addToBlockchain(
|
||||
ledgerAnchor: LedgerAnchor,
|
||||
accountBalances: AccountBalances,
|
||||
): boolean {
|
||||
try {
|
||||
try {
|
||||
timeUsed.reset()
|
||||
const result = blockchain.createAndAddConfirmedTransactionExternFast(
|
||||
transaction,
|
||||
@ -30,7 +30,9 @@ export function addToBlockchain(
|
||||
return result
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
const matches = error.message.match(/not enough Gradido Balance for (send coins|operation), needed: -?(\d+\.\d+), exist: (\d+\.\d+)/)
|
||||
const matches = error.message.match(
|
||||
/not enough Gradido Balance for (send coins|operation), needed: -?(\d+\.\d+), exist: (\d+\.\d+)/,
|
||||
)
|
||||
if (matches) {
|
||||
const needed = parseFloat(matches[2])
|
||||
const exist = parseFloat(matches[3])
|
||||
@ -40,8 +42,9 @@ export function addToBlockchain(
|
||||
// const wekingheim = InMemoryBlockchainProvider.getInstance().getBlockchain('wekingheim')
|
||||
// const lastTransactionw = wekingheim?.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)}`)
|
||||
const lastTransaction = blockchain.findOne(Filter.LAST_TRANSACTION)
|
||||
throw new Error(
|
||||
`Transaction ${transaction.toJson(true)} not added: ${error}, last transaction was: ${lastTransaction?.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,23 +1,36 @@
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { AccountBalances, GradidoTransactionBuilder, InMemoryBlockchainProvider, LedgerAnchor } from 'gradido-blockchain-js'
|
||||
import {
|
||||
AccountBalances,
|
||||
GradidoTransactionBuilder,
|
||||
InMemoryBlockchainProvider,
|
||||
LedgerAnchor,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { CONFIG } from '../../config'
|
||||
import { deriveFromSeed } from '../../data/deriveKeyPair'
|
||||
import { Hex32, hex32Schema } from '../../schemas/typeGuard.schema'
|
||||
import { AUF_ACCOUNT_DERIVATION_INDEX, GMW_ACCOUNT_DERIVATION_INDEX, hardenDerivationIndex } from '../../utils/derivationHelper'
|
||||
import {
|
||||
AUF_ACCOUNT_DERIVATION_INDEX,
|
||||
GMW_ACCOUNT_DERIVATION_INDEX,
|
||||
hardenDerivationIndex,
|
||||
} from '../../utils/derivationHelper'
|
||||
import { toFolderName } from '../../utils/filesystem'
|
||||
import { addToBlockchain } from './blockchain'
|
||||
import { Context } from './Context'
|
||||
import { Balance } from './data/Balance'
|
||||
import { loadAdminUsersCache, loadCommunities, loadContributionLinkModeratorCache } from './database'
|
||||
import {
|
||||
loadAdminUsersCache,
|
||||
loadCommunities,
|
||||
loadContributionLinkModeratorCache,
|
||||
} from './database'
|
||||
import { CommunityContext } from './valibot.schema'
|
||||
import { toFolderName } from '../../utils/filesystem'
|
||||
|
||||
export async function bootstrap(): Promise<Context> {
|
||||
const context = await Context.create()
|
||||
context.communities = await bootstrapCommunities(context)
|
||||
await Promise.all([
|
||||
loadContributionLinkModeratorCache(context.db),
|
||||
loadAdminUsersCache(context.db)
|
||||
loadAdminUsersCache(context.db),
|
||||
])
|
||||
return context
|
||||
}
|
||||
@ -39,17 +52,23 @@ async function bootstrapCommunities(context: Context): Promise<Map<string, Commu
|
||||
} else {
|
||||
seed = v.parse(hex32Schema, randomBytes(32).toString('hex'))
|
||||
}
|
||||
|
||||
|
||||
let creationDate = communityDb.creationDate
|
||||
if (communityDb.userMinCreatedAt && communityDb.userMinCreatedAt < communityDb.creationDate) {
|
||||
// create community root transaction 1 minute before first user
|
||||
creationDate = new Date(new Date(communityDb.userMinCreatedAt).getTime() - 1000 * 60)
|
||||
}
|
||||
const communityKeyPair = deriveFromSeed(seed)
|
||||
const gmwKeyPair = communityKeyPair.deriveChild(hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX))
|
||||
const aufKeyPair = communityKeyPair.deriveChild(hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX))
|
||||
const gmwKeyPair = communityKeyPair.deriveChild(
|
||||
hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX),
|
||||
)
|
||||
const aufKeyPair = communityKeyPair.deriveChild(
|
||||
hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX),
|
||||
)
|
||||
if (!communityKeyPair || !gmwKeyPair || !aufKeyPair) {
|
||||
throw new Error(`Error on creating key pair for community ${JSON.stringify(communityDb, null, 2)}`)
|
||||
throw new Error(
|
||||
`Error on creating key pair for community ${JSON.stringify(communityDb, null, 2)}`,
|
||||
)
|
||||
}
|
||||
const builder = new GradidoTransactionBuilder()
|
||||
builder
|
||||
@ -59,7 +78,7 @@ async function bootstrapCommunities(context: Context): Promise<Map<string, Commu
|
||||
communityKeyPair.getPublicKey(),
|
||||
gmwKeyPair.getPublicKey(),
|
||||
aufKeyPair.getPublicKey(),
|
||||
)
|
||||
)
|
||||
.sign(communityKeyPair)
|
||||
|
||||
const communityContext: CommunityContext = {
|
||||
|
||||
@ -1,96 +1,104 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { AccountBalance, GradidoUnit, MemoryBlockPtr } from 'gradido-blockchain-js'
|
||||
import { legacyCalculateDecay } from '../utils'
|
||||
import { NegativeBalanceError } from '../errors'
|
||||
import { legacyCalculateDecay } from '../utils'
|
||||
|
||||
export class Balance {
|
||||
private balance: GradidoUnit
|
||||
private date: Date
|
||||
private publicKey: MemoryBlockPtr
|
||||
private communityId: string
|
||||
private balance: GradidoUnit
|
||||
private date: Date
|
||||
private publicKey: MemoryBlockPtr
|
||||
private communityId: string
|
||||
|
||||
constructor(publicKey: MemoryBlockPtr, communityId: string) {
|
||||
this.balance = new GradidoUnit(0)
|
||||
this.date = new Date()
|
||||
this.publicKey = publicKey
|
||||
this.communityId = communityId
|
||||
constructor(publicKey: MemoryBlockPtr, communityId: string) {
|
||||
this.balance = new GradidoUnit(0)
|
||||
this.date = new Date()
|
||||
this.publicKey = publicKey
|
||||
this.communityId = communityId
|
||||
}
|
||||
|
||||
static fromAccountBalance(
|
||||
accountBalance: AccountBalance,
|
||||
confirmedAt: Date,
|
||||
communityId: string,
|
||||
): Balance {
|
||||
const balance = new Balance(accountBalance.getPublicKey()!, communityId)
|
||||
balance.update(accountBalance.getBalance(), confirmedAt)
|
||||
return balance
|
||||
}
|
||||
|
||||
getBalance(): GradidoUnit {
|
||||
return this.balance
|
||||
}
|
||||
|
||||
getDate(): Date {
|
||||
return this.date
|
||||
}
|
||||
|
||||
updateLegacyDecay(amount: GradidoUnit, date: Date) {
|
||||
// make sure to copy instead of referencing
|
||||
const previousBalanceString = this.balance.toString()
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
} else {
|
||||
const decayedBalance = legacyCalculateDecay(
|
||||
new Decimal(this.balance.toString()),
|
||||
this.date,
|
||||
date,
|
||||
).toDecimalPlaces(4, Decimal.ROUND_CEIL)
|
||||
const newBalance = decayedBalance.add(new Decimal(amount.toString()))
|
||||
this.balance = GradidoUnit.fromString(newBalance.toString())
|
||||
this.date = date
|
||||
}
|
||||
|
||||
static fromAccountBalance(accountBalance: AccountBalance, confirmedAt: Date, communityId: string): Balance {
|
||||
const balance = new Balance(accountBalance.getPublicKey()!, communityId)
|
||||
balance.update(accountBalance.getBalance(), confirmedAt)
|
||||
return balance
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
if (this.balance.lt(GradidoUnit.fromGradidoCent(100).negated())) {
|
||||
const previousDecayedBalance = legacyCalculateDecay(
|
||||
new Decimal(previousBalanceString),
|
||||
previousDate,
|
||||
date,
|
||||
)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.updateLegacyDecay`,
|
||||
previousBalanceString,
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
} else {
|
||||
this.balance = GradidoUnit.zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBalance(): GradidoUnit {
|
||||
return this.balance
|
||||
update(amount: GradidoUnit, date: Date) {
|
||||
const previousBalance = new GradidoUnit(this.balance.toString())
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
} else {
|
||||
this.balance = this.balance.calculateDecay(this.date, date).add(amount)
|
||||
this.date = date
|
||||
}
|
||||
|
||||
getDate(): Date {
|
||||
return this.date
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
// ignore diffs less than a gradido cent
|
||||
if (this.balance.lt(GradidoUnit.fromGradidoCent(100).negated())) {
|
||||
const previousDecayedBalance = this.balance.calculateDecay(previousDate, date)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.update`,
|
||||
previousBalance.toString(),
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
} else {
|
||||
this.balance = GradidoUnit.zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLegacyDecay(amount: GradidoUnit, date: Date) {
|
||||
// make sure to copy instead of referencing
|
||||
const previousBalanceString = this.balance.toString()
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
} else {
|
||||
const decayedBalance = legacyCalculateDecay(new Decimal(this.balance.toString()), this.date, date ).toDecimalPlaces(4, Decimal.ROUND_CEIL)
|
||||
const newBalance = decayedBalance.add(new Decimal(amount.toString()))
|
||||
this.balance = GradidoUnit.fromString(newBalance.toString())
|
||||
this.date = date
|
||||
}
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
if (this.balance.lt(GradidoUnit.fromGradidoCent(100).negated())) {
|
||||
const previousDecayedBalance = legacyCalculateDecay(new Decimal(previousBalanceString), previousDate, date)
|
||||
const rounded = previousDecayedBalance.toDecimalPlaces(4, Decimal.ROUND_CEIL)
|
||||
console.log(`rounded: ${rounded}}`)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.updateLegacyDecay`,
|
||||
previousBalanceString,
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
} else {
|
||||
this.balance = GradidoUnit.zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(amount: GradidoUnit, date: Date) {
|
||||
const previousBalance = new GradidoUnit(this.balance.toString())
|
||||
const previousDate = new Date(this.date.getTime())
|
||||
|
||||
if (this.balance.equal(GradidoUnit.zero())) {
|
||||
this.balance = amount
|
||||
this.date = date
|
||||
} else {
|
||||
this.balance = this.balance
|
||||
.calculateDecay(this.date, date)
|
||||
.add(amount)
|
||||
this.date = date
|
||||
}
|
||||
if (this.balance.lt(GradidoUnit.zero())) {
|
||||
// ignore diffs less than a gradido cent
|
||||
if (this.balance.lt(GradidoUnit.fromGradidoCent(100).negated())) {
|
||||
const previousDecayedBalance = this.balance.calculateDecay(previousDate, date)
|
||||
throw new NegativeBalanceError(
|
||||
`negative Gradido amount detected in Balance.update`,
|
||||
previousBalance.toString(),
|
||||
amount.toString(),
|
||||
previousDecayedBalance.toString(),
|
||||
)
|
||||
} else {
|
||||
this.balance = GradidoUnit.zero()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAccountBalance(): AccountBalance {
|
||||
return new AccountBalance(this.publicKey, this.balance, this.communityId)
|
||||
}
|
||||
getAccountBalance(): AccountBalance {
|
||||
return new AccountBalance(this.publicKey, this.balance, this.communityId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,10 @@ export function generateKeyPairCommunity(
|
||||
if (!keyPair) {
|
||||
throw new Error(`Couldn't create key pair for community ${community.communityUuid}`)
|
||||
}
|
||||
const communityKeyPairKey = new KeyPairIdentifierLogic({ communityTopicId: topicId, communityId: community.communityUuid }).getKey()
|
||||
const communityKeyPairKey = new KeyPairIdentifierLogic({
|
||||
communityTopicId: topicId,
|
||||
communityId: community.communityUuid,
|
||||
}).getKey()
|
||||
cache.addKeyPair(communityKeyPairKey, keyPair)
|
||||
logger.info(`Community Key Pair added with key: ${communityKeyPairKey}`)
|
||||
}
|
||||
|
||||
@ -1,18 +1,8 @@
|
||||
import { asc, eq, isNotNull, sql } from 'drizzle-orm'
|
||||
import { MySql2Database } from 'drizzle-orm/mysql2'
|
||||
import * as v from 'valibot'
|
||||
import {
|
||||
communitiesTable,
|
||||
eventsTable,
|
||||
userRolesTable,
|
||||
usersTable
|
||||
} from './drizzle.schema'
|
||||
import {
|
||||
CommunityDb,
|
||||
UserDb,
|
||||
communityDbSchema,
|
||||
userDbSchema,
|
||||
} from './valibot.schema'
|
||||
import { communitiesTable, eventsTable, userRolesTable, usersTable } from './drizzle.schema'
|
||||
import { CommunityDb, communityDbSchema, UserDb, userDbSchema } from './valibot.schema'
|
||||
|
||||
export const contributionLinkModerators = new Map<number, UserDb>()
|
||||
export const adminUsers = new Map<string, UserDb>()
|
||||
@ -29,7 +19,10 @@ export async function loadContributionLinkModeratorCache(db: MySql2Database): Pr
|
||||
.orderBy(asc(eventsTable.id))
|
||||
|
||||
result.map((row: any) => {
|
||||
contributionLinkModerators.set(row.event.involvedContributionLinkId, v.parse(userDbSchema, row.user))
|
||||
contributionLinkModerators.set(
|
||||
row.event.involvedContributionLinkId,
|
||||
v.parse(userDbSchema, row.user),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@ -69,7 +62,10 @@ export async function loadCommunities(db: MySql2Database): Promise<CommunityDb[]
|
||||
})
|
||||
}
|
||||
|
||||
export async function loadUserByGradidoId(db: MySql2Database, gradidoId: string): Promise<UserDb | null> {
|
||||
export async function loadUserByGradidoId(
|
||||
db: MySql2Database,
|
||||
gradidoId: string,
|
||||
): Promise<UserDb | null> {
|
||||
const result = await db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
@ -78,4 +74,3 @@ export async function loadUserByGradidoId(db: MySql2Database, gradidoId: string)
|
||||
|
||||
return result.length ? v.parse(userDbSchema, result[0]) : null
|
||||
}
|
||||
|
||||
|
||||
@ -25,23 +25,23 @@ export const communitiesTable = mysqlTable(
|
||||
)
|
||||
|
||||
export const contributionsTable = mysqlTable('contributions', {
|
||||
id: int().autoincrement().notNull(),
|
||||
id: int().autoincrement().notNull(),
|
||||
userId: int('user_id').default(sql`NULL`),
|
||||
contributionDate: datetime("contribution_date", { mode: 'string'}).default(sql`NULL`),
|
||||
memo: varchar({ length: 512 }).notNull(),
|
||||
amount: decimal({ precision: 40, scale: 20 }).notNull(),
|
||||
contributionLinkId: int('contribution_link_id').default(sql`NULL`),
|
||||
confirmedBy: int('confirmed_by').default(sql`NULL`),
|
||||
confirmedAt: datetime('confirmed_at', { mode: 'string'}).default(sql`NULL`),
|
||||
contributionStatus: varchar('contribution_status', { length: 12 }).default('\'PENDING\'').notNull(),
|
||||
transactionId: int('transaction_id').default(sql`NULL`),
|
||||
contributionDate: datetime('contribution_date', { mode: 'string' }).default(sql`NULL`),
|
||||
memo: varchar({ length: 512 }).notNull(),
|
||||
amount: decimal({ precision: 40, scale: 20 }).notNull(),
|
||||
contributionLinkId: int('contribution_link_id').default(sql`NULL`),
|
||||
confirmedBy: int('confirmed_by').default(sql`NULL`),
|
||||
confirmedAt: datetime('confirmed_at', { mode: 'string' }).default(sql`NULL`),
|
||||
contributionStatus: varchar('contribution_status', { length: 12 }).default("'PENDING'").notNull(),
|
||||
transactionId: int('transaction_id').default(sql`NULL`),
|
||||
})
|
||||
|
||||
export const eventsTable = mysqlTable('events', {
|
||||
id: int().autoincrement().notNull(),
|
||||
type: varchar({ length: 100 }).notNull(),
|
||||
actingUserId: int('acting_user_id').notNull(),
|
||||
involvedContributionLinkId: int('involved_contribution_link_id').default(sql`NULL`),
|
||||
id: int().autoincrement().notNull(),
|
||||
type: varchar({ length: 100 }).notNull(),
|
||||
actingUserId: int('acting_user_id').notNull(),
|
||||
involvedContributionLinkId: int('involved_contribution_link_id').default(sql`NULL`),
|
||||
})
|
||||
|
||||
export const usersTable = mysqlTable(
|
||||
@ -55,19 +55,19 @@ export const usersTable = mysqlTable(
|
||||
createdAt: datetime('created_at', { mode: 'string', fsp: 3 })
|
||||
.default(sql`current_timestamp(3)`)
|
||||
.notNull(),
|
||||
|
||||
},
|
||||
(table) => [unique('uuid_key').on(table.gradidoId, table.communityUuid)],
|
||||
)
|
||||
|
||||
export const userRolesTable = mysqlTable('user_roles', {
|
||||
id: int().autoincrement().notNull(),
|
||||
userId: int('user_id').notNull(),
|
||||
role: varchar({ length: 40 }).notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('user_id').on(table.userId),
|
||||
])
|
||||
export const userRolesTable = mysqlTable(
|
||||
'user_roles',
|
||||
{
|
||||
id: int().autoincrement().notNull(),
|
||||
userId: int('user_id').notNull(),
|
||||
role: varchar({ length: 40 }).notNull(),
|
||||
},
|
||||
(table) => [index('user_id').on(table.userId)],
|
||||
)
|
||||
|
||||
export const transactionsTable = mysqlTable(
|
||||
'transactions',
|
||||
@ -84,8 +84,8 @@ export const transactionsTable = mysqlTable(
|
||||
creationDate: datetime('creation_date', { mode: 'string', fsp: 3 }).default(sql`NULL`),
|
||||
userId: int('user_id').notNull(),
|
||||
linkedUserId: int('linked_user_id').default(sql`NULL`),
|
||||
linkedUserCommunityUuid: char("linked_user_community_uuid", { length: 36 }).default(sql`NULL`),
|
||||
linkedUserGradidoId: char("linked_user_gradido_id", { length: 36 }).default(sql`NULL`),
|
||||
linkedUserCommunityUuid: char('linked_user_community_uuid', { length: 36 }).default(sql`NULL`),
|
||||
linkedUserGradidoId: char('linked_user_gradido_id', { length: 36 }).default(sql`NULL`),
|
||||
linkedTransactionId: int('linked_transaction_id').default(sql`NULL`),
|
||||
},
|
||||
(table) => [index('user_id').on(table.userId)],
|
||||
@ -95,12 +95,12 @@ export const transactionLinksTable = mysqlTable('transaction_links', {
|
||||
id: int().autoincrement().notNull(),
|
||||
userId: int().notNull(),
|
||||
amount: decimal({ precision: 40, scale: 20 }).notNull(),
|
||||
holdAvailableAmount: decimal("hold_available_amount", { precision: 40, scale: 20 }).notNull(),
|
||||
holdAvailableAmount: decimal('hold_available_amount', { precision: 40, scale: 20 }).notNull(),
|
||||
memo: varchar({ length: 255 }).notNull(),
|
||||
code: varchar({ length: 24 }).notNull(),
|
||||
createdAt: datetime({ mode: 'string' }).notNull(),
|
||||
deletedAt: datetime({ mode: 'string' }).default(sql`NULL`),
|
||||
validUntil: datetime({ mode: 'string' }).notNull(),
|
||||
redeemedAt: datetime({ mode: 'string'}).default(sql`NULL`),
|
||||
redeemedBy: int().default(sql`NULL`),
|
||||
redeemedAt: datetime({ mode: 'string' }).default(sql`NULL`),
|
||||
redeemedBy: int().default(sql`NULL`),
|
||||
})
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import * as v from 'valibot'
|
||||
|
||||
export class NotEnoughGradidoBalanceError extends Error {
|
||||
constructor(public needed: number, public exist: number) {
|
||||
super(`Not enough Gradido Balance for send coins, needed: ${needed} Gradido, exist: ${exist} Gradido`)
|
||||
constructor(
|
||||
public needed: number,
|
||||
public exist: number,
|
||||
) {
|
||||
super(
|
||||
`Not enough Gradido Balance for send coins, needed: ${needed} Gradido, exist: ${exist} Gradido`,
|
||||
)
|
||||
this.name = 'NotEnoughGradidoBalanceError'
|
||||
}
|
||||
}
|
||||
@ -46,7 +51,12 @@ export class BlockchainError extends Error {
|
||||
}
|
||||
|
||||
export class NegativeBalanceError extends Error {
|
||||
constructor(message: string, previousBalanceString: string, amount: string, previousDecayedBalance: string) {
|
||||
constructor(
|
||||
message: string,
|
||||
previousBalanceString: string,
|
||||
amount: string,
|
||||
previousDecayedBalance: string,
|
||||
) {
|
||||
const parts: string[] = [`NegativeBalanceError in ${message}`]
|
||||
parts.push(`Previous balance: ${previousBalanceString}`)
|
||||
parts.push(`Amount: ${amount}`)
|
||||
@ -54,4 +64,4 @@ export class NegativeBalanceError extends Error {
|
||||
super(parts.join('\n'))
|
||||
this.name = 'NegativeBalanceError'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import { Filter, Profiler, ThreadingPolicy_Half, verifySignatures } from 'gradido-blockchain-js'
|
||||
import { onShutdown } from '../../../../shared/src/helper/onShutdown'
|
||||
import { exportAllCommunities } from './binaryExport'
|
||||
import { bootstrap } from './bootstrap'
|
||||
import { syncDbWithBlockchainContext } from './interaction/syncDbWithBlockchain/syncDbWithBlockchain.context'
|
||||
import { Filter, Profiler, ThreadingPolicy_Half, verifySignatures } from 'gradido-blockchain-js'
|
||||
// import { hello } from '../../../zig/hello.zig'
|
||||
|
||||
// import { hello } from '../../../zig/hello.zig'
|
||||
|
||||
const BATCH_SIZE = 1000
|
||||
|
||||
async function main() {
|
||||
// hello()
|
||||
// return
|
||||
// return
|
||||
// prepare in memory blockchains
|
||||
const context = await bootstrap()
|
||||
onShutdown(async (reason, error) => {
|
||||
@ -22,8 +23,8 @@ async function main() {
|
||||
// synchronize to in memory blockchain
|
||||
try {
|
||||
await syncDbWithBlockchainContext(context, BATCH_SIZE)
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
} catch (e) {
|
||||
context.logger.error(e)
|
||||
//context.logBlogchain(v.parse(uuidv4Schema, 'e70da33e-5976-4767-bade-aa4e4fa1c01a'))
|
||||
}
|
||||
|
||||
@ -31,9 +32,15 @@ async function main() {
|
||||
// bulk verify transaction signatures
|
||||
for (const communityContext of context.communities.values()) {
|
||||
// verifySignatures(Filter.ALL_TRANSACTIONS, ThreadingPolicy_Half)
|
||||
const result = verifySignatures(Filter.ALL_TRANSACTIONS, communityContext.communityId, ThreadingPolicy_Half)
|
||||
if(!result.isEmpty()){
|
||||
throw new Error(`Verification of signatures failed for community ${communityContext.communityId}`)
|
||||
const result = verifySignatures(
|
||||
Filter.ALL_TRANSACTIONS,
|
||||
communityContext.communityId,
|
||||
ThreadingPolicy_Half,
|
||||
)
|
||||
if (!result.isEmpty()) {
|
||||
throw new Error(
|
||||
`Verification of signatures failed for community ${communityContext.communityId}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
context.logger.info(`verified in ${timeUsed.string()}`)
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import {
|
||||
AccountBalances,
|
||||
Filter,
|
||||
InMemoryBlockchain,
|
||||
KeyPairEd25519,
|
||||
MemoryBlockPtr,
|
||||
Profiler,
|
||||
SearchDirection_DESC,
|
||||
GradidoTransactionBuilder
|
||||
import {
|
||||
AccountBalances,
|
||||
Filter,
|
||||
GradidoTransactionBuilder,
|
||||
InMemoryBlockchain,
|
||||
KeyPairEd25519,
|
||||
MemoryBlockPtr,
|
||||
Profiler,
|
||||
SearchDirection_DESC,
|
||||
} from 'gradido-blockchain-js'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY } from '../../../../config/const'
|
||||
@ -21,7 +21,7 @@ export type IndexType = {
|
||||
id: number
|
||||
}
|
||||
export let nanosBalanceForUser = 0
|
||||
const lastBalanceOfUserTimeUsed = new Profiler
|
||||
const lastBalanceOfUserTimeUsed = new Profiler()
|
||||
|
||||
export abstract class AbstractSyncRole<ItemType> {
|
||||
private items: ItemType[] = []
|
||||
@ -40,16 +40,22 @@ export abstract class AbstractSyncRole<ItemType> {
|
||||
|
||||
getAccountKeyPair(communityContext: CommunityContext, gradidoId: Uuidv4): KeyPairEd25519 {
|
||||
return this.context.cache.getKeyPairSync(gradidoId, () => {
|
||||
return deriveFromKeyPairAndIndex(deriveFromKeyPairAndUuid(communityContext.keyPair, gradidoId), 1)
|
||||
return deriveFromKeyPairAndIndex(
|
||||
deriveFromKeyPairAndUuid(communityContext.keyPair, gradidoId),
|
||||
1,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
getLastBalanceForUser(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, communityId: string
|
||||
getLastBalanceForUser(
|
||||
publicKey: MemoryBlockPtr,
|
||||
blockchain: InMemoryBlockchain,
|
||||
communityId: string,
|
||||
): Balance {
|
||||
lastBalanceOfUserTimeUsed.reset()
|
||||
if (publicKey.isEmpty()) {
|
||||
throw new Error('publicKey is empty')
|
||||
}
|
||||
}
|
||||
const f = Filter.lastBalanceFor(publicKey)
|
||||
f.setCommunityId(communityId)
|
||||
const lastSenderTransaction = blockchain.findOne(f)
|
||||
@ -58,19 +64,31 @@ export abstract class AbstractSyncRole<ItemType> {
|
||||
}
|
||||
const lastConfirmedTransaction = lastSenderTransaction.getConfirmedTransaction()
|
||||
if (!lastConfirmedTransaction) {
|
||||
throw new Error(`invalid transaction, getConfirmedTransaction call failed for transaction nr: ${lastSenderTransaction.getTransactionNr()}`)
|
||||
|
||||
throw new Error(
|
||||
`invalid transaction, getConfirmedTransaction call failed for transaction nr: ${lastSenderTransaction.getTransactionNr()}`,
|
||||
)
|
||||
}
|
||||
const senderLastAccountBalance = lastConfirmedTransaction.getAccountBalance(publicKey, communityId)
|
||||
const senderLastAccountBalance = lastConfirmedTransaction.getAccountBalance(
|
||||
publicKey,
|
||||
communityId,
|
||||
)
|
||||
if (!senderLastAccountBalance) {
|
||||
return new Balance(publicKey, communityId)
|
||||
}
|
||||
const result = Balance.fromAccountBalance(senderLastAccountBalance, lastConfirmedTransaction.getConfirmedAt().getDate(), communityId)
|
||||
const result = Balance.fromAccountBalance(
|
||||
senderLastAccountBalance,
|
||||
lastConfirmedTransaction.getConfirmedAt().getDate(),
|
||||
communityId,
|
||||
)
|
||||
nanosBalanceForUser += lastBalanceOfUserTimeUsed.nanos()
|
||||
return result
|
||||
}
|
||||
|
||||
logLastBalanceChangingTransactions(publicKey: MemoryBlockPtr, blockchain: InMemoryBlockchain, transactionCount: number = 5) {
|
||||
logLastBalanceChangingTransactions(
|
||||
publicKey: MemoryBlockPtr,
|
||||
blockchain: InMemoryBlockchain,
|
||||
transactionCount: number = 5,
|
||||
) {
|
||||
if (!this.context.logger.isDebugEnabled()) {
|
||||
return
|
||||
}
|
||||
@ -111,7 +129,7 @@ export abstract class AbstractSyncRole<ItemType> {
|
||||
return this.items.length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
toBlockchain(): void {
|
||||
if (this.isEmpty()) {
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { and, asc, eq, gt, isNotNull, or } from 'drizzle-orm'
|
||||
import * as v from 'valibot'
|
||||
import { Context } from '../../Context'
|
||||
import { contributionLinkModerators } from '../../database'
|
||||
import { CreationTransactionDb, creationTransactionDbSchema } from '../../valibot.schema'
|
||||
import { CreationsSyncRole } from './CreationsSync.role'
|
||||
import { contributionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { ContributionStatus } from '../../data/ContributionStatus'
|
||||
import { contributionLinkModerators } from '../../database'
|
||||
import { contributionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { DatabaseError } from '../../errors'
|
||||
import { IndexType } from './AbstractSync.role'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { CreationTransactionDb, creationTransactionDbSchema } from '../../valibot.schema'
|
||||
import { IndexType } from './AbstractSync.role'
|
||||
import { CreationsSyncRole } from './CreationsSync.role'
|
||||
|
||||
export class ContributionLinkTransactionSyncRole extends CreationsSyncRole {
|
||||
constructor(readonly context: Context) {
|
||||
@ -25,30 +25,34 @@ export class ContributionLinkTransactionSyncRole extends CreationsSyncRole {
|
||||
user: usersTable,
|
||||
})
|
||||
.from(contributionsTable)
|
||||
.where(and(
|
||||
isNotNull(contributionsTable.contributionLinkId),
|
||||
eq(contributionsTable.contributionStatus, ContributionStatus.CONFIRMED),
|
||||
or(
|
||||
gt(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(contributionsTable.transactionId, lastIndex.id)
|
||||
)
|
||||
)
|
||||
))
|
||||
.where(
|
||||
and(
|
||||
isNotNull(contributionsTable.contributionLinkId),
|
||||
eq(contributionsTable.contributionStatus, ContributionStatus.CONFIRMED),
|
||||
or(
|
||||
gt(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(contributionsTable.transactionId, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.innerJoin(usersTable, eq(contributionsTable.userId, usersTable.id))
|
||||
.orderBy(asc(contributionsTable.confirmedAt), asc(contributionsTable.transactionId))
|
||||
.limit(count)
|
||||
|
||||
|
||||
const verifiedCreationTransactions: CreationTransactionDb[] = []
|
||||
for(const row of result) {
|
||||
for (const row of result) {
|
||||
if (!row.contribution.contributionLinkId) {
|
||||
throw new Error(`expect contributionLinkId to be set: ${JSON.stringify(row.contribution, null, 2)}`)
|
||||
throw new Error(
|
||||
`expect contributionLinkId to be set: ${JSON.stringify(row.contribution, null, 2)}`,
|
||||
)
|
||||
}
|
||||
const item = {
|
||||
...row.contribution,
|
||||
user: row.user,
|
||||
confirmedByUser: contributionLinkModerators.get(row.contribution.contributionLinkId)
|
||||
confirmedByUser: contributionLinkModerators.get(row.contribution.contributionLinkId),
|
||||
}
|
||||
if (!item.confirmedByUser || item.userId === item.confirmedByUser.id) {
|
||||
this.context.logger.warn(`skipped Contribution Link Transaction ${row.contribution.id}`)
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
import { and, asc, eq, isNull, gt, or } from 'drizzle-orm'
|
||||
import { and, asc, eq, gt, isNull, or } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import {
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
Filter,
|
||||
GradidoTransactionBuilder,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
SearchDirection_DESC,
|
||||
TransactionType_CREATION,
|
||||
TransferAmount
|
||||
import {
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
Filter,
|
||||
GradidoTransactionBuilder,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
SearchDirection_DESC,
|
||||
TransactionType_CREATION,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { ContributionStatus } from '../../data/ContributionStatus'
|
||||
import { Context } from '../../Context'
|
||||
import {
|
||||
contributionsTable,
|
||||
usersTable
|
||||
} from '../../drizzle.schema'
|
||||
import { ContributionStatus } from '../../data/ContributionStatus'
|
||||
import { contributionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { CommunityContext, CreationTransactionDb, creationTransactionDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import {
|
||||
CommunityContext,
|
||||
CreationTransactionDb,
|
||||
creationTransactionDbSchema,
|
||||
} from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
|
||||
export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
|
||||
export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
constructor(context: Context) {
|
||||
super(context)
|
||||
this.accountBalances.reserve(3)
|
||||
@ -38,16 +38,16 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
}
|
||||
|
||||
getLastIndex(): IndexType {
|
||||
const lastItem = this.peekLast()
|
||||
return { date: lastItem.confirmedAt, id: lastItem.transactionId }
|
||||
}
|
||||
const lastItem = this.peekLast()
|
||||
return { date: lastItem.confirmedAt, id: lastItem.transactionId }
|
||||
}
|
||||
|
||||
itemTypeName(): string {
|
||||
return 'creationTransactions'
|
||||
}
|
||||
|
||||
async loadFromDb(lastIndex: IndexType, count: number): Promise<CreationTransactionDb[]> {
|
||||
const confirmedByUsers = alias(usersTable, 'confirmedByUser')
|
||||
const confirmedByUsers = alias(usersTable, 'confirmedByUser')
|
||||
const result = await this.context.db
|
||||
.select({
|
||||
contribution: contributionsTable,
|
||||
@ -55,22 +55,24 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
confirmedByUser: confirmedByUsers,
|
||||
})
|
||||
.from(contributionsTable)
|
||||
.where(and(
|
||||
isNull(contributionsTable.contributionLinkId),
|
||||
eq(contributionsTable.contributionStatus, ContributionStatus.CONFIRMED),
|
||||
or(
|
||||
gt(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(contributionsTable.transactionId, lastIndex.id)
|
||||
)
|
||||
)
|
||||
))
|
||||
.where(
|
||||
and(
|
||||
isNull(contributionsTable.contributionLinkId),
|
||||
eq(contributionsTable.contributionStatus, ContributionStatus.CONFIRMED),
|
||||
or(
|
||||
gt(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(contributionsTable.confirmedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(contributionsTable.transactionId, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.innerJoin(usersTable, eq(contributionsTable.userId, usersTable.id))
|
||||
.innerJoin(confirmedByUsers, eq(contributionsTable.confirmedBy, confirmedByUsers.id))
|
||||
.orderBy(asc(contributionsTable.confirmedAt), asc(contributionsTable.transactionId))
|
||||
.limit(count)
|
||||
|
||||
|
||||
return result.map((row) => {
|
||||
const item = {
|
||||
...row.contribution,
|
||||
@ -86,12 +88,12 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
}
|
||||
|
||||
buildTransaction(
|
||||
item: CreationTransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
signerKeyPair: KeyPairEd25519
|
||||
item: CreationTransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
signerKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.confirmedAt)
|
||||
.setRecipientCommunity(communityContext.communityId)
|
||||
.addMemo(
|
||||
@ -102,19 +104,27 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
),
|
||||
)
|
||||
.setTransactionCreation(
|
||||
new TransferAmount(recipientKeyPair.getPublicKey(), item.amount, communityContext.communityId),
|
||||
new TransferAmount(
|
||||
recipientKeyPair.getPublicKey(),
|
||||
item.amount,
|
||||
communityContext.communityId,
|
||||
),
|
||||
item.contributionDate,
|
||||
)
|
||||
.sign(signerKeyPair)
|
||||
}
|
||||
|
||||
calculateAccountBalances(
|
||||
item: CreationTransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
recipientPublicKey: MemoryBlockPtr
|
||||
item: CreationTransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
const balance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
const balance = this.getLastBalanceForUser(
|
||||
recipientPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
|
||||
// calculate decay since last balance with legacy calculation method
|
||||
balance.updateLegacyDecay(item.amount, item.confirmedAt)
|
||||
@ -131,9 +141,11 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
const communityContext = this.context.getCommunityContextByUuid(item.user.communityUuid)
|
||||
const blockchain = communityContext.blockchain
|
||||
if (item.confirmedByUser.communityUuid !== item.user.communityUuid) {
|
||||
throw new Error(`contribution was confirmed from other community: ${JSON.stringify(item, null, 2)}`)
|
||||
throw new Error(
|
||||
`contribution was confirmed from other community: ${JSON.stringify(item, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const recipientKeyPair = this.getAccountKeyPair(communityContext, item.user.gradidoId)
|
||||
const recipientPublicKey = recipientKeyPair.getPublicKey()
|
||||
const signerKeyPair = this.getAccountKeyPair(communityContext, item.confirmedByUser.gradidoId)
|
||||
@ -148,14 +160,16 @@ export class CreationsSyncRole extends AbstractSyncRole<CreationTransactionDb> {
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_CONTRIBUTION_ID),
|
||||
this.calculateAccountBalances(item, communityContext, recipientPublicKey),
|
||||
)
|
||||
} catch(e) {
|
||||
const f= new Filter()
|
||||
} catch (e) {
|
||||
const f = new Filter()
|
||||
f.transactionType = TransactionType_CREATION
|
||||
f.searchDirection = SearchDirection_DESC
|
||||
f.pagination.size = 1
|
||||
const lastContribution = blockchain.findOne(f)
|
||||
if (lastContribution) {
|
||||
this.context.logger.warn(`last contribution: ${lastContribution.getConfirmedTransaction()?.toJson(true)}`)
|
||||
this.context.logger.warn(
|
||||
`last contribution: ${lastContribution.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
|
||||
@ -1,34 +1,38 @@
|
||||
import { CommunityContext, DeletedTransactionLinkDb, deletedTransactionLinKDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { transactionLinksTable, usersTable } from '../../drizzle.schema'
|
||||
import { and, lt, asc, isNotNull, eq, or, gt } from 'drizzle-orm'
|
||||
import * as v from 'valibot'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
Filter,
|
||||
GradidoDeferredTransfer,
|
||||
import { and, asc, eq, gt, isNotNull, lt, or } from 'drizzle-orm'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
Filter,
|
||||
GradidoDeferredTransfer,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { Balance } from '../../data/Balance'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { Context } from '../../Context'
|
||||
import { Balance } from '../../data/Balance'
|
||||
import { transactionLinksTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import {
|
||||
CommunityContext,
|
||||
DeletedTransactionLinkDb,
|
||||
deletedTransactionLinKDbSchema,
|
||||
} from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
|
||||
export class DeletedTransactionLinksSyncRole extends AbstractSyncRole<DeletedTransactionLinkDb> {
|
||||
constructor(context: Context) {
|
||||
super(context)
|
||||
this.accountBalances.reserve(2)
|
||||
}
|
||||
|
||||
|
||||
getDate(): Date {
|
||||
return this.peek().deletedAt
|
||||
}
|
||||
@ -57,15 +61,15 @@ export class DeletedTransactionLinksSyncRole extends AbstractSyncRole<DeletedTra
|
||||
gt(transactionLinksTable.deletedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionLinksTable.deletedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionLinksTable.id, lastIndex.id)
|
||||
)
|
||||
)
|
||||
)
|
||||
gt(transactionLinksTable.id, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.innerJoin(usersTable, eq(transactionLinksTable.userId, usersTable.id))
|
||||
.orderBy(asc(transactionLinksTable.deletedAt), asc(transactionLinksTable.id))
|
||||
.limit(count)
|
||||
|
||||
|
||||
return result.map((row) => {
|
||||
const item = {
|
||||
...row.transactionLink,
|
||||
@ -80,95 +84,118 @@ export class DeletedTransactionLinksSyncRole extends AbstractSyncRole<DeletedTra
|
||||
}
|
||||
|
||||
buildTransaction(
|
||||
communityContext: CommunityContext,
|
||||
item: DeletedTransactionLinkDb,
|
||||
linkFundingTransactionNr: number,
|
||||
restAmount: GradidoUnit,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
linkFundingPublicKey: MemoryBlockPtr,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.deletedAt)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setRedeemDeferredTransfer(
|
||||
linkFundingTransactionNr,
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), restAmount, communityContext.communityId),
|
||||
linkFundingPublicKey,
|
||||
communityContext: CommunityContext,
|
||||
item: DeletedTransactionLinkDb,
|
||||
linkFundingTransactionNr: number,
|
||||
restAmount: GradidoUnit,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
linkFundingPublicKey: MemoryBlockPtr,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.deletedAt)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setRedeemDeferredTransfer(
|
||||
linkFundingTransactionNr,
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(
|
||||
senderKeyPair.getPublicKey(),
|
||||
restAmount,
|
||||
communityContext.communityId,
|
||||
),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
linkFundingPublicKey,
|
||||
),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
}
|
||||
|
||||
calculateBalances(
|
||||
item: DeletedTransactionLinkDb,
|
||||
item: DeletedTransactionLinkDb,
|
||||
fundingTransaction: GradidoDeferredTransfer,
|
||||
senderLastBalance: Balance,
|
||||
communityContext: CommunityContext,
|
||||
senderPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
|
||||
this.accountBalances.clear()
|
||||
|
||||
const fundingUserLastBalance = this.getLastBalanceForUser(
|
||||
fundingTransaction.getSenderPublicKey()!,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId
|
||||
fundingTransaction.getSenderPublicKey()!,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
fundingUserLastBalance.updateLegacyDecay(senderLastBalance.getBalance(), item.deletedAt)
|
||||
|
||||
|
||||
// account of link is set to zero, gdd will be send back to initiator
|
||||
this.accountBalances.add(new AccountBalance(senderPublicKey, GradidoUnit.zero(), communityContext.communityId))
|
||||
this.accountBalances.add(
|
||||
new AccountBalance(senderPublicKey, GradidoUnit.zero(), communityContext.communityId),
|
||||
)
|
||||
this.accountBalances.add(fundingUserLastBalance.getAccountBalance())
|
||||
return this.accountBalances
|
||||
}
|
||||
|
||||
pushToBlockchain(item: DeletedTransactionLinkDb): void {
|
||||
const communityContext = this.context.getCommunityContextByUuid(item.user.communityUuid)
|
||||
const blockchain = communityContext.blockchain
|
||||
|
||||
const senderKeyPair = deriveFromCode(item.code)
|
||||
const senderPublicKey = senderKeyPair.getPublicKey()
|
||||
|
||||
if (!senderKeyPair || !senderPublicKey) {
|
||||
throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
}
|
||||
|
||||
const transaction = blockchain.findOne(Filter.lastBalanceFor(senderPublicKey))
|
||||
if (!transaction) {
|
||||
throw new Error(`expect transaction for code: ${item.code}`)
|
||||
}
|
||||
// should be funding transaction
|
||||
if (!transaction.isDeferredTransfer()) {
|
||||
throw new Error(`expect funding transaction: ${transaction.getConfirmedTransaction()?.toJson(true)}`)
|
||||
}
|
||||
const body = transaction.getConfirmedTransaction()?.getGradidoTransaction()?.getTransactionBody();
|
||||
const deferredTransfer = body?.getDeferredTransfer()
|
||||
if (!deferredTransfer || !deferredTransfer.getRecipientPublicKey()?.equal(senderPublicKey)) {
|
||||
throw new Error(`expect funding transaction to belong to code: ${item.code}: ${transaction.getConfirmedTransaction()?.toJson(true)}`)
|
||||
}
|
||||
const linkFundingPublicKey = deferredTransfer.getSenderPublicKey()
|
||||
if (!linkFundingPublicKey) {
|
||||
throw new Error(`missing sender public key of transaction link founder`)
|
||||
}
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
senderLastBalance.updateLegacyDecay(GradidoUnit.zero(), item.deletedAt)
|
||||
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(
|
||||
communityContext,
|
||||
item, transaction.getTransactionNr(),
|
||||
senderLastBalance.getBalance(),
|
||||
senderKeyPair,
|
||||
linkFundingPublicKey,
|
||||
).build(),
|
||||
blockchain,
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_LINK_ID),
|
||||
this.calculateBalances(item, deferredTransfer, senderLastBalance, communityContext, senderPublicKey),
|
||||
)
|
||||
} catch(e) {
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
const communityContext = this.context.getCommunityContextByUuid(item.user.communityUuid)
|
||||
const blockchain = communityContext.blockchain
|
||||
|
||||
const senderKeyPair = deriveFromCode(item.code)
|
||||
const senderPublicKey = senderKeyPair.getPublicKey()
|
||||
|
||||
if (!senderKeyPair || !senderPublicKey) {
|
||||
throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
}
|
||||
|
||||
|
||||
const transaction = blockchain.findOne(Filter.lastBalanceFor(senderPublicKey))
|
||||
if (!transaction) {
|
||||
throw new Error(`expect transaction for code: ${item.code}`)
|
||||
}
|
||||
// should be funding transaction
|
||||
if (!transaction.isDeferredTransfer()) {
|
||||
throw new Error(
|
||||
`expect funding transaction: ${transaction.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
const body = transaction
|
||||
.getConfirmedTransaction()
|
||||
?.getGradidoTransaction()
|
||||
?.getTransactionBody()
|
||||
const deferredTransfer = body?.getDeferredTransfer()
|
||||
if (!deferredTransfer || !deferredTransfer.getRecipientPublicKey()?.equal(senderPublicKey)) {
|
||||
throw new Error(
|
||||
`expect funding transaction to belong to code: ${item.code}: ${transaction.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
const linkFundingPublicKey = deferredTransfer.getSenderPublicKey()
|
||||
if (!linkFundingPublicKey) {
|
||||
throw new Error(`missing sender public key of transaction link founder`)
|
||||
}
|
||||
const senderLastBalance = this.getLastBalanceForUser(
|
||||
senderPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
senderLastBalance.updateLegacyDecay(GradidoUnit.zero(), item.deletedAt)
|
||||
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(
|
||||
communityContext,
|
||||
item,
|
||||
transaction.getTransactionNr(),
|
||||
senderLastBalance.getBalance(),
|
||||
senderKeyPair,
|
||||
linkFundingPublicKey,
|
||||
).build(),
|
||||
blockchain,
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_LINK_ID),
|
||||
this.calculateBalances(
|
||||
item,
|
||||
deferredTransfer,
|
||||
senderLastBalance,
|
||||
communityContext,
|
||||
senderPublicKey,
|
||||
),
|
||||
)
|
||||
} catch (e) {
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,36 @@
|
||||
import { and, asc, eq, isNotNull, isNull, gt, or } from 'drizzle-orm'
|
||||
import { and, asc, eq, gt, isNotNull, isNull, or } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import {
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount
|
||||
import {
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { TransactionTypeId } from '../../data/TransactionTypeId'
|
||||
import { transactionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError, NegativeBalanceError, NotEnoughGradidoBalanceError } from '../../errors'
|
||||
import {
|
||||
BlockchainError,
|
||||
DatabaseError,
|
||||
NegativeBalanceError,
|
||||
NotEnoughGradidoBalanceError,
|
||||
} from '../../errors'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { CommunityContext, TransactionDb, transactionDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { Context } from '../../Context'
|
||||
|
||||
export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
constructor(context: Context) {
|
||||
super(context)
|
||||
this.accountBalances.reserve(2)
|
||||
}
|
||||
|
||||
|
||||
getDate(): Date {
|
||||
return this.peek().balanceDate
|
||||
}
|
||||
@ -58,74 +63,83 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
or(
|
||||
gt(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionsTable.id, lastIndex.id)
|
||||
)
|
||||
)
|
||||
)
|
||||
eq(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionsTable.id, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.innerJoin(usersTable, eq(transactionsTable.userId, usersTable.id))
|
||||
.innerJoin(linkedUsers, eq(transactionsTable.linkedUserId, linkedUsers.id))
|
||||
.orderBy(asc(transactionsTable.balanceDate), asc(transactionsTable.id))
|
||||
.limit(count)
|
||||
|
||||
|
||||
return result.map((row) => {
|
||||
const item = {
|
||||
...row.transaction,
|
||||
user: row.user,
|
||||
linkedUser: row.linkedUser,
|
||||
}
|
||||
...row.transaction,
|
||||
user: row.user,
|
||||
linkedUser: row.linkedUser,
|
||||
}
|
||||
try {
|
||||
return v.parse(transactionDbSchema, item)
|
||||
} catch (e) {
|
||||
throw new DatabaseError('loadLocalTransferTransactions', item, e as Error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
buildTransaction(
|
||||
communityContext: CommunityContext,
|
||||
item: TransactionDb,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.balanceDate)
|
||||
.addMemo(new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), item.amount, communityContext.communityId),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
communityContext: CommunityContext,
|
||||
item: TransactionDb,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.balanceDate)
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), item.amount, communityContext.communityId),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
}
|
||||
|
||||
calculateBalances(
|
||||
item: TransactionDb,
|
||||
item: TransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
senderPublicKey: MemoryBlockPtr,
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
const recipientLastBalance = this.getLastBalanceForUser(recipientPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
|
||||
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)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, communityContext.blockchain)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
recipientLastBalance.updateLegacyDecay(item.amount, item.balanceDate)
|
||||
|
||||
|
||||
this.accountBalances.add(senderLastBalance.getAccountBalance())
|
||||
this.accountBalances.add(recipientLastBalance.getAccountBalance())
|
||||
return this.accountBalances
|
||||
@ -135,15 +149,17 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
const communityContext = this.context.getCommunityContextByUuid(item.user.communityUuid)
|
||||
const blockchain = communityContext.blockchain
|
||||
if (item.linkedUser.communityUuid !== item.user.communityUuid) {
|
||||
throw new Error(`transfer between user from different communities: ${JSON.stringify(item, null, 2)}`)
|
||||
throw new Error(
|
||||
`transfer between user from different communities: ${JSON.stringify(item, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// I use the received transaction so user and linked user are swapped and user is recipient and linkedUser ist sender
|
||||
const senderKeyPair = this.getAccountKeyPair(communityContext, item.linkedUser.gradidoId)
|
||||
const senderPublicKey = senderKeyPair.getPublicKey()
|
||||
const recipientKeyPair = this.getAccountKeyPair(communityContext, item.user.gradidoId)
|
||||
const recipientPublicKey = recipientKeyPair.getPublicKey()
|
||||
|
||||
|
||||
if (!senderKeyPair || !senderPublicKey || !recipientKeyPair || !recipientPublicKey) {
|
||||
throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
}
|
||||
@ -155,9 +171,9 @@ export class LocalTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_ID),
|
||||
this.calculateBalances(item, communityContext, senderPublicKey, recipientPublicKey),
|
||||
)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e instanceof NotEnoughGradidoBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, blockchain)
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, blockchain)
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
|
||||
@ -1,36 +1,40 @@
|
||||
import { and, asc, eq, isNotNull, isNull, or, gt } from 'drizzle-orm'
|
||||
import {
|
||||
import { and, asc, eq, gt, isNotNull, isNull, or } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
Filter,
|
||||
GradidoDeferredTransfer,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { transactionLinksTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { CommunityContext, RedeemedTransactionLinkDb, redeemedTransactionLinkDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { Context } from '../../Context'
|
||||
import {
|
||||
CommunityContext,
|
||||
RedeemedTransactionLinkDb,
|
||||
redeemedTransactionLinkDbSchema,
|
||||
} from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
|
||||
export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTransactionLinkDb> {
|
||||
export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTransactionLinkDb> {
|
||||
constructor(context: Context) {
|
||||
super(context)
|
||||
this.accountBalances.reserve(3)
|
||||
}
|
||||
|
||||
|
||||
getDate(): Date {
|
||||
return this.peek().redeemedAt
|
||||
}
|
||||
@ -62,17 +66,17 @@ export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTra
|
||||
or(
|
||||
gt(transactionLinksTable.redeemedAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionLinksTable.redeemedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionLinksTable.id, lastIndex.id)
|
||||
)
|
||||
)
|
||||
)
|
||||
eq(transactionLinksTable.redeemedAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionLinksTable.id, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.innerJoin(usersTable, eq(transactionLinksTable.userId, usersTable.id))
|
||||
.innerJoin(redeemedByUser, eq(transactionLinksTable.redeemedBy, redeemedByUser.id))
|
||||
.orderBy(asc(transactionLinksTable.redeemedAt), asc(transactionLinksTable.id))
|
||||
.limit(count)
|
||||
|
||||
|
||||
return result.map((row) => {
|
||||
const item = {
|
||||
...row.transactionLink,
|
||||
@ -88,66 +92,74 @@ export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTra
|
||||
}
|
||||
|
||||
buildTransaction(
|
||||
communityContext: CommunityContext,
|
||||
item: RedeemedTransactionLinkDb,
|
||||
linkFundingTransactionNr: number,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.redeemedAt)
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
communityContext: CommunityContext,
|
||||
item: RedeemedTransactionLinkDb,
|
||||
linkFundingTransactionNr: number,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.redeemedAt)
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setRedeemDeferredTransfer(
|
||||
linkFundingTransactionNr,
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(
|
||||
senderKeyPair.getPublicKey(),
|
||||
item.amount,
|
||||
communityContext.communityId,
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setRedeemDeferredTransfer(
|
||||
linkFundingTransactionNr,
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), item.amount, communityContext.communityId),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
recipientKeyPair.getPublicKey(),
|
||||
),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
}
|
||||
|
||||
calculateBalances(
|
||||
item: RedeemedTransactionLinkDb,
|
||||
item: RedeemedTransactionLinkDb,
|
||||
fundingTransaction: GradidoDeferredTransfer,
|
||||
communityContext: CommunityContext,
|
||||
senderPublicKey: MemoryBlockPtr,
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
|
||||
|
||||
const senderLastBalance = this.getLastBalanceForUser(
|
||||
senderPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId
|
||||
senderPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
const fundingUserLastBalance = this.getLastBalanceForUser(
|
||||
fundingTransaction.getSenderPublicKey()!,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId
|
||||
fundingTransaction.getSenderPublicKey()!,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
const recipientLastBalance = this.getLastBalanceForUser(
|
||||
recipientPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId
|
||||
recipientPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
|
||||
if (senderLastBalance.getAccountBalance().getBalance().lt(item.amount)) {
|
||||
throw new Error(`sender has not enough balance (${senderLastBalance.getAccountBalance().getBalance().toString()}) to send ${item.amount.toString()} to ${recipientPublicKey.convertToHex()}`)
|
||||
throw new Error(
|
||||
`sender has not enough balance (${senderLastBalance.getAccountBalance().getBalance().toString()}) to send ${item.amount.toString()} to ${recipientPublicKey.convertToHex()}`,
|
||||
)
|
||||
}
|
||||
senderLastBalance.updateLegacyDecay(item.amount.negated(), item.redeemedAt)
|
||||
fundingUserLastBalance.updateLegacyDecay(senderLastBalance.getBalance(), item.redeemedAt)
|
||||
recipientLastBalance.updateLegacyDecay(item.amount, item.redeemedAt)
|
||||
|
||||
|
||||
// account of link is set to zero, and change send back to link creator
|
||||
this.accountBalances.add(new AccountBalance(senderPublicKey, GradidoUnit.zero(), communityContext.communityId))
|
||||
this.accountBalances.add(
|
||||
new AccountBalance(senderPublicKey, GradidoUnit.zero(), communityContext.communityId),
|
||||
)
|
||||
this.accountBalances.add(recipientLastBalance.getAccountBalance())
|
||||
this.accountBalances.add(fundingUserLastBalance.getAccountBalance())
|
||||
return this.accountBalances
|
||||
@ -165,29 +177,48 @@ export class RedeemTransactionLinksSyncRole extends AbstractSyncRole<RedeemedTra
|
||||
if (!senderKeyPair || !senderPublicKey || !recipientKeyPair || !recipientPublicKey) {
|
||||
throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
}
|
||||
|
||||
|
||||
const transaction = blockchain.findOne(Filter.lastBalanceFor(senderPublicKey))
|
||||
if (!transaction) {
|
||||
throw new Error(`expect transaction for code: ${item.code}`)
|
||||
}
|
||||
// should be funding transaction
|
||||
if (!transaction.isDeferredTransfer()) {
|
||||
throw new Error(`expect funding transaction: ${transaction.getConfirmedTransaction()?.toJson(true)}`)
|
||||
throw new Error(
|
||||
`expect funding transaction: ${transaction.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
const body = transaction.getConfirmedTransaction()?.getGradidoTransaction()?.getTransactionBody();
|
||||
const body = transaction
|
||||
.getConfirmedTransaction()
|
||||
?.getGradidoTransaction()
|
||||
?.getTransactionBody()
|
||||
const deferredTransfer = body?.getDeferredTransfer()
|
||||
if (!deferredTransfer || !deferredTransfer.getRecipientPublicKey()?.equal(senderPublicKey)) {
|
||||
throw new Error(`expect funding transaction to belong to code: ${item.code}: ${transaction.getConfirmedTransaction()?.toJson(true)}`)
|
||||
throw new Error(
|
||||
`expect funding transaction to belong to code: ${item.code}: ${transaction.getConfirmedTransaction()?.toJson(true)}`,
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(communityContext, item, transaction.getTransactionNr(), senderKeyPair, recipientKeyPair).build(),
|
||||
this.buildTransaction(
|
||||
communityContext,
|
||||
item,
|
||||
transaction.getTransactionNr(),
|
||||
senderKeyPair,
|
||||
recipientKeyPair,
|
||||
).build(),
|
||||
blockchain,
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_LINK_ID),
|
||||
this.calculateBalances(item, deferredTransfer, communityContext, senderPublicKey, recipientPublicKey),
|
||||
this.calculateBalances(
|
||||
item,
|
||||
deferredTransfer,
|
||||
communityContext,
|
||||
senderPublicKey,
|
||||
recipientPublicKey,
|
||||
),
|
||||
)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +1,39 @@
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import { transactionsTable, usersTable } from '../../drizzle.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { CommunityContext, TransactionDb, transactionDbSchema, UserDb } from '../../valibot.schema'
|
||||
import * as v from 'valibot'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { TransactionTypeId } from '../../data/TransactionTypeId'
|
||||
import { DatabaseError, NegativeBalanceError } from '../../errors'
|
||||
import { asc, and, eq, gt, ne, or, inArray, isNull } from 'drizzle-orm'
|
||||
import { NotEnoughGradidoBalanceError } from '../../errors'
|
||||
import { BlockchainError } from '../../errors'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { AccountBalance, AccountBalances, AuthenticatedEncryption, EncryptedMemo, GradidoTransactionBuilder, GradidoUnit, KeyPairEd25519, LedgerAnchor, MemoryBlockPtr, TransferAmount } from 'gradido-blockchain-js'
|
||||
import { Decimal } from 'decimal.js'
|
||||
import { and, asc, eq, gt, inArray, isNull, ne, or } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/mysql-core'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { TransactionTypeId } from '../../data/TransactionTypeId'
|
||||
import { transactionsTable, usersTable } from '../../drizzle.schema'
|
||||
import {
|
||||
BlockchainError,
|
||||
DatabaseError,
|
||||
NegativeBalanceError,
|
||||
NotEnoughGradidoBalanceError,
|
||||
} from '../../errors'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { CommunityContext, TransactionDb, transactionDbSchema, UserDb } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
|
||||
export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb> {
|
||||
constructor(context: Context) {
|
||||
super(context)
|
||||
this.accountBalances.reserve(1)
|
||||
}
|
||||
|
||||
|
||||
getDate(): Date {
|
||||
return this.peek().balanceDate
|
||||
}
|
||||
@ -50,23 +64,23 @@ export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb>
|
||||
or(
|
||||
gt(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionsTable.balanceDate, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionsTable.id, lastIndex.id)
|
||||
)
|
||||
)
|
||||
)
|
||||
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,
|
||||
}
|
||||
...row.transaction,
|
||||
user: row.user,
|
||||
linkedUser: row.linkedUser,
|
||||
}
|
||||
if (item.typeId === TransactionTypeId.SEND && item.amount) {
|
||||
item.amount = new Decimal(item.amount).neg().toString()
|
||||
}
|
||||
@ -75,101 +89,120 @@ export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb>
|
||||
} catch (e) {
|
||||
throw new DatabaseError('loadRemoteTransferTransactions', item, e as Error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
buildTransaction(
|
||||
item: TransactionDb,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
senderCommunityId: string,
|
||||
recipientCommunityId: string,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.balanceDate)
|
||||
.addMemo(new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(senderCommunityId)
|
||||
.setRecipientCommunity(recipientCommunityId)
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), item.amount, senderCommunityId),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
item: TransactionDb,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
senderCommunityId: string,
|
||||
recipientCommunityId: string,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.balanceDate)
|
||||
.addMemo(
|
||||
new EncryptedMemo(
|
||||
item.memo,
|
||||
new AuthenticatedEncryption(senderKeyPair),
|
||||
new AuthenticatedEncryption(recipientKeyPair),
|
||||
),
|
||||
)
|
||||
.setSenderCommunity(senderCommunityId)
|
||||
.setRecipientCommunity(recipientCommunityId)
|
||||
.setTransactionTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), item.amount, senderCommunityId),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
}
|
||||
|
||||
calculateBalances(
|
||||
item: TransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
coinCommunityId: string,
|
||||
amount: GradidoUnit,
|
||||
publicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
if (communityContext.foreign) {
|
||||
this.accountBalances.add(new AccountBalance(publicKey, GradidoUnit.zero(), coinCommunityId))
|
||||
return this.accountBalances
|
||||
} else {
|
||||
// try to use same coins from this community
|
||||
let lastBalance = this.getLastBalanceForUser(publicKey, communityContext.blockchain, coinCommunityId)
|
||||
if (lastBalance.getBalance().equal(GradidoUnit.zero()) || lastBalance.getBalance().calculateDecay(lastBalance.getDate(), item.balanceDate).lt(amount)) {
|
||||
// don't work, so we use or own coins
|
||||
lastBalance = this.getLastBalanceForUser(publicKey, communityContext.blockchain, communityContext.communityId)
|
||||
}
|
||||
|
||||
try {
|
||||
lastBalance.updateLegacyDecay(amount, item.balanceDate)
|
||||
} catch(e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
console.log(`coin community id: ${coinCommunityId}, context community id: ${communityContext.communityId}`)
|
||||
this.logLastBalanceChangingTransactions(publicKey, communityContext.blockchain, 1)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
this.accountBalances.add(lastBalance.getAccountBalance())
|
||||
return this.accountBalances
|
||||
item: TransactionDb,
|
||||
communityContext: CommunityContext,
|
||||
coinCommunityId: string,
|
||||
amount: GradidoUnit,
|
||||
publicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
if (communityContext.foreign) {
|
||||
this.accountBalances.add(new AccountBalance(publicKey, GradidoUnit.zero(), coinCommunityId))
|
||||
return this.accountBalances
|
||||
} else {
|
||||
// try to use same coins from this community
|
||||
let lastBalance = this.getLastBalanceForUser(
|
||||
publicKey,
|
||||
communityContext.blockchain,
|
||||
coinCommunityId,
|
||||
)
|
||||
if (
|
||||
lastBalance.getBalance().equal(GradidoUnit.zero()) ||
|
||||
lastBalance.getBalance().calculateDecay(lastBalance.getDate(), item.balanceDate).lt(amount)
|
||||
) {
|
||||
// don't work, so we use or own coins
|
||||
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, 1)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
this.accountBalances.add(lastBalance.getAccountBalance())
|
||||
return this.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 }
|
||||
)
|
||||
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)
|
||||
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`)
|
||||
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 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 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
|
||||
item,
|
||||
senderKeyPair,
|
||||
recipientKeyPair,
|
||||
senderCommunityContext.communityId,
|
||||
recipientCommunityContext.communityId,
|
||||
)
|
||||
const outboundTransaction = transactionBuilder.buildOutbound()
|
||||
|
||||
@ -178,11 +211,17 @@ export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb>
|
||||
outboundTransaction,
|
||||
senderBlockchain,
|
||||
ledgerAnchor,
|
||||
this.calculateBalances(item, senderCommunityContext, senderCommunityContext.communityId, item.amount.negated(), senderPublicKey),
|
||||
this.calculateBalances(
|
||||
item,
|
||||
senderCommunityContext,
|
||||
senderCommunityContext.communityId,
|
||||
item.amount.negated(),
|
||||
senderPublicKey,
|
||||
),
|
||||
)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e instanceof NotEnoughGradidoBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, senderBlockchain)
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, senderBlockchain)
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
@ -193,11 +232,17 @@ export class RemoteTransactionsSyncRole extends AbstractSyncRole<TransactionDb>
|
||||
inboundTransaction,
|
||||
recipientBlockchain,
|
||||
ledgerAnchor,
|
||||
this.calculateBalances(item, recipientCommunityContext, senderCommunityContext.communityId, item.amount, recipientPublicKey),
|
||||
this.calculateBalances(
|
||||
item,
|
||||
recipientCommunityContext,
|
||||
senderCommunityContext.communityId,
|
||||
item.amount,
|
||||
recipientPublicKey,
|
||||
),
|
||||
)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e instanceof NotEnoughGradidoBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(recipientPublicKey, recipientBlockchain)
|
||||
this.logLastBalanceChangingTransactions(recipientPublicKey, recipientBlockchain)
|
||||
}
|
||||
throw new BlockchainError(`Error adding ${this.itemTypeName()}`, item, e as Error)
|
||||
}
|
||||
|
||||
@ -1,28 +1,28 @@
|
||||
import { asc, eq, or, gt, and } from 'drizzle-orm'
|
||||
import {
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { and, asc, eq, gt, or } from 'drizzle-orm'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
DurationSeconds,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount
|
||||
AccountBalances,
|
||||
AuthenticatedEncryption,
|
||||
DurationSeconds,
|
||||
EncryptedMemo,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoTransfer,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
TransferAmount,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { transactionLinksTable, usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError, NegativeBalanceError } from '../../errors'
|
||||
import { reverseLegacyDecay, toMysqlDateTime } from '../../utils'
|
||||
import { CommunityContext, TransactionLinkDb, transactionLinkDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { deriveFromCode } from '../../../../data/deriveKeyPair'
|
||||
import { reverseLegacyDecay, toMysqlDateTime } from '../../utils'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Context } from '../../Context'
|
||||
|
||||
export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<TransactionLinkDb> {
|
||||
constructor(context: Context) {
|
||||
@ -47,16 +47,18 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
|
||||
.select()
|
||||
.from(transactionLinksTable)
|
||||
.innerJoin(usersTable, eq(transactionLinksTable.userId, usersTable.id))
|
||||
.where(or(
|
||||
gt(transactionLinksTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionLinksTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionLinksTable.id, lastIndex.id)
|
||||
)
|
||||
))
|
||||
.where(
|
||||
or(
|
||||
gt(transactionLinksTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(transactionLinksTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(transactionLinksTable.id, lastIndex.id),
|
||||
),
|
||||
),
|
||||
)
|
||||
.orderBy(asc(transactionLinksTable.createdAt), asc(transactionLinksTable.id))
|
||||
.limit(count)
|
||||
|
||||
|
||||
return result.map((row) => {
|
||||
const item = {
|
||||
...row.transaction_links,
|
||||
@ -72,11 +74,11 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
|
||||
|
||||
buildTransaction(
|
||||
communityContext: CommunityContext,
|
||||
item: TransactionLinkDb,
|
||||
item: TransactionLinkDb,
|
||||
blockedAmount: GradidoUnit,
|
||||
duration: DurationSeconds,
|
||||
senderKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
recipientKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.createdAt)
|
||||
@ -90,81 +92,126 @@ export class TransactionLinkFundingsSyncRole extends AbstractSyncRole<Transactio
|
||||
.setSenderCommunity(communityContext.communityId)
|
||||
.setDeferredTransfer(
|
||||
new GradidoTransfer(
|
||||
new TransferAmount(senderKeyPair.getPublicKey(), blockedAmount, communityContext.communityId),
|
||||
new TransferAmount(
|
||||
senderKeyPair.getPublicKey(),
|
||||
blockedAmount,
|
||||
communityContext.communityId,
|
||||
),
|
||||
recipientKeyPair.getPublicKey(),
|
||||
),
|
||||
duration,
|
||||
)
|
||||
)
|
||||
.sign(senderKeyPair)
|
||||
}
|
||||
|
||||
calculateBalances(
|
||||
item: TransactionLinkDb,
|
||||
blockedAmount: GradidoUnit,
|
||||
communityContext: CommunityContext,
|
||||
senderPublicKey: MemoryBlockPtr,
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
let senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
try {
|
||||
senderLastBalance.updateLegacyDecay(blockedAmount.negated(), item.createdAt)
|
||||
} catch(e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, communityContext.blockchain)
|
||||
this.context.logger.debug(`sender public key: ${senderPublicKey.convertToHex()}`)
|
||||
throw e
|
||||
}
|
||||
item: TransactionLinkDb,
|
||||
blockedAmount: GradidoUnit,
|
||||
communityContext: CommunityContext,
|
||||
senderPublicKey: MemoryBlockPtr,
|
||||
recipientPublicKey: MemoryBlockPtr,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
const senderLastBalance = this.getLastBalanceForUser(
|
||||
senderPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
try {
|
||||
senderLastBalance.updateLegacyDecay(blockedAmount.negated(), item.createdAt)
|
||||
} catch (e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
this.logLastBalanceChangingTransactions(senderPublicKey, communityContext.blockchain)
|
||||
this.context.logger.debug(`sender public key: ${senderPublicKey.convertToHex()}`)
|
||||
throw e
|
||||
}
|
||||
|
||||
this.accountBalances.add(senderLastBalance.getAccountBalance())
|
||||
this.accountBalances.add(new AccountBalance(recipientPublicKey, blockedAmount, communityContext.communityId))
|
||||
return this.accountBalances
|
||||
}
|
||||
|
||||
this.accountBalances.add(senderLastBalance.getAccountBalance())
|
||||
this.accountBalances.add(
|
||||
new AccountBalance(recipientPublicKey, blockedAmount, communityContext.communityId),
|
||||
)
|
||||
return this.accountBalances
|
||||
}
|
||||
|
||||
pushToBlockchain(item: TransactionLinkDb): void {
|
||||
const communityContext = this.context.getCommunityContextByUuid(item.user.communityUuid)
|
||||
const blockchain = communityContext.blockchain
|
||||
|
||||
|
||||
const senderKeyPair = this.getAccountKeyPair(communityContext, item.user.gradidoId)
|
||||
const senderPublicKey = senderKeyPair.getPublicKey()
|
||||
const recipientKeyPair = deriveFromCode(item.code)
|
||||
const recipientPublicKey = recipientKeyPair.getPublicKey()
|
||||
|
||||
|
||||
if (!senderKeyPair || !senderPublicKey || !recipientKeyPair || !recipientPublicKey) {
|
||||
throw new Error(`missing key for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
}
|
||||
let duration = new DurationSeconds((item.validUntil.getTime() - item.createdAt.getTime()) / 1000)
|
||||
let blockedAmount = GradidoUnit.fromString(reverseLegacyDecay(new Decimal(item.amount.toString()), duration.getSeconds()).toString())
|
||||
const duration = new DurationSeconds(
|
||||
(item.validUntil.getTime() - item.createdAt.getTime()) / 1000,
|
||||
)
|
||||
let blockedAmount = GradidoUnit.fromString(
|
||||
reverseLegacyDecay(new Decimal(item.amount.toString()), duration.getSeconds()).toString(),
|
||||
)
|
||||
let accountBalances: AccountBalances
|
||||
try {
|
||||
accountBalances = this.calculateBalances(item, blockedAmount, communityContext, senderPublicKey, recipientPublicKey)
|
||||
} catch(e) {
|
||||
accountBalances = this.calculateBalances(
|
||||
item,
|
||||
blockedAmount,
|
||||
communityContext,
|
||||
senderPublicKey,
|
||||
recipientPublicKey,
|
||||
)
|
||||
} catch (e) {
|
||||
if (item.deletedAt && e instanceof NegativeBalanceError) {
|
||||
const senderLastBalance = this.getLastBalanceForUser(senderPublicKey, communityContext.blockchain, communityContext.communityId)
|
||||
const senderLastBalance = this.getLastBalanceForUser(
|
||||
senderPublicKey,
|
||||
communityContext.blockchain,
|
||||
communityContext.communityId,
|
||||
)
|
||||
senderLastBalance.updateLegacyDecay(GradidoUnit.zero(), item.createdAt)
|
||||
const oldBlockedAmountString = blockedAmount.toString()
|
||||
blockedAmount = senderLastBalance.getBalance()
|
||||
accountBalances = this.calculateBalances(item, blockedAmount, communityContext, senderPublicKey, recipientPublicKey)
|
||||
accountBalances = this.calculateBalances(
|
||||
item,
|
||||
blockedAmount,
|
||||
communityContext,
|
||||
senderPublicKey,
|
||||
recipientPublicKey,
|
||||
)
|
||||
this.context.logger.warn(
|
||||
`workaround: fix founding for deleted link, reduce funding to actual sender balance: ${senderPublicKey.convertToHex()}: from ${oldBlockedAmountString} GDD to ${blockedAmount.toString()} GDD`
|
||||
`workaround: fix founding for deleted link, reduce funding to actual sender balance: ${senderPublicKey.convertToHex()}: from ${oldBlockedAmountString} GDD to ${blockedAmount.toString()} GDD`,
|
||||
)
|
||||
} else {
|
||||
this.context.logger.error(`error calculate account balances for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`)
|
||||
this.context.logger.error(
|
||||
`error calculate account balances for ${this.itemTypeName()}: ${JSON.stringify(item, null, 2)}`,
|
||||
)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(communityContext, item, blockedAmount, duration, senderKeyPair, recipientKeyPair).build(),
|
||||
this.buildTransaction(
|
||||
communityContext,
|
||||
item,
|
||||
blockedAmount,
|
||||
duration,
|
||||
senderKeyPair,
|
||||
recipientKeyPair,
|
||||
).build(),
|
||||
blockchain,
|
||||
new LedgerAnchor(item.id, LedgerAnchor.Type_LEGACY_GRADIDO_DB_TRANSACTION_LINK_ID),
|
||||
accountBalances,
|
||||
)
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (e instanceof NegativeBalanceError) {
|
||||
if (!item.deletedAt && !item.redeemedAt && item.validUntil.getTime() < new Date().getTime()) {
|
||||
this.context.logger.warn(`TransactionLinks: ${item.id} skipped, because else it lead to negative balance error, but it wasn't used.`)
|
||||
if (
|
||||
!item.deletedAt &&
|
||||
!item.redeemedAt &&
|
||||
item.validUntil.getTime() < new Date().getTime()
|
||||
) {
|
||||
this.context.logger.warn(
|
||||
`TransactionLinks: ${item.id} skipped, because else it lead to negative balance error, but it wasn't used.`,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
import { asc, and, gt, eq, or } from 'drizzle-orm'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
AddressType_COMMUNITY_HUMAN,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr
|
||||
import { and, asc, eq, gt, or } from 'drizzle-orm'
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalances,
|
||||
AddressType_COMMUNITY_HUMAN,
|
||||
GradidoTransactionBuilder,
|
||||
GradidoUnit,
|
||||
KeyPairEd25519,
|
||||
LedgerAnchor,
|
||||
MemoryBlockPtr,
|
||||
} from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { deriveFromKeyPairAndUuid } from '../../../../data/deriveKeyPair'
|
||||
import { Uuidv4Hash } from '../../../../data/Uuidv4Hash'
|
||||
import { addToBlockchain } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { usersTable } from '../../drizzle.schema'
|
||||
import { BlockchainError, DatabaseError } from '../../errors'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { CommunityContext, UserDb, userDbSchema } from '../../valibot.schema'
|
||||
import { AbstractSyncRole, IndexType } from './AbstractSync.role'
|
||||
import { toMysqlDateTime } from '../../utils'
|
||||
import { Context } from '../../Context'
|
||||
|
||||
export class UsersSyncRole extends AbstractSyncRole<UserDb> {
|
||||
constructor(context: Context) {
|
||||
@ -40,20 +40,22 @@ export class UsersSyncRole extends AbstractSyncRole<UserDb> {
|
||||
|
||||
async loadFromDb(lastIndex: IndexType, count: number): Promise<UserDb[]> {
|
||||
const result = await this.context.db
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(and(
|
||||
.select()
|
||||
.from(usersTable)
|
||||
.where(
|
||||
and(
|
||||
or(
|
||||
gt(usersTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
and(
|
||||
eq(usersTable.createdAt, toMysqlDateTime(lastIndex.date)),
|
||||
gt(usersTable.id, lastIndex.id)
|
||||
)
|
||||
)
|
||||
))
|
||||
.orderBy(asc(usersTable.createdAt), asc(usersTable.id))
|
||||
.limit(count)
|
||||
|
||||
gt(usersTable.id, lastIndex.id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.orderBy(asc(usersTable.createdAt), asc(usersTable.id))
|
||||
.limit(count)
|
||||
|
||||
return result.map((row) => {
|
||||
try {
|
||||
return v.parse(userDbSchema, row)
|
||||
@ -65,10 +67,10 @@ export class UsersSyncRole extends AbstractSyncRole<UserDb> {
|
||||
|
||||
buildTransaction(
|
||||
communityContext: CommunityContext,
|
||||
item: UserDb,
|
||||
communityKeyPair: KeyPairEd25519,
|
||||
accountKeyPair: KeyPairEd25519,
|
||||
userKeyPair: KeyPairEd25519
|
||||
item: UserDb,
|
||||
communityKeyPair: KeyPairEd25519,
|
||||
accountKeyPair: KeyPairEd25519,
|
||||
userKeyPair: KeyPairEd25519,
|
||||
): GradidoTransactionBuilder {
|
||||
return this.transactionBuilder
|
||||
.setCreatedAt(item.createdAt)
|
||||
@ -84,9 +86,14 @@ export class UsersSyncRole extends AbstractSyncRole<UserDb> {
|
||||
.sign(userKeyPair)
|
||||
}
|
||||
|
||||
calculateAccountBalances(accountPublicKey: MemoryBlockPtr, communityContext: CommunityContext,): AccountBalances {
|
||||
calculateAccountBalances(
|
||||
accountPublicKey: MemoryBlockPtr,
|
||||
communityContext: CommunityContext,
|
||||
): AccountBalances {
|
||||
this.accountBalances.clear()
|
||||
this.accountBalances.add(new AccountBalance(accountPublicKey, GradidoUnit.zero(), communityContext.communityId))
|
||||
this.accountBalances.add(
|
||||
new AccountBalance(accountPublicKey, GradidoUnit.zero(), communityContext.communityId),
|
||||
)
|
||||
return this.accountBalances
|
||||
}
|
||||
|
||||
@ -101,7 +108,13 @@ export class UsersSyncRole extends AbstractSyncRole<UserDb> {
|
||||
|
||||
try {
|
||||
addToBlockchain(
|
||||
this.buildTransaction(communityContext, item, communityContext.keyPair, accountKeyPair, userKeyPair).build(),
|
||||
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, communityContext),
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { Profiler } from 'gradido-blockchain-js'
|
||||
import { Context } from '../../Context'
|
||||
import { CreationsSyncRole } from './CreationsSync.role'
|
||||
import { LocalTransactionsSyncRole } from './LocalTransactionsSync.role'
|
||||
import { UsersSyncRole } from './UsersSync.role'
|
||||
import { TransactionLinkFundingsSyncRole } from './TransactionLinkFundingsSync.role'
|
||||
import { RedeemTransactionLinksSyncRole } from './RedeemTransactionLinksSync.role'
|
||||
import { ContributionLinkTransactionSyncRole } from './ContributionLinkTransactionSync.role'
|
||||
import { DeletedTransactionLinksSyncRole } from './DeletedTransactionLinksSync.role'
|
||||
import { RemoteTransactionsSyncRole } from './RemoteTransactionsSync.role'
|
||||
import { callTime } from '../../blockchain'
|
||||
import { Context } from '../../Context'
|
||||
import { nanosBalanceForUser } from './AbstractSync.role'
|
||||
import { ContributionLinkTransactionSyncRole } from './ContributionLinkTransactionSync.role'
|
||||
import { CreationsSyncRole } from './CreationsSync.role'
|
||||
import { DeletedTransactionLinksSyncRole } from './DeletedTransactionLinksSync.role'
|
||||
import { LocalTransactionsSyncRole } from './LocalTransactionsSync.role'
|
||||
import { RedeemTransactionLinksSyncRole } from './RedeemTransactionLinksSync.role'
|
||||
import { RemoteTransactionsSyncRole } from './RemoteTransactionsSync.role'
|
||||
import { TransactionLinkFundingsSyncRole } from './TransactionLinkFundingsSync.role'
|
||||
import { UsersSyncRole } from './UsersSync.role'
|
||||
|
||||
export async function syncDbWithBlockchainContext(context: Context, batchSize: number) {
|
||||
const timeUsedDB = new Profiler()
|
||||
@ -38,7 +38,7 @@ export async function syncDbWithBlockchainContext(context: Context, batchSize: n
|
||||
const loadedItemsCount = results.reduce((acc, c) => acc + c, 0)
|
||||
// log only, if at least one new item was loaded
|
||||
if (loadedItemsCount && isDebug) {
|
||||
context.logger.debug(`${loadedItemsCount} new items loaded from db in ${timeUsedDB.string()}`)
|
||||
context.logger.debug(`${loadedItemsCount} new items loaded from db in ${timeUsedDB.string()}`)
|
||||
}
|
||||
|
||||
// remove empty containers
|
||||
@ -53,17 +53,21 @@ 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()}`)
|
||||
}
|
||||
available[0].toBlockchain()
|
||||
available[0].toBlockchain()
|
||||
transactionsCount++
|
||||
if (isDebug) {
|
||||
if (timeBetweenPrints.millis() > 100) {
|
||||
if (isDebug) {
|
||||
if (timeBetweenPrints.millis() > 100) {
|
||||
process.stdout.write(`successfully added to blockchain: ${transactionsCount}\r`)
|
||||
timeBetweenPrints.reset()
|
||||
}
|
||||
transactionsCountSinceLastLog++
|
||||
transactionsCountSinceLastLog++
|
||||
if (transactionsCountSinceLastLog >= batchSize) {
|
||||
context.logger.debug(`${transactionsCountSinceLastLog} transactions added to blockchain in ${timeUsedBlockchain.string()}`)
|
||||
context.logger.info(`Time for createAndConfirm: ${((callTime - lastPrintedCallTime) / 1000 / 1000).toFixed(2)} milliseconds`)
|
||||
context.logger.debug(
|
||||
`${transactionsCountSinceLastLog} transactions added to blockchain in ${timeUsedBlockchain.string()}`,
|
||||
)
|
||||
context.logger.info(
|
||||
`Time for createAndConfirm: ${((callTime - lastPrintedCallTime) / 1000 / 1000).toFixed(2)} milliseconds`,
|
||||
)
|
||||
lastPrintedCallTime = callTime
|
||||
timeUsedBlockchain.reset()
|
||||
transactionsCountSinceLastLog = 0
|
||||
@ -76,8 +80,14 @@ export async function syncDbWithBlockchainContext(context: Context, batchSize: n
|
||||
}
|
||||
}
|
||||
}
|
||||
process.stdout.write(`successfully added to blockchain: ${transactionsCount}\n`)
|
||||
context.logger.info(`Synced ${transactionsCount} transactions to blockchain in ${timeUsedAll.string()}`)
|
||||
context.logger.info(`Time for createAndConfirm: ${(callTime / 1000 / 1000 / 1000).toFixed(2)} seconds`)
|
||||
context.logger.info(`Time for call lastBalance of user: ${(nanosBalanceForUser / 1000 / 1000 / 1000).toFixed(2)} seconds`)
|
||||
process.stdout.write(`successfully added to blockchain: ${transactionsCount}\n`)
|
||||
context.logger.info(
|
||||
`Synced ${transactionsCount} transactions to blockchain in ${timeUsedAll.string()}`,
|
||||
)
|
||||
context.logger.info(
|
||||
`Time for createAndConfirm: ${(callTime / 1000 / 1000 / 1000).toFixed(2)} seconds`,
|
||||
)
|
||||
context.logger.info(
|
||||
`Time for call lastBalance of user: ${(nanosBalanceForUser / 1000 / 1000 / 1000).toFixed(2)} seconds`,
|
||||
)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export function bytesToKbyte(bytes: number): string {
|
||||
}
|
||||
|
||||
export function bytesString(bytes: number): string {
|
||||
if (bytes > (1024 * 1024)) {
|
||||
if (bytes > 1024 * 1024) {
|
||||
return `${bytesToMbyte(bytes)} MB`
|
||||
} else if (bytes > 1024) {
|
||||
return `${bytesToKbyte(bytes)} KB`
|
||||
@ -50,7 +50,9 @@ export function legacyCalculateDecay(amount: Decimal, from: Date, to: Date): Dec
|
||||
const startBlockMs = DECAY_START_TIME.getTime()
|
||||
|
||||
if (toMs < fromMs) {
|
||||
throw new Error(`calculateDecay: to (${to.toISOString()}) < from (${from.toISOString()}), reverse decay calculation is invalid`)
|
||||
throw new Error(
|
||||
`calculateDecay: to (${to.toISOString()}) < from (${from.toISOString()}), reverse decay calculation is invalid`,
|
||||
)
|
||||
}
|
||||
|
||||
// decay started after end date; no decay
|
||||
@ -59,7 +61,7 @@ export function legacyCalculateDecay(amount: Decimal, from: Date, to: Date): Dec
|
||||
}
|
||||
// decay started before start date; decay for full duration
|
||||
let duration = (toMs - fromMs) / 1000
|
||||
|
||||
|
||||
// decay started between start and end date; decay from decay start till end date
|
||||
if (startBlockMs >= fromMs) {
|
||||
duration = (toMs - startBlockMs) / 1000
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { GradidoUnit, InMemoryBlockchain, KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
import { booleanSchema, dateSchema } from '../../schemas/typeConverter.schema'
|
||||
@ -9,7 +10,6 @@ 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))
|
||||
|
||||
@ -51,53 +51,71 @@ export const transactionBaseSchema = v.object({
|
||||
user: userDbSchema,
|
||||
})
|
||||
|
||||
export const transactionDbSchema = v.pipe(
|
||||
v.object({
|
||||
...transactionBaseSchema.entries,
|
||||
typeId: v.enum(TransactionTypeId),
|
||||
balanceDate: dateSchema,
|
||||
linkedUser: userDbSchema,
|
||||
}),
|
||||
v.custom((value: any) => {
|
||||
if (
|
||||
value.user &&
|
||||
value.linkedUser &&
|
||||
!value.transactionLinkCode &&
|
||||
value.user.gradidoId === value.linkedUser.gradidoId
|
||||
) {
|
||||
throw new Error(
|
||||
`expect user to be different from linkedUser: ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
// check that user and linked user exist before transaction balance date
|
||||
const balanceDate = new Date(value.balanceDate)
|
||||
if (
|
||||
value.user.createdAt.getTime() >= balanceDate.getTime() ||
|
||||
value.linkedUser?.createdAt.getTime() >= balanceDate.getTime()
|
||||
) {
|
||||
throw new Error(
|
||||
`at least one user was created after transaction balance date, logic error! ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
export const transactionDbSchema = v.pipe(v.object({
|
||||
...transactionBaseSchema.entries,
|
||||
typeId: v.enum(TransactionTypeId),
|
||||
balanceDate: dateSchema,
|
||||
linkedUser: userDbSchema,
|
||||
}), v.custom((value: any) => {
|
||||
if (value.user && value.linkedUser && !value.transactionLinkCode && value.user.gradidoId === value.linkedUser.gradidoId) {
|
||||
throw new Error(`expect user to be different from linkedUser: ${JSON.stringify(value, null, 2)}`)
|
||||
}
|
||||
// check that user and linked user exist before transaction balance date
|
||||
const balanceDate = new Date(value.balanceDate)
|
||||
if (
|
||||
value.user.createdAt.getTime() >= balanceDate.getTime() ||
|
||||
value.linkedUser?.createdAt.getTime() >= balanceDate.getTime()
|
||||
) {
|
||||
throw new Error(
|
||||
`at least one user was created after transaction balance date, logic error! ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}))
|
||||
return value
|
||||
}),
|
||||
)
|
||||
|
||||
export const creationTransactionDbSchema = v.pipe(v.object({
|
||||
...transactionBaseSchema.entries,
|
||||
contributionDate: dateSchema,
|
||||
confirmedAt: dateSchema,
|
||||
confirmedByUser: userDbSchema,
|
||||
transactionId: positiveNumberSchema,
|
||||
}), v.custom((value: any) => {
|
||||
if (value.user && value.confirmedByUser && value.user.gradidoId === value.confirmedByUser.gradidoId) {
|
||||
throw new Error(`expect user to be different from confirmedByUser: ${JSON.stringify(value, null, 2)}`)
|
||||
}
|
||||
// check that user and confirmedByUser exist before transaction balance date
|
||||
const confirmedAt = new Date(value.confirmedAt)
|
||||
if (
|
||||
value.user.createdAt.getTime() >= confirmedAt.getTime() ||
|
||||
value.confirmedByUser?.createdAt.getTime() >= confirmedAt.getTime()
|
||||
) {
|
||||
throw new Error(
|
||||
`at least one user was created after transaction confirmedAt date, logic error! ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}))
|
||||
export const creationTransactionDbSchema = v.pipe(
|
||||
v.object({
|
||||
...transactionBaseSchema.entries,
|
||||
contributionDate: dateSchema,
|
||||
confirmedAt: dateSchema,
|
||||
confirmedByUser: userDbSchema,
|
||||
transactionId: positiveNumberSchema,
|
||||
}),
|
||||
v.custom((value: any) => {
|
||||
if (
|
||||
value.user &&
|
||||
value.confirmedByUser &&
|
||||
value.user.gradidoId === value.confirmedByUser.gradidoId
|
||||
) {
|
||||
throw new Error(
|
||||
`expect user to be different from confirmedByUser: ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
// check that user and confirmedByUser exist before transaction balance date
|
||||
const confirmedAt = new Date(value.confirmedAt)
|
||||
if (
|
||||
value.user.createdAt.getTime() >= confirmedAt.getTime() ||
|
||||
value.confirmedByUser?.createdAt.getTime() >= confirmedAt.getTime()
|
||||
) {
|
||||
throw new Error(
|
||||
`at least one user was created after transaction confirmedAt date, logic error! ${JSON.stringify(value, null, 2)}`,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}),
|
||||
)
|
||||
|
||||
export const transactionLinkDbSchema = v.object({
|
||||
...transactionBaseSchema.entries,
|
||||
|
||||
@ -89,7 +89,7 @@ export const confirmedTransactionSchema = v.pipe(
|
||||
if (typeof data === 'object' && 'base64' in data && 'communityId' in data) {
|
||||
return confirmedTransactionFromBase64(data.base64, data.communityId)
|
||||
}
|
||||
throw new Error('invalid data, community id missing, couldn\'t deserialize')
|
||||
throw new Error("invalid data, community id missing, couldn't deserialize")
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@ -31,4 +31,4 @@ export function checkPathExist(path: string, createIfMissing: boolean = false):
|
||||
|
||||
export function toFolderName(name: string): string {
|
||||
return name.toLowerCase().replace(/[^a-z0-9]/g, '_')
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,10 @@ import { AccountType } from '../data/AccountType.enum'
|
||||
import { AddressType } from '../data/AddressType.enum'
|
||||
import { Uuidv4 } from '../schemas/typeGuard.schema'
|
||||
|
||||
export const confirmedTransactionFromBase64 = (base64: string, communityId: Uuidv4): ConfirmedTransaction => {
|
||||
export const confirmedTransactionFromBase64 = (
|
||||
base64: string,
|
||||
communityId: Uuidv4,
|
||||
): ConfirmedTransaction => {
|
||||
const confirmedTransactionBinaryPtr = MemoryBlock.createPtr(MemoryBlock.fromBase64(base64))
|
||||
const deserializer = new InteractionDeserialize(
|
||||
confirmedTransactionBinaryPtr,
|
||||
|
||||
@ -22,9 +22,7 @@ export function decayFormula(value: Decimal, seconds: number): Decimal {
|
||||
// chatgpt: We convert to string here to avoid precision loss:
|
||||
// .pow(seconds) can internally round the result, especially for large values of `seconds`.
|
||||
// Using .toString() ensures full precision is preserved in the multiplication.
|
||||
return value.mul(
|
||||
DECAY_FACTOR.pow(seconds).toString(),
|
||||
)
|
||||
return value.mul(DECAY_FACTOR.pow(seconds).toString())
|
||||
}
|
||||
|
||||
// legacy reverse decay formula
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user