main.rs 48 KB

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