diff --git a/src/cmd.ts b/src/cmd.ts index a5c64e8..4d7bab4 100644 --- a/src/cmd.ts +++ b/src/cmd.ts @@ -285,6 +285,21 @@ function matchingCommand(yargs: yargs.Argv<{}>): { type: "string", }, }) + .command("getCarsIdsWithState", "Get cars Ids and matching state", { + replicaIndex: { + description: "Replica index", + alias: "r", + demandOption: true, + type: "number", + }, + + path: { + description: "cars Ids file path", + alias: "p", + demandOption: true, + type: "string", + }, + }) .command("bidding", "Bidding matching", { matchingId: { description: "Matching Id", @@ -299,6 +314,38 @@ function matchingCommand(yargs: yargs.Argv<{}>): { type: "string", }, }) + .command("closeMatching", "Close matching", { + matchingId: { + description: "Matching Id", + alias: "m", + demandOption: true, + type: "number", + }, + }) + .command("cancelMatching", "Cancel matching", { + matchingId: { + description: "Matching Id", + alias: "m", + demandOption: true, + type: "number", + }, + }) + .command("pauseMatching", "Pause matching", { + matchingId: { + description: "Matching Id", + alias: "m", + demandOption: true, + type: "number", + }, + }) + .command("resumeMatching", "Resume matching", { + matchingId: { + description: "Matching Id", + alias: "m", + demandOption: true, + type: "number", + }, + }) .command("getMatchingState", "Get matching state", { matchingId: { description: @@ -552,6 +599,8 @@ async function dataset( datasetId: Number(argv.datasetId), }) break + default: + console.log("Unknown command.") } } @@ -593,6 +642,13 @@ async function matching( path: String(argv.path), }) break + case "getCarsIdsWithState": + await new Matching().getCarsIdsWithState({ + context, + replicaIndex: Number(argv.replicaIndex), + path: String(argv.path), + }) + break case "bidding": await new Matching().bidding({ context, @@ -600,12 +656,38 @@ async function matching( amount: BigInt(String(argv.amount)), }) break + case "closeMatching": + await new Matching().closeMatching({ + context, + matchingId: Number(argv.matchingId), + }) + break + case "cancelMatching": + await new Matching().cancelMatching({ + context, + matchingId: Number(argv.matchingId), + }) + break + case "pauseMatching": + await new Matching().pauseMatching({ + context, + matchingId: Number(argv.matchingId), + }) + break + case "resumeMatching": + await new Matching().resumeMatching({ + context, + matchingId: Number(argv.matchingId), + }) + break case "getMatchingState": await new Matching().getMatchingState({ context, matchingId: Number(argv.matchingId), }) break + default: + console.log("Unknown command.") } } @@ -665,6 +747,8 @@ async function storage( matchingId: Number(argv.matchingId), }) break + default: + console.log("Unknown command.") } } @@ -704,5 +788,7 @@ async function finance( }) ) break + default: + console.log("Unknown command.") } } diff --git a/src/matching/repo/index.ts b/src/matching/repo/index.ts index e5991f6..a5e2022 100644 --- a/src/matching/repo/index.ts +++ b/src/matching/repo/index.ts @@ -19,7 +19,7 @@ ********************************************************************************/ import fs from "fs" -import { MatchingState } from "@dataswapjs/dataswapjs" +import { MatchingState, Car } from "@dataswapjs/dataswapjs" import { handleEvmError, logMethodCall } from "../../shared/utils/utils" import { MatchingMetadataSubmitInfo, @@ -132,6 +132,86 @@ export class Matching { return true } + /** + * Closes a matching with the specified matching ID. + * @param options - An object containing the context and matching ID. + * @returns A Promise that resolves to true if the matching is closed successfully. + */ + @logMethodCall(["context"]) + async closeMatching(options: { + context: Context + matchingId: number + }): Promise { + options.context.evm.matchingBids + .getWallet() + .add(process.env.privateKey!) + await handleEvmError( + options.context.evm.matchingBids.closeMatching(options.matchingId) + ) + return true + } + + /** + * Cancels a matching with the specified matching ID. + * @param options - An object containing the context and matching ID. + * @returns A Promise that resolves to true if the matching is canceled successfully. + */ + @logMethodCall(["context"]) + async cancelMatching(options: { + context: Context + matchingId: number + }): Promise { + options.context.evm.matchingBids + .getWallet() + .add(process.env.datasetPreparerPrivateKey!) + await handleEvmError( + options.context.evm.matchingBids.cancelMatching(options.matchingId) + ) + return true + } + + /** + * Pauses a matching with the specified matching ID. + * @param options - An object containing the context and matching ID. + * @returns A Promise that resolves to true if the matching is paused successfully. + */ + @logMethodCall(["context"]) + async pauseMatching(options: { + context: Context + matchingId: number + }): Promise { + options.context.evm.matchingMetadata + .getWallet() + .add(process.env.datasetPreparerPrivateKey!) + await handleEvmError( + options.context.evm.matchingMetadata.pauseMatching( + options.matchingId + ) + ) + return true + } + + /** + * Resumes a matching with the specified matching ID. + * @param options - An object containing the context and matching ID. + * @returns A Promise that resolves to true if the matching is resumed successfully. + */ + @logMethodCall(["context"]) + async resumeMatching(options: { + context: Context + matchingId: number + }): Promise { + options.context.evm.matchingMetadata + .getWallet() + .add(process.env.datasetPreparerPrivateKey!) + await handleEvmError( + options.context.evm.matchingMetadata.resumeMatching( + options.matchingId + ) + ) + return true + } + /** * Retrieves the IDs of cars based on the provided JSON file path. * @@ -143,13 +223,50 @@ export class Matching { context: Context path: string }): Promise { - const ids = JSON.parse(fs.readFileSync(options.path).toString()) + const cars = JSON.parse(fs.readFileSync(options.path).toString()) return await handleEvmError( - options.context.evm.carstore.getCarsIds(ids.carsHash) + options.context.evm.carstore.getCarsIds(cars.carsHash) ) } + /** + * Retrieves the IDs of cars based on the provided JSON file path. + * + * @param options An object containing the context and the path to the JSON file. + * @returns A Promise resolving to an array of BigInt values representing the car IDs. + */ + @logMethodCall(["context"]) + async getCarsIdsWithState(options: { + context: Context + replicaIndex: number + path: string + }): Promise<{ matchinged: string[]; unmatching: string[] }> { + const cars = JSON.parse(fs.readFileSync(options.path).toString()) + + const ids: bigint[] = await handleEvmError( + options.context.evm.carstore.getCarsIds(cars.carsHash) + ) + const unmatching: bigint[] = [] + const matchinged: bigint[] = [] + + for (const id of ids) { + const car = (await handleEvmError( + options.context.evm.carstore.getCar(id) + )) as Car + + if ( + car.matchingIds && + car.matchingIds[options.replicaIndex] != (undefined || 0) + ) { + matchinged.push(id) + } else { + unmatching.push(id) + } + } + return await this.formatIdsWithState({ matchinged, unmatching }) + } + /** * Places a bid on a matching. * @param options - The options object containing the context, matching ID, and bid amount. @@ -189,4 +306,46 @@ export class Matching { ) ) } + + /** + * Formats the given IDs with their state (matchinged/unmatching). + * @param options The options object containing unmatching and matchinged IDs. + * @returns A Promise resolving to an object with formatted unmatching and matchinged IDs. + */ + private async formatIdsWithState(options: { + matchinged: bigint[] + unmatching: bigint[] + }): Promise<{ matchinged: string[]; unmatching: string[] }> { + const unmatchingIds: string[] = this.formatRange(options.unmatching) + const matchingedIds: string[] = this.formatRange(options.matchinged) + + return { matchinged: matchingedIds, unmatching: unmatchingIds } + } + + /** + * Formats the given array of IDs into ranges (e.g., [1, 2, 3, 5] => ["1-3", "5"]). + * @param ids The array of IDs to be formatted. + * @returns An array of strings representing formatted ranges of IDs. + */ + private formatRange(ids: bigint[]): string[] { + // Sort the IDs and remove duplicates + const sortedIds = Array.from(new Set(ids)).sort( + (a, b) => Number(a) - Number(b) + ) + const ranges: string[] = [] + + let start = sortedIds[0] + let end = sortedIds[0] + for (let i = 1; i < sortedIds.length; i++) { + if (sortedIds[i] - end === BigInt(1)) { + end = sortedIds[i] + } else { + ranges.push(start === end ? `${start}` : `${start}-${end}`) + start = end = sortedIds[i] + } + } + ranges.push(start === end ? `${start}` : `${start}-${end}`) + + return ranges + } }