Browse Source

cli, lang: Upgradeable IDL commands

Armani Ferrante 4 years ago
parent
commit
a314ffd1cf
2 changed files with 114 additions and 7 deletions
  1. 113 6
      cli/src/main.rs
  2. 1 1
      lang/src/idl.rs

+ 113 - 6
cli/src/main.rs

@@ -1,6 +1,6 @@
 use crate::config::{read_all_programs, Config, Program};
 use anchor_lang::idl::IdlAccount;
-use anchor_lang::{AnchorDeserialize, AnchorSerialize};
+use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
 use anchor_syn::idl::Idl;
 use anyhow::{anyhow, Result};
 use clap::Clap;
@@ -99,12 +99,33 @@ pub enum IdlCommand {
         #[clap(short, long)]
         filepath: String,
     },
-    /// Updates the IDL to the new file.
-    Update {
+    /// Upgrades the IDL to the new file.
+    Upgrade {
         program_id: Pubkey,
         #[clap(short, long)]
         filepath: String,
     },
+    /// Sets a new authority on the IDL account.
+    SetAuthority {
+        /// Program to change the IDL authority.
+        #[clap(short, long)]
+        program_id: Pubkey,
+        /// New authority of the IDL account.
+        #[clap(short, long)]
+        new_authority: Pubkey,
+    },
+    /// Command to remove the ability to modify the IDL account. This should
+    /// likely be used in conjection with eliminating an "upgrade authority" on
+    /// the program.
+    EraseAuthority {
+        #[clap(short, long)]
+        program_id: Pubkey,
+    },
+    /// Outputs the authority for the IDL account.
+    Authority {
+        /// The program to view.
+        program_id: Pubkey,
+    },
     /// Parses an IDL from source.
     Parse {
         /// Path to the program's interface definition.
@@ -310,10 +331,16 @@ fn idl(subcmd: IdlCommand) -> Result<()> {
             program_id,
             filepath,
         } => idl_init(program_id, filepath),
-        IdlCommand::Update {
+        IdlCommand::Upgrade {
             program_id,
             filepath,
-        } => idl_update(program_id, filepath),
+        } => idl_upgrade(program_id, filepath),
+        IdlCommand::SetAuthority {
+            program_id,
+            new_authority,
+        } => idl_set_authority(program_id, new_authority),
+        IdlCommand::EraseAuthority { program_id } => idl_erase_authority(program_id),
+        IdlCommand::Authority { program_id } => idl_authority(program_id),
         IdlCommand::Parse { file, out } => idl_parse(file, out),
         IdlCommand::Fetch { program_id, out } => idl_fetch(program_id, out),
     }
@@ -333,7 +360,7 @@ fn idl_init(program_id: Pubkey, idl_filepath: String) -> Result<()> {
     })
 }
 
-fn idl_update(program_id: Pubkey, idl_filepath: String) -> Result<()> {
+fn idl_upgrade(program_id: Pubkey, idl_filepath: String) -> Result<()> {
     with_workspace(|cfg, _path, _cargo| {
         let bytes = std::fs::read(idl_filepath)?;
         let idl: Idl = serde_json::from_reader(&*bytes)?;
@@ -345,6 +372,86 @@ fn idl_update(program_id: Pubkey, idl_filepath: String) -> Result<()> {
     })
 }
 
+fn idl_authority(program_id: Pubkey) -> Result<()> {
+    with_workspace(|cfg, _path, _cargo| {
+        let client = RpcClient::new(cfg.cluster.url().to_string());
+        let idl_address = IdlAccount::address(&program_id);
+
+        let account = client.get_account(&idl_address)?;
+        let mut data: &[u8] = &account.data;
+        let idl_account: IdlAccount = AccountDeserialize::try_deserialize(&mut data)?;
+
+        println!("{:?}", idl_account.authority);
+
+        Ok(())
+    })
+}
+
+fn idl_set_authority(program_id: Pubkey, new_authority: Pubkey) -> Result<()> {
+    with_workspace(|cfg, _path, _cargo| {
+        // Misc.
+        let idl_address = IdlAccount::address(&program_id);
+        let keypair = solana_sdk::signature::read_keypair_file(&cfg.wallet.to_string())
+            .map_err(|_| anyhow!("Unable to read keypair file"))?;
+        let client = RpcClient::new(cfg.cluster.url().to_string());
+
+        // Instruction data.
+        let data =
+            serialize_idl_ix(anchor_lang::idl::IdlInstruction::SetAuthority { new_authority })?;
+
+        // Instruction accounts.
+        let accounts = vec![
+            AccountMeta::new(idl_address, false),
+            AccountMeta::new_readonly(keypair.pubkey(), true),
+        ];
+
+        // Instruction.
+        let ix = Instruction {
+            program_id,
+            accounts,
+            data,
+        };
+        // Send transaction.
+        let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
+        let tx = Transaction::new_signed_with_payer(
+            &[ix],
+            Some(&keypair.pubkey()),
+            &[&keypair],
+            recent_hash,
+        );
+        client.send_and_confirm_transaction_with_spinner_and_config(
+            &tx,
+            CommitmentConfig::single(),
+            RpcSendTransactionConfig {
+                skip_preflight: true,
+                ..RpcSendTransactionConfig::default()
+            },
+        )?;
+
+        println!("Authority update complete.");
+
+        Ok(())
+    })
+}
+
+fn idl_erase_authority(program_id: Pubkey) -> Result<()> {
+    println!("Are you sure you want to erase the IDL authority: [y/n]");
+
+    let stdin = std::io::stdin();
+    let mut stdin_lines = stdin.lock().lines();
+    let input = stdin_lines.next().unwrap().unwrap();
+    if input != "y" {
+        println!("Not erasing.");
+        return Ok(());
+    }
+
+    // Program will treat the zero authority as erased.
+    let new_authority = Pubkey::new_from_array([0u8; 32]);
+    idl_set_authority(program_id, new_authority)?;
+
+    Ok(())
+}
+
 // Clears out *all* IDL data. The authority for the IDL must be the configured
 // wallet.
 fn idl_clear(cfg: &Config, program_id: &Pubkey) -> Result<()> {

+ 1 - 1
lang/src/idl.rs

@@ -43,7 +43,7 @@ pub type IdlCreateAccounts<'info> = crate::ctor::Ctor<'info>;
 pub struct IdlAccounts<'info> {
     #[account(mut, has_one = authority)]
     pub idl: ProgramAccount<'info, IdlAccount>,
-    #[account(signer)]
+    #[account(signer, "authority.key != &Pubkey::new_from_array([0u8; 32])")]
     pub authority: AccountInfo<'info>,
 }