Skip to content

Commit

Permalink
feat: add support for --sbf-program flag in test-validator (#1393)
Browse files Browse the repository at this point in the history
* feat: add support for `--sbf-program` flag in test-validator

This allows adding SBF programs to the genesis configuration with upgrades disabled. The flag accepts pairs of arguments (address and path) to specify programs, and validates input to ensure correct usage.

* feat: enhance test-validator with program validation and system program checks + add tests

* * added usage example about how to use --sbf-program flag programs in test-validator command
* added test check to validate the program deployment and ensure the account is executable
  • Loading branch information
sergeytimoshin authored Dec 15, 2024
1 parent a2e5bf8 commit 204895c
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 40 deletions.
3 changes: 2 additions & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@
"test-decompress-sol": "mocha ./test/commands/decompress-sol/index.test.ts -t 10000000 --exit",
"test-compress-spl": "mocha ./test/commands/compress-spl/index.test.ts -t 10000000 --exit",
"test-decompress-spl": "mocha ./test/commands/decompress-spl/index.test.ts -t 10000000 --exit",
"test-test-validator": "mocha ./test/commands/test-validator/index.test.ts -t 10000000 --exit",
"kill": "killall solana-test-validator || true && killall solana-test-val || true && sleep 1",
"test-cli": "pnpm test-config && pnpm kill",
"test": "pnpm kill && pnpm test-cli && pnpm test-utils && pnpm test-create-mint && pnpm test-mint-to && pnpm test-transfer && pnpm test-merge-token-accounts && pnpm test-create-token-pool && pnpm test-compress-spl && pnpm test-decompress-spl && pnpm test-balance && pnpm test-compress-sol && pnpm test-decompress-sol && pnpm test-approve-and-mint-to",
"test": "pnpm kill && pnpm test-cli && pnpm test-utils && pnpm test-create-mint && pnpm test-mint-to && pnpm test-transfer && pnpm test-merge-token-accounts && pnpm test-create-token-pool && pnpm test-compress-spl && pnpm test-decompress-spl && pnpm test-balance && pnpm test-compress-sol && pnpm test-decompress-sol && pnpm test-approve-and-mint-to && pnpm test-test-validator",
"install-local": "pnpm build && pnpm global remove @lightprotocol/zk-compression-cli || true && pnpm global add $PWD",
"version": "oclif readme && git add README.md"
},
Expand Down
76 changes: 74 additions & 2 deletions cli/src/commands/test-validator/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Command, Flags } from "@oclif/core";
import { initTestEnv, stopTestEnv } from "../../utils/initTestEnv";
import {
initTestEnv,
stopTestEnv,
SYSTEM_PROGRAMS,
} from "../../utils/initTestEnv";
import { CustomLoader } from "../../utils/index";
import path from "path";
import fs from "fs";
Expand All @@ -13,9 +17,13 @@ class SetupCommand extends Command {
"$ light test-validator --skip-indexer",
"$ light test-validator --geyser-config ./config.json",
'$ light test-validator --validator-args "--limit-ledger-size 50000000"',
"$ light test-validator --sbf-program <address> <path/program>",
];

protected finally(_: Error | undefined): Promise<any> {
protected finally(err: Error | undefined): Promise<any> {
if (err) {
console.error(err);
}
process.exit();
}

Expand Down Expand Up @@ -119,8 +127,55 @@ class SetupCommand extends Command {
required: false,
exclusive: ["geyser-config"],
}),
"sbf-program": Flags.string({
description:
"Add a SBF program to the genesis configuration with upgrades disabled. If the ledger already exists then this parameter is silently ignored. First argument can be a pubkey string or path to a keypair",
required: false,
multiple: true,
summary: "Usage: --sbf-program <address> <path/program_name.so>",
}),
};

validatePrograms(programs: { address: string; path: string }[]): void {
// Check for duplicate addresses among provided programs
const addresses = new Set<string>();
for (const program of programs) {
if (addresses.has(program.address)) {
this.error(`Duplicate program address detected: ${program.address}`);
}
addresses.add(program.address);

// Get the program filename from the path
const programFileName = path.basename(program.path);

// Check for collisions with system programs (both address and filename)
const systemProgramCollision = SYSTEM_PROGRAMS.find(
(sysProg) =>
sysProg.id === program.address ||
(sysProg.name && programFileName === sysProg.name),
);

if (systemProgramCollision) {
const collisionType =
systemProgramCollision.id === program.address
? `address (${program.address})`
: `filename (${programFileName})`;

this.error(
`Program ${collisionType} collides with system program ` +
`"${systemProgramCollision.name || systemProgramCollision.id}". ` +
`System programs cannot be overwritten.`,
);
}

// Validate program file exists
const programPath = path.resolve(program.path);
if (!fs.existsSync(programPath)) {
this.error(`Program file not found: ${programPath}`);
}
}
}

