main.rs 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679
  1. //! CLI for workspace management of anchor programs.
  2. use crate::config::{read_all_programs, Config, Program, ProgramWorkspace, WalletPath};
  3. use anchor_client::Cluster;
  4. use anchor_lang::idl::{IdlAccount, IdlInstruction};
  5. use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
  6. use anchor_syn::idl::Idl;
  7. use anyhow::{anyhow, Context, Result};
  8. use clap::Clap;
  9. use flate2::read::ZlibDecoder;
  10. use flate2::write::ZlibEncoder;
  11. use flate2::Compression;
  12. use rand::rngs::OsRng;
  13. use serde::{Deserialize, Serialize};
  14. use solana_client::rpc_client::RpcClient;
  15. use solana_client::rpc_config::RpcSendTransactionConfig;
  16. use solana_program::instruction::{AccountMeta, Instruction};
  17. use solana_sdk::account_utils::StateMut;
  18. use solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState;
  19. use solana_sdk::commitment_config::CommitmentConfig;
  20. use solana_sdk::pubkey::Pubkey;
  21. use solana_sdk::signature::Keypair;
  22. use solana_sdk::signature::Signer;
  23. use solana_sdk::sysvar;
  24. use solana_sdk::transaction::Transaction;
  25. use std::collections::HashMap;
  26. use std::fs::{self, File};
  27. use std::io::prelude::*;
  28. use std::path::{Path, PathBuf};
  29. use std::process::{Child, Stdio};
  30. use std::string::ToString;
  31. mod config;
  32. mod template;
  33. // Version of the docker image.
  34. const VERSION: &str = env!("CARGO_PKG_VERSION");
  35. const DOCKER_BUILDER_VERSION: &str = VERSION;
  36. #[derive(Debug, Clap)]
  37. #[clap(version = VERSION)]
  38. pub struct Opts {
  39. #[clap(flatten)]
  40. pub cfg_override: ConfigOverride,
  41. #[clap(subcommand)]
  42. pub command: Command,
  43. }
  44. #[derive(Debug, Clap)]
  45. pub struct ConfigOverride {
  46. /// Cluster override.
  47. #[clap(global = true, long = "provider.cluster")]
  48. cluster: Option<Cluster>,
  49. /// Wallet override.
  50. #[clap(global = true, long = "provider.wallet")]
  51. wallet: Option<WalletPath>,
  52. }
  53. #[derive(Debug, Clap)]
  54. pub enum Command {
  55. /// Initializes a workspace.
  56. Init {
  57. name: String,
  58. #[clap(short, long)]
  59. typescript: bool,
  60. },
  61. /// Builds the workspace.
  62. Build {
  63. /// Output directory for the IDL.
  64. #[clap(short, long)]
  65. idl: Option<String>,
  66. /// True if the build artifact needs to be deterministic and verifiable.
  67. #[clap(short, long)]
  68. verifiable: bool,
  69. #[clap(short, long)]
  70. program_name: Option<String>,
  71. },
  72. /// Verifies the on-chain bytecode matches the locally compiled artifact.
  73. /// Run this command inside a program subdirectory, i.e., in the dir
  74. /// containing the program's Cargo.toml.
  75. Verify {
  76. /// The deployed program to compare against.
  77. program_id: Pubkey,
  78. },
  79. /// Runs integration tests against a localnetwork.
  80. Test {
  81. /// Use this flag if you want to run tests against previously deployed
  82. /// programs.
  83. #[clap(long)]
  84. skip_deploy: bool,
  85. /// Flag to skip starting a local validator, if the configured cluster
  86. /// url is a localnet.
  87. #[clap(long)]
  88. skip_local_validator: bool,
  89. /// Flag to skip building the program in the workspace,
  90. /// use this to save time when running test and the program code is not altered.
  91. #[clap(long)]
  92. skip_build: bool,
  93. file: Option<String>,
  94. },
  95. /// Creates a new program.
  96. New { name: String },
  97. /// Commands for interacting with interface definitions.
  98. Idl {
  99. #[clap(subcommand)]
  100. subcmd: IdlCommand,
  101. },
  102. /// Deploys each program in the workspace.
  103. Deploy {
  104. #[clap(short, long)]
  105. program_name: Option<String>,
  106. },
  107. /// Runs the deploy migration script.
  108. Migrate,
  109. /// Deploys, initializes an IDL, and migrates all in one command.
  110. Launch {
  111. /// True if the build should be verifiable. If deploying to mainnet,
  112. /// this should almost always be set.
  113. #[clap(short, long)]
  114. verifiable: bool,
  115. #[clap(short, long)]
  116. program_name: Option<String>,
  117. },
  118. /// Upgrades a single program. The configured wallet must be the upgrade
  119. /// authority.
  120. Upgrade {
  121. /// The program to upgrade.
  122. #[clap(short, long)]
  123. program_id: Pubkey,
  124. /// Filepath to the new program binary.
  125. program_filepath: String,
  126. },
  127. #[cfg(feature = "dev")]
  128. /// Runs an airdrop loop, continuously funding the configured wallet.
  129. Airdrop {
  130. #[clap(short, long)]
  131. url: Option<String>,
  132. },
  133. /// Cluster commands.
  134. Cluster {
  135. #[clap(subcommand)]
  136. subcmd: ClusterCommand,
  137. },
  138. /// Starts a node shell with an Anchor client setup according to the local
  139. /// config.
  140. Shell,
  141. /// Runs the script defined by the current workspace's Anchor.toml.
  142. Run {
  143. /// The name of the script to run.
  144. script: String,
  145. },
  146. }
  147. #[derive(Debug, Clap)]
  148. pub enum IdlCommand {
  149. /// Initializes a program's IDL account. Can only be run once.
  150. Init {
  151. program_id: Pubkey,
  152. #[clap(short, long)]
  153. filepath: String,
  154. },
  155. /// Writes an IDL into a buffer account. This can be used with SetBuffer
  156. /// to perform an upgrade.
  157. WriteBuffer {
  158. program_id: Pubkey,
  159. #[clap(short, long)]
  160. filepath: String,
  161. },
  162. /// Sets a new IDL buffer for the program.
  163. SetBuffer {
  164. program_id: Pubkey,
  165. /// Address of the buffer account to set as the idl on the program.
  166. #[clap(short, long)]
  167. buffer: Pubkey,
  168. },
  169. /// Upgrades the IDL to the new file. An alias for first writing and then
  170. /// then setting the idl buffer account.
  171. Upgrade {
  172. program_id: Pubkey,
  173. #[clap(short, long)]
  174. filepath: String,
  175. },
  176. /// Sets a new authority on the IDL account.
  177. SetAuthority {
  178. /// The IDL account buffer to set the authority of. If none is given,
  179. /// then the canonical IDL account is used.
  180. address: Option<Pubkey>,
  181. /// Program to change the IDL authority.
  182. #[clap(short, long)]
  183. program_id: Pubkey,
  184. /// New authority of the IDL account.
  185. #[clap(short, long)]
  186. new_authority: Pubkey,
  187. },
  188. /// Command to remove the ability to modify the IDL account. This should
  189. /// likely be used in conjection with eliminating an "upgrade authority" on
  190. /// the program.
  191. EraseAuthority {
  192. #[clap(short, long)]
  193. program_id: Pubkey,
  194. },
  195. /// Outputs the authority for the IDL account.
  196. Authority {
  197. /// The program to view.
  198. program_id: Pubkey,
  199. },
  200. /// Parses an IDL from source.
  201. Parse {
  202. /// Path to the program's interface definition.
  203. #[clap(short, long)]
  204. file: String,
  205. /// Output file for the idl (stdout if not specified).
  206. #[clap(short, long)]
  207. out: Option<String>,
  208. },
  209. /// Fetches an IDL for the given address from a cluster.
  210. /// The address can be a program, IDL account, or IDL buffer.
  211. Fetch {
  212. address: Pubkey,
  213. /// Output file for the idl (stdout if not specified).
  214. #[clap(short, long)]
  215. out: Option<String>,
  216. },
  217. }
  218. #[derive(Debug, Clap)]
  219. pub enum ClusterCommand {
  220. /// Prints common cluster urls.
  221. List,
  222. }
  223. fn main() -> Result<()> {
  224. let opts = Opts::parse();
  225. match opts.command {
  226. Command::Init { name, typescript } => init(&opts.cfg_override, name, typescript),
  227. Command::New { name } => new(&opts.cfg_override, name),
  228. Command::Build {
  229. idl,
  230. verifiable,
  231. program_name,
  232. } => build(&opts.cfg_override, idl, verifiable, program_name),
  233. Command::Verify { program_id } => verify(&opts.cfg_override, program_id),
  234. Command::Deploy { program_name } => deploy(&opts.cfg_override, program_name),
  235. Command::Upgrade {
  236. program_id,
  237. program_filepath,
  238. } => upgrade(&opts.cfg_override, program_id, program_filepath),
  239. Command::Idl { subcmd } => idl(&opts.cfg_override, subcmd),
  240. Command::Migrate => migrate(&opts.cfg_override),
  241. Command::Launch {
  242. verifiable,
  243. program_name,
  244. } => launch(&opts.cfg_override, verifiable, program_name),
  245. Command::Test {
  246. skip_deploy,
  247. skip_local_validator,
  248. skip_build,
  249. file,
  250. } => test(
  251. &opts.cfg_override,
  252. skip_deploy,
  253. skip_local_validator,
  254. skip_build,
  255. file,
  256. ),
  257. #[cfg(feature = "dev")]
  258. Command::Airdrop => airdrop(cfg_override),
  259. Command::Cluster { subcmd } => cluster(subcmd),
  260. Command::Shell => shell(&opts.cfg_override),
  261. Command::Run { script } => run(&opts.cfg_override, script),
  262. }
  263. }
  264. fn init(cfg_override: &ConfigOverride, name: String, typescript: bool) -> Result<()> {
  265. let cfg = Config::discover(cfg_override)?;
  266. if cfg.is_some() {
  267. println!("Anchor workspace already initialized");
  268. }
  269. fs::create_dir(name.clone())?;
  270. std::env::set_current_dir(&name)?;
  271. fs::create_dir("app")?;
  272. let cfg = Config::default();
  273. let toml = cfg.to_string();
  274. let mut file = File::create("Anchor.toml")?;
  275. file.write_all(toml.as_bytes())?;
  276. // Build virtual manifest.
  277. let mut virt_manifest = File::create("Cargo.toml")?;
  278. virt_manifest.write_all(template::virtual_manifest().as_bytes())?;
  279. // Initialize .gitignore file
  280. let mut virt_manifest = File::create(".gitignore")?;
  281. virt_manifest.write_all(template::git_ignore().as_bytes())?;
  282. // Build the program.
  283. fs::create_dir("programs")?;
  284. new_program(&name)?;
  285. // Build the test suite.
  286. fs::create_dir("tests")?;
  287. // Build the migrations directory.
  288. fs::create_dir("migrations")?;
  289. if typescript {
  290. // Build typescript config
  291. let mut ts_config = File::create("tsconfig.json")?;
  292. ts_config.write_all(template::ts_config().as_bytes())?;
  293. let mut deploy = File::create("migrations/deploy.ts")?;
  294. deploy.write_all(template::ts_deploy_script().as_bytes())?;
  295. let mut mocha = File::create(&format!("tests/{}.ts", name))?;
  296. mocha.write_all(template::ts_mocha(&name).as_bytes())?;
  297. } else {
  298. let mut mocha = File::create(&format!("tests/{}.js", name))?;
  299. mocha.write_all(template::mocha(&name).as_bytes())?;
  300. let mut deploy = File::create("migrations/deploy.js")?;
  301. deploy.write_all(template::deploy_script().as_bytes())?;
  302. }
  303. println!("{} initialized", name);
  304. Ok(())
  305. }
  306. // Creates a new program crate in the `programs/<name>` directory.
  307. fn new(cfg_override: &ConfigOverride, name: String) -> Result<()> {
  308. with_workspace(cfg_override, |_cfg, path, _cargo| {
  309. match path.parent() {
  310. None => {
  311. println!("Unable to make new program");
  312. }
  313. Some(parent) => {
  314. std::env::set_current_dir(&parent)?;
  315. new_program(&name)?;
  316. println!("Created new program.");
  317. }
  318. };
  319. Ok(())
  320. })
  321. }
  322. // Creates a new program crate in the current directory with `name`.
  323. fn new_program(name: &str) -> Result<()> {
  324. fs::create_dir(&format!("programs/{}", name))?;
  325. fs::create_dir(&format!("programs/{}/src/", name))?;
  326. let mut cargo_toml = File::create(&format!("programs/{}/Cargo.toml", name))?;
  327. cargo_toml.write_all(template::cargo_toml(name).as_bytes())?;
  328. let mut xargo_toml = File::create(&format!("programs/{}/Xargo.toml", name))?;
  329. xargo_toml.write_all(template::xargo_toml().as_bytes())?;
  330. let mut lib_rs = File::create(&format!("programs/{}/src/lib.rs", name))?;
  331. lib_rs.write_all(template::lib_rs(name).as_bytes())?;
  332. Ok(())
  333. }
  334. fn build(
  335. cfg_override: &ConfigOverride,
  336. idl: Option<String>,
  337. verifiable: bool,
  338. program_name: Option<String>,
  339. ) -> Result<()> {
  340. if let Some(program_name) = program_name {
  341. for program in read_all_programs()? {
  342. let p = program.path.file_name().unwrap().to_str().unwrap();
  343. if program_name.as_str() == p {
  344. std::env::set_current_dir(&program.path)?;
  345. }
  346. }
  347. }
  348. let (cfg, path, cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
  349. let idl_out = match idl {
  350. Some(idl) => Some(PathBuf::from(idl)),
  351. None => {
  352. let cfg_parent = match path.parent() {
  353. None => return Err(anyhow!("Invalid Anchor.toml")),
  354. Some(parent) => parent,
  355. };
  356. fs::create_dir_all(cfg_parent.join("target/idl"))?;
  357. Some(cfg_parent.join("target/idl"))
  358. }
  359. };
  360. match cargo {
  361. None => build_all(&cfg, path, idl_out, verifiable)?,
  362. Some(ct) => build_cwd(path.as_path(), ct, idl_out, verifiable)?,
  363. };
  364. set_workspace_dir_or_exit();
  365. Ok(())
  366. }
  367. fn build_all(
  368. _cfg: &Config,
  369. cfg_path: PathBuf,
  370. idl_out: Option<PathBuf>,
  371. verifiable: bool,
  372. ) -> Result<()> {
  373. let cur_dir = std::env::current_dir()?;
  374. let r = match cfg_path.parent() {
  375. None => Err(anyhow!("Invalid Anchor.toml at {}", cfg_path.display())),
  376. Some(parent) => {
  377. let files = fs::read_dir(parent.join("programs"))?;
  378. for f in files {
  379. let p = f?.path();
  380. build_cwd(
  381. cfg_path.as_path(),
  382. p.join("Cargo.toml"),
  383. idl_out.clone(),
  384. verifiable,
  385. )?;
  386. }
  387. Ok(())
  388. }
  389. };
  390. std::env::set_current_dir(cur_dir)?;
  391. r
  392. }
  393. // Runs the build command outside of a workspace.
  394. fn build_cwd(
  395. cfg_path: &Path,
  396. cargo_toml: PathBuf,
  397. idl_out: Option<PathBuf>,
  398. verifiable: bool,
  399. ) -> Result<()> {
  400. match cargo_toml.parent() {
  401. None => return Err(anyhow!("Unable to find parent")),
  402. Some(p) => std::env::set_current_dir(&p)?,
  403. };
  404. match verifiable {
  405. false => _build_cwd(idl_out),
  406. true => build_cwd_verifiable(cfg_path.parent().unwrap()),
  407. }
  408. }
  409. // Builds an anchor program in a docker image and copies the build artifacts
  410. // into the `target/` directory.
  411. fn build_cwd_verifiable(workspace_dir: &Path) -> Result<()> {
  412. // Docker vars.
  413. let container_name = "anchor-program";
  414. let image_name = format!("projectserum/build:v{}", DOCKER_BUILDER_VERSION);
  415. let volume_mount = format!(
  416. "{}:/workdir",
  417. workspace_dir.canonicalize()?.display().to_string()
  418. );
  419. // Create output dirs.
  420. fs::create_dir_all(workspace_dir.join("target/deploy"))?;
  421. fs::create_dir_all(workspace_dir.join("target/idl"))?;
  422. // Build the program in docker.
  423. let exit = std::process::Command::new("docker")
  424. .args(&[
  425. "run",
  426. "--name",
  427. container_name,
  428. "-v",
  429. &volume_mount,
  430. &image_name,
  431. "anchor",
  432. "build",
  433. ])
  434. .stdout(Stdio::inherit())
  435. .stderr(Stdio::inherit())
  436. .output()
  437. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  438. if !exit.status.success() {
  439. println!("Error building program");
  440. return Ok(());
  441. }
  442. let idl = extract_idl("src/lib.rs")?;
  443. // Copy the binary out of the docker image.
  444. let out_file = format!("../../target/deploy/{}.so", idl.name);
  445. let bin_artifact = format!("{}:/workdir/target/deploy/{}.so", container_name, idl.name);
  446. let exit = std::process::Command::new("docker")
  447. .args(&["cp", &bin_artifact, &out_file])
  448. .stdout(Stdio::inherit())
  449. .stderr(Stdio::inherit())
  450. .output()
  451. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  452. if !exit.status.success() {
  453. return Ok(());
  454. }
  455. // Copy the idl out of the docker image.
  456. let out_file = format!("../../target/idl/{}.json", idl.name);
  457. let idl_artifact = format!("{}:/workdir/target/idl/{}.json", container_name, idl.name);
  458. let exit = std::process::Command::new("docker")
  459. .args(&["cp", &idl_artifact, &out_file])
  460. .stdout(Stdio::inherit())
  461. .stderr(Stdio::inherit())
  462. .output()
  463. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  464. if !exit.status.success() {
  465. return Ok(());
  466. }
  467. // Remove the docker image.
  468. let exit = std::process::Command::new("docker")
  469. .args(&["rm", container_name])
  470. .stdout(Stdio::inherit())
  471. .stderr(Stdio::inherit())
  472. .output()
  473. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  474. if !exit.status.success() {
  475. std::process::exit(exit.status.code().unwrap_or(1));
  476. }
  477. Ok(())
  478. }
  479. fn _build_cwd(idl_out: Option<PathBuf>) -> Result<()> {
  480. let exit = std::process::Command::new("cargo")
  481. .arg("build-bpf")
  482. .stdout(Stdio::inherit())
  483. .stderr(Stdio::inherit())
  484. .output()
  485. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  486. if !exit.status.success() {
  487. std::process::exit(exit.status.code().unwrap_or(1));
  488. }
  489. // Always assume idl is located ar src/lib.rs.
  490. let idl = extract_idl("src/lib.rs")?;
  491. let out = match idl_out {
  492. None => PathBuf::from(".").join(&idl.name).with_extension("json"),
  493. Some(o) => PathBuf::from(&o.join(&idl.name).with_extension("json")),
  494. };
  495. write_idl(&idl, OutFile::File(out))
  496. }
  497. fn verify(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
  498. let (cfg, _path, cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
  499. let cargo = cargo.ok_or_else(|| anyhow!("Must be inside program subdirectory."))?;
  500. let program_dir = cargo.parent().unwrap();
  501. // Build the program we want to verify.
  502. let cur_dir = std::env::current_dir()?;
  503. build(cfg_override, None, true, None)?;
  504. std::env::set_current_dir(&cur_dir)?;
  505. let local_idl = extract_idl("src/lib.rs")?;
  506. // Verify binary.
  507. let bin_path = program_dir
  508. .join("../../target/deploy/")
  509. .join(format!("{}.so", local_idl.name));
  510. let is_buffer = verify_bin(program_id, &bin_path, cfg.provider.cluster.url())?;
  511. // Verify IDL (only if it's not a buffer account).
  512. if !is_buffer {
  513. std::env::set_current_dir(program_dir)?;
  514. let deployed_idl = fetch_idl(cfg_override, program_id)?;
  515. if local_idl != deployed_idl {
  516. println!("Error: IDLs don't match");
  517. std::process::exit(1);
  518. }
  519. }
  520. println!("{} is verified.", program_id);
  521. Ok(())
  522. }
  523. fn verify_bin(program_id: Pubkey, bin_path: &Path, cluster: &str) -> Result<bool> {
  524. let client = RpcClient::new(cluster.to_string());
  525. // Get the deployed build artifacts.
  526. let (deployed_bin, is_buffer) = {
  527. let account = client
  528. .get_account_with_commitment(&program_id, CommitmentConfig::default())?
  529. .value
  530. .map_or(Err(anyhow!("Account not found")), Ok)?;
  531. match account.state()? {
  532. UpgradeableLoaderState::Program {
  533. programdata_address,
  534. } => (
  535. client
  536. .get_account_with_commitment(&programdata_address, CommitmentConfig::default())?
  537. .value
  538. .map_or(Err(anyhow!("Account not found")), Ok)?
  539. .data[UpgradeableLoaderState::programdata_data_offset().unwrap_or(0)..]
  540. .to_vec(),
  541. false,
  542. ),
  543. UpgradeableLoaderState::Buffer { .. } => {
  544. let offset = UpgradeableLoaderState::buffer_data_offset().unwrap_or(0);
  545. (account.data[offset..].to_vec(), true)
  546. }
  547. _ => return Err(anyhow!("Invalid program id")),
  548. }
  549. };
  550. let mut local_bin = {
  551. let mut f = File::open(bin_path)?;
  552. let mut contents = vec![];
  553. f.read_to_end(&mut contents)?;
  554. contents
  555. };
  556. // The deployed program probably has zero bytes appended. The default is
  557. // 2x the binary size in case of an upgrade.
  558. if local_bin.len() < deployed_bin.len() {
  559. local_bin.append(&mut vec![0; deployed_bin.len() - local_bin.len()]);
  560. }
  561. // Finally, check the bytes.
  562. if local_bin != deployed_bin {
  563. println!("Error: Binaries don't match");
  564. std::process::exit(1);
  565. }
  566. Ok(is_buffer)
  567. }
  568. // Fetches an IDL for the given program_id.
  569. fn fetch_idl(cfg_override: &ConfigOverride, idl_addr: Pubkey) -> Result<Idl> {
  570. let cfg = Config::discover(cfg_override)?
  571. .expect("Inside a workspace")
  572. .0;
  573. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  574. let mut account = client
  575. .get_account_with_commitment(&idl_addr, CommitmentConfig::processed())?
  576. .value
  577. .map_or(Err(anyhow!("Account not found")), Ok)?;
  578. if account.executable {
  579. let idl_addr = IdlAccount::address(&idl_addr);
  580. account = client
  581. .get_account_with_commitment(&idl_addr, CommitmentConfig::processed())?
  582. .value
  583. .map_or(Err(anyhow!("Account not found")), Ok)?;
  584. }
  585. // Cut off account discriminator.
  586. let mut d: &[u8] = &account.data[8..];
  587. let idl_account: IdlAccount = AnchorDeserialize::deserialize(&mut d)?;
  588. let mut z = ZlibDecoder::new(&idl_account.data[..]);
  589. let mut s = Vec::new();
  590. z.read_to_end(&mut s)?;
  591. serde_json::from_slice(&s[..]).map_err(Into::into)
  592. }
  593. fn extract_idl(file: &str) -> Result<Idl> {
  594. let file = shellexpand::tilde(file);
  595. anchor_syn::idl::file::parse(&*file)
  596. }
  597. fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> {
  598. match subcmd {
  599. IdlCommand::Init {
  600. program_id,
  601. filepath,
  602. } => idl_init(cfg_override, program_id, filepath),
  603. IdlCommand::WriteBuffer {
  604. program_id,
  605. filepath,
  606. } => idl_write_buffer(cfg_override, program_id, filepath).map(|_| ()),
  607. IdlCommand::SetBuffer { program_id, buffer } => {
  608. idl_set_buffer(cfg_override, program_id, buffer)
  609. }
  610. IdlCommand::Upgrade {
  611. program_id,
  612. filepath,
  613. } => idl_upgrade(cfg_override, program_id, filepath),
  614. IdlCommand::SetAuthority {
  615. program_id,
  616. address,
  617. new_authority,
  618. } => idl_set_authority(cfg_override, program_id, address, new_authority),
  619. IdlCommand::EraseAuthority { program_id } => idl_erase_authority(cfg_override, program_id),
  620. IdlCommand::Authority { program_id } => idl_authority(cfg_override, program_id),
  621. IdlCommand::Parse { file, out } => idl_parse(file, out),
  622. IdlCommand::Fetch { address, out } => idl_fetch(cfg_override, address, out),
  623. }
  624. }
  625. fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: String) -> Result<()> {
  626. with_workspace(cfg_override, |cfg, _path, _cargo| {
  627. let keypair = cfg.provider.wallet.to_string();
  628. let bytes = std::fs::read(idl_filepath)?;
  629. let idl: Idl = serde_json::from_reader(&*bytes)?;
  630. let idl_address = create_idl_account(cfg, &keypair, &program_id, &idl)?;
  631. println!("Idl account created: {:?}", idl_address);
  632. Ok(())
  633. })
  634. }
  635. fn idl_write_buffer(
  636. cfg_override: &ConfigOverride,
  637. program_id: Pubkey,
  638. idl_filepath: String,
  639. ) -> Result<Pubkey> {
  640. with_workspace(cfg_override, |cfg, _path, _cargo| {
  641. let keypair = cfg.provider.wallet.to_string();
  642. let bytes = std::fs::read(idl_filepath)?;
  643. let idl: Idl = serde_json::from_reader(&*bytes)?;
  644. let idl_buffer = create_idl_buffer(cfg, &keypair, &program_id, &idl)?;
  645. idl_write(cfg, &program_id, &idl, idl_buffer)?;
  646. println!("Idl buffer created: {:?}", idl_buffer);
  647. Ok(idl_buffer)
  648. })
  649. }
  650. fn idl_set_buffer(cfg_override: &ConfigOverride, program_id: Pubkey, buffer: Pubkey) -> Result<()> {
  651. with_workspace(cfg_override, |cfg, _path, _cargo| {
  652. let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
  653. .map_err(|_| anyhow!("Unable to read keypair file"))?;
  654. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  655. // Instruction to set the buffer onto the IdlAccount.
  656. let set_buffer_ix = {
  657. let accounts = vec![
  658. AccountMeta::new(buffer, false),
  659. AccountMeta::new(IdlAccount::address(&program_id), false),
  660. AccountMeta::new(keypair.pubkey(), true),
  661. ];
  662. let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec();
  663. data.append(&mut IdlInstruction::SetBuffer.try_to_vec()?);
  664. Instruction {
  665. program_id,
  666. accounts,
  667. data,
  668. }
  669. };
  670. // Build the transaction.
  671. let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
  672. let tx = Transaction::new_signed_with_payer(
  673. &[set_buffer_ix],
  674. Some(&keypair.pubkey()),
  675. &[&keypair],
  676. recent_hash,
  677. );
  678. // Send the transaction.
  679. client.send_and_confirm_transaction_with_spinner_and_config(
  680. &tx,
  681. CommitmentConfig::confirmed(),
  682. RpcSendTransactionConfig {
  683. skip_preflight: true,
  684. ..RpcSendTransactionConfig::default()
  685. },
  686. )?;
  687. Ok(())
  688. })
  689. }
  690. fn idl_upgrade(
  691. cfg_override: &ConfigOverride,
  692. program_id: Pubkey,
  693. idl_filepath: String,
  694. ) -> Result<()> {
  695. let buffer = idl_write_buffer(cfg_override, program_id, idl_filepath)?;
  696. idl_set_buffer(cfg_override, program_id, buffer)
  697. }
  698. fn idl_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
  699. with_workspace(cfg_override, |cfg, _path, _cargo| {
  700. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  701. let idl_address = {
  702. let account = client
  703. .get_account_with_commitment(&program_id, CommitmentConfig::processed())?
  704. .value
  705. .map_or(Err(anyhow!("Account not found")), Ok)?;
  706. if account.executable {
  707. IdlAccount::address(&program_id)
  708. } else {
  709. program_id
  710. }
  711. };
  712. let account = client.get_account(&idl_address)?;
  713. let mut data: &[u8] = &account.data;
  714. let idl_account: IdlAccount = AccountDeserialize::try_deserialize(&mut data)?;
  715. println!("{:?}", idl_account.authority);
  716. Ok(())
  717. })
  718. }
  719. fn idl_set_authority(
  720. cfg_override: &ConfigOverride,
  721. program_id: Pubkey,
  722. address: Option<Pubkey>,
  723. new_authority: Pubkey,
  724. ) -> Result<()> {
  725. with_workspace(cfg_override, |cfg, _path, _cargo| {
  726. // Misc.
  727. let idl_address = match address {
  728. None => IdlAccount::address(&program_id),
  729. Some(addr) => addr,
  730. };
  731. let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
  732. .map_err(|_| anyhow!("Unable to read keypair file"))?;
  733. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  734. // Instruction data.
  735. let data =
  736. serialize_idl_ix(anchor_lang::idl::IdlInstruction::SetAuthority { new_authority })?;
  737. // Instruction accounts.
  738. let accounts = vec![
  739. AccountMeta::new(idl_address, false),
  740. AccountMeta::new_readonly(keypair.pubkey(), true),
  741. ];
  742. // Instruction.
  743. let ix = Instruction {
  744. program_id,
  745. accounts,
  746. data,
  747. };
  748. // Send transaction.
  749. let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
  750. let tx = Transaction::new_signed_with_payer(
  751. &[ix],
  752. Some(&keypair.pubkey()),
  753. &[&keypair],
  754. recent_hash,
  755. );
  756. client.send_and_confirm_transaction_with_spinner_and_config(
  757. &tx,
  758. CommitmentConfig::confirmed(),
  759. RpcSendTransactionConfig {
  760. skip_preflight: true,
  761. ..RpcSendTransactionConfig::default()
  762. },
  763. )?;
  764. println!("Authority update complete.");
  765. Ok(())
  766. })
  767. }
  768. fn idl_erase_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
  769. println!("Are you sure you want to erase the IDL authority: [y/n]");
  770. let stdin = std::io::stdin();
  771. let mut stdin_lines = stdin.lock().lines();
  772. let input = stdin_lines.next().unwrap().unwrap();
  773. if input != "y" {
  774. println!("Not erasing.");
  775. return Ok(());
  776. }
  777. // Program will treat the zero authority as erased.
  778. let new_authority = Pubkey::new_from_array([0u8; 32]);
  779. idl_set_authority(cfg_override, program_id, None, new_authority)?;
  780. Ok(())
  781. }
  782. // Write the idl to the account buffer, chopping up the IDL into pieces
  783. // and sending multiple transactions in the event the IDL doesn't fit into
  784. // a single transaction.
  785. fn idl_write(cfg: &Config, program_id: &Pubkey, idl: &Idl, idl_address: Pubkey) -> Result<()> {
  786. // Remove the metadata before deploy.
  787. let mut idl = idl.clone();
  788. idl.metadata = None;
  789. // Misc.
  790. let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
  791. .map_err(|_| anyhow!("Unable to read keypair file"))?;
  792. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  793. // Serialize and compress the idl.
  794. let idl_data = {
  795. let json_bytes = serde_json::to_vec(&idl)?;
  796. let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
  797. e.write_all(&json_bytes)?;
  798. e.finish()?
  799. };
  800. const MAX_WRITE_SIZE: usize = 1000;
  801. let mut offset = 0;
  802. while offset < idl_data.len() {
  803. // Instruction data.
  804. let data = {
  805. let start = offset;
  806. let end = std::cmp::min(offset + MAX_WRITE_SIZE, idl_data.len());
  807. serialize_idl_ix(anchor_lang::idl::IdlInstruction::Write {
  808. data: idl_data[start..end].to_vec(),
  809. })?
  810. };
  811. // Instruction accounts.
  812. let accounts = vec![
  813. AccountMeta::new(idl_address, false),
  814. AccountMeta::new_readonly(keypair.pubkey(), true),
  815. ];
  816. // Instruction.
  817. let ix = Instruction {
  818. program_id: *program_id,
  819. accounts,
  820. data,
  821. };
  822. // Send transaction.
  823. let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
  824. let tx = Transaction::new_signed_with_payer(
  825. &[ix],
  826. Some(&keypair.pubkey()),
  827. &[&keypair],
  828. recent_hash,
  829. );
  830. client.send_and_confirm_transaction_with_spinner_and_config(
  831. &tx,
  832. CommitmentConfig::confirmed(),
  833. RpcSendTransactionConfig {
  834. skip_preflight: true,
  835. ..RpcSendTransactionConfig::default()
  836. },
  837. )?;
  838. offset += MAX_WRITE_SIZE;
  839. }
  840. Ok(())
  841. }
  842. fn idl_parse(file: String, out: Option<String>) -> Result<()> {
  843. let idl = extract_idl(&file)?;
  844. let out = match out {
  845. None => OutFile::Stdout,
  846. Some(out) => OutFile::File(PathBuf::from(out)),
  847. };
  848. write_idl(&idl, out)
  849. }
  850. fn idl_fetch(cfg_override: &ConfigOverride, address: Pubkey, out: Option<String>) -> Result<()> {
  851. let idl = fetch_idl(cfg_override, address)?;
  852. let out = match out {
  853. None => OutFile::Stdout,
  854. Some(out) => OutFile::File(PathBuf::from(out)),
  855. };
  856. write_idl(&idl, out)
  857. }
  858. fn write_idl(idl: &Idl, out: OutFile) -> Result<()> {
  859. let idl_json = serde_json::to_string_pretty(idl)?;
  860. match out {
  861. OutFile::Stdout => println!("{}", idl_json),
  862. OutFile::File(out) => std::fs::write(out, idl_json)?,
  863. };
  864. Ok(())
  865. }
  866. enum OutFile {
  867. Stdout,
  868. File(PathBuf),
  869. }
  870. // Builds, deploys, and tests all workspace programs in a single command.
  871. fn test(
  872. cfg_override: &ConfigOverride,
  873. skip_deploy: bool,
  874. skip_local_validator: bool,
  875. skip_build: bool,
  876. file: Option<String>,
  877. ) -> Result<()> {
  878. with_workspace(cfg_override, |cfg, _path, _cargo| {
  879. // Build if needed.
  880. if !skip_build {
  881. build(cfg_override, None, false, None)?;
  882. }
  883. // Run the deploy against the cluster in two cases:
  884. //
  885. // 1. The cluster is not localnet.
  886. // 2. The cluster is localnet, but we're not booting a local validator.
  887. //
  888. // In either case, skip the deploy if the user specifies.
  889. let is_localnet = cfg.provider.cluster == Cluster::Localnet;
  890. if (!is_localnet || skip_local_validator) && !skip_deploy {
  891. deploy(cfg_override, None)?;
  892. }
  893. // Start local test validator, if needed.
  894. let mut validator_handle = None;
  895. if is_localnet && (!skip_local_validator) {
  896. let flags = match skip_deploy {
  897. true => None,
  898. false => Some(genesis_flags(cfg)?),
  899. };
  900. validator_handle = Some(start_test_validator(cfg, flags)?);
  901. }
  902. // Setup log reader.
  903. let log_streams = stream_logs(cfg.provider.cluster.url());
  904. // Run the tests.
  905. let test_result: Result<_> = {
  906. let ts_config_exist = Path::new("tsconfig.json").exists();
  907. let cmd = if ts_config_exist { "ts-mocha" } else { "mocha" };
  908. let mut args = if ts_config_exist {
  909. vec![cmd, "-p", "./tsconfig.json"]
  910. } else {
  911. vec![cmd]
  912. };
  913. args.extend_from_slice(&[
  914. "-t",
  915. "1000000",
  916. if let Some(ref file) = file {
  917. file
  918. } else if ts_config_exist {
  919. "tests/**/*.ts"
  920. } else {
  921. "tests/"
  922. },
  923. ]);
  924. std::process::Command::new("npx")
  925. .args(args)
  926. .env("ANCHOR_PROVIDER_URL", cfg.provider.cluster.url())
  927. .stdout(Stdio::inherit())
  928. .stderr(Stdio::inherit())
  929. .output()
  930. .map_err(anyhow::Error::from)
  931. .context(cmd)
  932. };
  933. // Check all errors and shut down.
  934. if let Some(mut child) = validator_handle {
  935. if let Err(err) = child.kill() {
  936. println!("Failed to kill subprocess {}: {}", child.id(), err);
  937. }
  938. }
  939. for mut child in log_streams? {
  940. if let Err(err) = child.kill() {
  941. println!("Failed to kill subprocess {}: {}", child.id(), err);
  942. }
  943. }
  944. match test_result {
  945. Ok(exit) => {
  946. if !exit.status.success() {
  947. std::process::exit(exit.status.code().unwrap());
  948. }
  949. }
  950. Err(err) => {
  951. println!("Failed to run test: {:#}", err)
  952. }
  953. }
  954. Ok(())
  955. })
  956. }
  957. // Returns the solana-test-validator flags to embed the workspace programs
  958. // in the genesis block. This allows us to run tests without every deploying.
  959. fn genesis_flags(cfg: &Config) -> Result<Vec<String>> {
  960. let mut flags = Vec::new();
  961. for mut program in read_all_programs()? {
  962. let binary_path = program.binary_path().display().to_string();
  963. let kp = Keypair::generate(&mut OsRng);
  964. let address = kp.pubkey().to_string();
  965. flags.push("--bpf-program".to_string());
  966. flags.push(address.clone());
  967. flags.push(binary_path);
  968. // Add program address to the IDL.
  969. program.idl.metadata = Some(serde_json::to_value(IdlTestMetadata { address })?);
  970. // Persist it.
  971. let idl_out = PathBuf::from("target/idl")
  972. .join(&program.idl.name)
  973. .with_extension("json");
  974. write_idl(&program.idl, OutFile::File(idl_out))?;
  975. }
  976. if let Some(test) = cfg.test.as_ref() {
  977. for entry in &test.genesis {
  978. flags.push("--bpf-program".to_string());
  979. flags.push(entry.address.clone());
  980. flags.push(entry.program.clone());
  981. }
  982. }
  983. Ok(flags)
  984. }
  985. fn stream_logs(url: &str) -> Result<Vec<std::process::Child>> {
  986. let program_logs_dir = ".anchor/program-logs";
  987. if Path::new(program_logs_dir).exists() {
  988. std::fs::remove_dir_all(program_logs_dir)?;
  989. }
  990. fs::create_dir_all(program_logs_dir)?;
  991. let mut handles = vec![];
  992. for program in read_all_programs()? {
  993. let mut file = File::open(&format!("target/idl/{}.json", program.lib_name))?;
  994. let mut contents = vec![];
  995. file.read_to_end(&mut contents)?;
  996. let idl: Idl = serde_json::from_slice(&contents)?;
  997. let metadata = idl
  998. .metadata
  999. .ok_or_else(|| anyhow!("Program address not found."))?;
  1000. let metadata: IdlTestMetadata = serde_json::from_value(metadata)?;
  1001. let log_file = File::create(format!(
  1002. "{}/{}.{}.log",
  1003. program_logs_dir, metadata.address, program.idl.name
  1004. ))?;
  1005. let stdio = std::process::Stdio::from(log_file);
  1006. let child = std::process::Command::new("solana")
  1007. .arg("logs")
  1008. .arg(metadata.address)
  1009. .arg("--url")
  1010. .arg(url)
  1011. .stdout(stdio)
  1012. .spawn()?;
  1013. handles.push(child);
  1014. }
  1015. Ok(handles)
  1016. }
  1017. #[derive(Debug, Serialize, Deserialize)]
  1018. pub struct IdlTestMetadata {
  1019. address: String,
  1020. }
  1021. fn start_test_validator(cfg: &Config, flags: Option<Vec<String>>) -> Result<Child> {
  1022. fs::create_dir_all(".anchor")?;
  1023. let test_ledger_filename = ".anchor/test-ledger";
  1024. let test_ledger_log_filename = ".anchor/test-ledger-log.txt";
  1025. if Path::new(test_ledger_filename).exists() {
  1026. std::fs::remove_dir_all(test_ledger_filename)?;
  1027. }
  1028. if Path::new(test_ledger_log_filename).exists() {
  1029. std::fs::remove_file(test_ledger_log_filename)?;
  1030. }
  1031. // Start a validator for testing.
  1032. let test_validator_stdout = File::create(test_ledger_log_filename)?;
  1033. let test_validator_stderr = test_validator_stdout.try_clone()?;
  1034. let validator_handle = std::process::Command::new("solana-test-validator")
  1035. .arg("--ledger")
  1036. .arg(test_ledger_filename)
  1037. .arg("--mint")
  1038. .arg(cfg.wallet_kp()?.pubkey().to_string())
  1039. .args(flags.unwrap_or_default())
  1040. .stdout(Stdio::from(test_validator_stdout))
  1041. .stderr(Stdio::from(test_validator_stderr))
  1042. .spawn()
  1043. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  1044. // Wait for the validator to be ready.
  1045. let client = RpcClient::new("http://localhost:8899".to_string());
  1046. let mut count = 0;
  1047. let ms_wait = 5000;
  1048. while count < ms_wait {
  1049. let r = client.get_recent_blockhash();
  1050. if r.is_ok() {
  1051. break;
  1052. }
  1053. std::thread::sleep(std::time::Duration::from_millis(1));
  1054. count += 1;
  1055. }
  1056. if count == 5000 {
  1057. println!("Unable to start test validator.");
  1058. std::process::exit(1);
  1059. }
  1060. Ok(validator_handle)
  1061. }
  1062. fn deploy(cfg_override: &ConfigOverride, program_name: Option<String>) -> Result<()> {
  1063. _deploy(cfg_override, program_name).map(|_| ())
  1064. }
  1065. fn _deploy(
  1066. cfg_override: &ConfigOverride,
  1067. program_str: Option<String>,
  1068. ) -> Result<Vec<(Pubkey, Program)>> {
  1069. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1070. let url = cfg.provider.cluster.url().to_string();
  1071. let keypair = cfg.provider.wallet.to_string();
  1072. // Deploy the programs.
  1073. println!("Deploying workspace: {}", url);
  1074. println!("Upgrade authority: {}", keypair);
  1075. let mut programs = Vec::new();
  1076. for mut program in read_all_programs()? {
  1077. if let Some(single_prog_str) = &program_str {
  1078. let program_name = program.path.file_name().unwrap().to_str().unwrap();
  1079. if single_prog_str.as_str() != program_name {
  1080. continue;
  1081. }
  1082. }
  1083. let binary_path = program.binary_path().display().to_string();
  1084. println!(
  1085. "Deploying program {:?}...",
  1086. program.path.file_name().unwrap().to_str().unwrap()
  1087. );
  1088. println!("Program path: {}...", binary_path);
  1089. // Write the program's keypair filepath. This forces a new deploy
  1090. // address.
  1091. let program_kp = Keypair::generate(&mut OsRng);
  1092. let mut file = File::create(program.anchor_keypair_path())?;
  1093. file.write_all(format!("{:?}", &program_kp.to_bytes()).as_bytes())?;
  1094. // Send deploy transactions.
  1095. let exit = std::process::Command::new("solana")
  1096. .arg("program")
  1097. .arg("deploy")
  1098. .arg("--url")
  1099. .arg(&url)
  1100. .arg("--keypair")
  1101. .arg(&keypair)
  1102. .arg("--program-id")
  1103. .arg(program.anchor_keypair_path().display().to_string())
  1104. .arg(&binary_path)
  1105. .stdout(Stdio::inherit())
  1106. .stderr(Stdio::inherit())
  1107. .output()
  1108. .expect("Must deploy");
  1109. if !exit.status.success() {
  1110. println!("There was a problem deploying: {:?}.", exit);
  1111. std::process::exit(exit.status.code().unwrap_or(1));
  1112. }
  1113. // Add program address to the IDL.
  1114. program.idl.metadata = Some(serde_json::to_value(IdlTestMetadata {
  1115. address: program_kp.pubkey().to_string(),
  1116. })?);
  1117. // Persist it.
  1118. let idl_out = PathBuf::from("target/idl")
  1119. .join(&program.idl.name)
  1120. .with_extension("json");
  1121. write_idl(&program.idl, OutFile::File(idl_out))?;
  1122. programs.push((program_kp.pubkey(), program))
  1123. }
  1124. println!("Deploy success");
  1125. Ok(programs)
  1126. })
  1127. }
  1128. fn upgrade(
  1129. cfg_override: &ConfigOverride,
  1130. program_id: Pubkey,
  1131. program_filepath: String,
  1132. ) -> Result<()> {
  1133. let path: PathBuf = program_filepath.parse().unwrap();
  1134. let program_filepath = path.canonicalize()?.display().to_string();
  1135. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1136. let exit = std::process::Command::new("solana")
  1137. .arg("program")
  1138. .arg("deploy")
  1139. .arg("--url")
  1140. .arg(cfg.provider.cluster.url())
  1141. .arg("--keypair")
  1142. .arg(&cfg.provider.wallet.to_string())
  1143. .arg("--program-id")
  1144. .arg(program_id.to_string())
  1145. .arg(&program_filepath)
  1146. .stdout(Stdio::inherit())
  1147. .stderr(Stdio::inherit())
  1148. .output()
  1149. .expect("Must deploy");
  1150. if !exit.status.success() {
  1151. println!("There was a problem deploying: {:?}.", exit);
  1152. std::process::exit(exit.status.code().unwrap_or(1));
  1153. }
  1154. Ok(())
  1155. })
  1156. }
  1157. fn launch(
  1158. cfg_override: &ConfigOverride,
  1159. verifiable: bool,
  1160. program_name: Option<String>,
  1161. ) -> Result<()> {
  1162. // Build and deploy.
  1163. build(cfg_override, None, verifiable, program_name.clone())?;
  1164. let programs = _deploy(cfg_override, program_name)?;
  1165. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1166. let keypair = cfg.provider.wallet.to_string();
  1167. // Add metadata to all IDLs.
  1168. for (address, program) in programs {
  1169. // Store the IDL on chain.
  1170. let idl_address = create_idl_account(cfg, &keypair, &address, &program.idl)?;
  1171. println!("IDL account created: {}", idl_address.to_string());
  1172. }
  1173. // Run migration script.
  1174. if Path::new("migrations/deploy.js").exists() || Path::new("migrations/deploy.ts").exists()
  1175. {
  1176. migrate(cfg_override)?;
  1177. }
  1178. Ok(())
  1179. })
  1180. }
  1181. // The Solana CLI doesn't redeploy a program if this file exists.
  1182. // So remove it to make all commands explicit.
  1183. fn clear_program_keys() -> Result<()> {
  1184. for program in read_all_programs()? {
  1185. let anchor_keypair_path = program.anchor_keypair_path();
  1186. if Path::exists(&anchor_keypair_path) {
  1187. std::fs::remove_file(anchor_keypair_path).expect("Always remove");
  1188. }
  1189. }
  1190. Ok(())
  1191. }
  1192. fn create_idl_account(
  1193. cfg: &Config,
  1194. keypair_path: &str,
  1195. program_id: &Pubkey,
  1196. idl: &Idl,
  1197. ) -> Result<Pubkey> {
  1198. // Misc.
  1199. let idl_address = IdlAccount::address(program_id);
  1200. let keypair = solana_sdk::signature::read_keypair_file(keypair_path)
  1201. .map_err(|_| anyhow!("Unable to read keypair file"))?;
  1202. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  1203. let idl_data = serialize_idl(idl)?;
  1204. // Run `Create instruction.
  1205. {
  1206. let data = serialize_idl_ix(anchor_lang::idl::IdlInstruction::Create {
  1207. data_len: (idl_data.len() as u64) * 2, // Double for future growth.
  1208. })?;
  1209. let program_signer = Pubkey::find_program_address(&[], program_id).0;
  1210. let accounts = vec![
  1211. AccountMeta::new_readonly(keypair.pubkey(), true),
  1212. AccountMeta::new(idl_address, false),
  1213. AccountMeta::new_readonly(program_signer, false),
  1214. AccountMeta::new_readonly(solana_program::system_program::ID, false),
  1215. AccountMeta::new_readonly(*program_id, false),
  1216. AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
  1217. ];
  1218. let ix = Instruction {
  1219. program_id: *program_id,
  1220. accounts,
  1221. data,
  1222. };
  1223. let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
  1224. let tx = Transaction::new_signed_with_payer(
  1225. &[ix],
  1226. Some(&keypair.pubkey()),
  1227. &[&keypair],
  1228. recent_hash,
  1229. );
  1230. client.send_and_confirm_transaction_with_spinner_and_config(
  1231. &tx,
  1232. CommitmentConfig::confirmed(),
  1233. RpcSendTransactionConfig {
  1234. skip_preflight: true,
  1235. ..RpcSendTransactionConfig::default()
  1236. },
  1237. )?;
  1238. }
  1239. // Write directly to the IDL account buffer.
  1240. idl_write(cfg, program_id, idl, IdlAccount::address(program_id))?;
  1241. Ok(idl_address)
  1242. }
  1243. fn create_idl_buffer(
  1244. cfg: &Config,
  1245. keypair_path: &str,
  1246. program_id: &Pubkey,
  1247. idl: &Idl,
  1248. ) -> Result<Pubkey> {
  1249. let keypair = solana_sdk::signature::read_keypair_file(keypair_path)
  1250. .map_err(|_| anyhow!("Unable to read keypair file"))?;
  1251. let client = RpcClient::new(cfg.provider.cluster.url().to_string());
  1252. let buffer = Keypair::generate(&mut OsRng);
  1253. // Creates the new buffer account with the system program.
  1254. let create_account_ix = {
  1255. let space = 8 + 32 + 4 + serialize_idl(idl)?.len() as usize;
  1256. let lamports = client.get_minimum_balance_for_rent_exemption(space)?;
  1257. solana_sdk::system_instruction::create_account(
  1258. &keypair.pubkey(),
  1259. &buffer.pubkey(),
  1260. lamports,
  1261. space as u64,
  1262. program_id,
  1263. )
  1264. };
  1265. // Program instruction to create the buffer.
  1266. let create_buffer_ix = {
  1267. let accounts = vec![
  1268. AccountMeta::new(buffer.pubkey(), false),
  1269. AccountMeta::new_readonly(keypair.pubkey(), true),
  1270. AccountMeta::new_readonly(sysvar::rent::ID, false),
  1271. ];
  1272. let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec();
  1273. data.append(&mut IdlInstruction::CreateBuffer.try_to_vec()?);
  1274. Instruction {
  1275. program_id: *program_id,
  1276. accounts,
  1277. data,
  1278. }
  1279. };
  1280. // Build the transaction.
  1281. let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
  1282. let tx = Transaction::new_signed_with_payer(
  1283. &[create_account_ix, create_buffer_ix],
  1284. Some(&keypair.pubkey()),
  1285. &[&keypair, &buffer],
  1286. recent_hash,
  1287. );
  1288. // Send the transaction.
  1289. client.send_and_confirm_transaction_with_spinner_and_config(
  1290. &tx,
  1291. CommitmentConfig::confirmed(),
  1292. RpcSendTransactionConfig {
  1293. skip_preflight: true,
  1294. ..RpcSendTransactionConfig::default()
  1295. },
  1296. )?;
  1297. Ok(buffer.pubkey())
  1298. }
  1299. // Serialize and compress the idl.
  1300. fn serialize_idl(idl: &Idl) -> Result<Vec<u8>> {
  1301. let json_bytes = serde_json::to_vec(idl)?;
  1302. let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
  1303. e.write_all(&json_bytes)?;
  1304. e.finish().map_err(Into::into)
  1305. }
  1306. fn serialize_idl_ix(ix_inner: anchor_lang::idl::IdlInstruction) -> Result<Vec<u8>> {
  1307. let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec();
  1308. data.append(&mut ix_inner.try_to_vec()?);
  1309. Ok(data)
  1310. }
  1311. fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
  1312. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1313. println!("Running migration deploy script");
  1314. let url = cfg.provider.cluster.url().to_string();
  1315. let cur_dir = std::env::current_dir()?;
  1316. let use_ts =
  1317. Path::new("tsconfig.json").exists() && Path::new("migrations/deploy.ts").exists();
  1318. if !Path::new(".anchor").exists() {
  1319. fs::create_dir(".anchor")?;
  1320. }
  1321. std::env::set_current_dir(".anchor")?;
  1322. let exit = if use_ts {
  1323. let module_path = cur_dir.join("migrations/deploy.ts");
  1324. let deploy_script_host_str =
  1325. template::deploy_ts_script_host(&url, &module_path.display().to_string());
  1326. std::fs::write("deploy.ts", deploy_script_host_str)?;
  1327. std::process::Command::new("ts-node")
  1328. .arg("deploy.ts")
  1329. .stdout(Stdio::inherit())
  1330. .stderr(Stdio::inherit())
  1331. .output()?
  1332. } else {
  1333. let module_path = cur_dir.join("migrations/deploy.js");
  1334. let deploy_script_host_str =
  1335. template::deploy_js_script_host(&url, &module_path.display().to_string());
  1336. std::fs::write("deploy.js", deploy_script_host_str)?;
  1337. std::process::Command::new("node")
  1338. .arg("deploy.js")
  1339. .stdout(Stdio::inherit())
  1340. .stderr(Stdio::inherit())
  1341. .output()?
  1342. };
  1343. if !exit.status.success() {
  1344. println!("Deploy failed.");
  1345. std::process::exit(exit.status.code().unwrap());
  1346. }
  1347. println!("Deploy complete.");
  1348. Ok(())
  1349. })
  1350. }
  1351. fn set_workspace_dir_or_exit() {
  1352. let d = match Config::discover(&ConfigOverride {
  1353. cluster: None,
  1354. wallet: None,
  1355. }) {
  1356. Err(_) => {
  1357. println!("Not in anchor workspace.");
  1358. std::process::exit(1);
  1359. }
  1360. Ok(d) => d,
  1361. };
  1362. match d {
  1363. None => {
  1364. println!("Not in anchor workspace.");
  1365. std::process::exit(1);
  1366. }
  1367. Some((_cfg, cfg_path, _inside_cargo)) => {
  1368. match cfg_path.parent() {
  1369. None => {
  1370. println!("Unable to make new program");
  1371. }
  1372. Some(parent) => {
  1373. if std::env::set_current_dir(&parent).is_err() {
  1374. println!("Not in anchor workspace.");
  1375. std::process::exit(1);
  1376. }
  1377. }
  1378. };
  1379. }
  1380. }
  1381. }
  1382. #[cfg(feature = "dev")]
  1383. fn airdrop(cfg_override: &ConfigOverride) -> Result<()> {
  1384. let url = cfg_override
  1385. .cluster
  1386. .unwrap_or_else(|| "https://api.devnet.solana.com".to_string());
  1387. loop {
  1388. let exit = std::process::Command::new("solana")
  1389. .arg("airdrop")
  1390. .arg("10")
  1391. .arg("--url")
  1392. .arg(&url)
  1393. .stdout(Stdio::inherit())
  1394. .stderr(Stdio::inherit())
  1395. .output()
  1396. .expect("Must airdrop");
  1397. if !exit.status.success() {
  1398. println!("There was a problem airdropping: {:?}.", exit);
  1399. std::process::exit(exit.status.code().unwrap_or(1));
  1400. }
  1401. std::thread::sleep(std::time::Duration::from_millis(10000));
  1402. }
  1403. }
  1404. fn cluster(_cmd: ClusterCommand) -> Result<()> {
  1405. println!("Cluster Endpoints:\n");
  1406. println!("* Mainnet - https://solana-api.projectserum.com");
  1407. println!("* Mainnet - https://api.mainnet-beta.solana.com");
  1408. println!("* Devnet - https://api.devnet.solana.com");
  1409. println!("* Testnet - https://api.testnet.solana.com");
  1410. Ok(())
  1411. }
  1412. fn shell(cfg_override: &ConfigOverride) -> Result<()> {
  1413. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1414. let programs = {
  1415. let mut idls: HashMap<String, Idl> = read_all_programs()?
  1416. .iter()
  1417. .map(|program| (program.idl.name.clone(), program.idl.clone()))
  1418. .collect();
  1419. // Insert all manually specified idls into the idl map.
  1420. if let Some(programs) = cfg.clusters.get(&cfg.provider.cluster) {
  1421. let _ = programs
  1422. .iter()
  1423. .map(|(name, pd)| {
  1424. if let Some(idl_fp) = &pd.idl {
  1425. let file_str =
  1426. std::fs::read_to_string(idl_fp).expect("Unable to read IDL file");
  1427. let idl = serde_json::from_str(&file_str).expect("Idl not readable");
  1428. idls.insert(name.clone(), idl);
  1429. }
  1430. })
  1431. .collect::<Vec<_>>();
  1432. }
  1433. match cfg.clusters.get(&cfg.provider.cluster) {
  1434. None => Vec::new(),
  1435. Some(programs) => programs
  1436. .iter()
  1437. .map(|(name, program_deployment)| ProgramWorkspace {
  1438. name: name.to_string(),
  1439. program_id: program_deployment.address,
  1440. idl: match idls.get(name) {
  1441. None => {
  1442. println!("Unable to find IDL for {}", name);
  1443. std::process::exit(1);
  1444. }
  1445. Some(idl) => idl.clone(),
  1446. },
  1447. })
  1448. .collect::<Vec<ProgramWorkspace>>(),
  1449. }
  1450. };
  1451. let js_code = template::node_shell(
  1452. cfg.provider.cluster.url(),
  1453. &cfg.provider.wallet.to_string(),
  1454. programs,
  1455. )?;
  1456. let mut child = std::process::Command::new("node")
  1457. .args(&["-e", &js_code, "-i", "--experimental-repl-await"])
  1458. .stdout(Stdio::inherit())
  1459. .stderr(Stdio::inherit())
  1460. .spawn()
  1461. .map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
  1462. if !child.wait()?.success() {
  1463. println!("Error running node shell");
  1464. return Ok(());
  1465. }
  1466. Ok(())
  1467. })
  1468. }
  1469. fn run(cfg_override: &ConfigOverride, script: String) -> Result<()> {
  1470. with_workspace(cfg_override, |cfg, _path, _cargo| {
  1471. let script = cfg
  1472. .scripts
  1473. .get(&script)
  1474. .ok_or_else(|| anyhow!("Unable to find script"))?;
  1475. let exit = std::process::Command::new("bash")
  1476. .arg("-c")
  1477. .arg(&script)
  1478. .stdout(Stdio::inherit())
  1479. .stderr(Stdio::inherit())
  1480. .output()
  1481. .unwrap();
  1482. if !exit.status.success() {
  1483. std::process::exit(exit.status.code().unwrap_or(1));
  1484. }
  1485. Ok(())
  1486. })
  1487. }
  1488. // with_workspace ensures the current working directory is always the top level
  1489. // workspace directory, i.e., where the `Anchor.toml` file is located, before
  1490. // and after the closure invocation.
  1491. //
  1492. // The closure passed into this function must never change the working directory
  1493. // to be outside the workspace. Doing so will have undefined behavior.
  1494. fn with_workspace<R>(
  1495. cfg_override: &ConfigOverride,
  1496. f: impl FnOnce(&Config, PathBuf, Option<PathBuf>) -> R,
  1497. ) -> R {
  1498. set_workspace_dir_or_exit();
  1499. clear_program_keys().unwrap();
  1500. let (cfg, cfg_path, cargo_toml) = Config::discover(cfg_override)
  1501. .expect("Previously set the workspace dir")
  1502. .expect("Anchor.toml must always exist");
  1503. let r = f(&cfg, cfg_path, cargo_toml);
  1504. set_workspace_dir_or_exit();
  1505. clear_program_keys().unwrap();
  1506. r
  1507. }