@j0nnyboi/amman
Version:
A modern mandatory toolbelt to help test solana SDK libraries and apps on a locally running validator.
147 lines • 6.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.restartValidator = exports.restartValidatorWithSnapshot = exports.restartValidatorWithAccountOverrides = exports.killValidatorChild = exports.waitForValidator = exports.startSolanaValidator = exports.buildSolanaValidatorArgs = void 0;
const child_process_1 = require("child_process");
const assets_1 = require("../assets");
const deactivate_features_1 = require("../utils/deactivate-features");
const fs_1 = require("../utils/fs");
const log_1 = require("../utils/log");
const ensure_validator_up_1 = require("./ensure-validator-up");
const prepare_config_1 = require("./prepare-config");
const process_accounts_1 = require("./process-accounts");
const process_snapshot_1 = require("./process-snapshot");
const { logDebug, logInfo, logTrace } = (0, log_1.scopedLog)('validator');
async function buildSolanaValidatorArgs(config, forceClone) {
logTrace('config %O', config);
const validatorConfig = config.validator;
const { programs, accountsCluster, accounts, ledgerDir, resetLedger, limitLedgerSize, websocketUrl, jsonRpcUrl, commitment, matchFeatures, deactivateFeatures, } = validatorConfig;
const { assetsFolder } = config;
// -----------------
// Setup Solana Config
// -----------------
const { configPath, cleanupConfig } = await (0, prepare_config_1.solanaConfig)({
websocketUrl,
jsonRpcUrl,
commitment,
});
let args = ['--quiet', '-C', configPath, '--ledger', ledgerDir];
if (resetLedger)
args.push('-r');
args.push(...['--limit-ledger-size', limitLedgerSize.toString()]);
// -----------------
// Deploy Programs
// -----------------
if (programs.length > 0) {
for (const { programId, deployPath } of programs) {
if (!(0, fs_1.canAccessSync)(deployPath)) {
throw new Error(`Cannot access program deploy path of ${deployPath}`);
}
args.push('--bpf-program');
args.push(programId);
args.push(deployPath);
}
}
// -----------------
// Deactivate Features
// -----------------
(0, deactivate_features_1.maybeDeactivateFeatures)(args, matchFeatures, deactivateFeatures);
// -----------------
// Add Cloned Accounts
// -----------------
const { accountsArgs, persistedAccountInfos, accountsFolder } = await (0, process_accounts_1.processAccounts)(accounts, accountsCluster, assetsFolder, forceClone);
args = [...args, ...accountsArgs];
// -----------------
// Add Snapshot
// -----------------
const { snapshotArgs, persistedSnapshotAccountInfos, snapshotAccounts, keypairs, } = await (0, process_snapshot_1.processSnapshot)(config.snapshot);
args = [...args, ...snapshotArgs];
return {
args,
persistedAccountInfos,
persistedSnapshotAccountInfos,
snapshotAccounts,
accountsFolder,
keypairs,
cleanupConfig,
};
}
exports.buildSolanaValidatorArgs = buildSolanaValidatorArgs;
async function startSolanaValidator(args, detached) {
logTrace('start %O', args);
const child = (0, child_process_1.spawn)('solana-test-validator', args, {
detached,
stdio: 'inherit',
});
child.unref();
await new Promise((resolve, reject) => {
child.on('spawn', resolve).on('error', reject);
});
return child;
}
exports.startSolanaValidator = startSolanaValidator;
async function waitForValidator(jsonRpcUrl, verifyFees, cleanupConfig) {
await (0, ensure_validator_up_1.ensureValidatorIsUp)(jsonRpcUrl, verifyFees);
await cleanupConfig();
logInfo('up and running');
}
exports.waitForValidator = waitForValidator;
function killValidatorChild(child) {
child.kill();
return new Promise((resolve, reject) => {
child.on('exit', resolve).on('error', reject);
});
}
exports.killValidatorChild = killValidatorChild;
/**
* Attempts to kill and restart the validator creating a snapshot of accounts and keypairs first.
* That same snapshot is then loaded on restart.
*
* @param accountOverrides allow to override some accounts that are written to the snapshot
*
*/
async function restartValidatorWithAccountOverrides(accountStates, ammanState, addresses,
// Keyed pubkey:label
accountLabels, keypairs, accountOverrides) {
const { config: snapshot, cleanupSnapshotDir } = await (0, assets_1.createTemporarySnapshot)(addresses, accountLabels, keypairs, accountOverrides);
const config = { ...ammanState.config, snapshot };
const res = await restartValidator(accountStates, ammanState, config);
await cleanupSnapshotDir();
return res;
}
exports.restartValidatorWithAccountOverrides = restartValidatorWithAccountOverrides;
/**
* Attempts to kill and restart the validator with the given snapshot.
*/
async function restartValidatorWithSnapshot(accountStates, ammanState, snapshotLabel) {
const snapshot = {
...ammanState.config.snapshot,
load: snapshotLabel,
};
const config = { ...ammanState.config, snapshot };
return restartValidator(accountStates, ammanState, config);
}
exports.restartValidatorWithSnapshot = restartValidatorWithSnapshot;
/**
* Attempts to kill and restart the validator with the provided config.
*
* NOTE: that for now this seems to only work once, i.e. the validator fails to
* handle transactions after it is restarted twice (they time out after 30secs)
*
*/
async function restartValidator(accountStates, ammanState, config) {
logDebug('Restarting validator');
accountStates.paused = true;
try {
await killValidatorChild(ammanState.validator);
const { args, cleanupConfig, ...rest } = await buildSolanaValidatorArgs(config, false);
const validator = await startSolanaValidator(args, ammanState.detached);
ammanState.validator = validator;
await waitForValidator(config.validator.jsonRpcUrl, config.validator.verifyFees, cleanupConfig);
return { args, ...rest };
}
finally {
accountStates.paused = false;
}
}
exports.restartValidator = restartValidator;
//# sourceMappingURL=solana-validator.js.map