Skip to content

Commit

Permalink
feat: update market command to use the new scan method in golem-js 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
SewerynKras committed Jul 2, 2024
1 parent 5ee20c7 commit ddb444d
Showing 1 changed file with 91 additions and 107 deletions.
198 changes: 91 additions & 107 deletions src/market/market.command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command, Option } from "commander";
import { GolemNetwork, MarketOrderSpec, OfferProposal, OfferProposalFilterFactory } from "@golem-sdk/golem-js";
import { GolemNetwork, ScanOptions, ScannedOffer } from "@golem-sdk/golem-js";
import chalk from "chalk";
import { switchMap, filter, scan, takeUntil, timer, last } from "rxjs";
import { filter, takeUntil, timer, toArray } from "rxjs";

export const marketCommand = new Command("market");

Expand All @@ -16,16 +16,17 @@ type MarketScanOptions = {
paymentNetwork: string;
paymentDriver: string;
subnetTag: string;
image: string;
minCpuCores: string;
minCpuThreads: string;
minMemGib: string;
minStorageGib: string;
maxStartPrice: string;
maxCpuPerHourPrice: string;
maxEnvPerHourPrice: string;
engine: string;
capabilities: string[];
engine?: string;
capabilities?: string[];
minMemGib?: string;
maxMemGib?: string;
minStorageGib?: string;
maxStorageGib?: string;
minCpuThreads?: string;
maxCpuThreads?: string;
minCpuCores?: string;
maxCpuCores?: string;
maxHourPrice?: string;
providerId?: string[];
providerName?: string[];
providerWallet?: string[];
Expand All @@ -38,18 +39,22 @@ marketCommand
.description("Runs a scan of the market with your criteria and presents results")
.addOption(new Option("-k, --yagna-appkey <key>", "Yagna app key to use").env("YAGNA_APPKEY").makeOptionMandatory())
.option("--yagna-url <url>", "Yagna API URL", "http://127.0.0.1:7465")
.option("-t, --scan-time <sec>", "Number of seconds to scan the market", "30")
.option("-t, --scan-time <sec>", "Number of seconds to scan the market", "10")
.option("--payment-network <name>", "The payment network to use", "polygon")
.option("--payment-driver <name>", "The payment driver to use", "erc20")
.option("--subnet-tag <name>", "The Golem Network's subnet to use", "public")
.option("--image <name>", "The Golem Registry image to use for the query", "golem/node:20-alpine")
.option("--min-cpu-cores <c>", "The minimal number of CPU cores to look for", "1")
.option("--min-cpu-threads <t>", "The minimal number of CPU threads to look for", "1")
.option("--min-mem-gib <m>", "The minimal memory size to look for", "0.5")
.option("--min-storage-gib <s>", "The minimal storage size to look for", "0.5")
.option("--max-start-price <sp>", "The max start price you're willing to pay (in GLM)", "10")
.option("--max-cpu-per-hour-price <sp>", "The max ENV price you're willing to pay (in GLM)", "10")
.option("--max-env-per-hour-price <sp>", "The max CPU time price you're willing to pay (in GLM/h)", "10")
.option("--min-cpu-cores <c>", "The minimal number of CPU cores to look for")
.option("--max-cpu-cores <c>", "The maximum number of CPU cores to look for")
.option("--min-cpu-threads <t>", "The minimal number of CPU threads to look for")
.option("--max-cpu-threads <t>", "The maximum number of CPU threads to look for")
.option("--min-mem-gib <m>", "The minimal memory size to look for")
.option("--max-mem-gib <m>", "The maximum memory size to look for")
.option("--min-storage-gib <s>", "The minimal storage size to look for")
.option("--max-storage-gib <s>", "The maximum storage size to look for")
.option(
"--max-hour-price <price>",
"The maximum price per hour of work to look for (start price + CPU/sec * 3600 + env/sec * 3600)",
)
.option("--engine <type>", "The runtime that you are interested in", "vm")
.option("--capabilities [capabilities...]", "List of capabilities listed in the offers", [])
.option("--provider-id [id...]", "Filter the results to only include proposals from providers with the given ids")
Expand All @@ -69,17 +74,17 @@ marketCommand
const paymentNetwork = options.paymentNetwork;
const paymentDriver = options.paymentDriver;

const maxStartPrice = parseFloat(options.maxStartPrice);
const maxCpuPerHourPrice = parseFloat(options.maxCpuPerHourPrice);
const maxEnvPerHourPrice = parseFloat(options.maxEnvPerHourPrice);

const subnetTag = options.subnetTag;

const imageTag = options.image;
const minCpuCores = parseInt(options.minCpuCores);
const minCpuThreads = parseInt(options.minCpuThreads);
const minMemGib = parseFloat(options.minMemGib);
const minStorageGib = parseFloat(options.minStorageGib);
const minCpuCores = options.minCpuCores !== undefined ? parseInt(options.minCpuCores) : undefined;
const maxCpuCores = options.maxCpuCores !== undefined ? parseInt(options.maxCpuCores) : undefined;
const minCpuThreads = options.minCpuThreads !== undefined ? parseInt(options.minCpuThreads) : undefined;
const maxCpuThreads = options.maxCpuThreads !== undefined ? parseInt(options.maxCpuThreads) : undefined;
const minMemGib = options.minMemGib !== undefined ? parseFloat(options.minMemGib) : undefined;
const maxMemGib = options.maxMemGib !== undefined ? parseFloat(options.maxMemGib) : undefined;
const minStorageGib = options.minStorageGib !== undefined ? parseFloat(options.minStorageGib) : undefined;
const maxStorageGib = options.maxStorageGib !== undefined ? parseFloat(options.maxStorageGib) : undefined;
const maxHourPrice = options.maxHourPrice !== undefined ? parseFloat(options.maxHourPrice) : undefined;
const engine = options.engine;
const capabilities = options.capabilities;
const providerIdFilter = (id: string) => (options.providerId ? options.providerId.includes(id) : true);
Expand All @@ -95,31 +100,23 @@ marketCommand
console.log("Payment network and driver:", paymentNetwork, paymentDriver);
console.log("Golem engine:", engine);
console.log("Provider capabilities:", capabilities);
console.log(
"Requirements for image '%s', %d cores, %d threads, %dGiB of memory, %dGiB of storage",
imageTag,
minCpuCores,
minCpuThreads,
minMemGib,
minStorageGib,
);
console.log(
"Price limitations: max start price %d GLM, max CPU price %d GLM/h, max ENV price %d, GLM/h",
maxStartPrice.toFixed(4),
maxCpuPerHourPrice.toFixed(4),
maxEnvPerHourPrice.toFixed(4),
);
console.log("Requirements:");
if (minCpuCores) console.log(" - Minimum number of CPU cores:", minCpuCores);
if (maxCpuCores) console.log(" - Maximum number of CPU cores:", maxCpuCores);
if (minCpuThreads) console.log(" - Minimum number of CPU threads:", minCpuThreads);
if (maxCpuThreads) console.log(" - Maximum number of CPU threads:", maxCpuThreads);
if (minMemGib) console.log(" - Minimum memory size:", minMemGib);
if (maxMemGib) console.log(" - Maximum memory size:", maxMemGib);
if (minStorageGib) console.log(" - Minimum storage size:", minStorageGib);
if (maxStorageGib) console.log(" - Maximum storage size:", maxStorageGib);
if (maxHourPrice) console.log(" - Maximum price per hour:", maxHourPrice);
}

