Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
410 changes: 410 additions & 0 deletions server/core-fund-flow-hardening.test.ts

Large diffs are not rendered by default.

192 changes: 192 additions & 0 deletions server/middleware/coreAtomicity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* RemitFlow — Core Operation Atomicity Guard
* ──────────────────────────────────────────
* Provides atomic fund flow protection for core routers that previously
* lacked middleware integration: savings, CBDC, bills, airtime, batch,
* stablecoin.swap, wallet operations.
*
* Each guarded operation gets:
* 1. Idempotency check (SHA-256 key → 24h cache)
* 2. TigerBeetle double-entry ledger
* 3. Kafka event publishing
* 4. Audit logging with feature label
*/
import { randomBytes } from "crypto";
import { createHash } from "crypto";
import { logger } from "../_core/logger";
import { publishEvent, KAFKA_TOPICS, type TransactionEvent } from "./kafka";
import { tigerBeetle } from "./middlewareIntegration";
import { createAuditLog } from "../db";

// ── Kafka Topics for Core Operations ────────────────────────────────────────
export const CORE_TOPICS = {
SAVINGS_DEPOSIT: "remitflow.savings.deposit",
SAVINGS_WITHDRAW: "remitflow.savings.withdraw",
CBDC_TRANSFER: "remitflow.cbdc.transfer",
CBDC_RECEIVE: "remitflow.cbdc.receive",
BILL_PAYMENT: "remitflow.bill.payment",
AIRTIME_TOPUP: "remitflow.airtime.topup",
BATCH_PAYMENT: "remitflow.batch.payment",
WALLET_TOPUP: "remitflow.wallet.topup",
WALLET_WITHDRAW: "remitflow.wallet.withdraw",
STABLECOIN_SWAP: "remitflow.stablecoin.swap",
} as const;

// ── Idempotency Key Generation ──────────────────────────────────────────────
export function generateIdempotencyKey(
userId: number,
operation: string,
...args: (string | number)[]
): string {
const raw = `${userId}:${operation}:${args.join(":")}`;
return createHash("sha256").update(raw).digest("hex");
}

// ── Idempotency Cache (in-memory, Redis-backed in production) ────────────────
const IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
const idempotencyCache = new Map<string, { result: unknown; expiresAt: number }>();

export function checkIdempotency(key: string): { cached: boolean; result?: unknown } {
const entry = idempotencyCache.get(key);
if (!entry) return { cached: false };
if (Date.now() > entry.expiresAt) {
idempotencyCache.delete(key);
return { cached: false };
}
return { cached: true, result: entry.result };
}

export function storeIdempotency(key: string, result: unknown): void {
idempotencyCache.set(key, { result, expiresAt: Date.now() + IDEMPOTENCY_TTL_MS });
}

// ── Operation Reference Generator ───────────────────────────────────────────
export function generateOpRef(prefix: string, userId: number): string {
return `${prefix}-${userId}-${Date.now()}-${randomBytes(3).toString("hex")}`;
}

// ── TigerBeetle Double-Entry for Core Ops ───────────────────────────────────
export async function recordCoreDoubleEntry(params: {
userId: number;
amount: number;
featureLabel: string;
transferId: string;
ledger?: number;
}): Promise<boolean> {
try {
const transferBigId = BigInt(Date.now()) * BigInt(1000) + BigInt(Math.floor(Math.random() * 1000));
const debitAccountId = BigInt(params.userId);
const creditAccountId = BigInt(params.userId + 1_000_000);
const amountCents = BigInt(Math.round(params.amount * 100));
await tigerBeetle.createTransfer({
id: transferBigId,
debitAccountId,
creditAccountId,
amount: amountCents,
ledger: params.ledger ?? 1,
code: 1,
});
return true;
} catch (err) {
logger.warn(
{ err: err instanceof Error ? err.message : String(err), feature: params.featureLabel },
"[CoreAtomicity] TigerBeetle degraded, using DB fallback"
);
return false;
}
}

// ── Kafka Event Publishing for Core Ops ─────────────────────────────────────
export async function publishCoreEvent(params: {
topic: string;
userId: number;
amount: number;
currency: string;
featureLabel: string;
operationRef: string;
eventType?: "created" | "completed" | "failed";
metadata?: Record<string, unknown>;
}): Promise<boolean> {
try {
const event: TransactionEvent = {
eventType: params.eventType ?? "completed",
transactionId: params.operationRef,
userId: params.userId,
amount: params.amount,
currency: params.currency,
status: params.eventType === "failed" ? "failed" : "completed",
timestamp: new Date().toISOString(),
};
await publishEvent(
params.topic as any,
params.operationRef,
{ ...event, feature: params.featureLabel, ...(params.metadata ?? {}) }
);
return true;
} catch (err) {
logger.warn(
{ err: err instanceof Error ? err.message : String(err), feature: params.featureLabel },
"[CoreAtomicity] Kafka publish degraded"
);
return false;
}
}

// ── Audit + TigerBeetle + Kafka Wrapper ─────────────────────────────────────
export async function auditCoreOperation(params: {
userId: number;
action: string;
description: string;
amount: number;
currency: string;
featureLabel: string;
operationRef: string;
kafkaTopic: string;
metadata?: Record<string, unknown>;
}): Promise<{
tigerBeetleRecorded: boolean;
kafkaPublished: boolean;
auditLogged: boolean;
}> {
const [tigerBeetleRecorded, kafkaPublished] = await Promise.all([
recordCoreDoubleEntry({
userId: params.userId,
amount: params.amount,
featureLabel: params.featureLabel,
transferId: params.operationRef,
}),
publishCoreEvent({
topic: params.kafkaTopic,
userId: params.userId,
amount: params.amount,
currency: params.currency,
featureLabel: params.featureLabel,
operationRef: params.operationRef,
metadata: params.metadata,
}),
]);

let auditLogged = false;
try {
await createAuditLog({
userId: params.userId,
action: params.action,
description: params.description,
metadata: {
operationRef: params.operationRef,
tigerBeetleRecorded,
kafkaPublished,
feature: params.featureLabel,
...params.metadata,
},
});
auditLogged = true;
} catch (err) {
logger.warn(
{ err: err instanceof Error ? err.message : String(err) },
"[CoreAtomicity] Audit log failed"
);
}

return { tigerBeetleRecorded, kafkaPublished, auditLogged };
}
Loading