dump.mjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/env zx
  2. import 'zx/globals';
  3. import {
  4. getExternalAccountAddresses,
  5. getExternalProgramAddresses,
  6. getExternalProgramOutputDir,
  7. } from '../utils.mjs';
  8. // Get input from environment variables.
  9. const rpc = process.env.RPC ?? 'https://api.mainnet-beta.solana.com';
  10. const outputDir = getExternalProgramOutputDir();
  11. await dump();
  12. /** Dump external programs binaries and accounts if needed. */
  13. async function dump() {
  14. // Ensure we have some external accounts to dump.
  15. const programs = getExternalProgramAddresses();
  16. const accounts = getExternalAccountAddresses();
  17. const external = [
  18. ...programs.map((program) => [program, 'so']),
  19. ...accounts.map((account) => [account, 'json']),
  20. ];
  21. if (external.length === 0) return;
  22. echo(`Dumping external accounts to '${outputDir}':`);
  23. // Create the output directory if needed.
  24. $`mkdir -p ${outputDir}`.quiet();
  25. // Copy the binaries from the chain or warn if they are different.
  26. await Promise.all(
  27. external.map(async ([address, extension]) => {
  28. const binary = `${address}.${extension}`;
  29. const hasBinary = await fs.exists(`${outputDir}/${binary}`);
  30. if (!hasBinary) {
  31. await copyFromChain(address, extension);
  32. echo(`Wrote account data to ${outputDir}/${binary}`);
  33. return;
  34. }
  35. let sha = 'sha256sum';
  36. let options = [];
  37. let hasShaChecksum = await which('sha256sum', { nothrow: true });
  38. // We might not have sha256sum on some systems, so we try shasum as well.
  39. if (!hasShaChecksum) {
  40. hasShaChecksum = await which('shasum', { nothrow: true });
  41. if (hasShaChecksum) {
  42. sha = 'shasum';
  43. options = ['-a', '256'];
  44. }
  45. }
  46. if (hasShaChecksum) {
  47. try {
  48. await copyFromChain(address, extension, 'onchain-');
  49. const [onChainHash, localHash] = await Promise.all([
  50. $`${sha} ${options} -b ${outputDir}/onchain-${binary} | cut -d ' ' -f 1`.quiet(),
  51. $`${sha} ${options} -b ${outputDir}/${binary} | cut -d ' ' -f 1`.quiet(),
  52. ]);
  53. if (onChainHash.toString() !== localHash.toString()) {
  54. echo(
  55. chalk.yellow('[ WARNING ]'),
  56. `on-chain and local binaries are different for '${address}'`
  57. );
  58. } else {
  59. echo(
  60. chalk.green('[ SKIPPED ]'),
  61. `on-chain and local binaries are the same for '${address}'`
  62. );
  63. }
  64. await $`rm ${outputDir}/onchain-${binary}`.quiet();
  65. } catch (error) {
  66. echo(
  67. chalk.yellow('[ WARNING ]'),
  68. `skipped check for '${address}' (error copying data from '${rpc}')`
  69. );
  70. }
  71. } else {
  72. echo(
  73. chalk.yellow('[ WARNING ]'),
  74. `skipped check for '${address}' (missing 'sha256sum' command)`
  75. );
  76. }
  77. })
  78. );
  79. }
  80. /** Helper function to copy external programs or accounts binaries from the chain. */
  81. async function copyFromChain(address, extension, prefix = '') {
  82. const binary = `${prefix}${address}.${extension}`;
  83. switch (extension) {
  84. case 'json':
  85. return $`solana account -u ${rpc} ${address} -o ${outputDir}/${binary} --output json >/dev/null`.quiet();
  86. case 'so':
  87. return $`solana program dump -u ${rpc} ${address} ${outputDir}/${binary} >/dev/null`.quiet();
  88. default:
  89. echo(chalk.red(`[ ERROR ] unknown account type for '${binary}'`));
  90. await $`exit 1`;
  91. }
  92. }