const glm = new GolemNetwork({
api: {
key: options.yagnaAppkey,
url: options.yagnaUrl,
},
payment: {
driver: paymentDriver,
network: paymentNetwork,
},
});

try {
Expand All @@ -134,82 +131,69 @@ marketCommand
return;
}

const priceLimiter = OfferProposalFilterFactory.limitPriceFilter({
start: maxStartPrice,
cpuPerSec: maxCpuPerHourPrice / 3600,
envPerSec: maxEnvPerHourPrice / 3600,
});

const scanningFilter = (p: OfferProposal) => {
const withinPriceRange = priceLimiter(p);
const isValidProviderId = providerIdFilter(p.provider.id);
const isValidProviderName = providerNameFilter(p.provider.name);
const isValidProviderWallet = providerWalletFilter(p.provider.walletAddress);

return withinPriceRange && isValidProviderId && isValidProviderName && isValidProviderWallet;
const priceFilter = (offer: ScannedOffer) => {
if (!maxHourPrice) {
return true;
}
const hourlyPrice = offer.pricing.start + offer.pricing.cpuSec * 3600 + offer.pricing.envSec * 3600;
return hourlyPrice <= maxHourPrice;
};

const order: MarketOrderSpec = {
demand: {
subnetTag,
workload: {
imageTag,
minCpuCores,
minCpuThreads,
minMemGib,
minStorageGib,
capabilities,
engine,
},
const scanOptions: ScanOptions = {
workload: {
capabilities,
engine,
maxCpuCores,
maxCpuThreads,
maxMemGib,
maxStorageGib,
minCpuCores,
minCpuThreads,
minMemGib,
minStorageGib,
},
market: {
rentHours: scanTime / 3600,
pricing: {
model: "linear",
maxCpuPerHourPrice,
maxEnvPerHourPrice,
maxStartPrice,
},
offerProposalFilter: scanningFilter,
payment: {
network: paymentNetwork,
driver: paymentDriver,
},
subnetTag,
};

const allocation = await glm.payment.createAllocation({
budget: 0,
expirationSec: scanTime,
});
const demandSpec = await glm.market.buildDemandDetails(order.demand, order.market, allocation);
const scanSpecification = glm.market.buildScanSpecification(scanOptions);

const paymentToken = ["mainnet", "polygon"].includes(paymentNetwork) ? "glm" : "tglm";
const paymentPlatform = `${paymentDriver}-${paymentNetwork}-${paymentToken}`;

glm.market
.publishAndRefreshDemand(demandSpec)
.scan(scanSpecification)
.pipe(
switchMap((demand) => glm.market.collectAllOfferProposals(demand)),
filter(scanningFilter),
scan((acc, proposal) => {
acc.push(proposal);
return acc;
}, [] as OfferProposal[]),
filter((offer) => priceFilter(offer)),
filter((offer) => providerIdFilter(offer.provider.id)),
filter((offer) => providerNameFilter(offer.provider.name)),
filter((offer) =>
providerWalletFilter(offer.properties[`golem.com.payment.platform.${paymentPlatform}.address`] as string),
),
takeUntil(timer(scanTime * 1000)),
last(),
toArray(),
)
.subscribe({
next: (proposals) => {
next: (offersFound) => {
if (!options.silent) {
console.log("Scan finished, here are the results");
console.log("Your market query was matched with %d proposals", proposals.length);
console.log("Your market query was matched with %d proposals", offersFound.length);
}

const displayProposals = proposals.map((p) => {
const memory = p.getDto()["memory"];
const storage = p.getDto()["storage"];
const displayProposals = offersFound.map((offer) => {
const memory = offer.memory;
const storage = offer.storage;
return {
providerId: p.provider.id,
providerName: p.provider.name,
startPrice: unifyPrice(p.pricing.start),
cpuPerHourPrice: unifyPrice(p.pricing.cpuSec * 3600),
envPerHourPrice: unifyPrice(p.pricing.envSec * 3600),
cpuCores: p.getDto()["cpuCores"],
cpuThreads: p.getDto()["cpuThreads"],
providerId: offer.provider.id,
providerName: offer.provider.name,
startPrice: unifyPrice(offer.pricing.start),
cpuPerHourPrice: unifyPrice(offer.pricing.cpuSec * 3600),
envPerHourPrice: unifyPrice(offer.pricing.envSec * 3600),
cpuCores: offer.cpuCores,
cpuThreads: offer.cpuThreads,
memoryGib: memory ? parseFloat(memory.toFixed(1)) : "N/A",
storageGib: storage ? parseFloat(storage.toFixed(1)) : "N/A",
};
Expand Down

0 comments on commit ddb444d

Please sign in to comment.