From 80838572c0732077397ccd3f0080fe24292b826e Mon Sep 17 00:00:00 2001 From: Console Developer <1159966+stalniy@users.noreply.github.com> Date: Mon, 18 May 2026 14:37:50 +0200 Subject: [PATCH 1/2] feat: align http request schema with group spec proto JSON representation --- .../http-schemas/bid-screening.schema.ts | 28 +++++++++++++------ .../__snapshots__/docs.spec.ts.snap | 20 +++---------- .../src/http-schemas/bid-screening.schema.ts | 28 +++++++++++++------ .../lib/groupspec-mapper/groupspec-mapper.ts | 9 +++--- .../stream-status-mapper.ts | 6 ++-- .../bid-screening.service.spec.ts | 2 -- .../bid-screening/bid-screening.service.ts | 4 +-- .../src/types/inventory.types.ts | 2 -- 8 files changed, 53 insertions(+), 46 deletions(-) diff --git a/apps/api/src/bid-screening/http-schemas/bid-screening.schema.ts b/apps/api/src/bid-screening/http-schemas/bid-screening.schema.ts index e550abbe8..773c041d1 100644 --- a/apps/api/src/bid-screening/http-schemas/bid-screening.schema.ts +++ b/apps/api/src/bid-screening/http-schemas/bid-screening.schema.ts @@ -4,7 +4,19 @@ import { z } from "@hono/zod-openapi"; const UIntStringSchema = z.string().regex(/^\d+$/, "Must be an unsigned integer string"); const ResourceValueSchema = z.object({ - val: UIntStringSchema.openapi({ description: "String-encoded integer value", example: "1000" }) + val: z + .string() + .max(80) + .transform(str => { + if (/^\d+$/.test(str)) return BigInt(str); + const parsed = Buffer.from(str, "base64").toString("utf-8"); + if (/^\d+$/.test(parsed)) return BigInt(parsed); + return NaN; + }) + .refine( + val => !Number.isFinite(val) && typeof val === "bigint" && val >= 0n, + "Must be a non-negative integer or its protobuf base64-encoded representation" + ) }); // Mirrors AttributeNameRegexpStringWildcard in akash-network/chain-sdk @@ -24,12 +36,12 @@ const StorageResourceSchema = z .object({ name: z.string().openapi({ description: "Storage volume name", example: "default" }), quantity: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }) .superRefine((vol, ctx) => { - const isPersistent = vol.attributes.some(a => a.key === "persistent" && a.value === "true"); + const isPersistent = vol.attributes?.some(a => a.key === "persistent" && a.value === "true"); if (!isPersistent) return; - const storageClass = vol.attributes.find(a => a.key === "class")?.value; + const storageClass = vol.attributes?.find(a => a.key === "class")?.value; if (!storageClass || storageClass === "ram") { ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -43,18 +55,18 @@ const ResourceSchema = z.object({ id: z.number().int().openapi({ description: "Resource unit ID", example: 1 }), cpu: z.object({ units: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), memory: z.object({ quantity: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), gpu: z.object({ units: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), storage: z.array(StorageResourceSchema), - endpoints: z.array(z.unknown()).optional() + endpoints: z.array(z.unknown()).optional().optional() }); const PriceSchema = z.object({ diff --git a/apps/api/test/functional/__snapshots__/docs.spec.ts.snap b/apps/api/test/functional/__snapshots__/docs.spec.ts.snap index 22356f4ba..bf4bb043d 100644 --- a/apps/api/test/functional/__snapshots__/docs.spec.ts.snap +++ b/apps/api/test/functional/__snapshots__/docs.spec.ts.snap @@ -2859,9 +2859,7 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` "units": { "properties": { "val": { - "description": "String-encoded integer value", - "example": "1000", - "pattern": "^\\d+$", + "maxLength": 80, "type": "string", }, }, @@ -2873,7 +2871,6 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` }, "required": [ "units", - "attributes", ], "type": "object", }, @@ -2913,9 +2910,7 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` "units": { "properties": { "val": { - "description": "String-encoded integer value", - "example": "1000", - "pattern": "^\\d+$", + "maxLength": 80, "type": "string", }, }, @@ -2927,7 +2922,6 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` }, "required": [ "units", - "attributes", ], "type": "object", }, @@ -2966,9 +2960,7 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` "quantity": { "properties": { "val": { - "description": "String-encoded integer value", - "example": "1000", - "pattern": "^\\d+$", + "maxLength": 80, "type": "string", }, }, @@ -2980,7 +2972,6 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` }, "required": [ "quantity", - "attributes", ], "type": "object", }, @@ -3020,9 +3011,7 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` "quantity": { "properties": { "val": { - "description": "String-encoded integer value", - "example": "1000", - "pattern": "^\\d+$", + "maxLength": 80, "type": "string", }, }, @@ -3035,7 +3024,6 @@ exports[`API Docs > GET /v1/doc > returns docs with all routes expected 1`] = ` "required": [ "name", "quantity", - "attributes", ], "type": "object", }, diff --git a/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts b/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts index 05ea09749..16d73c448 100644 --- a/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts +++ b/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts @@ -3,7 +3,19 @@ import { z } from "@hono/zod-openapi"; const UIntStringSchema = z.string().regex(/^\d+$/, "Must be an unsigned integer string"); const ResourceValueSchema = z.object({ - val: UIntStringSchema.openapi({ description: "String-encoded integer value", example: "1000" }) + val: z + .string() + .max(80) + .transform(str => { + if (/^\d+$/.test(str)) return BigInt(str); + const parsed = Buffer.from(str, "base64").toString("utf-8"); + if (/^\d+$/.test(parsed)) return BigInt(parsed); + return NaN; + }) + .refine( + val => !Number.isFinite(val) && typeof val === "bigint" && val >= 0n, + "Must be a non-negative integer or its protobuf base64-encoded representation" + ) }); // Mirrors AttributeNameRegexpStringWildcard in akash-network/chain-sdk @@ -23,12 +35,12 @@ const StorageResourceSchema = z .object({ name: z.string().openapi({ description: "Storage volume name", example: "default" }), quantity: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }) .superRefine((vol, ctx) => { - const isPersistent = vol.attributes.some(a => a.key === "persistent" && a.value === "true"); + const isPersistent = vol.attributes?.some(a => a.key === "persistent" && a.value === "true"); if (!isPersistent) return; - const storageClass = vol.attributes.find(a => a.key === "class")?.value; + const storageClass = vol.attributes?.find(a => a.key === "class")?.value; if (!storageClass || storageClass === "ram") { ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -42,18 +54,18 @@ const ResourceSchema = z.object({ id: z.number().int().openapi({ description: "Resource unit ID", example: 1 }), cpu: z.object({ units: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), memory: z.object({ quantity: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), gpu: z.object({ units: ResourceValueSchema, - attributes: z.array(AttributeSchema) + attributes: z.array(AttributeSchema).optional() }), storage: z.array(StorageResourceSchema), - endpoints: z.array(z.unknown()).optional() + endpoints: z.array(z.unknown()).optional().optional() }); const PriceSchema = z.object({ diff --git a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts index 6f0665682..1c5416883 100644 --- a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts +++ b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts @@ -10,6 +10,7 @@ function parseResourceValue(val: string): bigint { return parsed; } +const EMPTY_ARRAY = Object.freeze([]); export function mapGroupSpecToResourceUnits(request: GroupSpecJSON): RequestedResourceUnit[] { return request.resources.map(unit => { const resource = unit.resource; @@ -17,7 +18,7 @@ export function mapGroupSpecToResourceUnits(request: GroupSpecJSON): RequestedRe const storage: RequestedStorage[] = resource.storage.map(vol => ({ name: vol.name, quantity: parseResourceValue(vol.quantity.val), - attributes: vol.attributes + attributes: vol.attributes ?? EMPTY_ARRAY })); return { @@ -25,15 +26,15 @@ export function mapGroupSpecToResourceUnits(request: GroupSpecJSON): RequestedRe resources: { cpu: { units: parseResourceValue(resource.cpu.units.val), - attributes: resource.cpu.attributes + attributes: resource.cpu.attributes ?? EMPTY_ARRAY }, gpu: { units: parseResourceValue(resource.gpu.units.val), - attributes: resource.gpu.attributes + attributes: resource.gpu.attributes ?? EMPTY_ARRAY }, memory: { quantity: parseResourceValue(resource.memory.quantity.val), - attributes: resource.memory.attributes + attributes: resource.memory.attributes ?? EMPTY_ARRAY }, storage }, diff --git a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts index 061e0ec27..17e7a24af 100644 --- a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts +++ b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts @@ -66,8 +66,8 @@ function parseMantissa(s: string): { negative: boolean; digits: bigint; fracLen: return { negative, digits: BigInt((intPart || "0") + fracPart || "0"), fracLen: fracPart.length }; } -function pairFromSdk(pair: SdkResourcePair | undefined): ResourcePair { - return new ResourcePair(parseQuantity(pair?.allocatable), parseQuantity(pair?.allocated)); +function pairFromSdk(pair: SdkResourcePair | undefined, multiplier = 1n): ResourcePair { + return new ResourcePair(parseQuantity(pair?.allocatable) * multiplier, parseQuantity(pair?.allocated) * multiplier); } function mapGpuInfo(info: GPUInfo): GpuInfo { @@ -88,7 +88,7 @@ function mapNode(node: SdkNode): NodeState { const resources = node.resources; return { name: node.name, - cpu: pairFromSdk(resources?.cpu?.quantity), + cpu: pairFromSdk(resources?.cpu?.quantity, 1000n), memory: pairFromSdk(resources?.memory?.quantity), ephemeralStorage: pairFromSdk(resources?.ephemeralStorage), gpu: { diff --git a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts index f8c7fc15f..7f655e733 100644 --- a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts +++ b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts @@ -21,8 +21,6 @@ describe(BidScreeningService.name, () => { { owner: "akash1abc", hostUri: "https://provider.example.com:8443", - region: "us-east", - uptime7d: 0.998, isAudited: false } ]); diff --git a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.ts b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.ts index 4248c5a7e..f49b4a9d0 100644 --- a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.ts +++ b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.ts @@ -26,7 +26,7 @@ export class BidScreeningService { this.#logger.info({ event: "BID_SCREENING_START", resourceGroupCount: resourceUnits.length }); const candidates = await this.#repository.findCandidates(resourceUnits, request.requirements); - this.#logger.debug({ event: "BID_SCREENING_CANDIDATES_FETCHED", count: candidates.length }); + this.#logger.info({ event: "BID_SCREENING_CANDIDATES_FETCHED", count: candidates.length }); const results: BidScreeningResult[] = []; @@ -52,8 +52,6 @@ export class BidScreeningService { return { owner: candidate.owner, hostUri: candidate.hostUri, - region: candidate.ipRegion ?? null, - uptime7d: candidate.uptime7d ?? null, isAudited: candidate.isAudited }; } diff --git a/apps/provider-inventory/src/types/inventory.types.ts b/apps/provider-inventory/src/types/inventory.types.ts index 671680388..bb88ff25b 100644 --- a/apps/provider-inventory/src/types/inventory.types.ts +++ b/apps/provider-inventory/src/types/inventory.types.ts @@ -60,8 +60,6 @@ export interface MatchResult { export interface BidScreeningResult { owner: string; hostUri: string; - region: string | null; - uptime7d: number | null; isAudited: boolean; } From 20c2e2c91bb9af9750ffcafbb2bfad5ff98e2402 Mon Sep 17 00:00:00 2001 From: Console Developer <1159966+stalniy@users.noreply.github.com> Date: Mon, 18 May 2026 15:57:06 +0200 Subject: [PATCH 2/2] chore: fixes edge cases --- apps/provider-inventory/package.json | 2 +- .../src/http-schemas/bid-screening.schema.ts | 4 +-- .../groupspec-mapper/groupspec-mapper.spec.ts | 16 ++++------ .../lib/groupspec-mapper/groupspec-mapper.ts | 29 +++++++----------- .../stream-status-mapper.spec.ts | 30 +++++++++++++++++++ .../stream-status-mapper.ts | 20 ++++++------- .../bid-screening.service.spec.ts | 8 ++--- .../src/types/inventory.types.ts | 8 +---- 8 files changed, 64 insertions(+), 53 deletions(-) diff --git a/apps/provider-inventory/package.json b/apps/provider-inventory/package.json index 3c7488201..bf4ebe72f 100644 --- a/apps/provider-inventory/package.json +++ b/apps/provider-inventory/package.json @@ -6,7 +6,7 @@ "author": "Akash Network", "main": "dist/server.js", "scripts": { - "build": "NODE_ENV=production tsup", + "build": "NODE_ENV=production tsup && tsc --noEmit", "build:container": "dc build provider-inventory", "dev": "npm run migration:exec && npm run start", "format": "prettier --write ./*.{ts,js,json} **/*.{ts,js,json}", diff --git a/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts b/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts index 16d73c448..504efbdf9 100644 --- a/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts +++ b/apps/provider-inventory/src/http-schemas/bid-screening.schema.ts @@ -13,7 +13,7 @@ const ResourceValueSchema = z.object({ return NaN; }) .refine( - val => !Number.isFinite(val) && typeof val === "bigint" && val >= 0n, + (val): val is bigint => !Number.isFinite(val) && typeof val === "bigint" && val >= 0n, "Must be a non-negative integer or its protobuf base64-encoded representation" ) }); @@ -99,8 +99,6 @@ export type BidScreeningRequest = z.infer; const ProviderResultSchema = z.object({ owner: z.string().openapi({ description: "Provider address", example: "akash1q7spv2cw06yszgfp4f9ed59lkka6ytn8g4tkjf" }), hostUri: z.string().openapi({ description: "Provider HTTPS endpoint", example: "https://provider.europlots.com:8443" }), - region: z.string().nullable().openapi({ description: "Geo-resolved region from IP" }), - uptime7d: z.number().nullable().openapi({ description: "7-day uptime as decimal (0.0-1.0)", example: 0.998 }), isAudited: z.boolean().openapi({ description: "True if signed by a known auditor" }) }); diff --git a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.spec.ts b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.spec.ts index 77769cc35..cb678d3f6 100644 --- a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.spec.ts +++ b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.spec.ts @@ -49,27 +49,23 @@ describe(mapGroupSpecToResourceUnits.name, () => { expect(units[1].resources.gpu.units).toBe(1n); }); - it("throws for invalid resource value string", () => { - expect(() => setup({ invalidVal: true })).toThrow(); - }); - it("handles empty storage array", () => { const { units } = setup({ emptyStorage: true }); expect(units[0].resources.storage).toEqual([]); }); - function setup(input: { count?: number; multiGroup?: boolean; invalidVal?: boolean; emptyStorage?: boolean }) { + function setup(input: { count?: number; multiGroup?: boolean; emptyStorage?: boolean }) { const baseResource = { id: 1, - cpu: { units: { val: input.invalidVal ? "notanumber" : "1000" }, attributes: [] }, - memory: { quantity: { val: "1073741824" }, attributes: [] }, - gpu: { units: { val: "0" }, attributes: [] }, + cpu: { units: { val: 1000n }, attributes: [] }, + memory: { quantity: { val: 1073741824n }, attributes: [] }, + gpu: { units: { val: 0n }, attributes: [] }, storage: input.emptyStorage ? [] : [ { name: "default", - quantity: { val: "5368709120" }, + quantity: { val: 5368709120n }, attributes: [ { key: "persistent", value: "false" }, { key: "class", value: "ephemeral" } @@ -86,7 +82,7 @@ describe(mapGroupSpecToResourceUnits.name, () => { resource: { ...baseResource, id: 2, - gpu: { units: { val: "1" }, attributes: [{ key: "vendor/nvidia/model/a100", value: "true" }] } + gpu: { units: { val: 1n }, attributes: [{ key: "vendor/nvidia/model/a100", value: "true" }] } }, count: 1, price: { denom: "uakt", amount: "5000" } diff --git a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts index 1c5416883..7326d1dd3 100644 --- a/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts +++ b/apps/provider-inventory/src/lib/groupspec-mapper/groupspec-mapper.ts @@ -2,41 +2,34 @@ import type { GroupSpec } from "@akashnetwork/chain-sdk/private-types/akash.v1be import type { RequestedResourceUnit, RequestedStorage, ToJSON } from "../../types/inventory.types"; -function parseResourceValue(val: string): bigint { - const parsed = BigInt(val); - if (parsed < 0n) { - throw new Error(`Invalid resource value: ${val} must be a non-negative integer`); - } - return parsed; -} - const EMPTY_ARRAY = Object.freeze([]); export function mapGroupSpecToResourceUnits(request: GroupSpecJSON): RequestedResourceUnit[] { return request.resources.map(unit => { const resource = unit.resource; - const storage: RequestedStorage[] = resource.storage.map(vol => ({ - name: vol.name, - quantity: parseResourceValue(vol.quantity.val), - attributes: vol.attributes ?? EMPTY_ARRAY - })); - return { id: resource.id, resources: { cpu: { - units: parseResourceValue(resource.cpu.units.val), + units: resource.cpu.units.val, attributes: resource.cpu.attributes ?? EMPTY_ARRAY }, gpu: { - units: parseResourceValue(resource.gpu.units.val), + units: resource.gpu.units.val, attributes: resource.gpu.attributes ?? EMPTY_ARRAY }, memory: { - quantity: parseResourceValue(resource.memory.quantity.val), + quantity: resource.memory.quantity.val, attributes: resource.memory.attributes ?? EMPTY_ARRAY }, - storage + storage: resource.storage.map( + (s): RequestedStorage => ({ + name: s.name, + quantity: s.quantity.val, + attributes: s.attributes ?? EMPTY_ARRAY + }) + ), + endpoints: resource.endpoints ?? EMPTY_ARRAY }, count: unit.count }; diff --git a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.spec.ts b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.spec.ts index de9d02a66..40239fd06 100644 --- a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.spec.ts +++ b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.spec.ts @@ -125,4 +125,34 @@ describe(parseQuantity.name, () => { expect(parseQuantity({ string: unsafe.toString() })).toBe(unsafe); }); }); + + describe("with multiplier (pre-truncation scaling)", () => { + it("scales 500m to 500 with multiplier 1000n", () => { + expect(parseQuantity({ string: "500m" }, 1000n)).toBe(500n); + }); + + it("scales 1500m to 1500 with multiplier 1000n", () => { + expect(parseQuantity({ string: "1500m" }, 1000n)).toBe(1500n); + }); + + it("scales integer cores to millicores with multiplier 1000n", () => { + expect(parseQuantity({ string: "2" }, 1000n)).toBe(2000n); + }); + + it("scales fractional cores to millicores with multiplier 1000n", () => { + expect(parseQuantity({ string: "0.5" }, 1000n)).toBe(500n); + }); + + it("scales binary fractional values before truncation", () => { + expect(parseQuantity({ string: "1.5Ki" }, 1000n)).toBe(1536000n); + }); + + it("scales negative millicore values", () => { + expect(parseQuantity({ string: "-500m" }, 1000n)).toBe(-500n); + }); + + it("scales decimal-exponent millicore equivalents", () => { + expect(parseQuantity({ string: "5e-3" }, 1000n)).toBe(5n); + }); + }); }); diff --git a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts index 17e7a24af..d9c471944 100644 --- a/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts +++ b/apps/provider-inventory/src/lib/stream-status-mapper/stream-status-mapper.ts @@ -17,33 +17,33 @@ interface Quantity { const BINARY_POW2: Record = { Ki: 10, Mi: 20, Gi: 30, Ti: 40, Pi: 50, Ei: 60 }; const DECIMAL_POW10: Record = { n: -9, u: -6, m: -3, "": 0, k: 3, M: 6, G: 9, T: 12, P: 15, E: 18 }; -export function parseQuantity(q: Quantity | undefined): bigint { +export function parseQuantity(q: Quantity | undefined, multiplier: bigint = 1n): bigint { const raw = q?.string?.trim(); if (!raw) return 0n; const binary = /^(-?\d+(?:\.\d+)?)([KMGTPE]i)$/.exec(raw); - if (binary) return scaleByPow2(binary[1], BINARY_POW2[binary[2]]); + if (binary) return scaleByPow2(binary[1], BINARY_POW2[binary[2]], multiplier); const exponent = /^(-?\d+(?:\.\d+)?)[eE](-?\d+)$/.exec(raw); - if (exponent) return scaleByPow10(exponent[1], Number(exponent[2])); + if (exponent) return scaleByPow10(exponent[1], Number(exponent[2]), multiplier); const decimal = /^(-?\d+(?:\.\d+)?)([numkMGTPE]?)$/.exec(raw); - if (decimal) return scaleByPow10(decimal[1], DECIMAL_POW10[decimal[2]]); + if (decimal) return scaleByPow10(decimal[1], DECIMAL_POW10[decimal[2]], multiplier); return 0n; } -function scaleByPow2(mantissaStr: string, pow2: number): bigint { +function scaleByPow2(mantissaStr: string, pow2: number, multiplier: bigint): bigint { const { negative, digits, fracLen } = parseMantissa(mantissaStr); - const scaled = digits * (1n << BigInt(pow2)); + const scaled = digits * 2n ** BigInt(pow2) * multiplier; const truncated = fracLen === 0 ? scaled : scaled / 10n ** BigInt(fracLen); return negative ? -truncated : truncated; } -function scaleByPow10(mantissaStr: string, pow10: number): bigint { +function scaleByPow10(mantissaStr: string, pow10: number, multiplier: bigint): bigint { const { negative, digits, fracLen } = parseMantissa(mantissaStr); const netExp = pow10 - fracLen; - const scaled = netExp >= 0 ? digits * 10n ** BigInt(netExp) : digits / 10n ** BigInt(-netExp); + const scaled = netExp >= 0 ? digits * 10n ** BigInt(netExp) * multiplier : (digits * multiplier) / 10n ** BigInt(-netExp); return negative ? -scaled : scaled; } @@ -66,8 +66,8 @@ function parseMantissa(s: string): { negative: boolean; digits: bigint; fracLen: return { negative, digits: BigInt((intPart || "0") + fracPart || "0"), fracLen: fracPart.length }; } -function pairFromSdk(pair: SdkResourcePair | undefined, multiplier = 1n): ResourcePair { - return new ResourcePair(parseQuantity(pair?.allocatable) * multiplier, parseQuantity(pair?.allocated) * multiplier); +function pairFromSdk(pair: SdkResourcePair | undefined, multiplier: bigint = 1n): ResourcePair { + return new ResourcePair(parseQuantity(pair?.allocatable, multiplier), parseQuantity(pair?.allocated, multiplier)); } function mapGpuInfo(info: GPUInfo): GpuInfo { diff --git a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts index 7f655e733..bac565846 100644 --- a/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts +++ b/apps/provider-inventory/src/services/bid-screening/bid-screening.service.spec.ts @@ -140,10 +140,10 @@ function makeRequest( { resource: { id: 1, - cpu: { units: { val: "1000" }, attributes: [] }, - memory: { quantity: { val: "1073741824" }, attributes: [] }, - gpu: { units: { val: "0" }, attributes: [] }, - storage: [{ name: "default", quantity: { val: "5368709120" }, attributes: [{ key: "persistent", value: "false" }] }], + cpu: { units: { val: 1000n }, attributes: [] }, + memory: { quantity: { val: 1073741824n }, attributes: [] }, + gpu: { units: { val: 0n }, attributes: [] }, + storage: [{ name: "default", quantity: { val: 5368709120n }, attributes: [{ key: "persistent", value: "false" }] }], endpoints: [] }, count: 1, diff --git a/apps/provider-inventory/src/types/inventory.types.ts b/apps/provider-inventory/src/types/inventory.types.ts index bb88ff25b..7ac9b05db 100644 --- a/apps/provider-inventory/src/types/inventory.types.ts +++ b/apps/provider-inventory/src/types/inventory.types.ts @@ -63,10 +63,4 @@ export interface BidScreeningResult { isAudited: boolean; } -export type ToJSON = T extends Uint8Array - ? string - : T extends object - ? { -readonly [K in keyof T]: ToJSON> } - : T extends bigint - ? string - : T; +export type ToJSON = T extends Uint8Array ? bigint : T extends object ? { -readonly [K in keyof T]: ToJSON> } : T;