solana_cli/
program.rs

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