start-validator.mts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #!/usr/bin/env zx
  2. import { spawn } from 'node:child_process';
  3. import fs from 'node:fs';
  4. import 'zx/globals';
  5. import {
  6. getCargo,
  7. getExternalAccountAddresses,
  8. getExternalProgramAddresses,
  9. getExternalProgramOutputDir,
  10. getProgramFolders,
  11. workingDirectory,
  12. } from './utils.mts';
  13. // Check Solana version.
  14. await $`pnpm solana:check`;
  15. // Options and arguments.
  16. const restart = argv['restart'];
  17. // Keep the validator running when not using the restart flag.
  18. const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0;
  19. if (!restart && isValidatorRunning) {
  20. echo(chalk.yellow('Local validator is already running.'));
  21. process.exit();
  22. }
  23. // Initial message.
  24. const verb = isValidatorRunning ? 'Restarting' : 'Starting';
  25. // Get programs and accounts.
  26. // TODO: JOE C:
  27. // Disabled adding the BPF program until BPF implementation is complete.
  28. // Tests expect a valid System program to live at the corresponding address.
  29. const programs = [...getExternalPrograms()];
  30. const programPluralized = programs.length === 1 ? 'program' : 'programs';
  31. const accounts = [...getExternalAccounts()];
  32. const accountsPluralized = accounts.length === 1 ? 'account' : 'accounts';
  33. echo(
  34. `${verb} local validator with ${programs.length} custom ${programPluralized}` +
  35. (accounts.length > 0
  36. ? ` and ${accounts.length} external ${accountsPluralized}...`
  37. : `...`)
  38. );
  39. // Kill the validator if it's already running.
  40. if (isValidatorRunning) {
  41. await $`pkill -f solana-test-validator`.quiet();
  42. await sleep(1000);
  43. }
  44. // Global validator arguments.
  45. const args = [/* Reset ledger */ '-r'];
  46. // Load programs.
  47. programs.forEach(({ programId, deployPath }) => {
  48. args.push(/* Load BPF program */ '--bpf-program', programId, deployPath);
  49. });
  50. // Load accounts.
  51. accounts.forEach(({ account, deployPath }) => {
  52. args.push(/* Load account */ '--account', account, deployPath);
  53. });
  54. // Start the validator in detached mode.
  55. const cliLogs = path.join(os.tmpdir(), 'validator-cli.log');
  56. fs.writeFileSync(cliLogs, '');
  57. const out = fs.openSync(cliLogs, 'a');
  58. const err = fs.openSync(cliLogs, 'a');
  59. const validator = spawn('solana-test-validator', args, {
  60. detached: true,
  61. stdio: ['ignore', out, err],
  62. });
  63. validator.unref();
  64. // Wait for the validator to stabilize.
  65. const waitForValidator = spinner(
  66. 'Waiting for local validator to stabilize...',
  67. () =>
  68. new Promise((resolve, reject) => {
  69. setInterval(() => {
  70. const logs = fs.readFileSync(cliLogs, 'utf8');
  71. if (validator.exitCode !== null) {
  72. reject(logs);
  73. } else if (logs.includes('Confirmed Slot: 1')) {
  74. resolve(void 0);
  75. }
  76. }, 1000);
  77. })
  78. );
  79. try {
  80. await waitForValidator;
  81. echo(chalk.green('Local validator is up and running!'));
  82. } catch (error) {
  83. echo(error);
  84. echo(chalk.red('Could not start local validator.'));
  85. } finally {
  86. fs.rmSync(cliLogs);
  87. process.exit();
  88. }
  89. // TODO: JOE C:
  90. // Disabled adding the BPF program until BPF implementation is complete.
  91. // Tests expect a valid System program to live at the corresponding address.
  92. function _getPrograms() {
  93. const binaryDir = path.join(workingDirectory, 'target', 'deploy');
  94. return getProgramFolders().map((folder) => {
  95. const cargo = getCargo(folder);
  96. const name = cargo.package['name'].replace(/-/g, '_');
  97. return {
  98. programId: cargo.package['metadata'].solana['program-id'],
  99. deployPath: path.join(binaryDir, `${name}.so`),
  100. };
  101. });
  102. }
  103. function getExternalPrograms() {
  104. const binaryDir = getExternalProgramOutputDir();
  105. return getExternalProgramAddresses().map((address) => ({
  106. programId: address,
  107. deployPath: path.join(binaryDir, `${address}.so`),
  108. }));
  109. }
  110. function getExternalAccounts() {
  111. const binaryDir = getExternalProgramOutputDir();
  112. return getExternalAccountAddresses().map((address) => ({
  113. account: address,
  114. deployPath: path.join(binaryDir, `${address}.json`),
  115. }));
  116. }