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
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@moonlight-protocol/provider-platform",
"version": "0.7.0",
"version": "0.6.31",
"license": "MIT",
"exports": "./src/main.ts",
"tasks": {
Expand Down
26 changes: 9 additions & 17 deletions src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { Logger, LogLevel } from "@/utils/logger/index.ts";
import { loadOptionalEnv } from "@/utils/env/loadEnv.ts";

export const LOG_LEVEL = loadOptionalEnv("LOG_LEVEL") as keyof typeof LogLevel;

let LOG: Logger;

if (LOG_LEVEL !== undefined && LOG_LEVEL in LogLevel) {
LOG = new Logger(LogLevel[LOG_LEVEL]);
} else {
LOG = new Logger(LogLevel.INFO);

LOG.warn(
`LOG_LEVEL is not set or invalid. Defaulting to INFO. Received: ${LOG_LEVEL}`,
);
import { type Logger, newLogger, parseLevel } from "@/utils/logger/index.ts";

/**
* Creates the root logger from `LOG_LEVEL` env var. Called once in main.ts;
* the returned logger is threaded through to every service and route handler
* via dependency injection. There is no module-level singleton.
*/
export function createLogger(): Logger {
return newLogger(parseLevel(Deno.env.get("LOG_LEVEL")));
}

export { LOG };
3 changes: 1 addition & 2 deletions src/config/network.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NetworkConfig, NetworkProviders } from "@colibri/core";
import { StellarNetworkId } from "@moonlight/moonlight-sdk";
import * as E from "@/config/error.ts";
import { logAndThrow } from "@/utils/error/log-and-throw.ts";
import { loadOptionalEnv } from "@/utils/env/loadEnv.ts";

export function selectNetwork(envNetwork: string): {
Expand Down Expand Up @@ -53,6 +52,6 @@ export function selectNetwork(envNetwork: string): {
};
}
default:
logAndThrow(new E.INVALID_NETWORK());
throw new E.INVALID_NETWORK();
}
}
86 changes: 22 additions & 64 deletions src/core/mempool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,120 +6,78 @@ import {
MEMPOOL_SLOT_CAPACITY,
MEMPOOL_TTL_CHECK_INTERVAL_MS,
} from "@/config/env.ts";
import { LOG } from "@/config/logger.ts";
import type { Logger } from "@/utils/logger/index.ts";

/**
* Singleton instance of the Mempool
* Will be initialized during application startup
*/
export let mempool: Mempool;

/**
* Singleton instance of the Executor
* Will be initialized during application startup
*/
export let executor: Executor;

/**
* Singleton instance of the Verifier
* Will be initialized during application startup
*/
export let verifier: Verifier;

/**
* Singleton instance of the MetricsCollector
*/
export let metricsCollector: MetricsCollector;

/**
* Platform version, read once at startup from deno.json.
*/
export let platformVersion = "unknown";

/**
* Interval ID for TTL check
*/
let ttlCheckIntervalId: number | null = null;