async run() {
const { flags } = await this.parse(SetupCommand);
const loader = new CustomLoader("Performing setup tasks...\n");
Expand All @@ -139,7 +194,24 @@ class SetupCommand extends Command {
});
this.log("\nTest validator stopped successfully \x1b[32m✔\x1b[0m");
} else {
const rawValues = flags["sbf-program"] || [];

if (rawValues.length % 2 !== 0) {
this.error("Each --sbf-program flag must have exactly two arguments");
}

const programs: { address: string; path: string }[] = [];
for (let i = 0; i < rawValues.length; i += 2) {
programs.push({
address: rawValues[i],
path: rawValues[i + 1],
});
}

this.validatePrograms(programs);

await initTestEnv({
additionalPrograms: programs,
checkPhotonVersion: !flags["relax-indexer-version-constraint"],
indexer: !flags["skip-indexer"],
limitLedgerSize: flags["limit-ledger-size"],
Expand Down
58 changes: 30 additions & 28 deletions cli/src/utils/initTestEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,35 @@ import {
import { killProver, startProver } from "./processProverServer";
import { killIndexer, startIndexer } from "./processPhotonIndexer";

type Program = { id: string; name?: string; tag?: string; path?: string };
export const SYSTEM_PROGRAMS: Program[] = [
{
id: "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
name: "spl_noop.so",
tag: SPL_NOOP_PROGRAM_TAG,
},
{
id: "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7",
name: "light_system_program.so",
tag: LIGHT_SYSTEM_PROGRAM_TAG,
},
{
id: "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m",
name: "light_compressed_token.so",
tag: LIGHT_COMPRESSED_TOKEN_TAG,
},
{
id: "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq",
name: "account_compression.so",
tag: LIGHT_ACCOUNT_COMPRESSION_TAG,
},
{
id: "Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX",
name: "light_registry.so",
tag: LIGHT_REGISTRY_TAG,
},
];

export async function stopTestEnv(options: {
indexer: boolean;
prover: boolean;
Expand Down Expand Up @@ -208,35 +237,8 @@ export async function getSolanaArgs({
gossipHost?: string;
downloadBinaries?: boolean;
}): Promise<Array<string>> {
type Program = { id: string; name?: string; tag?: string; path?: string };
// TODO: adjust program tags
const programs: Program[] = [
{
id: "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV",
name: "spl_noop.so",
tag: SPL_NOOP_PROGRAM_TAG,
},
{
id: "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7",
name: "light_system_program.so",
tag: LIGHT_SYSTEM_PROGRAM_TAG,
},
{
id: "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m",
name: "light_compressed_token.so",
tag: LIGHT_COMPRESSED_TOKEN_TAG,
},
{
id: "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq",
name: "account_compression.so",
tag: LIGHT_ACCOUNT_COMPRESSION_TAG,
},
{
id: "Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX",
name: "light_registry.so",
tag: LIGHT_REGISTRY_TAG,
},
];
const programs: Program[] = [...SYSTEM_PROGRAMS];
if (additionalPrograms)
additionalPrograms.forEach((program) => {
programs.push({ id: program.address, path: program.path });
Expand Down
16 changes: 7 additions & 9 deletions cli/src/utils/processPhotonIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,14 @@ export async function startIndexer(
throw new Error(message);
} else {
console.log("Starting indexer...");
let args: string[] = [];
const args: string[] = [
"--port",
indexerPort.toString(),
"--rpc-url",
rpcUrl,
];
if (photonDatabaseUrl) {
args = [
"--db-url",
photonDatabaseUrl,
"--port",
indexerPort.toString(),
"--rpc-url",
rpcUrl,
];
args.push("--db-url", photonDatabaseUrl);
}
spawnBinary(INDEXER_PROCESS_NAME, args);
await waitForServers([{ port: indexerPort, path: "/getIndexerHealth" }]);
Expand Down
Loading

0 comments on commit 204895c

Please sign in to comment.