miraland_cli/
program.rs

1use {
2    crate::{
3        checks::*,
4        cli::{
5            log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
6            ProcessResult,
7        },
8    },
9    bip39::{Language, Mnemonic, MnemonicType, Seed},
10    clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
11    log::*,
12    miraland_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
13    miraland_clap_utils::{
14        self,
15        fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
16        hidden_unless_forced,
17        input_parsers::*,
18        input_validators::*,
19        keypair::*,
20        offline::{OfflineArgs, DUMP_TRANSACTION_MESSAGE, SIGN_ONLY_ARG},
21    },
22    miraland_cli_output::{
23        return_signers_with_config, CliProgram, CliProgramAccountType, CliProgramAuthority,
24        CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableBuffers,
25        CliUpgradeableProgram, CliUpgradeableProgramClosed, CliUpgradeableProgramExtended,
26        CliUpgradeablePrograms, ReturnSignersConfig,
27    },
28    miraland_client::{
29        connection_cache::ConnectionCache,
30        send_and_confirm_transactions_in_parallel::{
31            send_and_confirm_transactions_in_parallel_blocking, SendAndConfirmConfig,
32        },
33        tpu_client::{TpuClient, TpuClientConfig},
34    },
35    miraland_remote_wallet::remote_wallet::RemoteWalletManager,
36    miraland_rpc_client::rpc_client::RpcClient,
37    miraland_rpc_client_api::{
38        client_error::ErrorKind as ClientErrorKind,
39        config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig},
40        filter::{Memcmp, RpcFilterType},
41    },
42    miraland_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
43    miraland_bpf_loader_program::syscalls::create_program_runtime_environment_v1,
44    miraland_program_runtime::{compute_budget::ComputeBudget, invoke_context::InvokeContext},
45    solana_rbpf::{elf::Executable, verifier::RequisiteVerifier},
46    miraland_sdk::{
47        account::{is_executable, Account},
48        account_utils::StateMut,
49        bpf_loader, bpf_loader_deprecated,
50        bpf_loader_upgradeable::{self, UpgradeableLoaderState},
51        feature_set::FeatureSet,
52        instruction::{Instruction, InstructionError},
53        loader_instruction,
54        message::Message,
55        native_token::Mln,
56        packet::PACKET_DATA_SIZE,
57        pubkey::Pubkey,
58        signature::{keypair_from_seed, read_keypair_file, Keypair, Signature, Signer},
59        system_instruction::{self, SystemError},
60        system_program,
61        transaction::{Transaction, TransactionError},
62    },
63    std::{
64        fs::File,
65        io::{Read, Write},
66        mem::size_of,
67        path::PathBuf,
68        rc::Rc,
69        str::FromStr,
70        sync::Arc,
71    },
72};
73
74pub const CLOSE_PROGRAM_WARNING: &str = "WARNING! Closed programs cannot be recreated at the same \
75                                         program id. Once a program is closed, it can never be \
76                                         invoked again. To proceed with closing, rerun the \
77                                         `close` command with the `--bypass-warning` flag";
78
79#[derive(Debug, PartialEq, Eq)]
80pub enum ProgramCliCommand {
81    Deploy {
82        program_location: Option<String>,
83        fee_payer_signer_index: SignerIndex,
84        program_signer_index: Option<SignerIndex>,
85        program_pubkey: Option<Pubkey>,
86        buffer_signer_index: Option<SignerIndex>,
87        buffer_pubkey: Option<Pubkey>,
88        upgrade_authority_signer_index: SignerIndex,
89        is_final: bool,
90        max_len: Option<usize>,
91        allow_excessive_balance: bool,
92        skip_fee_check: bool,
93    },
94    Upgrade {
95        fee_payer_signer_index: SignerIndex,
96        program_pubkey: Pubkey,
97        buffer_pubkey: Pubkey,
98        upgrade_authority_signer_index: SignerIndex,
99        sign_only: bool,
100        dump_transaction_message: bool,
101        blockhash_query: BlockhashQuery,
102    },
103    WriteBuffer {
104        program_location: String,
105        fee_payer_signer_index: SignerIndex,
106        buffer_signer_index: Option<SignerIndex>,
107        buffer_pubkey: Option<Pubkey>,
108        buffer_authority_signer_index: SignerIndex,
109        max_len: Option<usize>,
110        skip_fee_check: bool,
111    },
112    SetBufferAuthority {
113        buffer_pubkey: Pubkey,
114        buffer_authority_index: Option<SignerIndex>,
115        new_buffer_authority: Pubkey,
116    },
117    SetUpgradeAuthority {
118        program_pubkey: Pubkey,
119        upgrade_authority_index: Option<SignerIndex>,
120        new_upgrade_authority: Option<Pubkey>,
121        sign_only: bool,
122        dump_transaction_message: bool,
123        blockhash_query: BlockhashQuery,
124    },
125    SetUpgradeAuthorityChecked {
126        program_pubkey: Pubkey,
127        upgrade_authority_index: SignerIndex,
128        new_upgrade_authority_index: SignerIndex,
129        sign_only: bool,
130        dump_transaction_message: bool,
131        blockhash_query: BlockhashQuery,
132    },
133    Show {
134        account_pubkey: Option<Pubkey>,
135        authority_pubkey: Pubkey,
136        get_programs: bool,
137        get_buffers: bool,
138        all: bool,
139        use_lamports_unit: bool,
140    },
141    Dump {
142        account_pubkey: Option<Pubkey>,
143        output_location: String,
144    },
145    Close {
146        account_pubkey: Option<Pubkey>,
147        recipient_pubkey: Pubkey,
148        authority_index: SignerIndex,
149        use_lamports_unit: bool,
150        bypass_warning: bool,
151    },
152    ExtendProgram {
153        program_pubkey: Pubkey,
154        additional_bytes: u32,
155    },
156}
157
158pub trait ProgramSubCommands {
159    fn program_subcommands(self) -> Self;
160}
161
162impl ProgramSubCommands for App<'_, '_> {
163    fn program_subcommands(self) -> Self {
164        self.subcommand(
165            SubCommand::with_name("program")
166                .about("Program management")
167                .setting(AppSettings::SubcommandRequiredElseHelp)
168                .arg(
169                    Arg::with_name("skip_fee_check")
170                        .long("skip-fee-check")
171                        .hidden(hidden_unless_forced())
172                        .takes_value(false)
173                        .global(true),
174                )
175                .subcommand(
176                    SubCommand::with_name("deploy")
177                        .about("Deploy an upgradeable program")
178                        .arg(
179                            Arg::with_name("program_location")
180                                .index(1)
181                                .value_name("PROGRAM_FILEPATH")
182                                .takes_value(true)
183                                .help("/path/to/program.so"),
184                        )
185                        .arg(fee_payer_arg())
186                        .arg(
187                            Arg::with_name("buffer")
188                                .long("buffer")
189                                .value_name("BUFFER_SIGNER")
190                                .takes_value(true)
191                                .validator(is_valid_signer)
192                                .help(
193                                    "Intermediate buffer account to write data to, which can be \
194                                     used to resume a failed deploy [default: random address]",
195                                ),
196                        )
197                        .arg(
198                            Arg::with_name("upgrade_authority")
199                                .long("upgrade-authority")
200                                .value_name("UPGRADE_AUTHORITY_SIGNER")
201                                .takes_value(true)
202                                .validator(is_valid_signer)
203                                .help(
204                                    "Upgrade authority [default: the default configured keypair]",
205                                ),
206                        )
207                        .arg(pubkey!(
208                            Arg::with_name("program_id")
209                                .long("program-id")
210                                .value_name("PROGRAM_ID"),
211                            "Executable program; must be a signer for initial deploys, \
212                             can be an address for upgrades [default: address of keypair at \
213                             /path/to/program-keypair.json if present, otherwise a random address]."
214                        ))
215                        .arg(
216                            Arg::with_name("final")
217                                .long("final")
218                                .help("The program will not be upgradeable"),
219                        )
220                        .arg(
221                            Arg::with_name("max_len")
222                                .long("max-len")
223                                .value_name("max_len")
224                                .takes_value(true)
225                                .required(false)
226                                .help(
227                                    "Maximum length of the upgradeable program \
228                                    [default: the length of the original deployed program]",
229                                ),
230                        )
231                        .arg(
232                            Arg::with_name("allow_excessive_balance")
233                                .long("allow-excessive-deploy-account-balance")
234                                .takes_value(false)
235                                .help(
236                                    "Use the designated program id even if the account already \
237                                     holds a large balance of MLN",
238                                ),
239                        ),
240                )
241                .subcommand(
242                    SubCommand::with_name("upgrade")
243                        .about("Upgrade an upgradeable program")
244                        .arg(pubkey!(
245                            Arg::with_name("buffer")
246                                .index(1)
247                                .required(true)
248                                .value_name("BUFFER_PUBKEY"),
249                            "Intermediate buffer account with new program data"
250                        ))
251                        .arg(pubkey!(
252                            Arg::with_name("program_id")
253                                .index(2)
254                                .required(true)
255                                .value_name("PROGRAM_ID"),
256                            "Executable program's address (pubkey)"
257                        ))
258                        .arg(fee_payer_arg())
259                        .arg(
260                            Arg::with_name("upgrade_authority")
261                                .long("upgrade-authority")
262                                .value_name("UPGRADE_AUTHORITY_SIGNER")
263                                .takes_value(true)
264                                .validator(is_valid_signer)
265                                .help(
266                                    "Upgrade authority [default: the default configured keypair]",
267                                ),
268                        )
269                        .offline_args(),
270                )
271                .subcommand(
272                    SubCommand::with_name("write-buffer")
273                        .about("Writes a program into a buffer account")
274                        .arg(
275                            Arg::with_name("program_location")
276                                .index(1)
277                                .value_name("PROGRAM_FILEPATH")
278                                .takes_value(true)
279                                .required(true)
280                                .help("/path/to/program.so"),
281                        )
282                        .arg(fee_payer_arg())
283                        .arg(
284                            Arg::with_name("buffer")
285                                .long("buffer")
286                                .value_name("BUFFER_SIGNER")
287                                .takes_value(true)
288                                .validator(is_valid_signer)
289                                .help(
290                                    "Buffer account to write data into [default: random address]",
291                                ),
292                        )
293                        .arg(
294                            Arg::with_name("buffer_authority")
295                                .long("buffer-authority")
296                                .value_name("BUFFER_AUTHORITY_SIGNER")
297                                .takes_value(true)
298                                .validator(is_valid_signer)
299                                .help("Buffer authority [default: the default configured keypair]"),
300                        )
301                        .arg(
302                            Arg::with_name("max_len")
303                                .long("max-len")
304                                .value_name("max_len")
305                                .takes_value(true)
306                                .required(false)
307                                .help(
308                                    "Maximum length of the upgradeable program \
309                                    [default: the length of the original deployed program]",
310                                ),
311                        ),
312                )
313                .subcommand(
314                    SubCommand::with_name("set-buffer-authority")
315                        .about("Set a new buffer authority")
316                        .arg(
317                            Arg::with_name("buffer")
318                                .index(1)
319                                .value_name("BUFFER_PUBKEY")
320                                .takes_value(true)
321                                .required(true)
322                                .help("Public key of the buffer"),
323                        )
324                        .arg(
325                            Arg::with_name("buffer_authority")
326                                .long("buffer-authority")
327                                .value_name("BUFFER_AUTHORITY_SIGNER")
328                                .takes_value(true)
329                                .validator(is_valid_signer)
330                                .help("Buffer authority [default: the default configured keypair]"),
331                        )
332                        .arg(pubkey!(
333                            Arg::with_name("new_buffer_authority")
334                                .long("new-buffer-authority")
335                                .value_name("NEW_BUFFER_AUTHORITY")
336                                .required(true),
337                            "New buffer authority."
338                        )),
339                )
340                .subcommand(
341                    SubCommand::with_name("set-upgrade-authority")
342                        .about("Set a new program authority")
343                        .arg(
344                            Arg::with_name("program_id")
345                                .index(1)
346                                .value_name("PROGRAM_ADDRESS")
347                                .takes_value(true)
348                                .required(true)
349                                .help("Address of the program to upgrade"),
350                        )
351                        .arg(
352                            Arg::with_name("upgrade_authority")
353                                .long("upgrade-authority")
354                                .value_name("UPGRADE_AUTHORITY_SIGNER")
355                                .takes_value(true)
356                                .validator(is_valid_signer)
357                                .help(
358                                    "Upgrade authority [default: the default configured keypair]",
359                                ),
360                        )
361                        .arg(
362                            Arg::with_name("new_upgrade_authority")
363                                .long("new-upgrade-authority")
364                                .value_name("NEW_UPGRADE_AUTHORITY")
365                                .required_unless("final")
366                                .takes_value(true)
367                                .help(
368                                    "New upgrade authority (keypair or pubkey). It is strongly \
369                                     recommended to pass in a keypair to prevent mistakes in \
370                                     setting the upgrade authority. You can opt out of this \
371                                     behavior by passing \
372                                     --skip-new-upgrade-authority-signer-check if you are really \
373                                     confident that you are setting the correct authority. \
374                                     Alternatively, If you wish to make the program immutable, \
375                                     you should ignore this arg and pass the --final flag.",
376                                ),
377                        )
378                        .arg(
379                            Arg::with_name("final")
380                                .long("final")
381                                .conflicts_with("new_upgrade_authority")
382                                .help("The program will not be upgradeable"),
383                        )
384                        .arg(
385                            Arg::with_name("skip_new_upgrade_authority_signer_check")
386                                .long("skip-new-upgrade-authority-signer-check")
387                                .requires("new_upgrade_authority")
388                                .takes_value(false)
389                                .help(
390                                    "Set this flag if you don't want the new authority to sign \
391                                     the set-upgrade-authority transaction.",
392                                ),
393                        )
394                        .offline_args(),
395                )
396                .subcommand(
397                    SubCommand::with_name("show")
398                        .about("Display information about a buffer or program")
399                        .arg(
400                            Arg::with_name("account")
401                                .index(1)
402                                .value_name("ACCOUNT_ADDRESS")
403                                .takes_value(true)
404                                .help("Address of the buffer or program to show"),
405                        )
406                        .arg(
407                            Arg::with_name("programs")
408                                .long("programs")
409                                .conflicts_with("account")
410                                .conflicts_with("buffers")
411                                .required_unless_one(&["account", "buffers"])
412                                .help("Show every upgradeable program that matches the authority"),
413                        )
414                        .arg(
415                            Arg::with_name("buffers")
416                                .long("buffers")
417                                .conflicts_with("account")
418                                .conflicts_with("programs")
419                                .required_unless_one(&["account", "programs"])
420                                .help("Show every upgradeable buffer that matches the authority"),
421                        )
422                        .arg(
423                            Arg::with_name("all")
424                                .long("all")
425                                .conflicts_with("account")
426                                .conflicts_with("buffer_authority")
427                                .help("Show accounts for all authorities"),
428                        )
429                        .arg(pubkey!(
430                            Arg::with_name("buffer_authority")
431                                .long("buffer-authority")
432                                .value_name("AUTHORITY")
433                                .conflicts_with("all"),
434                            "Authority [default: the default configured keypair]."
435                        ))
436                        .arg(
437                            Arg::with_name("lamports")
438                                .long("lamports")
439                                .takes_value(false)
440                                .help("Display balance in lamports instead of MLN"),
441                        ),
442                )
443                .subcommand(
444                    SubCommand::with_name("dump")
445                        .about("Write the program data to a file")
446                        .arg(
447                            Arg::with_name("account")
448                                .index(1)
449                                .value_name("ACCOUNT_ADDRESS")
450                                .takes_value(true)
451                                .required(true)
452                                .help("Address of the buffer or program"),
453                        )
454                        .arg(
455                            Arg::with_name("output_location")
456                                .index(2)
457                                .value_name("OUTPUT_FILEPATH")
458                                .takes_value(true)
459                                .required(true)
460                                .help("/path/to/program.so"),
461                        ),
462                )
463                .subcommand(
464                    SubCommand::with_name("close")
465                        .about("Close a program or buffer account and withdraw all lamports")
466                        .arg(
467                            Arg::with_name("account")
468                                .index(1)
469                                .value_name("ACCOUNT_ADDRESS")
470                                .takes_value(true)
471                                .help("Address of the program or buffer account to close"),
472                        )
473                        .arg(
474                            Arg::with_name("buffers")
475                                .long("buffers")
476                                .conflicts_with("account")
477                                .required_unless("account")
478                                .help("Close all buffer accounts that match the authority"),
479                        )
480                        .arg(
481                            Arg::with_name("authority")
482                                .long("authority")
483                                .alias("buffer-authority")
484                                .value_name("AUTHORITY_SIGNER")
485                                .takes_value(true)
486                                .validator(is_valid_signer)
487                                .help(
488                                    "Upgrade or buffer authority [default: the default configured \
489                                     keypair]",
490                                ),
491                        )
492                        .arg(pubkey!(
493                            Arg::with_name("recipient_account")
494                                .long("recipient")
495                                .value_name("RECIPIENT_ADDRESS"),
496                            "Recipient of closed account's lamports \
497                             [default: the default configured keypair]."
498                        ))
499                        .arg(
500                            Arg::with_name("lamports")
501                                .long("lamports")
502                                .takes_value(false)
503                                .help("Display balance in lamports instead of MLN"),
504                        )
505                        .arg(
506                            Arg::with_name("bypass_warning")
507                                .long("bypass-warning")
508                                .takes_value(false)
509                                .help("Bypass the permanent program closure warning"),
510                        ),
511                )
512                .subcommand(
513                    SubCommand::with_name("extend")
514                        .about(
515                            "Extend the length of an upgradeable program to deploy larger programs",
516                        )
517                        .arg(
518                            Arg::with_name("program_id")
519                                .index(1)
520                                .value_name("PROGRAM_ID")
521                                .takes_value(true)
522                                .required(true)
523                                .validator(is_valid_pubkey)
524                                .help("Address of the program to extend"),
525                        )
526                        .arg(
527                            Arg::with_name("additional_bytes")
528                                .index(2)
529                                .value_name("ADDITIONAL_BYTES")
530                                .takes_value(true)
531                                .required(true)
532                                .validator(is_parsable::<u32>)
533                                .help(
534                                    "Number of bytes that will be allocated for the program's \
535                                     data account",
536                                ),
537                        ),
538                ),
539        )
540        .subcommand(
541            SubCommand::with_name("deploy")
542                .about(
543                    "Deploy has been removed. Use `miraland program deploy` instead to deploy \
544                     upgradeable programs",
545                )
546                .setting(AppSettings::Hidden),
547        )
548    }
549}
550
551pub fn parse_program_subcommand(
552    matches: &ArgMatches<'_>,
553    default_signer: &DefaultSigner,
554    wallet_manager: &mut Option<Rc<RemoteWalletManager>>,
555) -> Result<CliCommandInfo, CliError> {
556    let (subcommand, sub_matches) = matches.subcommand();
557    let matches_skip_fee_check = matches.is_present("skip_fee_check");
558    let sub_matches_skip_fee_check = sub_matches
559        .map(|m| m.is_present("skip_fee_check"))
560        .unwrap_or(false);
561    let skip_fee_check = matches_skip_fee_check || sub_matches_skip_fee_check;
562
563    let response = match (subcommand, sub_matches) {
564        ("deploy", Some(matches)) => {
565            let (fee_payer, fee_payer_pubkey) =
566                signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
567
568            let mut bulk_signers = vec![
569                Some(default_signer.signer_from_path(matches, wallet_manager)?),
570                fee_payer, // if None, default signer will be supplied
571            ];
572
573            let program_location = matches
574                .value_of("program_location")
575                .map(|location| location.to_string());
576
577            let buffer_pubkey = if let Ok((buffer_signer, Some(buffer_pubkey))) =
578                signer_of(matches, "buffer", wallet_manager)
579            {
580                bulk_signers.push(buffer_signer);
581                Some(buffer_pubkey)
582            } else {
583                pubkey_of_signer(matches, "buffer", wallet_manager)?
584            };
585
586            let program_pubkey = if let Ok((program_signer, Some(program_pubkey))) =
587                signer_of(matches, "program_id", wallet_manager)
588            {
589                bulk_signers.push(program_signer);
590                Some(program_pubkey)
591            } else {
592                pubkey_of_signer(matches, "program_id", wallet_manager)?
593            };
594
595            let (upgrade_authority, upgrade_authority_pubkey) =
596                signer_of(matches, "upgrade_authority", wallet_manager)?;
597            bulk_signers.push(upgrade_authority);
598
599            let max_len = value_of(matches, "max_len");
600
601            let signer_info =
602                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
603
604            CliCommandInfo {
605                command: CliCommand::Program(ProgramCliCommand::Deploy {
606                    program_location,
607                    fee_payer_signer_index: signer_info.index_of(fee_payer_pubkey).unwrap(),
608                    program_signer_index: signer_info.index_of_or_none(program_pubkey),
609                    program_pubkey,
610                    buffer_signer_index: signer_info.index_of_or_none(buffer_pubkey),
611                    buffer_pubkey,
612                    upgrade_authority_signer_index: signer_info
613                        .index_of(upgrade_authority_pubkey)
614                        .unwrap(),
615                    is_final: matches.is_present("final"),
616                    max_len,
617                    allow_excessive_balance: matches.is_present("allow_excessive_balance"),
618                    skip_fee_check,
619                }),
620                signers: signer_info.signers,
621            }
622        }
623        ("upgrade", Some(matches)) => {
624            let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
625            let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
626            let blockhash_query = BlockhashQuery::new_from_matches(matches);
627            let buffer_pubkey = pubkey_of_signer(matches, "buffer", wallet_manager)
628                .unwrap()
629                .unwrap();
630            let program_pubkey = pubkey_of_signer(matches, "program_id", wallet_manager)
631                .unwrap()
632                .unwrap();
633
634            let (fee_payer, fee_payer_pubkey) =
635                signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
636
637            let mut bulk_signers = vec![
638                fee_payer, // if None, default signer will be supplied
639            ];
640
641            let (upgrade_authority, upgrade_authority_pubkey) =
642                signer_of(matches, "upgrade_authority", wallet_manager)?;
643            bulk_signers.push(upgrade_authority);
644
645            let signer_info =
646                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
647
648            CliCommandInfo {
649                command: CliCommand::Program(ProgramCliCommand::Upgrade {
650                    fee_payer_signer_index: signer_info.index_of(fee_payer_pubkey).unwrap(),
651                    program_pubkey,
652                    buffer_pubkey,
653                    upgrade_authority_signer_index: signer_info
654                        .index_of(upgrade_authority_pubkey)
655                        .unwrap(),
656                    sign_only,
657                    dump_transaction_message,
658                    blockhash_query,
659                }),
660                signers: signer_info.signers,
661            }
662        }
663        ("write-buffer", Some(matches)) => {
664            let (fee_payer, fee_payer_pubkey) =
665                signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
666
667            let mut bulk_signers = vec![
668                Some(default_signer.signer_from_path(matches, wallet_manager)?),
669                fee_payer, // if None, default signer will be supplied
670            ];
671
672            let buffer_pubkey = if let Ok((buffer_signer, Some(buffer_pubkey))) =
673                signer_of(matches, "buffer", wallet_manager)
674            {
675                bulk_signers.push(buffer_signer);
676                Some(buffer_pubkey)
677            } else {
678                pubkey_of_signer(matches, "buffer", wallet_manager)?
679            };
680
681            let (buffer_authority, buffer_authority_pubkey) =
682                signer_of(matches, "buffer_authority", wallet_manager)?;
683            bulk_signers.push(buffer_authority);
684
685            let max_len = value_of(matches, "max_len");
686
687            let signer_info =
688                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
689
690            CliCommandInfo {
691                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
692                    program_location: matches.value_of("program_location").unwrap().to_string(),
693                    fee_payer_signer_index: signer_info.index_of(fee_payer_pubkey).unwrap(),
694                    buffer_signer_index: signer_info.index_of_or_none(buffer_pubkey),
695                    buffer_pubkey,
696                    buffer_authority_signer_index: signer_info
697                        .index_of(buffer_authority_pubkey)
698                        .unwrap(),
699                    max_len,
700                    skip_fee_check,
701                }),
702                signers: signer_info.signers,
703            }
704        }
705        ("set-buffer-authority", Some(matches)) => {
706            let buffer_pubkey = pubkey_of(matches, "buffer").unwrap();
707
708            let (buffer_authority_signer, buffer_authority_pubkey) =
709                signer_of(matches, "buffer_authority", wallet_manager)?;
710            let new_buffer_authority =
711                pubkey_of_signer(matches, "new_buffer_authority", wallet_manager)?.unwrap();
712
713            let signer_info = default_signer.generate_unique_signers(
714                vec![
715                    Some(default_signer.signer_from_path(matches, wallet_manager)?),
716                    buffer_authority_signer,
717                ],
718                matches,
719                wallet_manager,
720            )?;
721
722            CliCommandInfo {
723                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
724                    buffer_pubkey,
725                    buffer_authority_index: signer_info.index_of(buffer_authority_pubkey),
726                    new_buffer_authority,
727                }),
728                signers: signer_info.signers,
729            }
730        }
731        ("set-upgrade-authority", Some(matches)) => {
732            let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
733            let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
734            let blockhash_query = BlockhashQuery::new_from_matches(matches);
735            let (upgrade_authority_signer, upgrade_authority_pubkey) =
736                signer_of(matches, "upgrade_authority", wallet_manager)?;
737            let program_pubkey = pubkey_of(matches, "program_id").unwrap();
738            let is_final = matches.is_present("final");
739            let new_upgrade_authority = if is_final {
740                None
741            } else {
742                pubkey_of_signer(matches, "new_upgrade_authority", wallet_manager)?
743            };
744
745            let mut signers = vec![
746                Some(default_signer.signer_from_path(matches, wallet_manager)?),
747                upgrade_authority_signer,
748            ];
749
750            if !is_final && !matches.is_present("skip_new_upgrade_authority_signer_check") {
751                let (new_upgrade_authority_signer, _) =
752                    signer_of(matches, "new_upgrade_authority", wallet_manager)?;
753                signers.push(new_upgrade_authority_signer);
754            }
755
756            let signer_info =
757                default_signer.generate_unique_signers(signers, matches, wallet_manager)?;
758
759            if matches.is_present("skip_new_upgrade_authority_signer_check") || is_final {
760                CliCommandInfo {
761                    command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
762                        program_pubkey,
763                        upgrade_authority_index: signer_info.index_of(upgrade_authority_pubkey),
764                        new_upgrade_authority,
765                        sign_only,
766                        dump_transaction_message,
767                        blockhash_query,
768                    }),
769                    signers: signer_info.signers,
770                }
771            } else {
772                CliCommandInfo {
773                    command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
774                        program_pubkey,
775                        upgrade_authority_index: signer_info
776                            .index_of(upgrade_authority_pubkey)
777                            .expect("upgrade authority is missing from signers"),
778                        new_upgrade_authority_index: signer_info
779                            .index_of(new_upgrade_authority)
780                            .expect("new upgrade authority is missing from signers"),
781                        sign_only,
782                        dump_transaction_message,
783                        blockhash_query,
784                    }),
785                    signers: signer_info.signers,
786                }
787            }
788        }
789        ("show", Some(matches)) => {
790            let authority_pubkey = if let Some(authority_pubkey) =
791                pubkey_of_signer(matches, "buffer_authority", wallet_manager)?
792            {
793                authority_pubkey
794            } else {
795                default_signer
796                    .signer_from_path(matches, wallet_manager)?
797                    .pubkey()
798            };
799
800            CliCommandInfo {
801                command: CliCommand::Program(ProgramCliCommand::Show {
802                    account_pubkey: pubkey_of(matches, "account"),
803                    authority_pubkey,
804                    get_programs: matches.is_present("programs"),
805                    get_buffers: matches.is_present("buffers"),
806                    all: matches.is_present("all"),
807                    use_lamports_unit: matches.is_present("lamports"),
808                }),
809                signers: vec![],
810            }
811        }
812        ("dump", Some(matches)) => CliCommandInfo {
813            command: CliCommand::Program(ProgramCliCommand::Dump {
814                account_pubkey: pubkey_of(matches, "account"),
815                output_location: matches.value_of("output_location").unwrap().to_string(),
816            }),
817            signers: vec![],
818        },
819        ("close", Some(matches)) => {
820            let account_pubkey = if matches.is_present("buffers") {
821                None
822            } else {
823                pubkey_of(matches, "account")
824            };
825
826            let recipient_pubkey = if let Some(recipient_pubkey) =
827                pubkey_of_signer(matches, "recipient_account", wallet_manager)?
828            {
829                recipient_pubkey
830            } else {
831                default_signer
832                    .signer_from_path(matches, wallet_manager)?
833                    .pubkey()
834            };
835
836            let (authority_signer, authority_pubkey) =
837                signer_of(matches, "authority", wallet_manager)?;
838
839            let signer_info = default_signer.generate_unique_signers(
840                vec![
841                    Some(default_signer.signer_from_path(matches, wallet_manager)?),
842                    authority_signer,
843                ],
844                matches,
845                wallet_manager,
846            )?;
847
848            CliCommandInfo {
849                command: CliCommand::Program(ProgramCliCommand::Close {
850                    account_pubkey,
851                    recipient_pubkey,
852                    authority_index: signer_info.index_of(authority_pubkey).unwrap(),
853                    use_lamports_unit: matches.is_present("lamports"),
854                    bypass_warning: matches.is_present("bypass_warning"),
855                }),
856                signers: signer_info.signers,
857            }
858        }
859        ("extend", Some(matches)) => {
860            let program_pubkey = pubkey_of(matches, "program_id").unwrap();
861            let additional_bytes = value_of(matches, "additional_bytes").unwrap();
862
863            let signer_info = default_signer.generate_unique_signers(
864                vec![Some(
865                    default_signer.signer_from_path(matches, wallet_manager)?,
866                )],
867                matches,
868                wallet_manager,
869            )?;
870
871            CliCommandInfo {
872                command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
873                    program_pubkey,
874                    additional_bytes,
875                }),
876                signers: signer_info.signers,
877            }
878        }
879        _ => unreachable!(),
880    };
881    Ok(response)
882}
883
884pub fn process_program_subcommand(
885    rpc_client: Arc<RpcClient>,
886    config: &CliConfig,
887    program_subcommand: &ProgramCliCommand,
888) -> ProcessResult {
889    match program_subcommand {
890        ProgramCliCommand::Deploy {
891            program_location,
892            fee_payer_signer_index,
893            program_signer_index,
894            program_pubkey,
895            buffer_signer_index,
896            buffer_pubkey,
897            upgrade_authority_signer_index,
898            is_final,
899            max_len,
900            allow_excessive_balance,
901            skip_fee_check,
902        } => process_program_deploy(
903            rpc_client,
904            config,
905            program_location,
906            *fee_payer_signer_index,
907            *program_signer_index,
908            *program_pubkey,
909            *buffer_signer_index,
910            *buffer_pubkey,
911            *upgrade_authority_signer_index,
912            *is_final,
913            *max_len,
914            *allow_excessive_balance,
915            *skip_fee_check,
916        ),
917        ProgramCliCommand::Upgrade {
918            fee_payer_signer_index,
919            program_pubkey,
920            buffer_pubkey,
921            upgrade_authority_signer_index,
922            sign_only,
923            dump_transaction_message,
924            blockhash_query,
925        } => process_program_upgrade(
926            rpc_client,
927            config,
928            *fee_payer_signer_index,
929            *program_pubkey,
930            *buffer_pubkey,
931            *upgrade_authority_signer_index,
932            *sign_only,
933            *dump_transaction_message,
934            blockhash_query,
935        ),
936        ProgramCliCommand::WriteBuffer {
937            program_location,
938            fee_payer_signer_index,
939            buffer_signer_index,
940            buffer_pubkey,
941            buffer_authority_signer_index,
942            max_len,
943            skip_fee_check,
944        } => process_write_buffer(
945            rpc_client,
946            config,
947            program_location,
948            *fee_payer_signer_index,
949            *buffer_signer_index,
950            *buffer_pubkey,
951            *buffer_authority_signer_index,
952            *max_len,
953            *skip_fee_check,
954        ),
955        ProgramCliCommand::SetBufferAuthority {
956            buffer_pubkey,
957            buffer_authority_index,
958            new_buffer_authority,
959        } => process_set_authority(
960            &rpc_client,
961            config,
962            None,
963            Some(*buffer_pubkey),
964            *buffer_authority_index,
965            Some(*new_buffer_authority),
966            false,
967            false,
968            &BlockhashQuery::default(),
969        ),
970        ProgramCliCommand::SetUpgradeAuthority {
971            program_pubkey,
972            upgrade_authority_index,
973            new_upgrade_authority,
974            sign_only,
975            dump_transaction_message,
976            blockhash_query,
977        } => process_set_authority(
978            &rpc_client,
979            config,
980            Some(*program_pubkey),
981            None,
982            *upgrade_authority_index,
983            *new_upgrade_authority,
984            *sign_only,
985            *dump_transaction_message,
986            blockhash_query,
987        ),
988        ProgramCliCommand::SetUpgradeAuthorityChecked {
989            program_pubkey,
990            upgrade_authority_index,
991            new_upgrade_authority_index,
992            sign_only,
993            dump_transaction_message,
994            blockhash_query,
995        } => process_set_authority_checked(
996            &rpc_client,
997            config,
998            *program_pubkey,
999            *upgrade_authority_index,
1000            *new_upgrade_authority_index,
1001            *sign_only,
1002            *dump_transaction_message,
1003            blockhash_query,
1004        ),
1005        ProgramCliCommand::Show {
1006            account_pubkey,
1007            authority_pubkey,
1008            get_programs,
1009            get_buffers,
1010            all,
1011            use_lamports_unit,
1012        } => process_show(
1013            &rpc_client,
1014            config,
1015            *account_pubkey,
1016            *authority_pubkey,
1017            *get_programs,
1018            *get_buffers,
1019            *all,
1020            *use_lamports_unit,
1021        ),
1022        ProgramCliCommand::Dump {
1023            account_pubkey,
1024            output_location,
1025        } => process_dump(&rpc_client, config, *account_pubkey, output_location),
1026        ProgramCliCommand::Close {
1027            account_pubkey,
1028            recipient_pubkey,
1029            authority_index,
1030            use_lamports_unit,
1031            bypass_warning,
1032        } => process_close(
1033            &rpc_client,
1034            config,
1035            *account_pubkey,
1036            *recipient_pubkey,
1037            *authority_index,
1038            *use_lamports_unit,
1039            *bypass_warning,
1040        ),
1041        ProgramCliCommand::ExtendProgram {
1042            program_pubkey,
1043            additional_bytes,
1044        } => process_extend_program(&rpc_client, config, *program_pubkey, *additional_bytes),
1045    }
1046}
1047
1048fn get_default_program_keypair(program_location: &Option<String>) -> Keypair {
1049    let program_keypair = {
1050        if let Some(program_location) = program_location {
1051            let mut keypair_file = PathBuf::new();
1052            keypair_file.push(program_location);
1053            let mut filename = keypair_file.file_stem().unwrap().to_os_string();
1054            filename.push("-keypair");
1055            keypair_file.set_file_name(filename);
1056            keypair_file.set_extension("json");
1057            if let Ok(keypair) = read_keypair_file(keypair_file.to_str().unwrap()) {
1058                keypair
1059            } else {
1060                Keypair::new()
1061            }
1062        } else {
1063            Keypair::new()
1064        }
1065    };
1066    program_keypair
1067}
1068
1069fn is_account_executable(account: &Account) -> bool {
1070    if account.owner == bpf_loader_deprecated::id() || account.owner == bpf_loader::id() {
1071        account.executable
1072    } else {
1073        let feature_set = FeatureSet::all_enabled();
1074        is_executable(account, &feature_set)
1075    }
1076}
1077
1078/// Deploy program using upgradeable loader. It also can process program upgrades
1079#[allow(clippy::too_many_arguments)]
1080fn process_program_deploy(
1081    rpc_client: Arc<RpcClient>,
1082    config: &CliConfig,
1083    program_location: &Option<String>,
1084    fee_payer_signer_index: SignerIndex,
1085    program_signer_index: Option<SignerIndex>,
1086    program_pubkey: Option<Pubkey>,
1087    buffer_signer_index: Option<SignerIndex>,
1088    buffer_pubkey: Option<Pubkey>,
1089    upgrade_authority_signer_index: SignerIndex,
1090    is_final: bool,
1091    max_len: Option<usize>,
1092    allow_excessive_balance: bool,
1093    skip_fee_check: bool,
1094) -> ProcessResult {
1095    let fee_payer_signer = config.signers[fee_payer_signer_index];
1096    let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
1097
1098    let (buffer_words, buffer_mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
1099    let (buffer_provided, buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
1100        (true, Some(config.signers[i]), config.signers[i].pubkey())
1101    } else if let Some(pubkey) = buffer_pubkey {
1102        (true, None, pubkey)
1103    } else {
1104        (
1105            false,
1106            Some(&buffer_keypair as &dyn Signer),
1107            buffer_keypair.pubkey(),
1108        )
1109    };
1110
1111    let default_program_keypair = get_default_program_keypair(program_location);
1112    let (program_signer, program_pubkey) = if let Some(i) = program_signer_index {
1113        (Some(config.signers[i]), config.signers[i].pubkey())
1114    } else if let Some(program_pubkey) = program_pubkey {
1115        (None, program_pubkey)
1116    } else {
1117        (
1118            Some(&default_program_keypair as &dyn Signer),
1119            default_program_keypair.pubkey(),
1120        )
1121    };
1122
1123    let do_initial_deploy = if let Some(account) = rpc_client
1124        .get_account_with_commitment(&program_pubkey, config.commitment)?
1125        .value
1126    {
1127        if account.owner != bpf_loader_upgradeable::id() {
1128            return Err(format!(
1129                "Account {program_pubkey} is not an upgradeable program or already in use"
1130            )
1131            .into());
1132        }
1133
1134        if !is_account_executable(&account) {
1135            // Continue an initial deploy
1136            true
1137        } else if let Ok(UpgradeableLoaderState::Program {
1138            programdata_address,
1139        }) = account.state()
1140        {
1141            if let Some(account) = rpc_client
1142                .get_account_with_commitment(&programdata_address, config.commitment)?
1143                .value
1144            {
1145                if let Ok(UpgradeableLoaderState::ProgramData {
1146                    slot: _,
1147                    upgrade_authority_address: program_authority_pubkey,
1148                }) = account.state()
1149                {
1150                    if program_authority_pubkey.is_none() {
1151                        return Err(
1152                            format!("Program {program_pubkey} is no longer upgradeable").into()
1153                        );
1154                    }
1155                    if program_authority_pubkey != Some(upgrade_authority_signer.pubkey()) {
1156                        return Err(format!(
1157                            "Program's authority {:?} does not match authority provided {:?}",
1158                            program_authority_pubkey,
1159                            upgrade_authority_signer.pubkey(),
1160                        )
1161                        .into());
1162                    }
1163                    // Do upgrade
1164                    false
1165                } else {
1166                    return Err(format!(
1167                        "Program {program_pubkey} has been closed, use a new Program Id"
1168                    )
1169                    .into());
1170                }
1171            } else {
1172                return Err(format!(
1173                    "Program {program_pubkey} has been closed, use a new Program Id"
1174                )
1175                .into());
1176            }
1177        } else {
1178            return Err(format!("{program_pubkey} is not an upgradeable program").into());
1179        }
1180    } else {
1181        // do new deploy
1182        true
1183    };
1184
1185    let (program_data, program_len) = if let Some(program_location) = program_location {
1186        let program_data = read_and_verify_elf(program_location)?;
1187        let program_len = program_data.len();
1188        (program_data, program_len)
1189    } else if buffer_provided {
1190        (
1191            vec![],
1192            fetch_buffer_len(&rpc_client, config, buffer_pubkey)?,
1193        )
1194    } else {
1195        return Err("Program location required if buffer not supplied".into());
1196    };
1197    let program_data_max_len = if let Some(len) = max_len {
1198        if program_len > len {
1199            return Err(
1200                "Max length specified not large enough to accommodate desired program".into(),
1201            );
1202        }
1203        len
1204    } else {
1205        program_len
1206    };
1207
1208    let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1209        UpgradeableLoaderState::size_of_programdata(program_data_max_len),
1210    )?;
1211
1212    let result = if do_initial_deploy {
1213        if program_signer.is_none() {
1214            return Err(
1215                "Initial deployments require a keypair be provided for the program id".into(),
1216            );
1217        }
1218        do_process_program_write_and_deploy(
1219            rpc_client.clone(),
1220            config,
1221            &program_data,
1222            program_len,
1223            program_data_max_len,
1224            min_rent_exempt_program_data_balance,
1225            &bpf_loader_upgradeable::id(),
1226            fee_payer_signer,
1227            Some(&[program_signer.unwrap(), upgrade_authority_signer]),
1228            buffer_signer,
1229            &buffer_pubkey,
1230            upgrade_authority_signer,
1231            allow_excessive_balance,
1232            skip_fee_check,
1233        )
1234    } else {
1235        do_process_program_upgrade(
1236            rpc_client.clone(),
1237            config,
1238            &program_data,
1239            program_len,
1240            min_rent_exempt_program_data_balance,
1241            fee_payer_signer,
1242            &program_pubkey,
1243            upgrade_authority_signer,
1244            &buffer_pubkey,
1245            buffer_signer,
1246            skip_fee_check,
1247        )
1248    };
1249    if result.is_ok() && is_final {
1250        process_set_authority(
1251            &rpc_client,
1252            config,
1253            Some(program_pubkey),
1254            None,
1255            Some(upgrade_authority_signer_index),
1256            None,
1257            false,
1258            false,
1259            &BlockhashQuery::default(),
1260        )?;
1261    }
1262    if result.is_err() && !buffer_provided {
1263        // We might have deployed "temporary" buffer but failed to deploy our program from this
1264        // buffer, reporting this to the user - so he can retry deploying re-using same buffer.
1265        report_ephemeral_mnemonic(buffer_words, buffer_mnemonic);
1266    }
1267    result
1268}
1269
1270fn fetch_buffer_len(
1271    rpc_client: &RpcClient,
1272    config: &CliConfig,
1273    buffer_pubkey: Pubkey,
1274) -> Result<usize, Box<dyn std::error::Error>> {
1275    // Check supplied buffer account
1276    if let Some(account) = rpc_client
1277        .get_account_with_commitment(&buffer_pubkey, config.commitment)?
1278        .value
1279    {
1280        if !bpf_loader_upgradeable::check_id(&account.owner) {
1281            return Err(format!(
1282                "Buffer account {buffer_pubkey} is not owned by the BPF Upgradeable Loader",
1283            )
1284            .into());
1285        }
1286
1287        match account.state() {
1288            Ok(UpgradeableLoaderState::Buffer { .. }) => {
1289                // continue if buffer is initialized
1290            }
1291            Ok(UpgradeableLoaderState::Program { .. }) => {
1292                return Err(format!("Cannot use program account {buffer_pubkey} as buffer").into());
1293            }
1294            Ok(UpgradeableLoaderState::ProgramData { .. }) => {
1295                return Err(
1296                    format!("Cannot use program data account {buffer_pubkey} as buffer",).into(),
1297                )
1298            }
1299            Ok(UpgradeableLoaderState::Uninitialized) => {
1300                return Err(format!("Buffer account {buffer_pubkey} is not initialized").into());
1301            }
1302            Err(_) => {
1303                return Err(
1304                    format!("Buffer account {buffer_pubkey} could not be deserialized").into(),
1305                )
1306            }
1307        };
1308
1309        let program_len = account
1310            .data
1311            .len()
1312            .saturating_sub(UpgradeableLoaderState::size_of_buffer_metadata());
1313
1314        Ok(program_len)
1315    } else {
1316        Err(format!("Buffer account {buffer_pubkey} not found, was it already consumed?",).into())
1317    }
1318}
1319
1320/// Upgrade existing program using upgradeable loader
1321#[allow(clippy::too_many_arguments)]
1322fn process_program_upgrade(
1323    rpc_client: Arc<RpcClient>,
1324    config: &CliConfig,
1325    fee_payer_signer_index: SignerIndex,
1326    program_id: Pubkey,
1327    buffer_pubkey: Pubkey,
1328    upgrade_authority_signer_index: SignerIndex,
1329    sign_only: bool,
1330    dump_transaction_message: bool,
1331    blockhash_query: &BlockhashQuery,
1332) -> ProcessResult {
1333    let fee_payer_signer = config.signers[fee_payer_signer_index];
1334    let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
1335
1336    let blockhash = blockhash_query.get_blockhash(&rpc_client, config.commitment)?;
1337    let message = Message::new_with_blockhash(
1338        &[bpf_loader_upgradeable::upgrade(
1339            &program_id,
1340            &buffer_pubkey,
1341            &upgrade_authority_signer.pubkey(),
1342            &fee_payer_signer.pubkey(),
1343        )],
1344        Some(&fee_payer_signer.pubkey()),
1345        &blockhash,
1346    );
1347
1348    if sign_only {
1349        let mut tx = Transaction::new_unsigned(message);
1350        let signers = &[fee_payer_signer, upgrade_authority_signer];
1351        // Using try_partial_sign here because fee_payer_signer might not be the fee payer we
1352        // end up using for this transaction (it might be NullSigner in `--sign-only` mode).
1353        tx.try_partial_sign(signers, blockhash)?;
1354        return_signers_with_config(
1355            &tx,
1356            &config.output_format,
1357            &ReturnSignersConfig {
1358                dump_transaction_message,
1359            },
1360        )
1361    } else {
1362        let fee = rpc_client.get_fee_for_message(&message)?;
1363        check_account_for_spend_and_fee_with_commitment(
1364            &rpc_client,
1365            &fee_payer_signer.pubkey(),
1366            0,
1367            fee,
1368            config.commitment,
1369        )?;
1370        let mut tx = Transaction::new_unsigned(message);
1371        let signers = &[fee_payer_signer, upgrade_authority_signer];
1372        tx.try_sign(signers, blockhash)?;
1373        let final_tx_sig = rpc_client
1374            .send_and_confirm_transaction_with_spinner(&tx)
1375            .map_err(|e| format!("Upgrading program failed: {e}"))?;
1376        let program_id = CliProgramId {
1377            program_id: program_id.to_string(),
1378            signature: Some(final_tx_sig.to_string()),
1379        };
1380        Ok(config.output_format.formatted_string(&program_id))
1381    }
1382}
1383
1384fn process_write_buffer(
1385    rpc_client: Arc<RpcClient>,
1386    config: &CliConfig,
1387    program_location: &str,
1388    fee_payer_signer_index: SignerIndex,
1389    buffer_signer_index: Option<SignerIndex>,
1390    buffer_pubkey: Option<Pubkey>,
1391    buffer_authority_signer_index: SignerIndex,
1392    max_len: Option<usize>,
1393    skip_fee_check: bool,
1394) -> ProcessResult {
1395    let fee_payer_signer = config.signers[fee_payer_signer_index];
1396    let buffer_authority = config.signers[buffer_authority_signer_index];
1397
1398    // Create ephemeral keypair to use for Buffer account, if not provided
1399    let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
1400    let (buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
1401        (Some(config.signers[i]), config.signers[i].pubkey())
1402    } else if let Some(pubkey) = buffer_pubkey {
1403        (None, pubkey)
1404    } else {
1405        (
1406            Some(&buffer_keypair as &dyn Signer),
1407            buffer_keypair.pubkey(),
1408        )
1409    };
1410
1411    if let Some(account) = rpc_client
1412        .get_account_with_commitment(&buffer_pubkey, config.commitment)?
1413        .value
1414    {
1415        if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1416            if authority_address.is_none() {
1417                return Err(format!("Buffer {buffer_pubkey} is immutable").into());
1418            }
1419            if authority_address != Some(buffer_authority.pubkey()) {
1420                return Err(format!(
1421                    "Buffer's authority {:?} does not match authority provided {}",
1422                    authority_address,
1423                    buffer_authority.pubkey()
1424                )
1425                .into());
1426            }
1427        } else {
1428            return Err(
1429                format!("{buffer_pubkey} is not an upgradeable loader buffer account").into(),
1430            );
1431        }
1432    }
1433
1434    let program_data = read_and_verify_elf(program_location)?;
1435    let buffer_data_max_len = if let Some(len) = max_len {
1436        len
1437    } else {
1438        program_data.len()
1439    };
1440    let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1441        UpgradeableLoaderState::size_of_programdata(buffer_data_max_len),
1442    )?;
1443
1444    let result = do_process_program_write_and_deploy(
1445        rpc_client,
1446        config,
1447        &program_data,
1448        program_data.len(),
1449        buffer_data_max_len,
1450        min_rent_exempt_program_data_balance,
1451        &bpf_loader_upgradeable::id(),
1452        fee_payer_signer,
1453        None,
1454        buffer_signer,
1455        &buffer_pubkey,
1456        buffer_authority,
1457        true,
1458        skip_fee_check,
1459    );
1460    if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
1461        report_ephemeral_mnemonic(words, mnemonic);
1462    }
1463    result
1464}
1465
1466fn process_set_authority(
1467    rpc_client: &RpcClient,
1468    config: &CliConfig,
1469    program_pubkey: Option<Pubkey>,
1470    buffer_pubkey: Option<Pubkey>,
1471    authority: Option<SignerIndex>,
1472    new_authority: Option<Pubkey>,
1473    sign_only: bool,
1474    dump_transaction_message: bool,
1475    blockhash_query: &BlockhashQuery,
1476) -> ProcessResult {
1477    let authority_signer = if let Some(index) = authority {
1478        config.signers[index]
1479    } else {
1480        return Err("Set authority requires the current authority".into());
1481    };
1482
1483    trace!("Set a new authority");
1484    let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1485
1486    let mut tx = if let Some(ref pubkey) = program_pubkey {
1487        Transaction::new_unsigned(Message::new(
1488            &[bpf_loader_upgradeable::set_upgrade_authority(
1489                pubkey,
1490                &authority_signer.pubkey(),
1491                new_authority.as_ref(),
1492            )],
1493            Some(&config.signers[0].pubkey()),
1494        ))
1495    } else if let Some(pubkey) = buffer_pubkey {
1496        if let Some(ref new_authority) = new_authority {
1497            Transaction::new_unsigned(Message::new(
1498                &[bpf_loader_upgradeable::set_buffer_authority(
1499                    &pubkey,
1500                    &authority_signer.pubkey(),
1501                    new_authority,
1502                )],
1503                Some(&config.signers[0].pubkey()),
1504            ))
1505        } else {
1506            return Err("Buffer authority cannot be None".into());
1507        }
1508    } else {
1509        return Err("Program or Buffer not provided".into());
1510    };
1511
1512    let signers = &[config.signers[0], authority_signer];
1513
1514    if sign_only {
1515        tx.try_partial_sign(signers, blockhash)?;
1516        return_signers_with_config(
1517            &tx,
1518            &config.output_format,
1519            &ReturnSignersConfig {
1520                dump_transaction_message,
1521            },
1522        )
1523    } else {
1524        tx.try_sign(signers, blockhash)?;
1525        rpc_client
1526            .send_and_confirm_transaction_with_spinner_and_config(
1527                &tx,
1528                config.commitment,
1529                RpcSendTransactionConfig {
1530                    preflight_commitment: Some(config.commitment.commitment),
1531                    ..RpcSendTransactionConfig::default()
1532                },
1533            )
1534            .map_err(|e| format!("Setting authority failed: {e}"))?;
1535
1536        let authority = CliProgramAuthority {
1537            authority: new_authority
1538                .map(|pubkey| pubkey.to_string())
1539                .unwrap_or_else(|| "none".to_string()),
1540            account_type: if program_pubkey.is_some() {
1541                CliProgramAccountType::Program
1542            } else {
1543                CliProgramAccountType::Buffer
1544            },
1545        };
1546        Ok(config.output_format.formatted_string(&authority))
1547    }
1548}
1549
1550fn process_set_authority_checked(
1551    rpc_client: &RpcClient,
1552    config: &CliConfig,
1553    program_pubkey: Pubkey,
1554    authority_index: SignerIndex,
1555    new_authority_index: SignerIndex,
1556    sign_only: bool,
1557    dump_transaction_message: bool,
1558    blockhash_query: &BlockhashQuery,
1559) -> ProcessResult {
1560    let authority_signer = config.signers[authority_index];
1561    let new_authority_signer = config.signers[new_authority_index];
1562
1563    trace!("Set a new (checked) authority");
1564    let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1565
1566    let mut tx = Transaction::new_unsigned(Message::new(
1567        &[bpf_loader_upgradeable::set_upgrade_authority_checked(
1568            &program_pubkey,
1569            &authority_signer.pubkey(),
1570            &new_authority_signer.pubkey(),
1571        )],
1572        Some(&config.signers[0].pubkey()),
1573    ));
1574
1575    let signers = &[config.signers[0], authority_signer, new_authority_signer];
1576    if sign_only {
1577        tx.try_partial_sign(signers, blockhash)?;
1578        return_signers_with_config(
1579            &tx,
1580            &config.output_format,
1581            &ReturnSignersConfig {
1582                dump_transaction_message,
1583            },
1584        )
1585    } else {
1586        tx.try_sign(signers, blockhash)?;
1587        rpc_client
1588            .send_and_confirm_transaction_with_spinner_and_config(
1589                &tx,
1590                config.commitment,
1591                RpcSendTransactionConfig {
1592                    preflight_commitment: Some(config.commitment.commitment),
1593                    ..RpcSendTransactionConfig::default()
1594                },
1595            )
1596            .map_err(|e| format!("Setting authority failed: {e}"))?;
1597
1598        let authority = CliProgramAuthority {
1599            authority: new_authority_signer.pubkey().to_string(),
1600            account_type: CliProgramAccountType::Program,
1601        };
1602        Ok(config.output_format.formatted_string(&authority))
1603    }
1604}
1605
1606const ACCOUNT_TYPE_SIZE: usize = 4;
1607const SLOT_SIZE: usize = size_of::<u64>();
1608const OPTION_SIZE: usize = 1;
1609const PUBKEY_LEN: usize = 32;
1610
1611fn get_buffers(
1612    rpc_client: &RpcClient,
1613    authority_pubkey: Option<Pubkey>,
1614    use_lamports_unit: bool,
1615) -> Result<CliUpgradeableBuffers, Box<dyn std::error::Error>> {
1616    let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1617        0,
1618        &[1, 0, 0, 0],
1619    ))];
1620    if let Some(authority_pubkey) = authority_pubkey {
1621        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1622            ACCOUNT_TYPE_SIZE,
1623            &[1],
1624        )));
1625        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1626            ACCOUNT_TYPE_SIZE + OPTION_SIZE,
1627            authority_pubkey.as_ref(),
1628        )));
1629    }
1630
1631    let results = get_accounts_with_filter(
1632        rpc_client,
1633        filters,
1634        ACCOUNT_TYPE_SIZE + OPTION_SIZE + PUBKEY_LEN,
1635    )?;
1636
1637    let mut buffers = vec![];
1638    for (address, account) in results.iter() {
1639        if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1640            buffers.push(CliUpgradeableBuffer {
1641                address: address.to_string(),
1642                authority: authority_address
1643                    .map(|pubkey| pubkey.to_string())
1644                    .unwrap_or_else(|| "none".to_string()),
1645                data_len: 0,
1646                lamports: account.lamports,
1647                use_lamports_unit,
1648            });
1649        } else {
1650            return Err(format!("Error parsing Buffer account {address}").into());
1651        }
1652    }
1653    Ok(CliUpgradeableBuffers {
1654        buffers,
1655        use_lamports_unit,
1656    })
1657}
1658
1659fn get_programs(
1660    rpc_client: &RpcClient,
1661    authority_pubkey: Option<Pubkey>,
1662    use_lamports_unit: bool,
1663) -> Result<CliUpgradeablePrograms, Box<dyn std::error::Error>> {
1664    let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1665        0,
1666        &[3, 0, 0, 0],
1667    ))];
1668    if let Some(authority_pubkey) = authority_pubkey {
1669        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1670            ACCOUNT_TYPE_SIZE + SLOT_SIZE,
1671            &[1],
1672        )));
1673        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1674            ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE,
1675            authority_pubkey.as_ref(),
1676        )));
1677    }
1678
1679    let results = get_accounts_with_filter(
1680        rpc_client,
1681        filters,
1682        ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE + PUBKEY_LEN,
1683    )?;
1684
1685    let mut programs = vec![];
1686    for (programdata_address, programdata_account) in results.iter() {
1687        if let Ok(UpgradeableLoaderState::ProgramData {
1688            slot,
1689            upgrade_authority_address,
1690        }) = programdata_account.state()
1691        {
1692            let mut bytes = vec![2, 0, 0, 0];
1693            bytes.extend_from_slice(programdata_address.as_ref());
1694            let filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &bytes))];
1695
1696            let results = get_accounts_with_filter(rpc_client, filters, 0)?;
1697            if results.len() != 1 {
1698                return Err(format!(
1699                    "Error: More than one Program associated with ProgramData account \
1700                     {programdata_address}"
1701                )
1702                .into());
1703            }
1704            programs.push(CliUpgradeableProgram {
1705                program_id: results[0].0.to_string(),
1706                owner: programdata_account.owner.to_string(),
1707                programdata_address: programdata_address.to_string(),
1708                authority: upgrade_authority_address
1709                    .map(|pubkey| pubkey.to_string())
1710                    .unwrap_or_else(|| "none".to_string()),
1711                last_deploy_slot: slot,
1712                data_len: programdata_account.data.len()
1713                    - UpgradeableLoaderState::size_of_programdata_metadata(),
1714                lamports: programdata_account.lamports,
1715                use_lamports_unit,
1716            });
1717        } else {
1718            return Err(format!("Error parsing ProgramData account {programdata_address}").into());
1719        }
1720    }
1721    Ok(CliUpgradeablePrograms {
1722        programs,
1723        use_lamports_unit,
1724    })
1725}
1726
1727fn get_accounts_with_filter(
1728    rpc_client: &RpcClient,
1729    filters: Vec<RpcFilterType>,
1730    length: usize,
1731) -> Result<Vec<(Pubkey, Account)>, Box<dyn std::error::Error>> {
1732    let results = rpc_client.get_program_accounts_with_config(
1733        &bpf_loader_upgradeable::id(),
1734        RpcProgramAccountsConfig {
1735            filters: Some(filters),
1736            account_config: RpcAccountInfoConfig {
1737                encoding: Some(UiAccountEncoding::Base64),
1738                data_slice: Some(UiDataSliceConfig { offset: 0, length }),
1739                ..RpcAccountInfoConfig::default()
1740            },
1741            ..RpcProgramAccountsConfig::default()
1742        },
1743    )?;
1744    Ok(results)
1745}
1746
1747fn process_show(
1748    rpc_client: &RpcClient,
1749    config: &CliConfig,
1750    account_pubkey: Option<Pubkey>,
1751    authority_pubkey: Pubkey,
1752    programs: bool,
1753    buffers: bool,
1754    all: bool,
1755    use_lamports_unit: bool,
1756) -> ProcessResult {
1757    if let Some(account_pubkey) = account_pubkey {
1758        if let Some(account) = rpc_client
1759            .get_account_with_commitment(&account_pubkey, config.commitment)?
1760            .value
1761        {
1762            if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
1763                Ok(config.output_format.formatted_string(&CliProgram {
1764                    program_id: account_pubkey.to_string(),
1765                    owner: account.owner.to_string(),
1766                    data_len: account.data.len(),
1767                }))
1768            } else if account.owner == bpf_loader_upgradeable::id() {
1769                if let Ok(UpgradeableLoaderState::Program {
1770                    programdata_address,
1771                }) = account.state()
1772                {
1773                    if let Some(programdata_account) = rpc_client
1774                        .get_account_with_commitment(&programdata_address, config.commitment)?
1775                        .value
1776                    {
1777                        if let Ok(UpgradeableLoaderState::ProgramData {
1778                            upgrade_authority_address,
1779                            slot,
1780                        }) = programdata_account.state()
1781                        {
1782                            Ok(config
1783                                .output_format
1784                                .formatted_string(&CliUpgradeableProgram {
1785                                    program_id: account_pubkey.to_string(),
1786                                    owner: account.owner.to_string(),
1787                                    programdata_address: programdata_address.to_string(),
1788                                    authority: upgrade_authority_address
1789                                        .map(|pubkey| pubkey.to_string())
1790                                        .unwrap_or_else(|| "none".to_string()),
1791                                    last_deploy_slot: slot,
1792                                    data_len: programdata_account.data.len()
1793                                        - UpgradeableLoaderState::size_of_programdata_metadata(),
1794                                    lamports: programdata_account.lamports,
1795                                    use_lamports_unit,
1796                                }))
1797                        } else {
1798                            Err(format!("Program {account_pubkey} has been closed").into())
1799                        }
1800                    } else {
1801                        Err(format!("Program {account_pubkey} has been closed").into())
1802                    }
1803                } else if let Ok(UpgradeableLoaderState::Buffer { authority_address }) =
1804                    account.state()
1805                {
1806                    Ok(config
1807                        .output_format
1808                        .formatted_string(&CliUpgradeableBuffer {
1809                            address: account_pubkey.to_string(),
1810                            authority: authority_address
1811                                .map(|pubkey| pubkey.to_string())
1812                                .unwrap_or_else(|| "none".to_string()),
1813                            data_len: account.data.len()
1814                                - UpgradeableLoaderState::size_of_buffer_metadata(),
1815                            lamports: account.lamports,
1816                            use_lamports_unit,
1817                        }))
1818                } else {
1819                    Err(format!(
1820                        "{account_pubkey} is not an upgradeable loader Buffer or Program account"
1821                    )
1822                    .into())
1823                }
1824            } else {
1825                Err(format!("{account_pubkey} is not an SBF program").into())
1826            }
1827        } else {
1828            Err(format!("Unable to find the account {account_pubkey}").into())
1829        }
1830    } else if programs {
1831        let authority_pubkey = if all { None } else { Some(authority_pubkey) };
1832        let programs = get_programs(rpc_client, authority_pubkey, use_lamports_unit)?;
1833        Ok(config.output_format.formatted_string(&programs))
1834    } else if buffers {
1835        let authority_pubkey = if all { None } else { Some(authority_pubkey) };
1836        let buffers = get_buffers(rpc_client, authority_pubkey, use_lamports_unit)?;
1837        Ok(config.output_format.formatted_string(&buffers))
1838    } else {
1839        Err("Invalid parameters".to_string().into())
1840    }
1841}
1842
1843fn process_dump(
1844    rpc_client: &RpcClient,
1845    config: &CliConfig,
1846    account_pubkey: Option<Pubkey>,
1847    output_location: &str,
1848) -> ProcessResult {
1849    if let Some(account_pubkey) = account_pubkey {
1850        if let Some(account) = rpc_client
1851            .get_account_with_commitment(&account_pubkey, config.commitment)?
1852            .value
1853        {
1854            if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
1855                let mut f = File::create(output_location)?;
1856                f.write_all(&account.data)?;
1857                Ok(format!("Wrote program to {output_location}"))
1858            } else if account.owner == bpf_loader_upgradeable::id() {
1859                if let Ok(UpgradeableLoaderState::Program {
1860                    programdata_address,
1861                }) = account.state()
1862                {
1863                    if let Some(programdata_account) = rpc_client
1864                        .get_account_with_commitment(&programdata_address, config.commitment)?
1865                        .value
1866                    {
1867                        if let Ok(UpgradeableLoaderState::ProgramData { .. }) =
1868                            programdata_account.state()
1869                        {
1870                            let offset = UpgradeableLoaderState::size_of_programdata_metadata();
1871                            let program_data = &programdata_account.data[offset..];
1872                            let mut f = File::create(output_location)?;
1873                            f.write_all(program_data)?;
1874                            Ok(format!("Wrote program to {output_location}"))
1875                        } else {
1876                            Err(format!("Program {account_pubkey} has been closed").into())
1877                        }
1878                    } else {
1879                        Err(format!("Program {account_pubkey} has been closed").into())
1880                    }
1881                } else if let Ok(UpgradeableLoaderState::Buffer { .. }) = account.state() {
1882                    let offset = UpgradeableLoaderState::size_of_buffer_metadata();
1883                    let program_data = &account.data[offset..];
1884                    let mut f = File::create(output_location)?;
1885                    f.write_all(program_data)?;
1886                    Ok(format!("Wrote program to {output_location}"))
1887                } else {
1888                    Err(format!(
1889                        "{account_pubkey} is not an upgradeable loader buffer or program account"
1890                    )
1891                    .into())
1892                }
1893            } else {
1894                Err(format!("{account_pubkey} is not an SBF program").into())
1895            }
1896        } else {
1897            Err(format!("Unable to find the account {account_pubkey}").into())
1898        }
1899    } else {
1900        Err("No account specified".into())
1901    }
1902}
1903
1904fn close(
1905    rpc_client: &RpcClient,
1906    config: &CliConfig,
1907    account_pubkey: &Pubkey,
1908    recipient_pubkey: &Pubkey,
1909    authority_signer: &dyn Signer,
1910    program_pubkey: Option<&Pubkey>,
1911) -> Result<(), Box<dyn std::error::Error>> {
1912    let blockhash = rpc_client.get_latest_blockhash()?;
1913
1914    let mut tx = Transaction::new_unsigned(Message::new(
1915        &[bpf_loader_upgradeable::close_any(
1916            account_pubkey,
1917            recipient_pubkey,
1918            Some(&authority_signer.pubkey()),
1919            program_pubkey,
1920        )],
1921        Some(&config.signers[0].pubkey()),
1922    ));
1923
1924    tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
1925    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
1926        &tx,
1927        config.commitment,
1928        RpcSendTransactionConfig {
1929            preflight_commitment: Some(config.commitment.commitment),
1930            ..RpcSendTransactionConfig::default()
1931        },
1932    );
1933    if let Err(err) = result {
1934        if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
1935            _,
1936            InstructionError::InvalidInstructionData,
1937        )) = err.kind()
1938        {
1939            return Err("Closing a buffer account is not supported by the cluster".into());
1940        } else if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
1941            _,
1942            InstructionError::InvalidArgument,
1943        )) = err.kind()
1944        {
1945            return Err("Closing a program account is not supported by the cluster".into());
1946        } else {
1947            return Err(format!("Close failed: {err}").into());
1948        }
1949    }
1950    Ok(())
1951}
1952
1953fn process_close(
1954    rpc_client: &RpcClient,
1955    config: &CliConfig,
1956    account_pubkey: Option<Pubkey>,
1957    recipient_pubkey: Pubkey,
1958    authority_index: SignerIndex,
1959    use_lamports_unit: bool,
1960    bypass_warning: bool,
1961) -> ProcessResult {
1962    let authority_signer = config.signers[authority_index];
1963
1964    if let Some(account_pubkey) = account_pubkey {
1965        if let Some(account) = rpc_client
1966            .get_account_with_commitment(&account_pubkey, config.commitment)?
1967            .value
1968        {
1969            match account.state() {
1970                Ok(UpgradeableLoaderState::Buffer { authority_address }) => {
1971                    if authority_address != Some(authority_signer.pubkey()) {
1972                        return Err(format!(
1973                            "Buffer account authority {:?} does not match {:?}",
1974                            authority_address,
1975                            Some(authority_signer.pubkey())
1976                        )
1977                        .into());
1978                    } else {
1979                        close(
1980                            rpc_client,
1981                            config,
1982                            &account_pubkey,
1983                            &recipient_pubkey,
1984                            authority_signer,
1985                            None,
1986                        )?;
1987                    }
1988                    Ok(config
1989                        .output_format
1990                        .formatted_string(&CliUpgradeableBuffers {
1991                            buffers: vec![CliUpgradeableBuffer {
1992                                address: account_pubkey.to_string(),
1993                                authority: authority_address
1994                                    .map(|pubkey| pubkey.to_string())
1995                                    .unwrap_or_else(|| "none".to_string()),
1996                                data_len: 0,
1997                                lamports: account.lamports,
1998                                use_lamports_unit,
1999                            }],
2000                            use_lamports_unit,
2001                        }))
2002                }
2003                Ok(UpgradeableLoaderState::Program {
2004                    programdata_address: programdata_pubkey,
2005                }) => {
2006                    if let Some(account) = rpc_client
2007                        .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2008                        .value
2009                    {
2010                        if let Ok(UpgradeableLoaderState::ProgramData {
2011                            slot: _,
2012                            upgrade_authority_address: authority_pubkey,
2013                        }) = account.state()
2014                        {
2015                            if authority_pubkey != Some(authority_signer.pubkey()) {
2016                                Err(format!(
2017                                    "Program authority {:?} does not match {:?}",
2018                                    authority_pubkey,
2019                                    Some(authority_signer.pubkey())
2020                                )
2021                                .into())
2022                            } else {
2023                                if !bypass_warning {
2024                                    return Err(String::from(CLOSE_PROGRAM_WARNING).into());
2025                                }
2026                                close(
2027                                    rpc_client,
2028                                    config,
2029                                    &programdata_pubkey,
2030                                    &recipient_pubkey,
2031                                    authority_signer,
2032                                    Some(&account_pubkey),
2033                                )?;
2034                                Ok(config.output_format.formatted_string(
2035                                    &CliUpgradeableProgramClosed {
2036                                        program_id: account_pubkey.to_string(),
2037                                        lamports: account.lamports,
2038                                        use_lamports_unit,
2039                                    },
2040                                ))
2041                            }
2042                        } else {
2043                            Err(format!("Program {account_pubkey} has been closed").into())
2044                        }
2045                    } else {
2046                        Err(format!("Program {account_pubkey} has been closed").into())
2047                    }
2048                }
2049                _ => Err(format!("{account_pubkey} is not a Program or Buffer account").into()),
2050            }
2051        } else {
2052            Err(format!("Unable to find the account {account_pubkey}").into())
2053        }
2054    } else {
2055        let buffers = get_buffers(
2056            rpc_client,
2057            Some(authority_signer.pubkey()),
2058            use_lamports_unit,
2059        )?;
2060
2061        let mut closed = vec![];
2062        for buffer in buffers.buffers.iter() {
2063            if close(
2064                rpc_client,
2065                config,
2066                &Pubkey::from_str(&buffer.address)?,
2067                &recipient_pubkey,
2068                authority_signer,
2069                None,
2070            )
2071            .is_ok()
2072            {
2073                closed.push(buffer.clone());
2074            }
2075        }
2076        Ok(config
2077            .output_format
2078            .formatted_string(&CliUpgradeableBuffers {
2079                buffers: closed,
2080                use_lamports_unit,
2081            }))
2082    }
2083}
2084
2085fn process_extend_program(
2086    rpc_client: &RpcClient,
2087    config: &CliConfig,
2088    program_pubkey: Pubkey,
2089    additional_bytes: u32,
2090) -> ProcessResult {
2091    let payer_pubkey = config.signers[0].pubkey();
2092
2093    if additional_bytes == 0 {
2094        return Err("Additional bytes must be greater than zero".into());
2095    }
2096
2097    let program_account = match rpc_client
2098        .get_account_with_commitment(&program_pubkey, config.commitment)?
2099        .value
2100    {
2101        Some(program_account) => Ok(program_account),
2102        None => Err(format!("Unable to find program {program_pubkey}")),
2103    }?;
2104
2105    if !bpf_loader_upgradeable::check_id(&program_account.owner) {
2106        return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2107    }
2108
2109    let programdata_pubkey = match program_account.state() {
2110        Ok(UpgradeableLoaderState::Program {
2111            programdata_address: programdata_pubkey,
2112        }) => Ok(programdata_pubkey),
2113        _ => Err(format!(
2114            "Account {program_pubkey} is not an upgradeable program"
2115        )),
2116    }?;
2117
2118    let programdata_account = match rpc_client
2119        .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2120        .value
2121    {
2122        Some(programdata_account) => Ok(programdata_account),
2123        None => Err(format!("Program {program_pubkey} is closed")),
2124    }?;
2125
2126    let upgrade_authority_address = match programdata_account.state() {
2127        Ok(UpgradeableLoaderState::ProgramData {
2128            slot: _,
2129            upgrade_authority_address,
2130        }) => Ok(upgrade_authority_address),
2131        _ => Err(format!("Program {program_pubkey} is closed")),
2132    }?;
2133
2134    match upgrade_authority_address {
2135        None => Err(format!("Program {program_pubkey} is not upgradeable")),
2136        _ => Ok(()),
2137    }?;
2138
2139    let blockhash = rpc_client.get_latest_blockhash()?;
2140
2141    let mut tx = Transaction::new_unsigned(Message::new(
2142        &[bpf_loader_upgradeable::extend_program(
2143            &program_pubkey,
2144            Some(&payer_pubkey),
2145            additional_bytes,
2146        )],
2147        Some(&payer_pubkey),
2148    ));
2149
2150    tx.try_sign(&[config.signers[0]], blockhash)?;
2151    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2152        &tx,
2153        config.commitment,
2154        RpcSendTransactionConfig {
2155            preflight_commitment: Some(config.commitment.commitment),
2156            ..RpcSendTransactionConfig::default()
2157        },
2158    );
2159    if let Err(err) = result {
2160        if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2161            _,
2162            InstructionError::InvalidInstructionData,
2163        )) = err.kind()
2164        {
2165            return Err("Extending a program is not supported by the cluster".into());
2166        } else {
2167            return Err(format!("Extend program failed: {err}").into());
2168        }
2169    }
2170
2171    Ok(config
2172        .output_format
2173        .formatted_string(&CliUpgradeableProgramExtended {
2174            program_id: program_pubkey.to_string(),
2175            additional_bytes,
2176        }))
2177}
2178
2179pub fn calculate_max_chunk_size<F>(create_msg: &F) -> usize
2180where
2181    F: Fn(u32, Vec<u8>) -> Message,
2182{
2183    let baseline_msg = create_msg(0, Vec::new());
2184    let tx_size = bincode::serialized_size(&Transaction {
2185        signatures: vec![
2186            Signature::default();
2187            baseline_msg.header.num_required_signatures as usize
2188        ],
2189        message: baseline_msg,
2190    })
2191    .unwrap() as usize;
2192    // add 1 byte buffer to account for shortvec encoding
2193    PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1)
2194}
2195
2196#[allow(clippy::too_many_arguments)]
2197fn do_process_program_write_and_deploy(
2198    rpc_client: Arc<RpcClient>,
2199    config: &CliConfig,
2200    program_data: &[u8], // can be empty, hence we have program_len
2201    program_len: usize,
2202    program_data_max_len: usize,
2203    min_rent_exempt_program_data_balance: u64,
2204    loader_id: &Pubkey,
2205    fee_payer_signer: &dyn Signer,
2206    program_signers: Option<&[&dyn Signer]>,
2207    buffer_signer: Option<&dyn Signer>,
2208    buffer_pubkey: &Pubkey,
2209    buffer_authority_signer: &dyn Signer,
2210    allow_excessive_balance: bool,
2211    skip_fee_check: bool,
2212) -> ProcessResult {
2213    let blockhash = rpc_client.get_latest_blockhash()?;
2214
2215    // Initialize buffer account or complete if already partially initialized
2216    let (initial_instructions, balance_needed) = if let Some(account) = rpc_client
2217        .get_account_with_commitment(buffer_pubkey, config.commitment)?
2218        .value
2219    {
2220        complete_partial_program_init(
2221            loader_id,
2222            &fee_payer_signer.pubkey(),
2223            buffer_pubkey,
2224            &account,
2225            if loader_id == &bpf_loader_upgradeable::id() {
2226                UpgradeableLoaderState::size_of_buffer(program_len)
2227            } else {
2228                program_len
2229            },
2230            min_rent_exempt_program_data_balance,
2231            allow_excessive_balance,
2232        )?
2233    } else if loader_id == &bpf_loader_upgradeable::id() {
2234        (
2235            bpf_loader_upgradeable::create_buffer(
2236                &fee_payer_signer.pubkey(),
2237                buffer_pubkey,
2238                &buffer_authority_signer.pubkey(),
2239                min_rent_exempt_program_data_balance,
2240                program_len,
2241            )?,
2242            min_rent_exempt_program_data_balance,
2243        )
2244    } else {
2245        (
2246            vec![system_instruction::create_account(
2247                &fee_payer_signer.pubkey(),
2248                buffer_pubkey,
2249                min_rent_exempt_program_data_balance,
2250                program_len as u64,
2251                loader_id,
2252            )],
2253            min_rent_exempt_program_data_balance,
2254        )
2255    };
2256    let initial_message = if !initial_instructions.is_empty() {
2257        Some(Message::new_with_blockhash(
2258            &initial_instructions,
2259            Some(&fee_payer_signer.pubkey()),
2260            &blockhash,
2261        ))
2262    } else {
2263        None
2264    };
2265
2266    // Create and add write messages
2267    let create_msg = |offset: u32, bytes: Vec<u8>| {
2268        let instruction = if loader_id == &bpf_loader_upgradeable::id() {
2269            bpf_loader_upgradeable::write(
2270                buffer_pubkey,
2271                &buffer_authority_signer.pubkey(),
2272                offset,
2273                bytes,
2274            )
2275        } else {
2276            loader_instruction::write(buffer_pubkey, loader_id, offset, bytes)
2277        };
2278        Message::new_with_blockhash(&[instruction], Some(&fee_payer_signer.pubkey()), &blockhash)
2279    };
2280
2281    let mut write_messages = vec![];
2282    let chunk_size = calculate_max_chunk_size(&create_msg);
2283    for (chunk, i) in program_data.chunks(chunk_size).zip(0..) {
2284        write_messages.push(create_msg((i * chunk_size) as u32, chunk.to_vec()));
2285    }
2286
2287    // Create and add final message
2288    let final_message = if let Some(program_signers) = program_signers {
2289        let message = if loader_id == &bpf_loader_upgradeable::id() {
2290            Message::new_with_blockhash(
2291                &bpf_loader_upgradeable::deploy_with_max_program_len(
2292                    &fee_payer_signer.pubkey(),
2293                    &program_signers[0].pubkey(),
2294                    buffer_pubkey,
2295                    &program_signers[1].pubkey(),
2296                    rpc_client.get_minimum_balance_for_rent_exemption(
2297                        UpgradeableLoaderState::size_of_program(),
2298                    )?,
2299                    program_data_max_len,
2300                )?,
2301                Some(&fee_payer_signer.pubkey()),
2302                &blockhash,
2303            )
2304        } else {
2305            Message::new_with_blockhash(
2306                &[loader_instruction::finalize(buffer_pubkey, loader_id)],
2307                Some(&fee_payer_signer.pubkey()),
2308                &blockhash,
2309            )
2310        };
2311        Some(message)
2312    } else {
2313        None
2314    };
2315
2316    if !skip_fee_check {
2317        check_payer(
2318            &rpc_client,
2319            config,
2320            fee_payer_signer.pubkey(),
2321            balance_needed,
2322            &initial_message,
2323            &write_messages,
2324            &final_message,
2325        )?;
2326    }
2327
2328    let final_tx_sig = send_deploy_messages(
2329        rpc_client,
2330        config,
2331        &initial_message,
2332        &write_messages,
2333        &final_message,
2334        fee_payer_signer,
2335        buffer_signer,
2336        Some(buffer_authority_signer),
2337        program_signers,
2338    )?;
2339
2340    if let Some(program_signers) = program_signers {
2341        let program_id = CliProgramId {
2342            program_id: program_signers[0].pubkey().to_string(),
2343            signature: final_tx_sig.as_ref().map(ToString::to_string),
2344        };
2345        Ok(config.output_format.formatted_string(&program_id))
2346    } else {
2347        let buffer = CliProgramBuffer {
2348            buffer: buffer_pubkey.to_string(),
2349        };
2350        Ok(config.output_format.formatted_string(&buffer))
2351    }
2352}
2353
2354#[allow(clippy::too_many_arguments)]
2355fn do_process_program_upgrade(
2356    rpc_client: Arc<RpcClient>,
2357    config: &CliConfig,
2358    program_data: &[u8], // can be empty, hence we have program_len
2359    program_len: usize,
2360    min_rent_exempt_program_data_balance: u64,
2361    fee_payer_signer: &dyn Signer,
2362    program_id: &Pubkey,
2363    upgrade_authority: &dyn Signer,
2364    buffer_pubkey: &Pubkey,
2365    buffer_signer: Option<&dyn Signer>,
2366    skip_fee_check: bool,
2367) -> ProcessResult {
2368    let blockhash = rpc_client.get_latest_blockhash()?;
2369
2370    let (initial_message, write_messages, balance_needed) =
2371        if let Some(buffer_signer) = buffer_signer {
2372            // Check Buffer account to see if partial initialization has occurred
2373            let (initial_instructions, balance_needed) = if let Some(account) = rpc_client
2374                .get_account_with_commitment(&buffer_signer.pubkey(), config.commitment)?
2375                .value
2376            {
2377                complete_partial_program_init(
2378                    &bpf_loader_upgradeable::id(),
2379                    &fee_payer_signer.pubkey(),
2380                    &buffer_signer.pubkey(),
2381                    &account,
2382                    UpgradeableLoaderState::size_of_buffer(program_len),
2383                    min_rent_exempt_program_data_balance,
2384                    true,
2385                )?
2386            } else {
2387                (
2388                    bpf_loader_upgradeable::create_buffer(
2389                        &fee_payer_signer.pubkey(),
2390                        buffer_pubkey,
2391                        &upgrade_authority.pubkey(),
2392                        min_rent_exempt_program_data_balance,
2393                        program_len,
2394                    )?,
2395                    min_rent_exempt_program_data_balance,
2396                )
2397            };
2398
2399            let initial_message = if !initial_instructions.is_empty() {
2400                Some(Message::new_with_blockhash(
2401                    &initial_instructions,
2402                    Some(&fee_payer_signer.pubkey()),
2403                    &blockhash,
2404                ))
2405            } else {
2406                None
2407            };
2408
2409            let buffer_signer_pubkey = buffer_signer.pubkey();
2410            let upgrade_authority_pubkey = upgrade_authority.pubkey();
2411            let create_msg = |offset: u32, bytes: Vec<u8>| {
2412                let instruction = bpf_loader_upgradeable::write(
2413                    &buffer_signer_pubkey,
2414                    &upgrade_authority_pubkey,
2415                    offset,
2416                    bytes,
2417                );
2418                Message::new_with_blockhash(
2419                    &[instruction],
2420                    Some(&fee_payer_signer.pubkey()),
2421                    &blockhash,
2422                )
2423            };
2424
2425            // Create and add write messages
2426            let mut write_messages = vec![];
2427            let chunk_size = calculate_max_chunk_size(&create_msg);
2428            for (chunk, i) in program_data.chunks(chunk_size).zip(0..) {
2429                write_messages.push(create_msg((i * chunk_size) as u32, chunk.to_vec()));
2430            }
2431
2432            (initial_message, write_messages, balance_needed)
2433        } else {
2434            (None, vec![], 0)
2435        };
2436
2437    // Create and add final message
2438    let final_message = Message::new_with_blockhash(
2439        &[bpf_loader_upgradeable::upgrade(
2440            program_id,
2441            buffer_pubkey,
2442            &upgrade_authority.pubkey(),
2443            &fee_payer_signer.pubkey(),
2444        )],
2445        Some(&fee_payer_signer.pubkey()),
2446        &blockhash,
2447    );
2448    let final_message = Some(final_message);
2449
2450    if !skip_fee_check {
2451        check_payer(
2452            &rpc_client,
2453            config,
2454            fee_payer_signer.pubkey(),
2455            balance_needed,
2456            &initial_message,
2457            &write_messages,
2458            &final_message,
2459        )?;
2460    }
2461
2462    let final_tx_sig = send_deploy_messages(
2463        rpc_client,
2464        config,
2465        &initial_message,
2466        &write_messages,
2467        &final_message,
2468        fee_payer_signer,
2469        buffer_signer,
2470        Some(upgrade_authority),
2471        Some(&[upgrade_authority]),
2472    )?;
2473
2474    let program_id = CliProgramId {
2475        program_id: program_id.to_string(),
2476        signature: final_tx_sig.as_ref().map(ToString::to_string),
2477    };
2478    Ok(config.output_format.formatted_string(&program_id))
2479}
2480
2481fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
2482    let mut file = File::open(program_location)
2483        .map_err(|err| format!("Unable to open program file: {err}"))?;
2484    let mut program_data = Vec::new();
2485    file.read_to_end(&mut program_data)
2486        .map_err(|err| format!("Unable to read program file: {err}"))?;
2487
2488    // Verify the program
2489    let program_runtime_environment = create_program_runtime_environment_v1(
2490        &FeatureSet::all_enabled(),
2491        &ComputeBudget::default(),
2492        true,
2493        false,
2494    )
2495    .unwrap();
2496    let executable =
2497        Executable::<InvokeContext>::from_elf(&program_data, Arc::new(program_runtime_environment))
2498            .map_err(|err| format!("ELF error: {err}"))?;
2499
2500    executable
2501        .verify::<RequisiteVerifier>()
2502        .map_err(|err| format!("ELF error: {err}"))?;
2503
2504    Ok(program_data)
2505}
2506
2507fn complete_partial_program_init(
2508    loader_id: &Pubkey,
2509    payer_pubkey: &Pubkey,
2510    elf_pubkey: &Pubkey,
2511    account: &Account,
2512    account_data_len: usize,
2513    minimum_balance: u64,
2514    allow_excessive_balance: bool,
2515) -> Result<(Vec<Instruction>, u64), Box<dyn std::error::Error>> {
2516    let mut instructions: Vec<Instruction> = vec![];
2517    let mut balance_needed = 0;
2518    if is_account_executable(account) {
2519        return Err("Buffer account is already executable".into());
2520    }
2521    if account.owner != *loader_id && !system_program::check_id(&account.owner) {
2522        return Err("Buffer account passed is already in use by another program".into());
2523    }
2524    if !account.data.is_empty() && account.data.len() < account_data_len {
2525        return Err(
2526            "Buffer account passed is not large enough, may have been for a different deploy?"
2527                .into(),
2528        );
2529    }
2530
2531    if account.data.is_empty() && system_program::check_id(&account.owner) {
2532        instructions.push(system_instruction::allocate(
2533            elf_pubkey,
2534            account_data_len as u64,
2535        ));
2536        instructions.push(system_instruction::assign(elf_pubkey, loader_id));
2537        if account.lamports < minimum_balance {
2538            let balance = minimum_balance - account.lamports;
2539            instructions.push(system_instruction::transfer(
2540                payer_pubkey,
2541                elf_pubkey,
2542                balance,
2543            ));
2544            balance_needed = balance;
2545        } else if account.lamports > minimum_balance
2546            && system_program::check_id(&account.owner)
2547            && !allow_excessive_balance
2548        {
2549            return Err(format!(
2550                "Buffer account has a balance: {:?}; it may already be in use",
2551                Mln(account.lamports)
2552            )
2553            .into());
2554        }
2555    }
2556    Ok((instructions, balance_needed))
2557}
2558
2559fn check_payer(
2560    rpc_client: &RpcClient,
2561    config: &CliConfig,
2562    fee_payer_pubkey: Pubkey,
2563    balance_needed: u64,
2564    initial_message: &Option<Message>,
2565    write_messages: &[Message],
2566    final_message: &Option<Message>,
2567) -> Result<(), Box<dyn std::error::Error>> {
2568    let mut fee = 0;
2569    if let Some(message) = initial_message {
2570        fee += rpc_client.get_fee_for_message(message)?;
2571    }
2572    if !write_messages.is_empty() {
2573        // Assume all write messages cost the same
2574        if let Some(message) = write_messages.first() {
2575            fee += rpc_client.get_fee_for_message(message)? * (write_messages.len() as u64);
2576        }
2577    }
2578    if let Some(message) = final_message {
2579        fee += rpc_client.get_fee_for_message(message)?;
2580    }
2581    check_account_for_spend_and_fee_with_commitment(
2582        rpc_client,
2583        &fee_payer_pubkey,
2584        balance_needed,
2585        fee,
2586        config.commitment,
2587    )?;
2588    Ok(())
2589}
2590
2591fn send_deploy_messages(
2592    rpc_client: Arc<RpcClient>,
2593    config: &CliConfig,
2594    initial_message: &Option<Message>,
2595    write_messages: &[Message],
2596    final_message: &Option<Message>,
2597    fee_payer_signer: &dyn Signer,
2598    initial_signer: Option<&dyn Signer>,
2599    write_signer: Option<&dyn Signer>,
2600    final_signers: Option<&[&dyn Signer]>,
2601) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
2602    if let Some(message) = initial_message {
2603        if let Some(initial_signer) = initial_signer {
2604            trace!("Preparing the required accounts");
2605            let blockhash = rpc_client.get_latest_blockhash()?;
2606
2607            let mut initial_transaction = Transaction::new_unsigned(message.clone());
2608            // Most of the initial_transaction combinations require both the fee-payer and new program
2609            // account to sign the transaction. One (transfer) only requires the fee-payer signature.
2610            // This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
2611            // extraneous signature.
2612            if message.header.num_required_signatures == 2 {
2613                initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
2614            } else {
2615                initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
2616            }
2617            let result = rpc_client.send_and_confirm_transaction_with_spinner(&initial_transaction);
2618            log_instruction_custom_error::<SystemError>(result, config)
2619                .map_err(|err| format!("Account allocation failed: {err}"))?;
2620        } else {
2621            return Err("Buffer account not created yet, must provide a key pair".into());
2622        }
2623    }
2624
2625    if !write_messages.is_empty() {
2626        if let Some(write_signer) = write_signer {
2627            trace!("Writing program data");
2628            let connection_cache = if config.use_quic {
2629                ConnectionCache::new_quic("connection_cache_cli_program_quic", 1)
2630            } else {
2631                ConnectionCache::with_udp("connection_cache_cli_program_udp", 1)
2632            };
2633            let transaction_errors = match connection_cache {
2634                ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
2635                    rpc_client.clone(),
2636                    &config.websocket_url,
2637                    TpuClientConfig::default(),
2638                    cache,
2639                )?
2640                .send_and_confirm_messages_with_spinner(
2641                    write_messages,
2642                    &[fee_payer_signer, write_signer],
2643                ),
2644                ConnectionCache::Quic(cache) => {
2645                    let tpu_client_fut = miraland_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
2646                        rpc_client.get_inner_client().clone(),
2647                        config.websocket_url.as_str(),
2648                        miraland_client::tpu_client::TpuClientConfig::default(),
2649                        cache,
2650                    );
2651                    let tpu_client = rpc_client
2652                        .runtime()
2653                        .block_on(tpu_client_fut)
2654                        .expect("Should return a valid tpu client");
2655
2656                    send_and_confirm_transactions_in_parallel_blocking(
2657                        rpc_client.clone(),
2658                        Some(tpu_client),
2659                        write_messages,
2660                        &[fee_payer_signer, write_signer],
2661                        SendAndConfirmConfig {
2662                            resign_txs_count: Some(5),
2663                            with_spinner: true,
2664                        },
2665                    )
2666                },
2667            }
2668            .map_err(|err| format!("Data writes to account failed: {err}"))?
2669            .into_iter()
2670            .flatten()
2671            .collect::<Vec<_>>();
2672
2673            if !transaction_errors.is_empty() {
2674                for transaction_error in &transaction_errors {
2675                    error!("{:?}", transaction_error);
2676                }
2677                return Err(
2678                    format!("{} write transactions failed", transaction_errors.len()).into(),
2679                );
2680            }
2681        }
2682    }
2683
2684    if let Some(message) = final_message {
2685        if let Some(final_signers) = final_signers {
2686            trace!("Deploying program");
2687            let blockhash = rpc_client.get_latest_blockhash()?;
2688
2689            let mut final_tx = Transaction::new_unsigned(message.clone());
2690            let mut signers = final_signers.to_vec();
2691            signers.push(fee_payer_signer);
2692            final_tx.try_sign(&signers, blockhash)?;
2693            return Ok(Some(
2694                rpc_client
2695                    .send_and_confirm_transaction_with_spinner_and_config(
2696                        &final_tx,
2697                        config.commitment,
2698                        RpcSendTransactionConfig {
2699                            preflight_commitment: Some(config.commitment.commitment),
2700                            ..RpcSendTransactionConfig::default()
2701                        },
2702                    )
2703                    .map_err(|e| format!("Deploying program failed: {e}"))?,
2704            ));
2705        }
2706    }
2707
2708    Ok(None)
2709}
2710
2711fn create_ephemeral_keypair(
2712) -> Result<(usize, bip39::Mnemonic, Keypair), Box<dyn std::error::Error>> {
2713    const WORDS: usize = 12;
2714    let mnemonic = Mnemonic::new(MnemonicType::for_word_count(WORDS)?, Language::English);
2715    let seed = Seed::new(&mnemonic, "");
2716    let new_keypair = keypair_from_seed(seed.as_bytes())?;
2717
2718    Ok((WORDS, mnemonic, new_keypair))
2719}
2720
2721fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic) {
2722    let phrase: &str = mnemonic.phrase();
2723    let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
2724    eprintln!("{divider}\nRecover the intermediate account's ephemeral keypair file with");
2725    eprintln!("`miraland-keygen recover` and the following {words}-word seed phrase:");
2726    eprintln!("{divider}\n{phrase}\n{divider}");
2727    eprintln!("To resume a deploy, pass the recovered keypair as the");
2728    eprintln!("[BUFFER_SIGNER] to `miraland program deploy` or `miraland program write-buffer'.");
2729    eprintln!("Or to recover the account's lamports, pass it as the");
2730    eprintln!("[BUFFER_ACCOUNT_ADDRESS] argument to `miraland program close`.\n{divider}");
2731}
2732
2733#[cfg(test)]
2734mod tests {
2735    use {
2736        super::*,
2737        crate::{
2738            clap_app::get_clap_app,
2739            cli::{parse_command, process_command},
2740        },
2741        miraland_cli_output::OutputFormat,
2742        serde_json::Value,
2743        miraland_sdk::{hash::Hash, signature::write_keypair_file},
2744    };
2745
2746    fn make_tmp_path(name: &str) -> String {
2747        let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
2748        let keypair = Keypair::new();
2749
2750        let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
2751
2752        // whack any possible collision
2753        let _ignored = std::fs::remove_dir_all(&path);
2754        // whack any possible collision
2755        let _ignored = std::fs::remove_file(&path);
2756
2757        path
2758    }
2759
2760    #[test]
2761    #[allow(clippy::cognitive_complexity)]
2762    fn test_cli_parse_deploy() {
2763        let test_commands = get_clap_app("test", "desc", "version");
2764
2765        let default_keypair = Keypair::new();
2766        let keypair_file = make_tmp_path("keypair_file");
2767        write_keypair_file(&default_keypair, &keypair_file).unwrap();
2768        let default_signer = DefaultSigner::new("", &keypair_file);
2769
2770        let test_command = test_commands.clone().get_matches_from(vec![
2771            "test",
2772            "program",
2773            "deploy",
2774            "/Users/test/program.so",
2775        ]);
2776        assert_eq!(
2777            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2778            CliCommandInfo {
2779                command: CliCommand::Program(ProgramCliCommand::Deploy {
2780                    program_location: Some("/Users/test/program.so".to_string()),
2781                    fee_payer_signer_index: 0,
2782                    buffer_signer_index: None,
2783                    buffer_pubkey: None,
2784                    program_signer_index: None,
2785                    program_pubkey: None,
2786                    upgrade_authority_signer_index: 0,
2787                    is_final: false,
2788                    max_len: None,
2789                    allow_excessive_balance: false,
2790                    skip_fee_check: false,
2791                }),
2792                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
2793            }
2794        );
2795
2796        let test_command = test_commands.clone().get_matches_from(vec![
2797            "test",
2798            "program",
2799            "deploy",
2800            "/Users/test/program.so",
2801            "--max-len",
2802            "42",
2803        ]);
2804        assert_eq!(
2805            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2806            CliCommandInfo {
2807                command: CliCommand::Program(ProgramCliCommand::Deploy {
2808                    program_location: Some("/Users/test/program.so".to_string()),
2809                    fee_payer_signer_index: 0,
2810                    buffer_signer_index: None,
2811                    buffer_pubkey: None,
2812                    program_signer_index: None,
2813                    program_pubkey: None,
2814                    upgrade_authority_signer_index: 0,
2815                    is_final: false,
2816                    max_len: Some(42),
2817                    allow_excessive_balance: false,
2818                    skip_fee_check: false,
2819                }),
2820                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
2821            }
2822        );
2823
2824        let buffer_keypair = Keypair::new();
2825        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
2826        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
2827        let test_command = test_commands.clone().get_matches_from(vec![
2828            "test",
2829            "program",
2830            "deploy",
2831            "--buffer",
2832            &buffer_keypair_file,
2833        ]);
2834        assert_eq!(
2835            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2836            CliCommandInfo {
2837                command: CliCommand::Program(ProgramCliCommand::Deploy {
2838                    program_location: None,
2839                    fee_payer_signer_index: 0,
2840                    buffer_signer_index: Some(1),
2841                    buffer_pubkey: Some(buffer_keypair.pubkey()),
2842                    program_signer_index: None,
2843                    program_pubkey: None,
2844                    upgrade_authority_signer_index: 0,
2845                    is_final: false,
2846                    max_len: None,
2847                    allow_excessive_balance: false,
2848                    skip_fee_check: false,
2849                }),
2850                signers: vec![
2851                    read_keypair_file(&keypair_file).unwrap().into(),
2852                    read_keypair_file(&buffer_keypair_file).unwrap().into(),
2853                ],
2854            }
2855        );
2856
2857        let program_pubkey = Pubkey::new_unique();
2858        let test = test_commands.clone().get_matches_from(vec![
2859            "test",
2860            "program",
2861            "deploy",
2862            "/Users/test/program.so",
2863            "--program-id",
2864            &program_pubkey.to_string(),
2865        ]);
2866        assert_eq!(
2867            parse_command(&test, &default_signer, &mut None).unwrap(),
2868            CliCommandInfo {
2869                command: CliCommand::Program(ProgramCliCommand::Deploy {
2870                    program_location: Some("/Users/test/program.so".to_string()),
2871                    fee_payer_signer_index: 0,
2872                    buffer_signer_index: None,
2873                    buffer_pubkey: None,
2874                    program_signer_index: None,
2875                    program_pubkey: Some(program_pubkey),
2876                    upgrade_authority_signer_index: 0,
2877                    is_final: false,
2878                    max_len: None,
2879                    allow_excessive_balance: false,
2880                    skip_fee_check: false,
2881                }),
2882                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
2883            }
2884        );
2885
2886        let program_keypair = Keypair::new();
2887        let program_keypair_file = make_tmp_path("program_keypair_file");
2888        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
2889        let test = test_commands.clone().get_matches_from(vec![
2890            "test",
2891            "program",
2892            "deploy",
2893            "/Users/test/program.so",
2894            "--program-id",
2895            &program_keypair_file,
2896        ]);
2897        assert_eq!(
2898            parse_command(&test, &default_signer, &mut None).unwrap(),
2899            CliCommandInfo {
2900                command: CliCommand::Program(ProgramCliCommand::Deploy {
2901                    program_location: Some("/Users/test/program.so".to_string()),
2902                    fee_payer_signer_index: 0,
2903                    buffer_signer_index: None,
2904                    buffer_pubkey: None,
2905                    program_signer_index: Some(1),
2906                    program_pubkey: Some(program_keypair.pubkey()),
2907                    upgrade_authority_signer_index: 0,
2908                    is_final: false,
2909                    max_len: None,
2910                    allow_excessive_balance: false,
2911                    skip_fee_check: false,
2912                }),
2913                signers: vec![
2914                    read_keypair_file(&keypair_file).unwrap().into(),
2915                    read_keypair_file(&program_keypair_file).unwrap().into(),
2916                ],
2917            }
2918        );
2919
2920        let authority_keypair = Keypair::new();
2921        let authority_keypair_file = make_tmp_path("authority_keypair_file");
2922        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
2923        let test_command = test_commands.clone().get_matches_from(vec![
2924            "test",
2925            "program",
2926            "deploy",
2927            "/Users/test/program.so",
2928            "--upgrade-authority",
2929            &authority_keypair_file,
2930        ]);
2931        assert_eq!(
2932            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2933            CliCommandInfo {
2934                command: CliCommand::Program(ProgramCliCommand::Deploy {
2935                    program_location: Some("/Users/test/program.so".to_string()),
2936                    fee_payer_signer_index: 0,
2937                    buffer_signer_index: None,
2938                    buffer_pubkey: None,
2939                    program_signer_index: None,
2940                    program_pubkey: None,
2941                    upgrade_authority_signer_index: 1,
2942                    is_final: false,
2943                    max_len: None,
2944                    allow_excessive_balance: false,
2945                    skip_fee_check: false,
2946                }),
2947                signers: vec![
2948                    read_keypair_file(&keypair_file).unwrap().into(),
2949                    read_keypair_file(&authority_keypair_file).unwrap().into(),
2950                ],
2951            }
2952        );
2953
2954        let test_command = test_commands.clone().get_matches_from(vec![
2955            "test",
2956            "program",
2957            "deploy",
2958            "/Users/test/program.so",
2959            "--final",
2960        ]);
2961        assert_eq!(
2962            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2963            CliCommandInfo {
2964                command: CliCommand::Program(ProgramCliCommand::Deploy {
2965                    program_location: Some("/Users/test/program.so".to_string()),
2966                    fee_payer_signer_index: 0,
2967                    buffer_signer_index: None,
2968                    buffer_pubkey: None,
2969                    program_signer_index: None,
2970                    program_pubkey: None,
2971                    upgrade_authority_signer_index: 0,
2972                    is_final: true,
2973                    max_len: None,
2974                    skip_fee_check: false,
2975                    allow_excessive_balance: false,
2976                }),
2977                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
2978            }
2979        );
2980    }
2981
2982    #[test]
2983    #[allow(clippy::cognitive_complexity)]
2984    fn test_cli_parse_write_buffer() {
2985        let test_commands = get_clap_app("test", "desc", "version");
2986
2987        let default_keypair = Keypair::new();
2988        let keypair_file = make_tmp_path("keypair_file");
2989        write_keypair_file(&default_keypair, &keypair_file).unwrap();
2990        let default_signer = DefaultSigner::new("", &keypair_file);
2991
2992        // defaults
2993        let test_command = test_commands.clone().get_matches_from(vec![
2994            "test",
2995            "program",
2996            "write-buffer",
2997            "/Users/test/program.so",
2998        ]);
2999        assert_eq!(
3000            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3001            CliCommandInfo {
3002                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3003                    program_location: "/Users/test/program.so".to_string(),
3004                    fee_payer_signer_index: 0,
3005                    buffer_signer_index: None,
3006                    buffer_pubkey: None,
3007                    buffer_authority_signer_index: 0,
3008                    max_len: None,
3009                    skip_fee_check: false,
3010                }),
3011                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3012            }
3013        );
3014
3015        // specify max len
3016        let test_command = test_commands.clone().get_matches_from(vec![
3017            "test",
3018            "program",
3019            "write-buffer",
3020            "/Users/test/program.so",
3021            "--max-len",
3022            "42",
3023        ]);
3024        assert_eq!(
3025            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3026            CliCommandInfo {
3027                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3028                    program_location: "/Users/test/program.so".to_string(),
3029                    fee_payer_signer_index: 0,
3030                    buffer_signer_index: None,
3031                    buffer_pubkey: None,
3032                    buffer_authority_signer_index: 0,
3033                    max_len: Some(42),
3034                    skip_fee_check: false,
3035                }),
3036                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3037            }
3038        );
3039
3040        // specify buffer
3041        let buffer_keypair = Keypair::new();
3042        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3043        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3044        let test_command = test_commands.clone().get_matches_from(vec![
3045            "test",
3046            "program",
3047            "write-buffer",
3048            "/Users/test/program.so",
3049            "--buffer",
3050            &buffer_keypair_file,
3051        ]);
3052        assert_eq!(
3053            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3054            CliCommandInfo {
3055                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3056                    program_location: "/Users/test/program.so".to_string(),
3057                    fee_payer_signer_index: 0,
3058                    buffer_signer_index: Some(1),
3059                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3060                    buffer_authority_signer_index: 0,
3061                    max_len: None,
3062                    skip_fee_check: false,
3063                }),
3064                signers: vec![
3065                    read_keypair_file(&keypair_file).unwrap().into(),
3066                    read_keypair_file(&buffer_keypair_file).unwrap().into(),
3067                ],
3068            }
3069        );
3070
3071        // specify authority
3072        let authority_keypair = Keypair::new();
3073        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3074        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3075        let test_command = test_commands.clone().get_matches_from(vec![
3076            "test",
3077            "program",
3078            "write-buffer",
3079            "/Users/test/program.so",
3080            "--buffer-authority",
3081            &authority_keypair_file,
3082        ]);
3083        assert_eq!(
3084            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3085            CliCommandInfo {
3086                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3087                    program_location: "/Users/test/program.so".to_string(),
3088                    fee_payer_signer_index: 0,
3089                    buffer_signer_index: None,
3090                    buffer_pubkey: None,
3091                    buffer_authority_signer_index: 1,
3092                    max_len: None,
3093                    skip_fee_check: false,
3094                }),
3095                signers: vec![
3096                    read_keypair_file(&keypair_file).unwrap().into(),
3097                    read_keypair_file(&authority_keypair_file).unwrap().into(),
3098                ],
3099            }
3100        );
3101
3102        // specify both buffer and authority
3103        let buffer_keypair = Keypair::new();
3104        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3105        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3106        let authority_keypair = Keypair::new();
3107        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3108        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3109        let test_command = test_commands.clone().get_matches_from(vec![
3110            "test",
3111            "program",
3112            "write-buffer",
3113            "/Users/test/program.so",
3114            "--buffer",
3115            &buffer_keypair_file,
3116            "--buffer-authority",
3117            &authority_keypair_file,
3118        ]);
3119        assert_eq!(
3120            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3121            CliCommandInfo {
3122                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3123                    program_location: "/Users/test/program.so".to_string(),
3124                    fee_payer_signer_index: 0,
3125                    buffer_signer_index: Some(1),
3126                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3127                    buffer_authority_signer_index: 2,
3128                    max_len: None,
3129                    skip_fee_check: false,
3130                }),
3131                signers: vec![
3132                    read_keypair_file(&keypair_file).unwrap().into(),
3133                    read_keypair_file(&buffer_keypair_file).unwrap().into(),
3134                    read_keypair_file(&authority_keypair_file).unwrap().into(),
3135                ],
3136            }
3137        );
3138    }
3139
3140    #[test]
3141    #[allow(clippy::cognitive_complexity)]
3142    fn test_cli_parse_set_upgrade_authority() {
3143        let test_commands = get_clap_app("test", "desc", "version");
3144
3145        let default_keypair = Keypair::new();
3146        let keypair_file = make_tmp_path("keypair_file");
3147        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3148        let default_signer = DefaultSigner::new("", &keypair_file);
3149
3150        let program_pubkey = Pubkey::new_unique();
3151        let new_authority_pubkey = Pubkey::new_unique();
3152        let blockhash = Hash::new_unique();
3153
3154        let test_command = test_commands.clone().get_matches_from(vec![
3155            "test",
3156            "program",
3157            "set-upgrade-authority",
3158            &program_pubkey.to_string(),
3159            "--new-upgrade-authority",
3160            &new_authority_pubkey.to_string(),
3161            "--skip-new-upgrade-authority-signer-check",
3162            "--sign-only",
3163            "--dump-transaction-message",
3164            "--blockhash",
3165            blockhash.to_string().as_str(),
3166        ]);
3167        assert_eq!(
3168            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3169            CliCommandInfo {
3170                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3171                    program_pubkey,
3172                    upgrade_authority_index: Some(0),
3173                    new_upgrade_authority: Some(new_authority_pubkey),
3174                    sign_only: true,
3175                    dump_transaction_message: true,
3176                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
3177                }),
3178                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3179            }
3180        );
3181
3182        let program_pubkey = Pubkey::new_unique();
3183        let new_authority_pubkey = Keypair::new();
3184        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3185        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3186        let test_command = test_commands.clone().get_matches_from(vec![
3187            "test",
3188            "program",
3189            "set-upgrade-authority",
3190            &program_pubkey.to_string(),
3191            "--new-upgrade-authority",
3192            &new_authority_pubkey_file,
3193            "--skip-new-upgrade-authority-signer-check",
3194        ]);
3195        assert_eq!(
3196            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3197            CliCommandInfo {
3198                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3199                    program_pubkey,
3200                    upgrade_authority_index: Some(0),
3201                    new_upgrade_authority: Some(new_authority_pubkey.pubkey()),
3202                    sign_only: false,
3203                    dump_transaction_message: false,
3204                    blockhash_query: BlockhashQuery::default(),
3205                }),
3206                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3207            }
3208        );
3209
3210        let blockhash = Hash::new_unique();
3211        let program_pubkey = Pubkey::new_unique();
3212        let new_authority_pubkey = Keypair::new();
3213        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3214        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3215        let test_command = test_commands.clone().get_matches_from(vec![
3216            "test",
3217            "program",
3218            "set-upgrade-authority",
3219            &program_pubkey.to_string(),
3220            "--new-upgrade-authority",
3221            &new_authority_pubkey_file,
3222            "--sign-only",
3223            "--dump-transaction-message",
3224            "--blockhash",
3225            blockhash.to_string().as_str(),
3226        ]);
3227        assert_eq!(
3228            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3229            CliCommandInfo {
3230                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
3231                    program_pubkey,
3232                    upgrade_authority_index: 0,
3233                    new_upgrade_authority_index: 1,
3234                    sign_only: true,
3235                    dump_transaction_message: true,
3236                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
3237                }),
3238                signers: vec![
3239                    read_keypair_file(&keypair_file).unwrap().into(),
3240                    read_keypair_file(&new_authority_pubkey_file)
3241                        .unwrap()
3242                        .into(),
3243                ],
3244            }
3245        );
3246
3247        let program_pubkey = Pubkey::new_unique();
3248        let new_authority_pubkey = Keypair::new();
3249        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3250        write_keypair_file(&new_authority_pubkey, new_authority_pubkey_file).unwrap();
3251        let test_command = test_commands.clone().get_matches_from(vec![
3252            "test",
3253            "program",
3254            "set-upgrade-authority",
3255            &program_pubkey.to_string(),
3256            "--final",
3257        ]);
3258        assert_eq!(
3259            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3260            CliCommandInfo {
3261                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3262                    program_pubkey,
3263                    upgrade_authority_index: Some(0),
3264                    new_upgrade_authority: None,
3265                    sign_only: false,
3266                    dump_transaction_message: false,
3267                    blockhash_query: BlockhashQuery::default(),
3268                }),
3269                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3270            }
3271        );
3272
3273        let program_pubkey = Pubkey::new_unique();
3274        let authority = Keypair::new();
3275        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3276        write_keypair_file(&authority, &authority_keypair_file).unwrap();
3277        let test_command = test_commands.clone().get_matches_from(vec![
3278            "test",
3279            "program",
3280            "set-upgrade-authority",
3281            &program_pubkey.to_string(),
3282            "--upgrade-authority",
3283            &authority_keypair_file,
3284            "--final",
3285        ]);
3286        assert_eq!(
3287            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3288            CliCommandInfo {
3289                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3290                    program_pubkey,
3291                    upgrade_authority_index: Some(1),
3292                    new_upgrade_authority: None,
3293                    sign_only: false,
3294                    dump_transaction_message: false,
3295                    blockhash_query: BlockhashQuery::default(),
3296                }),
3297                signers: vec![
3298                    read_keypair_file(&keypair_file).unwrap().into(),
3299                    read_keypair_file(&authority_keypair_file).unwrap().into(),
3300                ],
3301            }
3302        );
3303    }
3304
3305    #[test]
3306    #[allow(clippy::cognitive_complexity)]
3307    fn test_cli_parse_set_buffer_authority() {
3308        let test_commands = get_clap_app("test", "desc", "version");
3309
3310        let default_keypair = Keypair::new();
3311        let keypair_file = make_tmp_path("keypair_file");
3312        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3313        let default_signer = DefaultSigner::new("", &keypair_file);
3314
3315        let buffer_pubkey = Pubkey::new_unique();
3316        let new_authority_pubkey = Pubkey::new_unique();
3317        let test_command = test_commands.clone().get_matches_from(vec![
3318            "test",
3319            "program",
3320            "set-buffer-authority",
3321            &buffer_pubkey.to_string(),
3322            "--new-buffer-authority",
3323            &new_authority_pubkey.to_string(),
3324        ]);
3325        assert_eq!(
3326            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3327            CliCommandInfo {
3328                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
3329                    buffer_pubkey,
3330                    buffer_authority_index: Some(0),
3331                    new_buffer_authority: new_authority_pubkey,
3332                }),
3333                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3334            }
3335        );
3336
3337        let buffer_pubkey = Pubkey::new_unique();
3338        let new_authority_keypair = Keypair::new();
3339        let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
3340        write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
3341        let test_command = test_commands.clone().get_matches_from(vec![
3342            "test",
3343            "program",
3344            "set-buffer-authority",
3345            &buffer_pubkey.to_string(),
3346            "--new-buffer-authority",
3347            &new_authority_keypair_file,
3348        ]);
3349        assert_eq!(
3350            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3351            CliCommandInfo {
3352                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
3353                    buffer_pubkey,
3354                    buffer_authority_index: Some(0),
3355                    new_buffer_authority: new_authority_keypair.pubkey(),
3356                }),
3357                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3358            }
3359        );
3360    }
3361
3362    #[test]
3363    #[allow(clippy::cognitive_complexity)]
3364    fn test_cli_parse_show() {
3365        let test_commands = get_clap_app("test", "desc", "version");
3366
3367        let default_keypair = Keypair::new();
3368        let keypair_file = make_tmp_path("keypair_file");
3369        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3370        let default_signer = DefaultSigner::new("", &keypair_file);
3371
3372        // defaults
3373        let buffer_pubkey = Pubkey::new_unique();
3374        let authority_keypair = Keypair::new();
3375        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3376        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3377
3378        let test_command = test_commands.clone().get_matches_from(vec![
3379            "test",
3380            "program",
3381            "show",
3382            &buffer_pubkey.to_string(),
3383        ]);
3384        assert_eq!(
3385            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3386            CliCommandInfo {
3387                command: CliCommand::Program(ProgramCliCommand::Show {
3388                    account_pubkey: Some(buffer_pubkey),
3389                    authority_pubkey: default_keypair.pubkey(),
3390                    get_programs: false,
3391                    get_buffers: false,
3392                    all: false,
3393                    use_lamports_unit: false,
3394                }),
3395                signers: vec![],
3396            }
3397        );
3398
3399        let test_command = test_commands.clone().get_matches_from(vec![
3400            "test",
3401            "program",
3402            "show",
3403            "--programs",
3404            "--all",
3405            "--lamports",
3406        ]);
3407        assert_eq!(
3408            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3409            CliCommandInfo {
3410                command: CliCommand::Program(ProgramCliCommand::Show {
3411                    account_pubkey: None,
3412                    authority_pubkey: default_keypair.pubkey(),
3413                    get_programs: true,
3414                    get_buffers: false,
3415                    all: true,
3416                    use_lamports_unit: true,
3417                }),
3418                signers: vec![],
3419            }
3420        );
3421
3422        let test_command = test_commands.clone().get_matches_from(vec![
3423            "test",
3424            "program",
3425            "show",
3426            "--buffers",
3427            "--all",
3428            "--lamports",
3429        ]);
3430        assert_eq!(
3431            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3432            CliCommandInfo {
3433                command: CliCommand::Program(ProgramCliCommand::Show {
3434                    account_pubkey: None,
3435                    authority_pubkey: default_keypair.pubkey(),
3436                    get_programs: false,
3437                    get_buffers: true,
3438                    all: true,
3439                    use_lamports_unit: true,
3440                }),
3441                signers: vec![],
3442            }
3443        );
3444
3445        let test_command = test_commands.clone().get_matches_from(vec![
3446            "test",
3447            "program",
3448            "show",
3449            "--buffers",
3450            "--buffer-authority",
3451            &authority_keypair.pubkey().to_string(),
3452        ]);
3453        assert_eq!(
3454            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3455            CliCommandInfo {
3456                command: CliCommand::Program(ProgramCliCommand::Show {
3457                    account_pubkey: None,
3458                    authority_pubkey: authority_keypair.pubkey(),
3459                    get_programs: false,
3460                    get_buffers: true,
3461                    all: false,
3462                    use_lamports_unit: false,
3463                }),
3464                signers: vec![],
3465            }
3466        );
3467
3468        let test_command = test_commands.clone().get_matches_from(vec![
3469            "test",
3470            "program",
3471            "show",
3472            "--buffers",
3473            "--buffer-authority",
3474            &authority_keypair_file,
3475        ]);
3476        assert_eq!(
3477            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3478            CliCommandInfo {
3479                command: CliCommand::Program(ProgramCliCommand::Show {
3480                    account_pubkey: None,
3481                    authority_pubkey: authority_keypair.pubkey(),
3482                    get_programs: false,
3483                    get_buffers: true,
3484                    all: false,
3485                    use_lamports_unit: false,
3486                }),
3487                signers: vec![],
3488            }
3489        );
3490    }
3491
3492    #[test]
3493    #[allow(clippy::cognitive_complexity)]
3494    fn test_cli_parse_close() {
3495        let test_commands = get_clap_app("test", "desc", "version");
3496
3497        let default_keypair = Keypair::new();
3498        let keypair_file = make_tmp_path("keypair_file");
3499        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3500        let default_signer = DefaultSigner::new("", &keypair_file);
3501
3502        // defaults
3503        let buffer_pubkey = Pubkey::new_unique();
3504        let recipient_pubkey = Pubkey::new_unique();
3505        let authority_keypair = Keypair::new();
3506        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3507
3508        let test_command = test_commands.clone().get_matches_from(vec![
3509            "test",
3510            "program",
3511            "close",
3512            &buffer_pubkey.to_string(),
3513        ]);
3514        assert_eq!(
3515            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3516            CliCommandInfo {
3517                command: CliCommand::Program(ProgramCliCommand::Close {
3518                    account_pubkey: Some(buffer_pubkey),
3519                    recipient_pubkey: default_keypair.pubkey(),
3520                    authority_index: 0,
3521                    use_lamports_unit: false,
3522                    bypass_warning: false,
3523                }),
3524                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3525            }
3526        );
3527
3528        // with bypass-warning
3529        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3530        let test_command = test_commands.clone().get_matches_from(vec![
3531            "test",
3532            "program",
3533            "close",
3534            &buffer_pubkey.to_string(),
3535            "--bypass-warning",
3536        ]);
3537        assert_eq!(
3538            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3539            CliCommandInfo {
3540                command: CliCommand::Program(ProgramCliCommand::Close {
3541                    account_pubkey: Some(buffer_pubkey),
3542                    recipient_pubkey: default_keypair.pubkey(),
3543                    authority_index: 0,
3544                    use_lamports_unit: false,
3545                    bypass_warning: true,
3546                }),
3547                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3548            }
3549        );
3550
3551        // with authority
3552        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3553        let test_command = test_commands.clone().get_matches_from(vec![
3554            "test",
3555            "program",
3556            "close",
3557            &buffer_pubkey.to_string(),
3558            "--buffer-authority",
3559            &authority_keypair_file,
3560        ]);
3561        assert_eq!(
3562            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3563            CliCommandInfo {
3564                command: CliCommand::Program(ProgramCliCommand::Close {
3565                    account_pubkey: Some(buffer_pubkey),
3566                    recipient_pubkey: default_keypair.pubkey(),
3567                    authority_index: 1,
3568                    use_lamports_unit: false,
3569                    bypass_warning: false,
3570                }),
3571                signers: vec![
3572                    read_keypair_file(&keypair_file).unwrap().into(),
3573                    read_keypair_file(&authority_keypair_file).unwrap().into(),
3574                ],
3575            }
3576        );
3577
3578        // with recipient
3579        let test_command = test_commands.clone().get_matches_from(vec![
3580            "test",
3581            "program",
3582            "close",
3583            &buffer_pubkey.to_string(),
3584            "--recipient",
3585            &recipient_pubkey.to_string(),
3586        ]);
3587        assert_eq!(
3588            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3589            CliCommandInfo {
3590                command: CliCommand::Program(ProgramCliCommand::Close {
3591                    account_pubkey: Some(buffer_pubkey),
3592                    recipient_pubkey,
3593                    authority_index: 0,
3594                    use_lamports_unit: false,
3595                    bypass_warning: false,
3596                }),
3597                signers: vec![read_keypair_file(&keypair_file).unwrap().into(),],
3598            }
3599        );
3600
3601        // --buffers and lamports
3602        let test_command = test_commands.clone().get_matches_from(vec![
3603            "test",
3604            "program",
3605            "close",
3606            "--buffers",
3607            "--lamports",
3608        ]);
3609        assert_eq!(
3610            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3611            CliCommandInfo {
3612                command: CliCommand::Program(ProgramCliCommand::Close {
3613                    account_pubkey: None,
3614                    recipient_pubkey: default_keypair.pubkey(),
3615                    authority_index: 0,
3616                    use_lamports_unit: true,
3617                    bypass_warning: false,
3618                }),
3619                signers: vec![read_keypair_file(&keypair_file).unwrap().into(),],
3620            }
3621        );
3622    }
3623
3624    #[test]
3625    fn test_cli_parse_extend_program() {
3626        let test_commands = get_clap_app("test", "desc", "version");
3627
3628        let default_keypair = Keypair::new();
3629        let keypair_file = make_tmp_path("keypair_file");
3630        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3631        let default_signer = DefaultSigner::new("", &keypair_file);
3632
3633        // defaults
3634        let program_pubkey = Pubkey::new_unique();
3635        let additional_bytes = 100;
3636
3637        let test_command = test_commands.clone().get_matches_from(vec![
3638            "test",
3639            "program",
3640            "extend",
3641            &program_pubkey.to_string(),
3642            &additional_bytes.to_string(),
3643        ]);
3644        assert_eq!(
3645            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3646            CliCommandInfo {
3647                command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
3648                    program_pubkey,
3649                    additional_bytes
3650                }),
3651                signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
3652            }
3653        );
3654    }
3655
3656    #[test]
3657    fn test_cli_keypair_file() {
3658        miraland_logger::setup();
3659
3660        let default_keypair = Keypair::new();
3661        let program_pubkey = Keypair::new();
3662        let deploy_path = make_tmp_path("deploy");
3663        let mut program_location = PathBuf::from(deploy_path.clone());
3664        program_location.push("noop");
3665        program_location.set_extension("so");
3666        let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
3667        pathbuf.push("tests");
3668        pathbuf.push("fixtures");
3669        pathbuf.push("noop");
3670        pathbuf.set_extension("so");
3671        let program_keypair_location = program_location.with_file_name("noop-keypair.json");
3672        std::fs::create_dir_all(deploy_path).unwrap();
3673        std::fs::copy(pathbuf, program_location.as_os_str()).unwrap();
3674        write_keypair_file(&program_pubkey, program_keypair_location).unwrap();
3675
3676        let config = CliConfig {
3677            rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
3678            command: CliCommand::Program(ProgramCliCommand::Deploy {
3679                program_location: Some(program_location.to_str().unwrap().to_string()),
3680                fee_payer_signer_index: 0,
3681                buffer_signer_index: None,
3682                buffer_pubkey: None,
3683                program_signer_index: None,
3684                program_pubkey: None,
3685                upgrade_authority_signer_index: 0,
3686                is_final: false,
3687                max_len: None,
3688                allow_excessive_balance: false,
3689                skip_fee_check: false,
3690            }),
3691            signers: vec![&default_keypair],
3692            output_format: OutputFormat::JsonCompact,
3693            ..CliConfig::default()
3694        };
3695
3696        let result = process_command(&config);
3697        let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
3698        let program_id = json
3699            .as_object()
3700            .unwrap()
3701            .get("programId")
3702            .unwrap()
3703            .as_str()
3704            .unwrap();
3705
3706        assert_eq!(
3707            program_id.parse::<Pubkey>().unwrap(),
3708            program_pubkey.pubkey()
3709        );
3710    }
3711}