/**
* Initializes the mempool singleton instance
* Should be called during application startup
*/
export function initializeMempool(): void {
export function initializeMempool(deps: { log: Logger }): void {
if (mempool) {
throw new Error("Mempool already initialized");
}
mempool = new Mempool(MEMPOOL_SLOT_CAPACITY);
mempool = new Mempool(MEMPOOL_SLOT_CAPACITY, deps);
}

/**
* Gets the mempool instance
* Throws if not initialized
*/
export function getMempool(): Mempool {
if (!mempool) {
throw new Error("Mempool not initialized. Call initializeMempool() first.");
}
return mempool;
}

/**
* Initializes the complete mempool system
* - Initializes Mempool and loads pending bundles from database
* - Starts Executor service
* - Starts Verifier service
* - Starts periodic TTL check
*/
export async function initializeMempoolSystem(): Promise<void> {
LOG.info("Initializing mempool system...");
export async function initializeMempoolSystem(
deps: { log: Logger },
): Promise<void> {
const log = deps.log.scope("mempoolSystem");
log.info("initializeMempoolSystem");
log.event("initializing mempool system");

// Initialize Mempool
initializeMempool();
initializeMempool(deps);
await mempool.initialize();

// Initialize Executor
executor = new Executor();
executor = new Executor(deps);
executor.start();

// Initialize Verifier
verifier = new Verifier();
verifier = new Verifier(deps);
verifier.start();

// Read platform version once at startup (import.meta.dirname resolves
// to the module's directory, so this works regardless of CWD)
// Read platform version once at startup
try {
const denoJsonPath =
new URL("../../../deno.json", import.meta.url).pathname;
const denoJson = JSON.parse(await Deno.readTextFile(denoJsonPath));
platformVersion = denoJson.version ?? "unknown";
} catch {
LOG.warn("Could not read deno.json for platform version");
} catch (err) {
log.error(err, "could not read deno.json for platform version");
}

// Initialize MetricsCollector
metricsCollector = new MetricsCollector(platformVersion);
metricsCollector = new MetricsCollector(platformVersion, deps);
metricsCollector.start();

// Start periodic TTL check
ttlCheckIntervalId = setInterval(async () => {
try {
await mempool.expireBundles();
} catch (error) {
LOG.error("Error during TTL check", {
error: error instanceof Error ? error.message : String(error),
});
log.error(error, "error during TTL check");
}
}, MEMPOOL_TTL_CHECK_INTERVAL_MS) as unknown as number;

LOG.info("Mempool system initialized successfully");
log.event("mempool system initialized successfully");
}

/**
* Shuts down the mempool system gracefully
* Stops all services and clears intervals
*/
export function shutdownMempoolSystem(): void {
LOG.info("Shutting down mempool system...");
export function shutdownMempoolSystem(deps: { log: Logger }): void {
const log = deps.log.scope("mempoolSystem");
log.event("shutting down mempool system");

if (executor) {
executor.stop();
Expand All @@ -138,5 +96,5 @@ export function shutdownMempoolSystem(): void {
ttlCheckIntervalId = null;
}

LOG.info("Mempool system shut down successfully");
log.event("mempool system shut down successfully");
}
135 changes: 73 additions & 62 deletions src/core/service/auth/challenge/create/create-challenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,70 +17,81 @@ import type {
import { assertOrThrow } from "@/utils/error/assert-or-throw.ts";
import { isDefined } from "@/utils/type-guards/is-defined.ts";
import * as E from "@/core/service/auth/challenge/create/error.ts";
import { logAndThrow } from "@/utils/error/log-and-throw.ts";
import { withSpan } from "@/core/tracing.ts";

export const P_CreateChallenge = ProcessEngine.create(
(input: GetChallengeInput): Promise<ChallengeData> => {
return withSpan("P_CreateChallenge", async (span) => {
const { ctx, query } = input;
const clientAccount = query.account;

span.addEvent("validating_client_account", {
"client.account": clientAccount ?? "undefined",
});
assertOrThrow(isDefined(clientAccount), new E.MISSING_CLIENT_ACCOUNT());

try {
span.addEvent("building_challenge_transaction");
const { tx, nonce, minTime, maxTime } = getChallengeTransaction(
clientAccount,
);

const xdr = tx.toXDR();
const txHash = tx.hash().toString("hex");

const dateCreated = new Date(minTime * 1000);
const expiresAt = new Date(maxTime * 1000);

const { clientIp, userAgent, requestId } = extractRequestMetadata(ctx);

span.addEvent("challenge_created", {
"challenge.txHash": txHash,
"challenge.clientAccount": clientAccount,
"challenge.requestId": requestId,
});

const output: ChallengeData = {
ctx,
challengeData: {
txHash: txHash,
clientAccount: clientAccount,
xdr,
nonce,
dateCreated: dateCreated,
requestId,
clientIp,
userAgent,
expiresAt,
},
};

return await output;
} catch (error) {
span.addEvent("challenge_creation_failed", {
"error.message": error instanceof Error
? error.message
: String(error),
import type { Logger } from "@/utils/logger/index.ts";

export const P_CreateChallenge = (deps: { log: Logger }) =>
ProcessEngine.create(
(input: GetChallengeInput): Promise<ChallengeData> => {
return withSpan("P_CreateChallenge", async (span) => {
const log = deps.log.scope("P_CreateChallenge");
log.info("P_CreateChallenge");
const { ctx, query } = input;
const clientAccount = query.account;
log.debug("clientAccount", clientAccount);

span.addEvent("validating_client_account", {
"client.account": clientAccount ?? "undefined",
});
logAndThrow(new E.FAILED_TO_CREATE_CHALLENGE(error));
}
});
},
{
name: "CreateChallengeProcessEngine",
},
);
log.event("validating client account");
assertOrThrow(isDefined(clientAccount), new E.MISSING_CLIENT_ACCOUNT());

try {
span.addEvent("building_challenge_transaction");
log.event("building challenge transaction");
const { tx, nonce, minTime, maxTime } = getChallengeTransaction(
clientAccount,
);

const xdr = tx.toXDR();
const txHash = tx.hash().toString("hex");

const dateCreated = new Date(minTime * 1000);
const expiresAt = new Date(maxTime * 1000);

const { clientIp, userAgent, requestId } = extractRequestMetadata(
ctx,
);

span.addEvent("challenge_created", {
"challenge.txHash": txHash,
"challenge.clientAccount": clientAccount,
"challenge.requestId": requestId,
});
log.debug("txHash", txHash);
log.event("challenge created");

const output: ChallengeData = {
ctx,
challengeData: {
txHash: txHash,
clientAccount: clientAccount,
xdr,
nonce,
dateCreated: dateCreated,
requestId,
clientIp,
userAgent,
expiresAt,
},
};

return await output;
} catch (error) {
span.addEvent("challenge_creation_failed", {
"error.message": error instanceof Error
? error.message
: String(error),
});
log.error(error, "challenge creation failed");
throw new E.FAILED_TO_CREATE_CHALLENGE(error);
}
});
},
{
name: "CreateChallengeProcessEngine",
},
);

const getChallengeTransaction = (
clientAccount: string,
Expand Down
Loading
Loading