Skip to main content

solana_cli/
program.rs

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