solana_cli/
program.rs

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