diff --git a/.github/packages/npm-package/ephemeralValidator.ts b/.github/packages/npm-package/ephemeralValidator.ts index 887cb707d..d3587379e 100755 --- a/.github/packages/npm-package/ephemeralValidator.ts +++ b/.github/packages/npm-package/ephemeralValidator.ts @@ -3,9 +3,9 @@ import fs from "fs"; import { spawn, spawnSync } from "child_process"; import path from "path"; import { arch, platform } from "os"; -import { version } from "./package.json"; +import { VERSIONS } from "./versions"; -const PACKAGE_VERSION = `ephemeral-validator ${version}`; +const PACKAGE_VERSION = `ephemeral-validator ${VERSIONS.EPHEMERAL_VALIDATOR}`; function getBinaryVersion(location: string): [string | null, string | null] { const result = spawnSync(location, ["--version"]); diff --git a/.github/packages/npm-package/package.json b/.github/packages/npm-package/package.json index bc8b8197a..8f6b5ef98 100644 --- a/.github/packages/npm-package/package.json +++ b/.github/packages/npm-package/package.json @@ -1,6 +1,6 @@ { "name": "@magicblock-labs/ephemeral-validator", - "version": "0.2.3", + "version": "0.2.5", "description": "MagicBlock Ephemeral Validator", "homepage": "https://github.com/magicblock-labs/ephemeral-validator#readme", "bugs": { @@ -13,7 +13,9 @@ "license": "Business Source License 1.1", "bin": { "ephemeral-validator": "ephemeralValidator.js", - "mb-test-validator": "mbTestValidator.js" + "mb-test-validator": "mbTestValidator.js", + "rpc-router": "rpcRouter.js", + "vrf-oracle": "vrfOracle.js" }, "scripts": { "typecheck": "tsc --noEmit", @@ -32,7 +34,15 @@ "@magicblock-labs/ephemeral-validator-darwin-x64": "0.2.3", "@magicblock-labs/ephemeral-validator-linux-arm64": "0.2.3", "@magicblock-labs/ephemeral-validator-linux-x64": "0.2.3", - "@magicblock-labs/ephemeral-validator-windows-x64": "0.2.3" + "@magicblock-labs/ephemeral-validator-windows-x64": "0.2.3", + "@magicblock-labs/vrf-oracle-linux-x64": "0.2.0", + "@magicblock-labs/vrf-oracle-linux-arm64": "0.2.0", + "@magicblock-labs/vrf-oracle-darwin-x64": "0.2.0", + "@magicblock-labs/vrf-oracle-darwin-arm64": "0.2.0", + "@magicblock-labs/rpc-router-linux-x64": "0.0.1", + "@magicblock-labs/rpc-router-linux-arm64": "0.0.1", + "@magicblock-labs/rpc-router-darwin-x64": "0.0.1", + "@magicblock-labs/rpc-router-darwin-arm64": "0.0.1" }, "publishConfig": { "access": "public" diff --git a/.github/packages/npm-package/rpcRouter.ts b/.github/packages/npm-package/rpcRouter.ts new file mode 100755 index 000000000..33a7b5671 --- /dev/null +++ b/.github/packages/npm-package/rpcRouter.ts @@ -0,0 +1,113 @@ +#!/usr/bin/env node +import fs from "fs"; +import { spawn, spawnSync } from "child_process"; +import path from "path"; +import { arch, platform } from "os"; +import { VERSIONS } from "./versions"; + +const PACKAGE_VERSION = `rpc-router ${VERSIONS.RPC_ROUTER}`; + +function getBinaryVersion(location: string): [string | null, string | null] { + const result = spawnSync(location, ["--version"]); + const error: string | null = + (result.error && result.error.toString()) || + (result.stderr.length > 0 && result.stderr.toString().trim()) || + null; + return [error, result.stdout && result.stdout.toString().trim()]; +} + +function getExePath(): string { + let os: string = platform(); + let extension = ""; + if (["win32", "cygwin"].includes(os)) { + os = "windows"; + extension = ".exe"; + } + const binaryName = `@magicblock-labs/rpc-router-${os}-${arch()}/bin/rpc-router${extension}`; + try { + return require.resolve(binaryName); + } catch (e) { + throw new Error( + `Couldn't find application binary inside node_modules for ${os}-${arch()}, expected location: ${binaryName}`, + ); + } +} + +function runWithForwardedExit(child: ReturnType): void { + child.on("exit", (code: number | null, signal: NodeJS.Signals | null) => { + process.on("exit", () => { + if (signal) { + process.kill(process.pid, signal); + } else if (code !== null) { + process.exit(code); + } + }); + }); + + process.on("SIGINT", () => { + child.kill("SIGINT"); + child.kill("SIGTERM"); + }); +} + +function runRpcRouter(location: string): void { + const args = process.argv.slice(2); + const env = { + ...process.env, + }; + const ephemeralValidator = spawn(location, args, { stdio: "inherit", env}); + runWithForwardedExit(ephemeralValidator); +} + +function tryPackageRpcRouter(): boolean { + try { + const path = getExePath(); + runRpcRouter(path); + return true; + } catch (e) { + console.error( + "Failed to run rpc-router from package:", + e instanceof Error ? e.message : e, + ); + return false; + } +} + +function trySystemRpcRouter(): void { + const absolutePath = process.env.PATH?.split(path.delimiter) + .filter((dir) => dir !== path.dirname(process.argv[1])) + .find((dir) => { + try { + fs.accessSync(`${dir}/rpc-router`, fs.constants.X_OK); + return true; + } catch { + return false; + } + }); + + if (!absolutePath) { + console.error( + `Could not find globally installed rpc-router, please install with cargo.`, + ); + process.exit(1); + } + + const absoluteBinaryPath = `${absolutePath}/rpc-router`; + const [error, binaryVersion] = getBinaryVersion(absoluteBinaryPath); + + if (error !== null) { + console.error(`Failed to get version of global binary: ${error}`); + return; + } + if (binaryVersion !== PACKAGE_VERSION) { + console.error( + `Globally installed rpc-router version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`, + ); + return; + } + + runRpcRouter(absoluteBinaryPath); +} + +// If the first argument is our special command, run the test validator and exit. +tryPackageRpcRouter() || trySystemRpcRouter(); diff --git a/.github/packages/npm-package/versions.ts b/.github/packages/npm-package/versions.ts new file mode 100644 index 000000000..6d930b9ff --- /dev/null +++ b/.github/packages/npm-package/versions.ts @@ -0,0 +1,5 @@ +export const VERSIONS = { + EPHEMERAL_VALIDATOR: "0.2.3", + RPC_ROUTER: "0.0.1", + VRF_ORACLE: "0.2.0", +} as const; \ No newline at end of file diff --git a/.github/packages/npm-package/vrfOracle.ts b/.github/packages/npm-package/vrfOracle.ts new file mode 100755 index 000000000..b675be6c2 --- /dev/null +++ b/.github/packages/npm-package/vrfOracle.ts @@ -0,0 +1,114 @@ +#!/usr/bin/env node +import fs from "fs"; +import { spawn, spawnSync } from "child_process"; +import path from "path"; +import { arch, platform } from "os"; +import { VERSIONS } from "./versions"; + +const PACKAGE_VERSION = `vrf-oracle ${VERSIONS.VRF_ORACLE}`; + +function getBinaryVersion(location: string): [string | null, string | null] { + const result = spawnSync(location, ["--version"]); + const error: string | null = + (result.error && result.error.toString()) || + (result.stderr.length > 0 && result.stderr.toString().trim()) || + null; + return [error, result.stdout && result.stdout.toString().trim()]; +} + +function getExePath(): string { + let os: string = platform(); + let extension = ""; + if (["win32", "cygwin"].includes(os)) { + os = "windows"; + extension = ".exe"; + } + const binaryName = `@magicblock-labs/vrf-oracle-${os}-${arch()}/bin/vrf-oracle${extension}`; + try { + return require.resolve(binaryName); + } catch (e) { + throw new Error( + `Couldn't find application binary inside node_modules for ${os}-${arch()}, expected location: ${binaryName}`, + ); + } +} + +function runWithForwardedExit(child: ReturnType): void { + child.on("exit", (code: number | null, signal: NodeJS.Signals | null) => { + process.on("exit", () => { + if (signal) { + process.kill(process.pid, signal); + } else if (code !== null) { + process.exit(code); + } + }); + }); + + process.on("SIGINT", () => { + child.kill("SIGINT"); + child.kill("SIGTERM"); + }); +} + +function runVrfOracle(location: string): void { + const args = process.argv.slice(2); + const env = { + ...process.env, + RUST_LOG: "quiet", + }; + const vrfOracle = spawn(location, args, { stdio: "inherit", env}); + runWithForwardedExit(vrfOracle); +} + +function tryPackageVrfOracle(): boolean { + try { + const path = getExePath(); + runVrfOracle(path); + return true; + } catch (e) { + console.error( + "Failed to run vrf-oracle from package:", + e instanceof Error ? e.message : e, + ); + return false; + } +} + +function trySystemVrfOracle(): void { + const absolutePath = process.env.PATH?.split(path.delimiter) + .filter((dir) => dir !== path.dirname(process.argv[1])) + .find((dir) => { + try { + fs.accessSync(`${dir}/vrf-oracle`, fs.constants.X_OK); + return true; + } catch { + return false; + } + }); + + if (!absolutePath) { + console.error( + `Could not find globally installed vrf-oracle, please install with cargo.`, + ); + process.exit(1); + } + + const absoluteBinaryPath = `${absolutePath}/vrf-oracle`; + const [error, binaryVersion] = getBinaryVersion(absoluteBinaryPath); + + if (error !== null) { + console.error(`Failed to get version of global binary: ${error}`); + return; + } + if (binaryVersion !== PACKAGE_VERSION) { + console.error( + `Globally installed vrf-oracle version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`, + ); + return; + } + + runVrfOracle(absoluteBinaryPath); +} + +// If the first argument is our special command, run the test validator and exit. +tryPackageVrfOracle() || trySystemVrfOracle();