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