solana_cli/
program.rs

1use {
2    crate::{
3        checks::*,
4        cli::{
5            log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
6            ProcessResult,
7        },
8        compute_budget::{
9            simulate_and_update_compute_unit_limit, ComputeUnitConfig,
10            UpdateComputeUnitLimitResult, WithComputeUnitConfig,
11        },
12        feature::{status_from_account, CliFeatureStatus},
13    },
14    bip39::{Language, Mnemonic, MnemonicType, Seed},
15    clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
16    log::*,
17    solana_account::{state_traits::StateMut, Account},
18    solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
19    solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1,
20    solana_clap_utils::{
21        self,
22        compute_budget::{compute_unit_price_arg, ComputeUnitLimit},
23        fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
24        hidden_unless_forced,
25        input_parsers::*,
26        input_validators::*,
27        keypair::*,
28        offline::{OfflineArgs, DUMP_TRANSACTION_MESSAGE, SIGN_ONLY_ARG},
29    },
30    solana_cli_output::{
31        return_signers_with_config, CliProgram, CliProgramAccountType, CliProgramAuthority,
32        CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableBuffers,
33        CliUpgradeableProgram, CliUpgradeableProgramClosed, CliUpgradeableProgramExtended,
34        CliUpgradeableProgramMigrated, CliUpgradeablePrograms, ReturnSignersConfig,
35    },
36    solana_client::{
37        connection_cache::ConnectionCache,
38        send_and_confirm_transactions_in_parallel::{
39            send_and_confirm_transactions_in_parallel_blocking_v2, SendAndConfirmConfigV2,
40        },
41        tpu_client::{TpuClient, TpuClientConfig},
42    },
43    solana_commitment_config::CommitmentConfig,
44    solana_compute_budget::compute_budget::ComputeBudget,
45    solana_feature_set::{FeatureSet, FEATURE_NAMES},
46    solana_instruction::{error::InstructionError, Instruction},
47    solana_keypair::{keypair_from_seed, read_keypair_file, Keypair},
48    solana_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        if feature_set.is_active(&solana_feature_set::enable_loader_v4::id()) {
1391            warn!("Loader-v4 is available now. Please migrate your program.");
1392        }
1393        if do_initial_deploy
1394            && feature_set.is_active(&solana_feature_set::disable_new_loader_v3_deployments::id())
1395        {
1396            return Err("No new programs can be deployed on loader-v3. Please use the program-v4 subcommand instead.".into());
1397        }
1398    }
1399
1400    let (program_data, program_len, buffer_program_data) =
1401        if let Some(program_location) = program_location {
1402            let program_data = read_and_verify_elf(program_location, feature_set)?;
1403            let program_len = program_data.len();
1404
1405            // If a buffer was provided, check if it has already been created and set up properly
1406            let buffer_program_data = if buffer_provided {
1407                fetch_buffer_program_data(
1408                    &rpc_client,
1409                    config,
1410                    Some(program_len),
1411                    buffer_pubkey,
1412                    upgrade_authority_signer.pubkey(),
1413                )?
1414            } else {
1415                None
1416            };
1417
1418            (program_data, program_len, buffer_program_data)
1419        } else if buffer_provided {
1420            let buffer_program_data = fetch_verified_buffer_program_data(
1421                &rpc_client,
1422                config,
1423                buffer_pubkey,
1424                upgrade_authority_signer.pubkey(),
1425                feature_set,
1426            )?;
1427
1428            (vec![], buffer_program_data.len(), Some(buffer_program_data))
1429        } else {
1430            return Err("Program location required if buffer not supplied".into());
1431        };
1432
1433    let program_data_max_len = if let Some(len) = max_len {
1434        if program_len > len {
1435            return Err(
1436                "Max length specified not large enough to accommodate desired program".into(),
1437            );
1438        }
1439        len
1440    } else {
1441        program_len
1442    };
1443
1444    let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1445        UpgradeableLoaderState::size_of_programdata(program_data_max_len),
1446    )?;
1447
1448    let result = if do_initial_deploy {
1449        if program_signer.is_none() {
1450            return Err(
1451                "Initial deployments require a keypair be provided for the program id".into(),
1452            );
1453        }
1454        do_process_program_deploy(
1455            rpc_client.clone(),
1456            config,
1457            &program_data,
1458            program_len,
1459            program_data_max_len,
1460            min_rent_exempt_program_data_balance,
1461            fee_payer_signer,
1462            &[program_signer.unwrap(), upgrade_authority_signer],
1463            buffer_signer,
1464            &buffer_pubkey,
1465            buffer_program_data,
1466            upgrade_authority_signer,
1467            skip_fee_check,
1468            compute_unit_price,
1469            max_sign_attempts,
1470            use_rpc,
1471        )
1472    } else {
1473        do_process_program_upgrade(
1474            rpc_client.clone(),
1475            config,
1476            &program_data,
1477            program_len,
1478            min_rent_exempt_program_data_balance,
1479            fee_payer_signer,
1480            &program_pubkey,
1481            upgrade_authority_signer,
1482            &buffer_pubkey,
1483            buffer_signer,
1484            buffer_program_data,
1485            skip_fee_check,
1486            compute_unit_price,
1487            max_sign_attempts,
1488            auto_extend,
1489            use_rpc,
1490        )
1491    };
1492    if result.is_ok() && is_final {
1493        process_set_authority(
1494            &rpc_client,
1495            config,
1496            Some(program_pubkey),
1497            None,
1498            Some(upgrade_authority_signer_index),
1499            None,
1500            false,
1501            false,
1502            &BlockhashQuery::default(),
1503        )?;
1504    }
1505    if result.is_err() && !buffer_provided {
1506        // We might have deployed "temporary" buffer but failed to deploy our program from this
1507        // buffer, reporting this to the user - so he can retry deploying re-using same buffer.
1508        report_ephemeral_mnemonic(buffer_words, buffer_mnemonic, &buffer_pubkey);
1509    }
1510    result
1511}
1512
1513fn fetch_verified_buffer_program_data(
1514    rpc_client: &RpcClient,
1515    config: &CliConfig,
1516    buffer_pubkey: Pubkey,
1517    buffer_authority: Pubkey,
1518    feature_set: FeatureSet,
1519) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1520    let Some(buffer_program_data) =
1521        fetch_buffer_program_data(rpc_client, config, None, buffer_pubkey, buffer_authority)?
1522    else {
1523        return Err(format!("Buffer account {buffer_pubkey} not found").into());
1524    };
1525
1526    verify_elf(&buffer_program_data, feature_set).map_err(|err| {
1527        format!(
1528            "Buffer account {buffer_pubkey} has invalid program data: {:?}",
1529            err
1530        )
1531    })?;
1532
1533    Ok(buffer_program_data)
1534}
1535
1536fn fetch_buffer_program_data(
1537    rpc_client: &RpcClient,
1538    config: &CliConfig,
1539    min_program_len: Option<usize>,
1540    buffer_pubkey: Pubkey,
1541    buffer_authority: Pubkey,
1542) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
1543    let Some(mut account) = rpc_client
1544        .get_account_with_commitment(&buffer_pubkey, config.commitment)?
1545        .value
1546    else {
1547        return Ok(None);
1548    };
1549
1550    if !bpf_loader_upgradeable::check_id(&account.owner) {
1551        return Err(format!(
1552            "Buffer account {buffer_pubkey} is not owned by the BPF Upgradeable Loader",
1553        )
1554        .into());
1555    }
1556
1557    if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1558        if authority_address.is_none() {
1559            return Err(format!("Buffer {buffer_pubkey} is immutable").into());
1560        }
1561        if authority_address != Some(buffer_authority) {
1562            return Err(format!(
1563                "Buffer's authority {:?} does not match authority provided {}",
1564                authority_address, buffer_authority
1565            )
1566            .into());
1567        }
1568    } else {
1569        return Err(format!("{buffer_pubkey} is not an upgradeable loader buffer account").into());
1570    }
1571
1572    if let Some(min_program_len) = min_program_len {
1573        let min_buffer_data_len = UpgradeableLoaderState::size_of_buffer(min_program_len);
1574        if account.data.len() < min_buffer_data_len {
1575            return Err(format!(
1576                "Buffer account data size ({}) is smaller than the minimum size ({})",
1577                account.data.len(),
1578                min_buffer_data_len
1579            )
1580            .into());
1581        }
1582    }
1583
1584    let buffer_program_data = account
1585        .data
1586        .split_off(UpgradeableLoaderState::size_of_buffer_metadata());
1587
1588    Ok(Some(buffer_program_data))
1589}
1590
1591/// Upgrade existing program using upgradeable loader
1592#[allow(clippy::too_many_arguments)]
1593fn process_program_upgrade(
1594    rpc_client: Arc<RpcClient>,
1595    config: &CliConfig,
1596    fee_payer_signer_index: SignerIndex,
1597    program_id: Pubkey,
1598    buffer_pubkey: Pubkey,
1599    upgrade_authority_signer_index: SignerIndex,
1600    sign_only: bool,
1601    dump_transaction_message: bool,
1602    blockhash_query: &BlockhashQuery,
1603    skip_feature_verification: bool,
1604) -> ProcessResult {
1605    let fee_payer_signer = config.signers[fee_payer_signer_index];
1606    let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
1607
1608    let blockhash = blockhash_query.get_blockhash(&rpc_client, config.commitment)?;
1609    let message = Message::new_with_blockhash(
1610        &[loader_v3_instruction::upgrade(
1611            &program_id,
1612            &buffer_pubkey,
1613            &upgrade_authority_signer.pubkey(),
1614            &fee_payer_signer.pubkey(),
1615        )],
1616        Some(&fee_payer_signer.pubkey()),
1617        &blockhash,
1618    );
1619
1620    if sign_only {
1621        let mut tx = Transaction::new_unsigned(message);
1622        let signers = &[fee_payer_signer, upgrade_authority_signer];
1623        // Using try_partial_sign here because fee_payer_signer might not be the fee payer we
1624        // end up using for this transaction (it might be NullSigner in `--sign-only` mode).
1625        tx.try_partial_sign(signers, blockhash)?;
1626        return_signers_with_config(
1627            &tx,
1628            &config.output_format,
1629            &ReturnSignersConfig {
1630                dump_transaction_message,
1631            },
1632        )
1633    } else {
1634        let feature_set = if skip_feature_verification {
1635            FeatureSet::all_enabled()
1636        } else {
1637            fetch_feature_set(&rpc_client)?
1638        };
1639
1640        fetch_verified_buffer_program_data(
1641            &rpc_client,
1642            config,
1643            buffer_pubkey,
1644            upgrade_authority_signer.pubkey(),
1645            feature_set,
1646        )?;
1647
1648        let fee = rpc_client.get_fee_for_message(&message)?;
1649        check_account_for_spend_and_fee_with_commitment(
1650            &rpc_client,
1651            &fee_payer_signer.pubkey(),
1652            0,
1653            fee,
1654            config.commitment,
1655        )?;
1656        let mut tx = Transaction::new_unsigned(message);
1657        let signers = &[fee_payer_signer, upgrade_authority_signer];
1658        tx.try_sign(signers, blockhash)?;
1659        let final_tx_sig = rpc_client
1660            .send_and_confirm_transaction_with_spinner_and_config(
1661                &tx,
1662                config.commitment,
1663                config.send_transaction_config,
1664            )
1665            .map_err(|e| format!("Upgrading program failed: {e}"))?;
1666        let program_id = CliProgramId {
1667            program_id: program_id.to_string(),
1668            signature: Some(final_tx_sig.to_string()),
1669        };
1670        Ok(config.output_format.formatted_string(&program_id))
1671    }
1672}
1673
1674#[allow(clippy::too_many_arguments)]
1675fn process_write_buffer(
1676    rpc_client: Arc<RpcClient>,
1677    config: &CliConfig,
1678    program_location: &str,
1679    fee_payer_signer_index: SignerIndex,
1680    buffer_signer_index: Option<SignerIndex>,
1681    buffer_pubkey: Option<Pubkey>,
1682    buffer_authority_signer_index: SignerIndex,
1683    max_len: Option<usize>,
1684    skip_fee_check: bool,
1685    compute_unit_price: Option<u64>,
1686    max_sign_attempts: usize,
1687    use_rpc: bool,
1688    skip_feature_verification: bool,
1689) -> ProcessResult {
1690    let fee_payer_signer = config.signers[fee_payer_signer_index];
1691    let buffer_authority = config.signers[buffer_authority_signer_index];
1692
1693    let feature_set = if skip_feature_verification {
1694        FeatureSet::all_enabled()
1695    } else {
1696        fetch_feature_set(&rpc_client)?
1697    };
1698
1699    let program_data = read_and_verify_elf(program_location, feature_set)?;
1700    let program_len = program_data.len();
1701
1702    // Create ephemeral keypair to use for Buffer account, if not provided
1703    let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
1704    let (buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
1705        (Some(config.signers[i]), config.signers[i].pubkey())
1706    } else if let Some(pubkey) = buffer_pubkey {
1707        (None, pubkey)
1708    } else {
1709        (
1710            Some(&buffer_keypair as &dyn Signer),
1711            buffer_keypair.pubkey(),
1712        )
1713    };
1714
1715    let buffer_program_data = fetch_buffer_program_data(
1716        &rpc_client,
1717        config,
1718        Some(program_len),
1719        buffer_pubkey,
1720        buffer_authority.pubkey(),
1721    )?;
1722
1723    let buffer_data_max_len = if let Some(len) = max_len {
1724        len
1725    } else {
1726        program_data.len()
1727    };
1728    let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1729        UpgradeableLoaderState::size_of_programdata(buffer_data_max_len),
1730    )?;
1731
1732    let result = do_process_write_buffer(
1733        rpc_client,
1734        config,
1735        &program_data,
1736        program_data.len(),
1737        min_rent_exempt_program_data_balance,
1738        fee_payer_signer,
1739        buffer_signer,
1740        &buffer_pubkey,
1741        buffer_program_data,
1742        buffer_authority,
1743        skip_fee_check,
1744        compute_unit_price,
1745        max_sign_attempts,
1746        use_rpc,
1747    );
1748    if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
1749        report_ephemeral_mnemonic(words, mnemonic, &buffer_pubkey);
1750    }
1751    result
1752}
1753
1754fn process_set_authority(
1755    rpc_client: &RpcClient,
1756    config: &CliConfig,
1757    program_pubkey: Option<Pubkey>,
1758    buffer_pubkey: Option<Pubkey>,
1759    authority: Option<SignerIndex>,
1760    new_authority: Option<Pubkey>,
1761    sign_only: bool,
1762    dump_transaction_message: bool,
1763    blockhash_query: &BlockhashQuery,
1764) -> ProcessResult {
1765    let authority_signer = if let Some(index) = authority {
1766        config.signers[index]
1767    } else {
1768        return Err("Set authority requires the current authority".into());
1769    };
1770
1771    trace!("Set a new authority");
1772    let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1773
1774    let mut tx = if let Some(ref pubkey) = program_pubkey {
1775        Transaction::new_unsigned(Message::new(
1776            &[loader_v3_instruction::set_upgrade_authority(
1777                pubkey,
1778                &authority_signer.pubkey(),
1779                new_authority.as_ref(),
1780            )],
1781            Some(&config.signers[0].pubkey()),
1782        ))
1783    } else if let Some(pubkey) = buffer_pubkey {
1784        if let Some(ref new_authority) = new_authority {
1785            Transaction::new_unsigned(Message::new(
1786                &[loader_v3_instruction::set_buffer_authority(
1787                    &pubkey,
1788                    &authority_signer.pubkey(),
1789                    new_authority,
1790                )],
1791                Some(&config.signers[0].pubkey()),
1792            ))
1793        } else {
1794            return Err("Buffer authority cannot be None".into());
1795        }
1796    } else {
1797        return Err("Program or Buffer not provided".into());
1798    };
1799
1800    let signers = &[config.signers[0], authority_signer];
1801
1802    if sign_only {
1803        tx.try_partial_sign(signers, blockhash)?;
1804        return_signers_with_config(
1805            &tx,
1806            &config.output_format,
1807            &ReturnSignersConfig {
1808                dump_transaction_message,
1809            },
1810        )
1811    } else {
1812        tx.try_sign(signers, blockhash)?;
1813        rpc_client
1814            .send_and_confirm_transaction_with_spinner_and_config(
1815                &tx,
1816                config.commitment,
1817                config.send_transaction_config,
1818            )
1819            .map_err(|e| format!("Setting authority failed: {e}"))?;
1820
1821        let authority = CliProgramAuthority {
1822            authority: new_authority
1823                .map(|pubkey| pubkey.to_string())
1824                .unwrap_or_else(|| "none".to_string()),
1825            account_type: if program_pubkey.is_some() {
1826                CliProgramAccountType::Program
1827            } else {
1828                CliProgramAccountType::Buffer
1829            },
1830        };
1831        Ok(config.output_format.formatted_string(&authority))
1832    }
1833}
1834
1835fn process_set_authority_checked(
1836    rpc_client: &RpcClient,
1837    config: &CliConfig,
1838    program_pubkey: Pubkey,
1839    authority_index: SignerIndex,
1840    new_authority_index: SignerIndex,
1841    sign_only: bool,
1842    dump_transaction_message: bool,
1843    blockhash_query: &BlockhashQuery,
1844) -> ProcessResult {
1845    let authority_signer = config.signers[authority_index];
1846    let new_authority_signer = config.signers[new_authority_index];
1847
1848    trace!("Set a new (checked) authority");
1849    let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1850
1851    let mut tx = Transaction::new_unsigned(Message::new(
1852        &[loader_v3_instruction::set_upgrade_authority_checked(
1853            &program_pubkey,
1854            &authority_signer.pubkey(),
1855            &new_authority_signer.pubkey(),
1856        )],
1857        Some(&config.signers[0].pubkey()),
1858    ));
1859
1860    let signers = &[config.signers[0], authority_signer, new_authority_signer];
1861    if sign_only {
1862        tx.try_partial_sign(signers, blockhash)?;
1863        return_signers_with_config(
1864            &tx,
1865            &config.output_format,
1866            &ReturnSignersConfig {
1867                dump_transaction_message,
1868            },
1869        )
1870    } else {
1871        tx.try_sign(signers, blockhash)?;
1872        rpc_client
1873            .send_and_confirm_transaction_with_spinner_and_config(
1874                &tx,
1875                config.commitment,
1876                config.send_transaction_config,
1877            )
1878            .map_err(|e| format!("Setting authority failed: {e}"))?;
1879
1880        let authority = CliProgramAuthority {
1881            authority: new_authority_signer.pubkey().to_string(),
1882            account_type: CliProgramAccountType::Program,
1883        };
1884        Ok(config.output_format.formatted_string(&authority))
1885    }
1886}
1887
1888const ACCOUNT_TYPE_SIZE: usize = 4;
1889const SLOT_SIZE: usize = size_of::<u64>();
1890const OPTION_SIZE: usize = 1;
1891const PUBKEY_LEN: usize = 32;
1892
1893fn get_buffers(
1894    rpc_client: &RpcClient,
1895    authority_pubkey: Option<Pubkey>,
1896    use_lamports_unit: bool,
1897) -> Result<CliUpgradeableBuffers, Box<dyn std::error::Error>> {
1898    let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1899        0,
1900        &[1, 0, 0, 0],
1901    ))];
1902    if let Some(authority_pubkey) = authority_pubkey {
1903        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1904            ACCOUNT_TYPE_SIZE,
1905            &[1],
1906        )));
1907        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1908            ACCOUNT_TYPE_SIZE + OPTION_SIZE,
1909            authority_pubkey.as_ref(),
1910        )));
1911    }
1912
1913    let results = get_accounts_with_filter(
1914        rpc_client,
1915        filters,
1916        ACCOUNT_TYPE_SIZE + OPTION_SIZE + PUBKEY_LEN,
1917    )?;
1918
1919    let mut buffers = vec![];
1920    for (address, account) in results.iter() {
1921        if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1922            buffers.push(CliUpgradeableBuffer {
1923                address: address.to_string(),
1924                authority: authority_address
1925                    .map(|pubkey| pubkey.to_string())
1926                    .unwrap_or_else(|| "none".to_string()),
1927                data_len: 0,
1928                lamports: account.lamports,
1929                use_lamports_unit,
1930            });
1931        } else {
1932            return Err(format!("Error parsing Buffer account {address}").into());
1933        }
1934    }
1935    Ok(CliUpgradeableBuffers {
1936        buffers,
1937        use_lamports_unit,
1938    })
1939}
1940
1941fn get_programs(
1942    rpc_client: &RpcClient,
1943    authority_pubkey: Option<Pubkey>,
1944    use_lamports_unit: bool,
1945) -> Result<CliUpgradeablePrograms, Box<dyn std::error::Error>> {
1946    let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1947        0,
1948        &[3, 0, 0, 0],
1949    ))];
1950    if let Some(authority_pubkey) = authority_pubkey {
1951        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1952            ACCOUNT_TYPE_SIZE + SLOT_SIZE,
1953            &[1],
1954        )));
1955        filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1956            ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE,
1957            authority_pubkey.as_ref(),
1958        )));
1959    }
1960
1961    let results = get_accounts_with_filter(
1962        rpc_client,
1963        filters,
1964        ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE + PUBKEY_LEN,
1965    )?;
1966
1967    let mut programs = vec![];
1968    for (programdata_address, programdata_account) in results.iter() {
1969        if let Ok(UpgradeableLoaderState::ProgramData {
1970            slot,
1971            upgrade_authority_address,
1972        }) = programdata_account.state()
1973        {
1974            let mut bytes = vec![2, 0, 0, 0];
1975            bytes.extend_from_slice(programdata_address.as_ref());
1976            let filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &bytes))];
1977
1978            let results = get_accounts_with_filter(rpc_client, filters, 0)?;
1979            if results.len() != 1 {
1980                return Err(format!(
1981                    "Error: More than one Program associated with ProgramData account \
1982                     {programdata_address}"
1983                )
1984                .into());
1985            }
1986            programs.push(CliUpgradeableProgram {
1987                program_id: results[0].0.to_string(),
1988                owner: programdata_account.owner.to_string(),
1989                programdata_address: programdata_address.to_string(),
1990                authority: upgrade_authority_address
1991                    .map(|pubkey| pubkey.to_string())
1992                    .unwrap_or_else(|| "none".to_string()),
1993                last_deploy_slot: slot,
1994                data_len: programdata_account
1995                    .data
1996                    .len()
1997                    .saturating_sub(UpgradeableLoaderState::size_of_programdata_metadata()),
1998                lamports: programdata_account.lamports,
1999                use_lamports_unit,
2000            });
2001        } else {
2002            return Err(format!("Error parsing ProgramData account {programdata_address}").into());
2003        }
2004    }
2005    Ok(CliUpgradeablePrograms {
2006        programs,
2007        use_lamports_unit,
2008    })
2009}
2010
2011fn get_accounts_with_filter(
2012    rpc_client: &RpcClient,
2013    filters: Vec<RpcFilterType>,
2014    length: usize,
2015) -> Result<Vec<(Pubkey, Account)>, Box<dyn std::error::Error>> {
2016    let results = rpc_client.get_program_accounts_with_config(
2017        &bpf_loader_upgradeable::id(),
2018        RpcProgramAccountsConfig {
2019            filters: Some(filters),
2020            account_config: RpcAccountInfoConfig {
2021                encoding: Some(UiAccountEncoding::Base64),
2022                data_slice: Some(UiDataSliceConfig { offset: 0, length }),
2023                ..RpcAccountInfoConfig::default()
2024            },
2025            ..RpcProgramAccountsConfig::default()
2026        },
2027    )?;
2028    Ok(results)
2029}
2030
2031fn process_show(
2032    rpc_client: &RpcClient,
2033    config: &CliConfig,
2034    account_pubkey: Option<Pubkey>,
2035    authority_pubkey: Pubkey,
2036    programs: bool,
2037    buffers: bool,
2038    all: bool,
2039    use_lamports_unit: bool,
2040) -> ProcessResult {
2041    if let Some(account_pubkey) = account_pubkey {
2042        if let Some(account) = rpc_client
2043            .get_account_with_commitment(&account_pubkey, config.commitment)?
2044            .value
2045        {
2046            if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
2047                Ok(config.output_format.formatted_string(&CliProgram {
2048                    program_id: account_pubkey.to_string(),
2049                    owner: account.owner.to_string(),
2050                    data_len: account.data.len(),
2051                }))
2052            } else if account.owner == bpf_loader_upgradeable::id() {
2053                if let Ok(UpgradeableLoaderState::Program {
2054                    programdata_address,
2055                }) = account.state()
2056                {
2057                    if let Some(programdata_account) = rpc_client
2058                        .get_account_with_commitment(&programdata_address, config.commitment)?
2059                        .value
2060                    {
2061                        if let Ok(UpgradeableLoaderState::ProgramData {
2062                            upgrade_authority_address,
2063                            slot,
2064                        }) = programdata_account.state()
2065                        {
2066                            Ok(config
2067                                .output_format
2068                                .formatted_string(&CliUpgradeableProgram {
2069                                    program_id: account_pubkey.to_string(),
2070                                    owner: account.owner.to_string(),
2071                                    programdata_address: programdata_address.to_string(),
2072                                    authority: upgrade_authority_address
2073                                        .map(|pubkey| pubkey.to_string())
2074                                        .unwrap_or_else(|| "none".to_string()),
2075                                    last_deploy_slot: slot,
2076                                    data_len: programdata_account.data.len().saturating_sub(
2077                                        UpgradeableLoaderState::size_of_programdata_metadata(),
2078                                    ),
2079                                    lamports: programdata_account.lamports,
2080                                    use_lamports_unit,
2081                                }))
2082                        } else {
2083                            Err(format!("Program {account_pubkey} has been closed").into())
2084                        }
2085                    } else {
2086                        Err(format!("Program {account_pubkey} has been closed").into())
2087                    }
2088                } else if let Ok(UpgradeableLoaderState::Buffer { authority_address }) =
2089                    account.state()
2090                {
2091                    Ok(config
2092                        .output_format
2093                        .formatted_string(&CliUpgradeableBuffer {
2094                            address: account_pubkey.to_string(),
2095                            authority: authority_address
2096                                .map(|pubkey| pubkey.to_string())
2097                                .unwrap_or_else(|| "none".to_string()),
2098                            data_len: account
2099                                .data
2100                                .len()
2101                                .saturating_sub(UpgradeableLoaderState::size_of_buffer_metadata()),
2102                            lamports: account.lamports,
2103                            use_lamports_unit,
2104                        }))
2105                } else {
2106                    Err(format!(
2107                        "{account_pubkey} is not an upgradeable loader Buffer or Program account"
2108                    )
2109                    .into())
2110                }
2111            } else {
2112                Err(format!("{account_pubkey} is not an SBF program").into())
2113            }
2114        } else {
2115            Err(format!("Unable to find the account {account_pubkey}").into())
2116        }
2117    } else if programs {
2118        let authority_pubkey = if all { None } else { Some(authority_pubkey) };
2119        let programs = get_programs(rpc_client, authority_pubkey, use_lamports_unit)?;
2120        Ok(config.output_format.formatted_string(&programs))
2121    } else if buffers {
2122        let authority_pubkey = if all { None } else { Some(authority_pubkey) };
2123        let buffers = get_buffers(rpc_client, authority_pubkey, use_lamports_unit)?;
2124        Ok(config.output_format.formatted_string(&buffers))
2125    } else {
2126        Err("Invalid parameters".to_string().into())
2127    }
2128}
2129
2130fn process_dump(
2131    rpc_client: &RpcClient,
2132    config: &CliConfig,
2133    account_pubkey: Option<Pubkey>,
2134    output_location: &str,
2135) -> ProcessResult {
2136    if let Some(account_pubkey) = account_pubkey {
2137        if let Some(account) = rpc_client
2138            .get_account_with_commitment(&account_pubkey, config.commitment)?
2139            .value
2140        {
2141            if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
2142                let mut f = File::create(output_location)?;
2143                f.write_all(&account.data)?;
2144                Ok(format!("Wrote program to {output_location}"))
2145            } else if account.owner == bpf_loader_upgradeable::id() {
2146                if let Ok(UpgradeableLoaderState::Program {
2147                    programdata_address,
2148                }) = account.state()
2149                {
2150                    if let Some(programdata_account) = rpc_client
2151                        .get_account_with_commitment(&programdata_address, config.commitment)?
2152                        .value
2153                    {
2154                        if let Ok(UpgradeableLoaderState::ProgramData { .. }) =
2155                            programdata_account.state()
2156                        {
2157                            let offset = UpgradeableLoaderState::size_of_programdata_metadata();
2158                            let program_data = &programdata_account.data[offset..];
2159                            let mut f = File::create(output_location)?;
2160                            f.write_all(program_data)?;
2161                            Ok(format!("Wrote program to {output_location}"))
2162                        } else {
2163                            Err(format!("Program {account_pubkey} has been closed").into())
2164                        }
2165                    } else {
2166                        Err(format!("Program {account_pubkey} has been closed").into())
2167                    }
2168                } else if let Ok(UpgradeableLoaderState::Buffer { .. }) = account.state() {
2169                    let offset = UpgradeableLoaderState::size_of_buffer_metadata();
2170                    let program_data = &account.data[offset..];
2171                    let mut f = File::create(output_location)?;
2172                    f.write_all(program_data)?;
2173                    Ok(format!("Wrote program to {output_location}"))
2174                } else {
2175                    Err(format!(
2176                        "{account_pubkey} is not an upgradeable loader buffer or program account"
2177                    )
2178                    .into())
2179                }
2180            } else {
2181                Err(format!("{account_pubkey} is not an SBF program").into())
2182            }
2183        } else {
2184            Err(format!("Unable to find the account {account_pubkey}").into())
2185        }
2186    } else {
2187        Err("No account specified".into())
2188    }
2189}
2190
2191fn close(
2192    rpc_client: &RpcClient,
2193    config: &CliConfig,
2194    account_pubkey: &Pubkey,
2195    recipient_pubkey: &Pubkey,
2196    authority_signer: &dyn Signer,
2197    program_pubkey: Option<&Pubkey>,
2198) -> Result<(), Box<dyn std::error::Error>> {
2199    let blockhash = rpc_client.get_latest_blockhash()?;
2200
2201    let mut tx = Transaction::new_unsigned(Message::new(
2202        &[loader_v3_instruction::close_any(
2203            account_pubkey,
2204            recipient_pubkey,
2205            Some(&authority_signer.pubkey()),
2206            program_pubkey,
2207        )],
2208        Some(&config.signers[0].pubkey()),
2209    ));
2210
2211    tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
2212    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2213        &tx,
2214        config.commitment,
2215        config.send_transaction_config,
2216    );
2217    if let Err(err) = result {
2218        if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2219            _,
2220            InstructionError::InvalidInstructionData,
2221        )) = err.kind()
2222        {
2223            return Err("Closing a buffer account is not supported by the cluster".into());
2224        } else if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2225            _,
2226            InstructionError::InvalidArgument,
2227        )) = err.kind()
2228        {
2229            return Err("Closing a program account is not supported by the cluster".into());
2230        } else {
2231            return Err(format!("Close failed: {err}").into());
2232        }
2233    }
2234    Ok(())
2235}
2236
2237fn process_close(
2238    rpc_client: &RpcClient,
2239    config: &CliConfig,
2240    account_pubkey: Option<Pubkey>,
2241    recipient_pubkey: Pubkey,
2242    authority_index: SignerIndex,
2243    use_lamports_unit: bool,
2244    bypass_warning: bool,
2245) -> ProcessResult {
2246    let authority_signer = config.signers[authority_index];
2247
2248    if let Some(account_pubkey) = account_pubkey {
2249        if let Some(account) = rpc_client
2250            .get_account_with_commitment(&account_pubkey, config.commitment)?
2251            .value
2252        {
2253            match account.state() {
2254                Ok(UpgradeableLoaderState::Buffer { authority_address }) => {
2255                    if authority_address != Some(authority_signer.pubkey()) {
2256                        return Err(format!(
2257                            "Buffer account authority {:?} does not match {:?}",
2258                            authority_address,
2259                            Some(authority_signer.pubkey())
2260                        )
2261                        .into());
2262                    } else {
2263                        close(
2264                            rpc_client,
2265                            config,
2266                            &account_pubkey,
2267                            &recipient_pubkey,
2268                            authority_signer,
2269                            None,
2270                        )?;
2271                    }
2272                    Ok(config
2273                        .output_format
2274                        .formatted_string(&CliUpgradeableBuffers {
2275                            buffers: vec![CliUpgradeableBuffer {
2276                                address: account_pubkey.to_string(),
2277                                authority: authority_address
2278                                    .map(|pubkey| pubkey.to_string())
2279                                    .unwrap_or_else(|| "none".to_string()),
2280                                data_len: 0,
2281                                lamports: account.lamports,
2282                                use_lamports_unit,
2283                            }],
2284                            use_lamports_unit,
2285                        }))
2286                }
2287                Ok(UpgradeableLoaderState::Program {
2288                    programdata_address: programdata_pubkey,
2289                }) => {
2290                    if let Some(account) = rpc_client
2291                        .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2292                        .value
2293                    {
2294                        if let Ok(UpgradeableLoaderState::ProgramData {
2295                            slot: _,
2296                            upgrade_authority_address: authority_pubkey,
2297                        }) = account.state()
2298                        {
2299                            if authority_pubkey != Some(authority_signer.pubkey()) {
2300                                Err(format!(
2301                                    "Program authority {:?} does not match {:?}",
2302                                    authority_pubkey,
2303                                    Some(authority_signer.pubkey())
2304                                )
2305                                .into())
2306                            } else {
2307                                if !bypass_warning {
2308                                    return Err(String::from(CLOSE_PROGRAM_WARNING).into());
2309                                }
2310                                close(
2311                                    rpc_client,
2312                                    config,
2313                                    &programdata_pubkey,
2314                                    &recipient_pubkey,
2315                                    authority_signer,
2316                                    Some(&account_pubkey),
2317                                )?;
2318                                Ok(config.output_format.formatted_string(
2319                                    &CliUpgradeableProgramClosed {
2320                                        program_id: account_pubkey.to_string(),
2321                                        lamports: account.lamports,
2322                                        use_lamports_unit,
2323                                    },
2324                                ))
2325                            }
2326                        } else {
2327                            Err(format!("Program {account_pubkey} has been closed").into())
2328                        }
2329                    } else {
2330                        Err(format!("Program {account_pubkey} has been closed").into())
2331                    }
2332                }
2333                _ => Err(format!("{account_pubkey} is not a Program or Buffer account").into()),
2334            }
2335        } else {
2336            Err(format!("Unable to find the account {account_pubkey}").into())
2337        }
2338    } else {
2339        let buffers = get_buffers(
2340            rpc_client,
2341            Some(authority_signer.pubkey()),
2342            use_lamports_unit,
2343        )?;
2344
2345        let mut closed = vec![];
2346        for buffer in buffers.buffers.iter() {
2347            if close(
2348                rpc_client,
2349                config,
2350                &Pubkey::from_str(&buffer.address)?,
2351                &recipient_pubkey,
2352                authority_signer,
2353                None,
2354            )
2355            .is_ok()
2356            {
2357                closed.push(buffer.clone());
2358            }
2359        }
2360        Ok(config
2361            .output_format
2362            .formatted_string(&CliUpgradeableBuffers {
2363                buffers: closed,
2364                use_lamports_unit,
2365            }))
2366    }
2367}
2368
2369fn process_extend_program(
2370    rpc_client: &RpcClient,
2371    config: &CliConfig,
2372    program_pubkey: Pubkey,
2373    additional_bytes: u32,
2374) -> ProcessResult {
2375    let payer_pubkey = config.signers[0].pubkey();
2376
2377    if additional_bytes == 0 {
2378        return Err("Additional bytes must be greater than zero".into());
2379    }
2380
2381    let program_account = match rpc_client
2382        .get_account_with_commitment(&program_pubkey, config.commitment)?
2383        .value
2384    {
2385        Some(program_account) => Ok(program_account),
2386        None => Err(format!("Unable to find program {program_pubkey}")),
2387    }?;
2388
2389    if !bpf_loader_upgradeable::check_id(&program_account.owner) {
2390        return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2391    }
2392
2393    let programdata_pubkey = match program_account.state() {
2394        Ok(UpgradeableLoaderState::Program {
2395            programdata_address: programdata_pubkey,
2396        }) => Ok(programdata_pubkey),
2397        _ => Err(format!(
2398            "Account {program_pubkey} is not an upgradeable program"
2399        )),
2400    }?;
2401
2402    let programdata_account = match rpc_client
2403        .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2404        .value
2405    {
2406        Some(programdata_account) => Ok(programdata_account),
2407        None => Err(format!("Program {program_pubkey} is closed")),
2408    }?;
2409
2410    let upgrade_authority_address = match programdata_account.state() {
2411        Ok(UpgradeableLoaderState::ProgramData {
2412            slot: _,
2413            upgrade_authority_address,
2414        }) => Ok(upgrade_authority_address),
2415        _ => Err(format!("Program {program_pubkey} is closed")),
2416    }?;
2417
2418    match upgrade_authority_address {
2419        None => Err(format!("Program {program_pubkey} is not upgradeable")),
2420        _ => Ok(()),
2421    }?;
2422
2423    let blockhash = rpc_client.get_latest_blockhash()?;
2424
2425    let mut tx = Transaction::new_unsigned(Message::new(
2426        &[loader_v3_instruction::extend_program(
2427            &program_pubkey,
2428            Some(&payer_pubkey),
2429            additional_bytes,
2430        )],
2431        Some(&payer_pubkey),
2432    ));
2433
2434    tx.try_sign(&[config.signers[0]], blockhash)?;
2435    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2436        &tx,
2437        config.commitment,
2438        config.send_transaction_config,
2439    );
2440    if let Err(err) = result {
2441        if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2442            _,
2443            InstructionError::InvalidInstructionData,
2444        )) = err.kind()
2445        {
2446            return Err("Extending a program is not supported by the cluster".into());
2447        } else {
2448            return Err(format!("Extend program failed: {err}").into());
2449        }
2450    }
2451
2452    Ok(config
2453        .output_format
2454        .formatted_string(&CliUpgradeableProgramExtended {
2455            program_id: program_pubkey.to_string(),
2456            additional_bytes,
2457        }))
2458}
2459
2460fn process_migrate_program(
2461    rpc_client: &RpcClient,
2462    config: &CliConfig,
2463    program_pubkey: Pubkey,
2464    authority_signer_index: SignerIndex,
2465    compute_unit_price: Option<u64>,
2466) -> ProcessResult {
2467    let payer_pubkey = config.signers[0].pubkey();
2468    let authority_signer = config.signers[authority_signer_index];
2469
2470    let program_account = match rpc_client
2471        .get_account_with_commitment(&program_pubkey, config.commitment)?
2472        .value
2473    {
2474        Some(program_account) => Ok(program_account),
2475        None => Err(format!("Unable to find program {program_pubkey}")),
2476    }?;
2477
2478    if !bpf_loader_upgradeable::check_id(&program_account.owner) {
2479        return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2480    }
2481
2482    let Ok(UpgradeableLoaderState::Program {
2483        programdata_address: programdata_pubkey,
2484    }) = program_account.state()
2485    else {
2486        return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2487    };
2488
2489    let Some(programdata_account) = rpc_client
2490        .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2491        .value
2492    else {
2493        return Err(format!("Program {program_pubkey} is closed").into());
2494    };
2495
2496    let upgrade_authority_address = match programdata_account.state() {
2497        Ok(UpgradeableLoaderState::ProgramData {
2498            slot: _slot,
2499            upgrade_authority_address,
2500        }) => upgrade_authority_address,
2501        _ => None,
2502    };
2503
2504    if authority_signer.pubkey() != upgrade_authority_address.unwrap_or(program_pubkey) {
2505        return Err(format!(
2506            "Upgrade authority {:?} does not match {:?}",
2507            upgrade_authority_address,
2508            Some(authority_signer.pubkey())
2509        )
2510        .into());
2511    }
2512
2513    let blockhash = rpc_client.get_latest_blockhash()?;
2514    let mut message = Message::new(
2515        &vec![loader_v3_instruction::migrate_program(
2516            &programdata_pubkey,
2517            &program_pubkey,
2518            &authority_signer.pubkey(),
2519        )]
2520        .with_compute_unit_config(&ComputeUnitConfig {
2521            compute_unit_price,
2522            compute_unit_limit: ComputeUnitLimit::Simulated,
2523        }),
2524        Some(&payer_pubkey),
2525    );
2526    simulate_and_update_compute_unit_limit(&ComputeUnitLimit::Simulated, rpc_client, &mut message)?;
2527
2528    let mut tx = Transaction::new_unsigned(message);
2529    tx.try_sign(&[config.signers[0], config.signers[1]], blockhash)?;
2530    let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2531        &tx,
2532        config.commitment,
2533        config.send_transaction_config,
2534    );
2535    if let Err(err) = result {
2536        if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2537            _,
2538            InstructionError::InvalidInstructionData,
2539        )) = err.kind()
2540        {
2541            return Err("Migrating a program is not supported by the cluster".into());
2542        } else {
2543            return Err(format!("Migrate program failed: {err}").into());
2544        }
2545    }
2546
2547    Ok(config
2548        .output_format
2549        .formatted_string(&CliUpgradeableProgramMigrated {
2550            program_id: program_pubkey.to_string(),
2551        }))
2552}
2553
2554pub fn calculate_max_chunk_size(baseline_msg: Message) -> usize {
2555    let tx_size = bincode::serialized_size(&Transaction {
2556        signatures: vec![
2557            Signature::default();
2558            baseline_msg.header.num_required_signatures as usize
2559        ],
2560        message: baseline_msg,
2561    })
2562    .unwrap() as usize;
2563    // add 1 byte buffer to account for shortvec encoding
2564    PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1)
2565}
2566
2567#[allow(clippy::too_many_arguments)]
2568fn do_process_program_deploy(
2569    rpc_client: Arc<RpcClient>,
2570    config: &CliConfig,
2571    program_data: &[u8], // can be empty, hence we have program_len
2572    program_len: usize,
2573    program_data_max_len: usize,
2574    min_rent_exempt_program_data_balance: u64,
2575    fee_payer_signer: &dyn Signer,
2576    program_signers: &[&dyn Signer],
2577    buffer_signer: Option<&dyn Signer>,
2578    buffer_pubkey: &Pubkey,
2579    buffer_program_data: Option<Vec<u8>>,
2580    buffer_authority_signer: &dyn Signer,
2581    skip_fee_check: bool,
2582    compute_unit_price: Option<u64>,
2583    max_sign_attempts: usize,
2584    use_rpc: bool,
2585) -> ProcessResult {
2586    let blockhash = rpc_client.get_latest_blockhash()?;
2587    let compute_unit_limit = ComputeUnitLimit::Simulated;
2588
2589    let (initial_instructions, balance_needed, buffer_program_data) =
2590        if let Some(buffer_program_data) = buffer_program_data {
2591            (vec![], 0, buffer_program_data)
2592        } else {
2593            (
2594                loader_v3_instruction::create_buffer(
2595                    &fee_payer_signer.pubkey(),
2596                    buffer_pubkey,
2597                    &buffer_authority_signer.pubkey(),
2598                    min_rent_exempt_program_data_balance,
2599                    program_len,
2600                )?,
2601                min_rent_exempt_program_data_balance,
2602                vec![0; program_len],
2603            )
2604        };
2605
2606    let initial_message = if !initial_instructions.is_empty() {
2607        Some(Message::new_with_blockhash(
2608            &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2609                compute_unit_price,
2610                compute_unit_limit,
2611            }),
2612            Some(&fee_payer_signer.pubkey()),
2613            &blockhash,
2614        ))
2615    } else {
2616        None
2617    };
2618
2619    // Create and add write messages
2620    let create_msg = |offset: u32, bytes: Vec<u8>| {
2621        let instruction = loader_v3_instruction::write(
2622            buffer_pubkey,
2623            &buffer_authority_signer.pubkey(),
2624            offset,
2625            bytes,
2626        );
2627
2628        let instructions = vec![instruction].with_compute_unit_config(&ComputeUnitConfig {
2629            compute_unit_price,
2630            compute_unit_limit,
2631        });
2632        Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2633    };
2634
2635    let mut write_messages = vec![];
2636    let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2637    for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2638        let offset = i.saturating_mul(chunk_size);
2639        if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2640            write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2641        }
2642    }
2643
2644    // Create and add final message
2645    let final_message = {
2646        #[allow(deprecated)]
2647        let instructions = loader_v3_instruction::deploy_with_max_program_len(
2648            &fee_payer_signer.pubkey(),
2649            &program_signers[0].pubkey(),
2650            buffer_pubkey,
2651            &program_signers[1].pubkey(),
2652            rpc_client
2653                .get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_program())?,
2654            program_data_max_len,
2655        )?
2656        .with_compute_unit_config(&ComputeUnitConfig {
2657            compute_unit_price,
2658            compute_unit_limit,
2659        });
2660
2661        Some(Message::new_with_blockhash(
2662            &instructions,
2663            Some(&fee_payer_signer.pubkey()),
2664            &blockhash,
2665        ))
2666    };
2667
2668    if !skip_fee_check {
2669        check_payer(
2670            &rpc_client,
2671            config,
2672            fee_payer_signer.pubkey(),
2673            balance_needed,
2674            &initial_message,
2675            &write_messages,
2676            &final_message,
2677        )?;
2678    }
2679
2680    let final_tx_sig = send_deploy_messages(
2681        rpc_client,
2682        config,
2683        initial_message,
2684        write_messages,
2685        final_message,
2686        fee_payer_signer,
2687        buffer_signer,
2688        Some(buffer_authority_signer),
2689        Some(program_signers),
2690        max_sign_attempts,
2691        use_rpc,
2692        &compute_unit_limit,
2693    )?;
2694
2695    let program_id = CliProgramId {
2696        program_id: program_signers[0].pubkey().to_string(),
2697        signature: final_tx_sig.as_ref().map(ToString::to_string),
2698    };
2699    Ok(config.output_format.formatted_string(&program_id))
2700}
2701
2702#[allow(clippy::too_many_arguments)]
2703fn do_process_write_buffer(
2704    rpc_client: Arc<RpcClient>,
2705    config: &CliConfig,
2706    program_data: &[u8], // can be empty, hence we have program_len
2707    program_len: usize,
2708    min_rent_exempt_program_data_balance: u64,
2709    fee_payer_signer: &dyn Signer,
2710    buffer_signer: Option<&dyn Signer>,
2711    buffer_pubkey: &Pubkey,
2712    buffer_program_data: Option<Vec<u8>>,
2713    buffer_authority_signer: &dyn Signer,
2714    skip_fee_check: bool,
2715    compute_unit_price: Option<u64>,
2716    max_sign_attempts: usize,
2717    use_rpc: bool,
2718) -> ProcessResult {
2719    let blockhash = rpc_client.get_latest_blockhash()?;
2720    let compute_unit_limit = ComputeUnitLimit::Simulated;
2721
2722    let (initial_instructions, balance_needed, buffer_program_data) =
2723        if let Some(buffer_program_data) = buffer_program_data {
2724            (vec![], 0, buffer_program_data)
2725        } else {
2726            (
2727                loader_v3_instruction::create_buffer(
2728                    &fee_payer_signer.pubkey(),
2729                    buffer_pubkey,
2730                    &buffer_authority_signer.pubkey(),
2731                    min_rent_exempt_program_data_balance,
2732                    program_len,
2733                )?,
2734                min_rent_exempt_program_data_balance,
2735                vec![0; program_len],
2736            )
2737        };
2738
2739    let initial_message = if !initial_instructions.is_empty() {
2740        Some(Message::new_with_blockhash(
2741            &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2742                compute_unit_price,
2743                compute_unit_limit,
2744            }),
2745            Some(&fee_payer_signer.pubkey()),
2746            &blockhash,
2747        ))
2748    } else {
2749        None
2750    };
2751
2752    // Create and add write messages
2753    let create_msg = |offset: u32, bytes: Vec<u8>| {
2754        let instruction = loader_v3_instruction::write(
2755            buffer_pubkey,
2756            &buffer_authority_signer.pubkey(),
2757            offset,
2758            bytes,
2759        );
2760
2761        let instructions = vec![instruction].with_compute_unit_config(&ComputeUnitConfig {
2762            compute_unit_price,
2763            compute_unit_limit,
2764        });
2765        Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2766    };
2767
2768    let mut write_messages = vec![];
2769    let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2770    for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2771        let offset = i.saturating_mul(chunk_size);
2772        if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2773            write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2774        }
2775    }
2776
2777    if !skip_fee_check {
2778        check_payer(
2779            &rpc_client,
2780            config,
2781            fee_payer_signer.pubkey(),
2782            balance_needed,
2783            &initial_message,
2784            &write_messages,
2785            &None,
2786        )?;
2787    }
2788
2789    let _final_tx_sig = send_deploy_messages(
2790        rpc_client,
2791        config,
2792        initial_message,
2793        write_messages,
2794        None,
2795        fee_payer_signer,
2796        buffer_signer,
2797        Some(buffer_authority_signer),
2798        None,
2799        max_sign_attempts,
2800        use_rpc,
2801        &compute_unit_limit,
2802    )?;
2803
2804    let buffer = CliProgramBuffer {
2805        buffer: buffer_pubkey.to_string(),
2806    };
2807    Ok(config.output_format.formatted_string(&buffer))
2808}
2809
2810#[allow(clippy::too_many_arguments)]
2811fn do_process_program_upgrade(
2812    rpc_client: Arc<RpcClient>,
2813    config: &CliConfig,
2814    program_data: &[u8], // can be empty, hence we have program_len
2815    program_len: usize,
2816    min_rent_exempt_program_data_balance: u64,
2817    fee_payer_signer: &dyn Signer,
2818    program_id: &Pubkey,
2819    upgrade_authority: &dyn Signer,
2820    buffer_pubkey: &Pubkey,
2821    buffer_signer: Option<&dyn Signer>,
2822    buffer_program_data: Option<Vec<u8>>,
2823    skip_fee_check: bool,
2824    compute_unit_price: Option<u64>,
2825    max_sign_attempts: usize,
2826    auto_extend: bool,
2827    use_rpc: bool,
2828) -> ProcessResult {
2829    let blockhash = rpc_client.get_latest_blockhash()?;
2830    let compute_unit_limit = ComputeUnitLimit::Simulated;
2831
2832    let (initial_message, write_messages, balance_needed) = if let Some(buffer_signer) =
2833        buffer_signer
2834    {
2835        let (mut initial_instructions, balance_needed, buffer_program_data) =
2836            if let Some(buffer_program_data) = buffer_program_data {
2837                (vec![], 0, buffer_program_data)
2838            } else {
2839                (
2840                    loader_v3_instruction::create_buffer(
2841                        &fee_payer_signer.pubkey(),
2842                        &buffer_signer.pubkey(),
2843                        &upgrade_authority.pubkey(),
2844                        min_rent_exempt_program_data_balance,
2845                        program_len,
2846                    )?,
2847                    min_rent_exempt_program_data_balance,
2848                    vec![0; program_len],
2849                )
2850            };
2851
2852        if auto_extend {
2853            extend_program_data_if_needed(
2854                &mut initial_instructions,
2855                &rpc_client,
2856                config.commitment,
2857                &fee_payer_signer.pubkey(),
2858                program_id,
2859                program_len,
2860            )?;
2861        }
2862
2863        let initial_message = if !initial_instructions.is_empty() {
2864            Some(Message::new_with_blockhash(
2865                &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2866                    compute_unit_price,
2867                    compute_unit_limit: ComputeUnitLimit::Simulated,
2868                }),
2869                Some(&fee_payer_signer.pubkey()),
2870                &blockhash,
2871            ))
2872        } else {
2873            None
2874        };
2875
2876        let buffer_signer_pubkey = buffer_signer.pubkey();
2877        let upgrade_authority_pubkey = upgrade_authority.pubkey();
2878        let create_msg = |offset: u32, bytes: Vec<u8>| {
2879            let instructions = vec![loader_v3_instruction::write(
2880                &buffer_signer_pubkey,
2881                &upgrade_authority_pubkey,
2882                offset,
2883                bytes,
2884            )]
2885            .with_compute_unit_config(&ComputeUnitConfig {
2886                compute_unit_price,
2887                compute_unit_limit,
2888            });
2889            Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2890        };
2891
2892        // Create and add write messages
2893        let mut write_messages = vec![];
2894        let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2895        for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2896            let offset = i.saturating_mul(chunk_size);
2897            if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2898                write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2899            }
2900        }
2901
2902        (initial_message, write_messages, balance_needed)
2903    } else {
2904        (None, vec![], 0)
2905    };
2906
2907    // Create and add final message
2908    let final_instructions = vec![loader_v3_instruction::upgrade(
2909        program_id,
2910        buffer_pubkey,
2911        &upgrade_authority.pubkey(),
2912        &fee_payer_signer.pubkey(),
2913    )]
2914    .with_compute_unit_config(&ComputeUnitConfig {
2915        compute_unit_price,
2916        compute_unit_limit,
2917    });
2918    let final_message = Message::new_with_blockhash(
2919        &final_instructions,
2920        Some(&fee_payer_signer.pubkey()),
2921        &blockhash,
2922    );
2923    let final_message = Some(final_message);
2924
2925    if !skip_fee_check {
2926        check_payer(
2927            &rpc_client,
2928            config,
2929            fee_payer_signer.pubkey(),
2930            balance_needed,
2931            &initial_message,
2932            &write_messages,
2933            &final_message,
2934        )?;
2935    }
2936
2937    let final_tx_sig = send_deploy_messages(
2938        rpc_client,
2939        config,
2940        initial_message,
2941        write_messages,
2942        final_message,
2943        fee_payer_signer,
2944        buffer_signer,
2945        Some(upgrade_authority),
2946        Some(&[upgrade_authority]),
2947        max_sign_attempts,
2948        use_rpc,
2949        &compute_unit_limit,
2950    )?;
2951
2952    let program_id = CliProgramId {
2953        program_id: program_id.to_string(),
2954        signature: final_tx_sig.as_ref().map(ToString::to_string),
2955    };
2956    Ok(config.output_format.formatted_string(&program_id))
2957}
2958
2959// Attempts to look up the program data account, and adds an extend program data instruction if the
2960// program data account is too small.
2961fn extend_program_data_if_needed(
2962    initial_instructions: &mut Vec<Instruction>,
2963    rpc_client: &RpcClient,
2964    commitment: CommitmentConfig,
2965    fee_payer: &Pubkey,
2966    program_id: &Pubkey,
2967    program_len: usize,
2968) -> Result<(), Box<dyn std::error::Error>> {
2969    let program_data_address = get_program_data_address(program_id);
2970
2971    let Some(program_data_account) = rpc_client
2972        .get_account_with_commitment(&program_data_address, commitment)?
2973        .value
2974    else {
2975        // Program data has not been allocated yet.
2976        return Ok(());
2977    };
2978
2979    let required_len = UpgradeableLoaderState::size_of_programdata(program_len);
2980    let max_permitted_data_length = usize::try_from(MAX_PERMITTED_DATA_LENGTH).unwrap();
2981    if required_len > max_permitted_data_length {
2982        let max_program_len = max_permitted_data_length
2983            .saturating_sub(UpgradeableLoaderState::size_of_programdata(0));
2984        return Err(format!(
2985            "New program ({program_id}) data account is too big: {required_len}.\n\
2986             Maximum program size: {max_program_len}.",
2987        )
2988        .into());
2989    }
2990
2991    let current_len = program_data_account.data.len();
2992    let additional_bytes = required_len.saturating_sub(current_len);
2993    if additional_bytes == 0 {
2994        // Current allocation is sufficient.
2995        return Ok(());
2996    }
2997
2998    let additional_bytes =
2999        u32::try_from(additional_bytes).expect("`u32` is big enough to hold an account size");
3000    initial_instructions.push(loader_v3_instruction::extend_program(
3001        program_id,
3002        Some(fee_payer),
3003        additional_bytes,
3004    ));
3005
3006    Ok(())
3007}
3008
3009fn read_and_verify_elf(
3010    program_location: &str,
3011    feature_set: FeatureSet,
3012) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
3013    let mut file = File::open(program_location)
3014        .map_err(|err| format!("Unable to open program file: {err}"))?;
3015    let mut program_data = Vec::new();
3016    file.read_to_end(&mut program_data)
3017        .map_err(|err| format!("Unable to read program file: {err}"))?;
3018
3019    verify_elf(&program_data, feature_set)?;
3020
3021    Ok(program_data)
3022}
3023
3024fn verify_elf(
3025    program_data: &[u8],
3026    feature_set: FeatureSet,
3027) -> Result<(), Box<dyn std::error::Error>> {
3028    // Verify the program
3029    let program_runtime_environment =
3030        create_program_runtime_environment_v1(&feature_set, &ComputeBudget::default(), true, false)
3031            .unwrap();
3032    let executable =
3033        Executable::<InvokeContext>::from_elf(program_data, Arc::new(program_runtime_environment))
3034            .map_err(|err| format!("ELF error: {err}"))?;
3035
3036    executable
3037        .verify::<RequisiteVerifier>()
3038        .map_err(|err| format!("ELF error: {err}").into())
3039}
3040
3041fn check_payer(
3042    rpc_client: &RpcClient,
3043    config: &CliConfig,
3044    fee_payer_pubkey: Pubkey,
3045    balance_needed: u64,
3046    initial_message: &Option<Message>,
3047    write_messages: &[Message],
3048    final_message: &Option<Message>,
3049) -> Result<(), Box<dyn std::error::Error>> {
3050    let mut fee = Saturating(0);
3051    if let Some(message) = initial_message {
3052        fee += rpc_client.get_fee_for_message(message)?;
3053    }
3054    // Assume all write messages cost the same
3055    if let Some(message) = write_messages.first() {
3056        fee += rpc_client
3057            .get_fee_for_message(message)?
3058            .saturating_mul(write_messages.len() as u64);
3059    }
3060    if let Some(message) = final_message {
3061        fee += rpc_client.get_fee_for_message(message)?;
3062    }
3063    check_account_for_spend_and_fee_with_commitment(
3064        rpc_client,
3065        &fee_payer_pubkey,
3066        balance_needed,
3067        fee.0,
3068        config.commitment,
3069    )?;
3070    Ok(())
3071}
3072
3073#[allow(clippy::too_many_arguments)]
3074fn send_deploy_messages(
3075    rpc_client: Arc<RpcClient>,
3076    config: &CliConfig,
3077    initial_message: Option<Message>,
3078    mut write_messages: Vec<Message>,
3079    final_message: Option<Message>,
3080    fee_payer_signer: &dyn Signer,
3081    initial_signer: Option<&dyn Signer>,
3082    write_signer: Option<&dyn Signer>,
3083    final_signers: Option<&[&dyn Signer]>,
3084    max_sign_attempts: usize,
3085    use_rpc: bool,
3086    compute_unit_limit: &ComputeUnitLimit,
3087) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
3088    if let Some(mut message) = initial_message {
3089        if let Some(initial_signer) = initial_signer {
3090            trace!("Preparing the required accounts");
3091            simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)?;
3092            let mut initial_transaction = Transaction::new_unsigned(message.clone());
3093            let blockhash = rpc_client.get_latest_blockhash()?;
3094
3095            // Most of the initial_transaction combinations require both the fee-payer and new program
3096            // account to sign the transaction. One (transfer) only requires the fee-payer signature.
3097            // This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
3098            // extraneous signature.
3099            if message.header.num_required_signatures == 2 {
3100                initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
3101            } else {
3102                initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
3103            }
3104            let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
3105                &initial_transaction,
3106                config.commitment,
3107                config.send_transaction_config,
3108            );
3109            log_instruction_custom_error::<SystemError>(result, config)
3110                .map_err(|err| format!("Account allocation failed: {err}"))?;
3111        } else {
3112            return Err("Buffer account not created yet, must provide a key pair".into());
3113        }
3114    }
3115
3116    if !write_messages.is_empty() {
3117        if let Some(write_signer) = write_signer {
3118            trace!("Writing program data");
3119
3120            // Simulate the first write message to get the number of compute units
3121            // consumed and then reuse that value as the compute unit limit for all
3122            // write messages.
3123            {
3124                let mut message = write_messages[0].clone();
3125                if let UpdateComputeUnitLimitResult::UpdatedInstructionIndex(ix_index) =
3126                    simulate_and_update_compute_unit_limit(
3127                        compute_unit_limit,
3128                        &rpc_client,
3129                        &mut message,
3130                    )?
3131                {
3132                    for msg in &mut write_messages {
3133                        // Write messages are all assumed to be identical except
3134                        // the program data being written. But just in case that
3135                        // assumption is broken, assert that we are only ever
3136                        // changing the instruction data for a compute budget
3137                        // instruction.
3138                        assert_eq!(msg.program_id(ix_index), Some(&compute_budget::id()));
3139                        msg.instructions[ix_index]
3140                            .data
3141                            .clone_from(&message.instructions[ix_index].data);
3142                    }
3143                }
3144            }
3145
3146            let connection_cache = if config.use_quic {
3147                ConnectionCache::new_quic("connection_cache_cli_program_quic", 1)
3148            } else {
3149                ConnectionCache::with_udp("connection_cache_cli_program_udp", 1)
3150            };
3151            let transaction_errors = match connection_cache {
3152                ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
3153                    rpc_client.clone(),
3154                    &config.websocket_url,
3155                    TpuClientConfig::default(),
3156                    cache,
3157                )?
3158                .send_and_confirm_messages_with_spinner(
3159                    &write_messages,
3160                    &[fee_payer_signer, write_signer],
3161                ),
3162                ConnectionCache::Quic(cache) => {
3163                    let tpu_client_fut = solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
3164                        rpc_client.get_inner_client().clone(),
3165                        config.websocket_url.as_str(),
3166                        solana_client::tpu_client::TpuClientConfig::default(),
3167                        cache,
3168                    );
3169                    let tpu_client = (!use_rpc).then(|| rpc_client
3170                        .runtime()
3171                        .block_on(tpu_client_fut)
3172                        .expect("Should return a valid tpu client")
3173                    );
3174
3175                    send_and_confirm_transactions_in_parallel_blocking_v2(
3176                        rpc_client.clone(),
3177                        tpu_client,
3178                        &write_messages,
3179                        &[fee_payer_signer, write_signer],
3180                        SendAndConfirmConfigV2 {
3181                            resign_txs_count: Some(max_sign_attempts),
3182                            with_spinner: true,
3183                            rpc_send_transaction_config: config.send_transaction_config,
3184                        },
3185                    )
3186                },
3187            }
3188            .map_err(|err| format!("Data writes to account failed: {err}"))?
3189            .into_iter()
3190            .flatten()
3191            .collect::<Vec<_>>();
3192
3193            if !transaction_errors.is_empty() {
3194                for transaction_error in &transaction_errors {
3195                    error!("{:?}", transaction_error);
3196                }
3197                return Err(
3198                    format!("{} write transactions failed", transaction_errors.len()).into(),
3199                );
3200            }
3201        }
3202    }
3203
3204    if let Some(mut message) = final_message {
3205        if let Some(final_signers) = final_signers {
3206            trace!("Deploying program");
3207
3208            simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)?;
3209            let mut final_tx = Transaction::new_unsigned(message);
3210            let blockhash = rpc_client.get_latest_blockhash()?;
3211            let mut signers = final_signers.to_vec();
3212            signers.push(fee_payer_signer);
3213            final_tx.try_sign(&signers, blockhash)?;
3214            return Ok(Some(
3215                rpc_client
3216                    .send_and_confirm_transaction_with_spinner_and_config(
3217                        &final_tx,
3218                        config.commitment,
3219                        config.send_transaction_config,
3220                    )
3221                    .map_err(|e| format!("Deploying program failed: {e}"))?,
3222            ));
3223        }
3224    }
3225
3226    Ok(None)
3227}
3228
3229fn create_ephemeral_keypair(
3230) -> Result<(usize, bip39::Mnemonic, Keypair), Box<dyn std::error::Error>> {
3231    const WORDS: usize = 12;
3232    let mnemonic = Mnemonic::new(MnemonicType::for_word_count(WORDS)?, Language::English);
3233    let seed = Seed::new(&mnemonic, "");
3234    let new_keypair = keypair_from_seed(seed.as_bytes())?;
3235
3236    Ok((WORDS, mnemonic, new_keypair))
3237}
3238
3239fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic, ephemeral_pubkey: &Pubkey) {
3240    let phrase: &str = mnemonic.phrase();
3241    let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
3242    eprintln!("{divider}\nRecover the intermediate account's ephemeral keypair file with");
3243    eprintln!("`solana-keygen recover` and the following {words}-word seed phrase:");
3244    eprintln!("{divider}\n{phrase}\n{divider}");
3245    eprintln!("To resume a deploy, pass the recovered keypair as the");
3246    eprintln!("[BUFFER_SIGNER] to `solana program deploy` or `solana program write-buffer'.");
3247    eprintln!("Or to recover the account's lamports, use:");
3248    eprintln!("{divider}\nsolana program close {ephemeral_pubkey}\n{divider}");
3249}
3250
3251fn fetch_feature_set(rpc_client: &RpcClient) -> Result<FeatureSet, Box<dyn std::error::Error>> {
3252    let mut feature_set = FeatureSet::default();
3253    for feature_ids in FEATURE_NAMES
3254        .keys()
3255        .cloned()
3256        .collect::<Vec<Pubkey>>()
3257        .chunks(MAX_MULTIPLE_ACCOUNTS)
3258    {
3259        rpc_client
3260            .get_multiple_accounts(feature_ids)?
3261            .into_iter()
3262            .zip(feature_ids)
3263            .for_each(|(account, feature_id)| {
3264                let activation_slot = account.and_then(status_from_account);
3265
3266                if let Some(CliFeatureStatus::Active(slot)) = activation_slot {
3267                    feature_set.activate(feature_id, slot);
3268                }
3269            });
3270    }
3271
3272    Ok(feature_set)
3273}
3274
3275#[cfg(test)]
3276mod tests {
3277    use {
3278        super::*,
3279        crate::{
3280            clap_app::get_clap_app,
3281            cli::{parse_command, process_command},
3282        },
3283        serde_json::Value,
3284        solana_cli_output::OutputFormat,
3285        solana_hash::Hash,
3286        solana_keypair::write_keypair_file,
3287    };
3288
3289    fn make_tmp_path(name: &str) -> String {
3290        let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
3291        let keypair = Keypair::new();
3292
3293        let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
3294
3295        // whack any possible collision
3296        let _ignored = std::fs::remove_dir_all(&path);
3297        // whack any possible collision
3298        let _ignored = std::fs::remove_file(&path);
3299
3300        path
3301    }
3302
3303    #[test]
3304    #[allow(clippy::cognitive_complexity)]
3305    fn test_cli_parse_deploy() {
3306        let test_commands = get_clap_app("test", "desc", "version");
3307
3308        let default_keypair = Keypair::new();
3309        let keypair_file = make_tmp_path("keypair_file");
3310        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3311        let default_signer = DefaultSigner::new("", &keypair_file);
3312
3313        let test_command = test_commands.clone().get_matches_from(vec![
3314            "test",
3315            "program",
3316            "deploy",
3317            "/Users/test/program.so",
3318        ]);
3319        assert_eq!(
3320            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3321            CliCommandInfo {
3322                command: CliCommand::Program(ProgramCliCommand::Deploy {
3323                    program_location: Some("/Users/test/program.so".to_string()),
3324                    fee_payer_signer_index: 0,
3325                    buffer_signer_index: None,
3326                    buffer_pubkey: None,
3327                    program_signer_index: None,
3328                    program_pubkey: None,
3329                    upgrade_authority_signer_index: 0,
3330                    is_final: false,
3331                    max_len: None,
3332                    skip_fee_check: false,
3333                    compute_unit_price: None,
3334                    max_sign_attempts: 5,
3335                    auto_extend: true,
3336                    use_rpc: false,
3337                    skip_feature_verification: false,
3338                }),
3339                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3340            }
3341        );
3342
3343        let test_command = test_commands.clone().get_matches_from(vec![
3344            "test",
3345            "program",
3346            "deploy",
3347            "/Users/test/program.so",
3348            "--max-len",
3349            "42",
3350        ]);
3351        assert_eq!(
3352            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3353            CliCommandInfo {
3354                command: CliCommand::Program(ProgramCliCommand::Deploy {
3355                    program_location: Some("/Users/test/program.so".to_string()),
3356                    fee_payer_signer_index: 0,
3357                    buffer_signer_index: None,
3358                    buffer_pubkey: None,
3359                    program_signer_index: None,
3360                    program_pubkey: None,
3361                    upgrade_authority_signer_index: 0,
3362                    is_final: false,
3363                    max_len: Some(42),
3364                    skip_fee_check: false,
3365                    compute_unit_price: None,
3366                    max_sign_attempts: 5,
3367                    auto_extend: true,
3368                    use_rpc: false,
3369                    skip_feature_verification: false,
3370                }),
3371                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3372            }
3373        );
3374
3375        let buffer_keypair = Keypair::new();
3376        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3377        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3378        let test_command = test_commands.clone().get_matches_from(vec![
3379            "test",
3380            "program",
3381            "deploy",
3382            "--buffer",
3383            &buffer_keypair_file,
3384        ]);
3385        assert_eq!(
3386            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3387            CliCommandInfo {
3388                command: CliCommand::Program(ProgramCliCommand::Deploy {
3389                    program_location: None,
3390                    fee_payer_signer_index: 0,
3391                    buffer_signer_index: Some(1),
3392                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3393                    program_signer_index: None,
3394                    program_pubkey: None,
3395                    upgrade_authority_signer_index: 0,
3396                    is_final: false,
3397                    max_len: None,
3398                    skip_fee_check: false,
3399                    compute_unit_price: None,
3400                    max_sign_attempts: 5,
3401                    auto_extend: true,
3402                    use_rpc: false,
3403                    skip_feature_verification: false,
3404                }),
3405                signers: vec![
3406                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3407                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3408                ],
3409            }
3410        );
3411
3412        let program_pubkey = Pubkey::new_unique();
3413        let test = test_commands.clone().get_matches_from(vec![
3414            "test",
3415            "program",
3416            "deploy",
3417            "/Users/test/program.so",
3418            "--program-id",
3419            &program_pubkey.to_string(),
3420        ]);
3421        assert_eq!(
3422            parse_command(&test, &default_signer, &mut None).unwrap(),
3423            CliCommandInfo {
3424                command: CliCommand::Program(ProgramCliCommand::Deploy {
3425                    program_location: Some("/Users/test/program.so".to_string()),
3426                    fee_payer_signer_index: 0,
3427                    buffer_signer_index: None,
3428                    buffer_pubkey: None,
3429                    program_signer_index: None,
3430                    program_pubkey: Some(program_pubkey),
3431                    upgrade_authority_signer_index: 0,
3432                    is_final: false,
3433                    max_len: None,
3434                    skip_fee_check: false,
3435                    compute_unit_price: None,
3436                    max_sign_attempts: 5,
3437                    auto_extend: true,
3438                    use_rpc: false,
3439                    skip_feature_verification: false,
3440                }),
3441                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3442            }
3443        );
3444
3445        let program_keypair = Keypair::new();
3446        let program_keypair_file = make_tmp_path("program_keypair_file");
3447        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
3448        let test = test_commands.clone().get_matches_from(vec![
3449            "test",
3450            "program",
3451            "deploy",
3452            "/Users/test/program.so",
3453            "--program-id",
3454            &program_keypair_file,
3455        ]);
3456        assert_eq!(
3457            parse_command(&test, &default_signer, &mut None).unwrap(),
3458            CliCommandInfo {
3459                command: CliCommand::Program(ProgramCliCommand::Deploy {
3460                    program_location: Some("/Users/test/program.so".to_string()),
3461                    fee_payer_signer_index: 0,
3462                    buffer_signer_index: None,
3463                    buffer_pubkey: None,
3464                    program_signer_index: Some(1),
3465                    program_pubkey: Some(program_keypair.pubkey()),
3466                    upgrade_authority_signer_index: 0,
3467                    is_final: false,
3468                    max_len: None,
3469                    skip_fee_check: false,
3470                    compute_unit_price: None,
3471                    max_sign_attempts: 5,
3472                    auto_extend: true,
3473                    use_rpc: false,
3474                    skip_feature_verification: false,
3475                }),
3476                signers: vec![
3477                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3478                    Box::new(read_keypair_file(&program_keypair_file).unwrap()),
3479                ],
3480            }
3481        );
3482
3483        let authority_keypair = Keypair::new();
3484        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3485        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3486        let test_command = test_commands.clone().get_matches_from(vec![
3487            "test",
3488            "program",
3489            "deploy",
3490            "/Users/test/program.so",
3491            "--upgrade-authority",
3492            &authority_keypair_file,
3493        ]);
3494        assert_eq!(
3495            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3496            CliCommandInfo {
3497                command: CliCommand::Program(ProgramCliCommand::Deploy {
3498                    program_location: Some("/Users/test/program.so".to_string()),
3499                    fee_payer_signer_index: 0,
3500                    buffer_signer_index: None,
3501                    buffer_pubkey: None,
3502                    program_signer_index: None,
3503                    program_pubkey: None,
3504                    upgrade_authority_signer_index: 1,
3505                    is_final: false,
3506                    max_len: None,
3507                    skip_fee_check: false,
3508                    compute_unit_price: None,
3509                    max_sign_attempts: 5,
3510                    auto_extend: true,
3511                    use_rpc: false,
3512                    skip_feature_verification: false,
3513                }),
3514                signers: vec![
3515                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3516                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3517                ],
3518            }
3519        );
3520
3521        let test_command = test_commands.clone().get_matches_from(vec![
3522            "test",
3523            "program",
3524            "deploy",
3525            "/Users/test/program.so",
3526            "--final",
3527        ]);
3528        assert_eq!(
3529            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3530            CliCommandInfo {
3531                command: CliCommand::Program(ProgramCliCommand::Deploy {
3532                    program_location: Some("/Users/test/program.so".to_string()),
3533                    fee_payer_signer_index: 0,
3534                    buffer_signer_index: None,
3535                    buffer_pubkey: None,
3536                    program_signer_index: None,
3537                    program_pubkey: None,
3538                    upgrade_authority_signer_index: 0,
3539                    is_final: true,
3540                    max_len: None,
3541                    skip_fee_check: false,
3542                    compute_unit_price: None,
3543                    max_sign_attempts: 5,
3544                    auto_extend: true,
3545                    use_rpc: false,
3546                    skip_feature_verification: false,
3547                }),
3548                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3549            }
3550        );
3551
3552        let test_command = test_commands.clone().get_matches_from(vec![
3553            "test",
3554            "program",
3555            "deploy",
3556            "/Users/test/program.so",
3557            "--max-sign-attempts",
3558            "1",
3559        ]);
3560        assert_eq!(
3561            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3562            CliCommandInfo {
3563                command: CliCommand::Program(ProgramCliCommand::Deploy {
3564                    program_location: Some("/Users/test/program.so".to_string()),
3565                    fee_payer_signer_index: 0,
3566                    buffer_signer_index: None,
3567                    buffer_pubkey: None,
3568                    program_signer_index: None,
3569                    program_pubkey: None,
3570                    upgrade_authority_signer_index: 0,
3571                    is_final: false,
3572                    max_len: None,
3573                    skip_fee_check: false,
3574                    compute_unit_price: None,
3575                    max_sign_attempts: 1,
3576                    auto_extend: true,
3577                    use_rpc: false,
3578                    skip_feature_verification: false,
3579                }),
3580                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3581            }
3582        );
3583
3584        let test_command = test_commands.clone().get_matches_from(vec![
3585            "test",
3586            "program",
3587            "deploy",
3588            "/Users/test/program.so",
3589            "--use-rpc",
3590        ]);
3591        assert_eq!(
3592            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3593            CliCommandInfo {
3594                command: CliCommand::Program(ProgramCliCommand::Deploy {
3595                    program_location: Some("/Users/test/program.so".to_string()),
3596                    fee_payer_signer_index: 0,
3597                    buffer_signer_index: None,
3598                    buffer_pubkey: None,
3599                    program_signer_index: None,
3600                    program_pubkey: None,
3601                    upgrade_authority_signer_index: 0,
3602                    is_final: false,
3603                    max_len: None,
3604                    skip_fee_check: false,
3605                    compute_unit_price: None,
3606                    max_sign_attempts: 5,
3607                    auto_extend: true,
3608                    use_rpc: true,
3609                    skip_feature_verification: false,
3610                }),
3611                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3612            }
3613        );
3614
3615        let test_command = test_commands.clone().get_matches_from(vec![
3616            "test",
3617            "program",
3618            "deploy",
3619            "/Users/test/program.so",
3620            "--skip-feature-verify",
3621        ]);
3622        assert_eq!(
3623            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3624            CliCommandInfo {
3625                command: CliCommand::Program(ProgramCliCommand::Deploy {
3626                    program_location: Some("/Users/test/program.so".to_string()),
3627                    fee_payer_signer_index: 0,
3628                    buffer_signer_index: None,
3629                    buffer_pubkey: None,
3630                    program_signer_index: None,
3631                    program_pubkey: None,
3632                    upgrade_authority_signer_index: 0,
3633                    is_final: false,
3634                    max_len: None,
3635                    skip_fee_check: false,
3636                    compute_unit_price: None,
3637                    max_sign_attempts: 5,
3638                    auto_extend: true,
3639                    use_rpc: false,
3640                    skip_feature_verification: true,
3641                }),
3642                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3643            }
3644        );
3645    }
3646
3647    #[test]
3648    fn test_cli_parse_upgrade() {
3649        let test_commands = get_clap_app("test", "desc", "version");
3650
3651        let default_keypair = Keypair::new();
3652        let keypair_file = make_tmp_path("keypair_file");
3653        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3654        let default_signer = DefaultSigner::new("", &keypair_file);
3655
3656        let program_key = Pubkey::new_unique();
3657        let buffer_key = Pubkey::new_unique();
3658        let test_command = test_commands.clone().get_matches_from(vec![
3659            "test",
3660            "program",
3661            "upgrade",
3662            format!("{}", buffer_key).as_str(),
3663            format!("{}", program_key).as_str(),
3664            "--skip-feature-verify",
3665        ]);
3666        assert_eq!(
3667            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3668            CliCommandInfo {
3669                command: CliCommand::Program(ProgramCliCommand::Upgrade {
3670                    fee_payer_signer_index: 0,
3671                    program_pubkey: program_key,
3672                    buffer_pubkey: buffer_key,
3673                    upgrade_authority_signer_index: 0,
3674                    sign_only: false,
3675                    dump_transaction_message: false,
3676                    blockhash_query: BlockhashQuery::default(),
3677                    skip_feature_verification: true,
3678                }),
3679                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3680            }
3681        );
3682    }
3683
3684    #[test]
3685    #[allow(clippy::cognitive_complexity)]
3686    fn test_cli_parse_write_buffer() {
3687        let test_commands = get_clap_app("test", "desc", "version");
3688
3689        let default_keypair = Keypair::new();
3690        let keypair_file = make_tmp_path("keypair_file");
3691        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3692        let default_signer = DefaultSigner::new("", &keypair_file);
3693
3694        // defaults
3695        let test_command = test_commands.clone().get_matches_from(vec![
3696            "test",
3697            "program",
3698            "write-buffer",
3699            "/Users/test/program.so",
3700        ]);
3701        assert_eq!(
3702            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3703            CliCommandInfo {
3704                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3705                    program_location: "/Users/test/program.so".to_string(),
3706                    fee_payer_signer_index: 0,
3707                    buffer_signer_index: None,
3708                    buffer_pubkey: None,
3709                    buffer_authority_signer_index: 0,
3710                    max_len: None,
3711                    skip_fee_check: false,
3712                    compute_unit_price: None,
3713                    max_sign_attempts: 5,
3714                    use_rpc: false,
3715                    skip_feature_verification: false,
3716                }),
3717                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3718            }
3719        );
3720
3721        // specify max len
3722        let test_command = test_commands.clone().get_matches_from(vec![
3723            "test",
3724            "program",
3725            "write-buffer",
3726            "/Users/test/program.so",
3727            "--max-len",
3728            "42",
3729        ]);
3730        assert_eq!(
3731            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3732            CliCommandInfo {
3733                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3734                    program_location: "/Users/test/program.so".to_string(),
3735                    fee_payer_signer_index: 0,
3736                    buffer_signer_index: None,
3737                    buffer_pubkey: None,
3738                    buffer_authority_signer_index: 0,
3739                    max_len: Some(42),
3740                    skip_fee_check: false,
3741                    compute_unit_price: None,
3742                    max_sign_attempts: 5,
3743                    use_rpc: false,
3744                    skip_feature_verification: false,
3745                }),
3746                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3747            }
3748        );
3749
3750        // specify buffer
3751        let buffer_keypair = Keypair::new();
3752        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3753        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3754        let test_command = test_commands.clone().get_matches_from(vec![
3755            "test",
3756            "program",
3757            "write-buffer",
3758            "/Users/test/program.so",
3759            "--buffer",
3760            &buffer_keypair_file,
3761        ]);
3762        assert_eq!(
3763            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3764            CliCommandInfo {
3765                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3766                    program_location: "/Users/test/program.so".to_string(),
3767                    fee_payer_signer_index: 0,
3768                    buffer_signer_index: Some(1),
3769                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3770                    buffer_authority_signer_index: 0,
3771                    max_len: None,
3772                    skip_fee_check: false,
3773                    compute_unit_price: None,
3774                    max_sign_attempts: 5,
3775                    use_rpc: false,
3776                    skip_feature_verification: false,
3777                }),
3778                signers: vec![
3779                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3780                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3781                ],
3782            }
3783        );
3784
3785        // specify authority
3786        let authority_keypair = Keypair::new();
3787        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3788        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3789        let test_command = test_commands.clone().get_matches_from(vec![
3790            "test",
3791            "program",
3792            "write-buffer",
3793            "/Users/test/program.so",
3794            "--buffer-authority",
3795            &authority_keypair_file,
3796        ]);
3797        assert_eq!(
3798            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3799            CliCommandInfo {
3800                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3801                    program_location: "/Users/test/program.so".to_string(),
3802                    fee_payer_signer_index: 0,
3803                    buffer_signer_index: None,
3804                    buffer_pubkey: None,
3805                    buffer_authority_signer_index: 1,
3806                    max_len: None,
3807                    skip_fee_check: false,
3808                    compute_unit_price: None,
3809                    max_sign_attempts: 5,
3810                    use_rpc: false,
3811                    skip_feature_verification: false,
3812                }),
3813                signers: vec![
3814                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3815                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3816                ],
3817            }
3818        );
3819
3820        // specify both buffer and authority
3821        let buffer_keypair = Keypair::new();
3822        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3823        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3824        let authority_keypair = Keypair::new();
3825        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3826        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3827        let test_command = test_commands.clone().get_matches_from(vec![
3828            "test",
3829            "program",
3830            "write-buffer",
3831            "/Users/test/program.so",
3832            "--buffer",
3833            &buffer_keypair_file,
3834            "--buffer-authority",
3835            &authority_keypair_file,
3836        ]);
3837        assert_eq!(
3838            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3839            CliCommandInfo {
3840                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3841                    program_location: "/Users/test/program.so".to_string(),
3842                    fee_payer_signer_index: 0,
3843                    buffer_signer_index: Some(1),
3844                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3845                    buffer_authority_signer_index: 2,
3846                    max_len: None,
3847                    skip_fee_check: false,
3848                    compute_unit_price: None,
3849                    max_sign_attempts: 5,
3850                    use_rpc: false,
3851                    skip_feature_verification: false,
3852                }),
3853                signers: vec![
3854                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3855                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3856                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3857                ],
3858            }
3859        );
3860
3861        // specify max sign attempts
3862        let test_command = test_commands.clone().get_matches_from(vec![
3863            "test",
3864            "program",
3865            "write-buffer",
3866            "/Users/test/program.so",
3867            "--max-sign-attempts",
3868            "10",
3869        ]);
3870        assert_eq!(
3871            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3872            CliCommandInfo {
3873                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3874                    program_location: "/Users/test/program.so".to_string(),
3875                    fee_payer_signer_index: 0,
3876                    buffer_signer_index: None,
3877                    buffer_pubkey: None,
3878                    buffer_authority_signer_index: 0,
3879                    max_len: None,
3880                    skip_fee_check: false,
3881                    compute_unit_price: None,
3882                    max_sign_attempts: 10,
3883                    use_rpc: false,
3884                    skip_feature_verification: false
3885                }),
3886                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3887            }
3888        );
3889
3890        // skip feature verification
3891        let test_command = test_commands.clone().get_matches_from(vec![
3892            "test",
3893            "program",
3894            "write-buffer",
3895            "/Users/test/program.so",
3896            "--skip-feature-verify",
3897        ]);
3898        assert_eq!(
3899            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3900            CliCommandInfo {
3901                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3902                    program_location: "/Users/test/program.so".to_string(),
3903                    fee_payer_signer_index: 0,
3904                    buffer_signer_index: None,
3905                    buffer_pubkey: None,
3906                    buffer_authority_signer_index: 0,
3907                    max_len: None,
3908                    skip_fee_check: false,
3909                    compute_unit_price: None,
3910                    max_sign_attempts: 5,
3911                    use_rpc: false,
3912                    skip_feature_verification: true,
3913                }),
3914                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3915            }
3916        );
3917    }
3918
3919    #[test]
3920    #[allow(clippy::cognitive_complexity)]
3921    fn test_cli_parse_set_upgrade_authority() {
3922        let test_commands = get_clap_app("test", "desc", "version");
3923
3924        let default_keypair = Keypair::new();
3925        let keypair_file = make_tmp_path("keypair_file");
3926        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3927        let default_signer = DefaultSigner::new("", &keypair_file);
3928
3929        let program_pubkey = Pubkey::new_unique();
3930        let new_authority_pubkey = Pubkey::new_unique();
3931        let blockhash = Hash::new_unique();
3932
3933        let test_command = test_commands.clone().get_matches_from(vec![
3934            "test",
3935            "program",
3936            "set-upgrade-authority",
3937            &program_pubkey.to_string(),
3938            "--new-upgrade-authority",
3939            &new_authority_pubkey.to_string(),
3940            "--skip-new-upgrade-authority-signer-check",
3941            "--sign-only",
3942            "--dump-transaction-message",
3943            "--blockhash",
3944            blockhash.to_string().as_str(),
3945        ]);
3946        assert_eq!(
3947            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3948            CliCommandInfo {
3949                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3950                    program_pubkey,
3951                    upgrade_authority_index: Some(0),
3952                    new_upgrade_authority: Some(new_authority_pubkey),
3953                    sign_only: true,
3954                    dump_transaction_message: true,
3955                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
3956                }),
3957                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3958            }
3959        );
3960
3961        let program_pubkey = Pubkey::new_unique();
3962        let new_authority_pubkey = Keypair::new();
3963        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3964        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3965        let test_command = test_commands.clone().get_matches_from(vec![
3966            "test",
3967            "program",
3968            "set-upgrade-authority",
3969            &program_pubkey.to_string(),
3970            "--new-upgrade-authority",
3971            &new_authority_pubkey_file,
3972            "--skip-new-upgrade-authority-signer-check",
3973        ]);
3974        assert_eq!(
3975            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3976            CliCommandInfo {
3977                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3978                    program_pubkey,
3979                    upgrade_authority_index: Some(0),
3980                    new_upgrade_authority: Some(new_authority_pubkey.pubkey()),
3981                    sign_only: false,
3982                    dump_transaction_message: false,
3983                    blockhash_query: BlockhashQuery::default(),
3984                }),
3985                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3986            }
3987        );
3988
3989        let blockhash = Hash::new_unique();
3990        let program_pubkey = Pubkey::new_unique();
3991        let new_authority_pubkey = Keypair::new();
3992        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3993        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3994        let test_command = test_commands.clone().get_matches_from(vec![
3995            "test",
3996            "program",
3997            "set-upgrade-authority",
3998            &program_pubkey.to_string(),
3999            "--new-upgrade-authority",
4000            &new_authority_pubkey_file,
4001            "--sign-only",
4002            "--dump-transaction-message",
4003            "--blockhash",
4004            blockhash.to_string().as_str(),
4005        ]);
4006        assert_eq!(
4007            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4008            CliCommandInfo {
4009                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
4010                    program_pubkey,
4011                    upgrade_authority_index: 0,
4012                    new_upgrade_authority_index: 1,
4013                    sign_only: true,
4014                    dump_transaction_message: true,
4015                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4016                }),
4017                signers: vec![
4018                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4019                    Box::new(read_keypair_file(&new_authority_pubkey_file).unwrap()),
4020                ],
4021            }
4022        );
4023
4024        let program_pubkey = Pubkey::new_unique();
4025        let new_authority_pubkey = Keypair::new();
4026        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4027        write_keypair_file(&new_authority_pubkey, new_authority_pubkey_file).unwrap();
4028        let test_command = test_commands.clone().get_matches_from(vec![
4029            "test",
4030            "program",
4031            "set-upgrade-authority",
4032            &program_pubkey.to_string(),
4033            "--final",
4034        ]);
4035        assert_eq!(
4036            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4037            CliCommandInfo {
4038                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4039                    program_pubkey,
4040                    upgrade_authority_index: Some(0),
4041                    new_upgrade_authority: None,
4042                    sign_only: false,
4043                    dump_transaction_message: false,
4044                    blockhash_query: BlockhashQuery::default(),
4045                }),
4046                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4047            }
4048        );
4049
4050        let program_pubkey = Pubkey::new_unique();
4051        let authority = Keypair::new();
4052        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4053        write_keypair_file(&authority, &authority_keypair_file).unwrap();
4054        let test_command = test_commands.clone().get_matches_from(vec![
4055            "test",
4056            "program",
4057            "set-upgrade-authority",
4058            &program_pubkey.to_string(),
4059            "--upgrade-authority",
4060            &authority_keypair_file,
4061            "--final",
4062        ]);
4063        assert_eq!(
4064            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4065            CliCommandInfo {
4066                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4067                    program_pubkey,
4068                    upgrade_authority_index: Some(1),
4069                    new_upgrade_authority: None,
4070                    sign_only: false,
4071                    dump_transaction_message: false,
4072                    blockhash_query: BlockhashQuery::default(),
4073                }),
4074                signers: vec![
4075                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4076                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4077                ],
4078            }
4079        );
4080    }
4081
4082    #[test]
4083    #[allow(clippy::cognitive_complexity)]
4084    fn test_cli_parse_set_buffer_authority() {
4085        let test_commands = get_clap_app("test", "desc", "version");
4086
4087        let default_keypair = Keypair::new();
4088        let keypair_file = make_tmp_path("keypair_file");
4089        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4090        let default_signer = DefaultSigner::new("", &keypair_file);
4091
4092        let buffer_pubkey = Pubkey::new_unique();
4093        let new_authority_pubkey = Pubkey::new_unique();
4094        let test_command = test_commands.clone().get_matches_from(vec![
4095            "test",
4096            "program",
4097            "set-buffer-authority",
4098            &buffer_pubkey.to_string(),
4099            "--new-buffer-authority",
4100            &new_authority_pubkey.to_string(),
4101        ]);
4102        assert_eq!(
4103            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4104            CliCommandInfo {
4105                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4106                    buffer_pubkey,
4107                    buffer_authority_index: Some(0),
4108                    new_buffer_authority: new_authority_pubkey,
4109                }),
4110                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4111            }
4112        );
4113
4114        let buffer_pubkey = Pubkey::new_unique();
4115        let new_authority_keypair = Keypair::new();
4116        let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
4117        write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
4118        let test_command = test_commands.clone().get_matches_from(vec![
4119            "test",
4120            "program",
4121            "set-buffer-authority",
4122            &buffer_pubkey.to_string(),
4123            "--new-buffer-authority",
4124            &new_authority_keypair_file,
4125        ]);
4126        assert_eq!(
4127            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4128            CliCommandInfo {
4129                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4130                    buffer_pubkey,
4131                    buffer_authority_index: Some(0),
4132                    new_buffer_authority: new_authority_keypair.pubkey(),
4133                }),
4134                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4135            }
4136        );
4137    }
4138
4139    #[test]
4140    #[allow(clippy::cognitive_complexity)]
4141    fn test_cli_parse_show() {
4142        let test_commands = get_clap_app("test", "desc", "version");
4143
4144        let default_keypair = Keypair::new();
4145        let keypair_file = make_tmp_path("keypair_file");
4146        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4147        let default_signer = DefaultSigner::new("", &keypair_file);
4148
4149        // defaults
4150        let buffer_pubkey = Pubkey::new_unique();
4151        let authority_keypair = Keypair::new();
4152        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4153        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4154
4155        let test_command = test_commands.clone().get_matches_from(vec![
4156            "test",
4157            "program",
4158            "show",
4159            &buffer_pubkey.to_string(),
4160        ]);
4161        assert_eq!(
4162            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4163            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4164                account_pubkey: Some(buffer_pubkey),
4165                authority_pubkey: default_keypair.pubkey(),
4166                get_programs: false,
4167                get_buffers: false,
4168                all: false,
4169                use_lamports_unit: false,
4170            }))
4171        );
4172
4173        let test_command = test_commands.clone().get_matches_from(vec![
4174            "test",
4175            "program",
4176            "show",
4177            "--programs",
4178            "--all",
4179            "--lamports",
4180        ]);
4181        assert_eq!(
4182            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4183            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4184                account_pubkey: None,
4185                authority_pubkey: default_keypair.pubkey(),
4186                get_programs: true,
4187                get_buffers: false,
4188                all: true,
4189                use_lamports_unit: true,
4190            }))
4191        );
4192
4193        let test_command = test_commands.clone().get_matches_from(vec![
4194            "test",
4195            "program",
4196            "show",
4197            "--buffers",
4198            "--all",
4199            "--lamports",
4200        ]);
4201        assert_eq!(
4202            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4203            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4204                account_pubkey: None,
4205                authority_pubkey: default_keypair.pubkey(),
4206                get_programs: false,
4207                get_buffers: true,
4208                all: true,
4209                use_lamports_unit: true,
4210            }))
4211        );
4212
4213        let test_command = test_commands.clone().get_matches_from(vec![
4214            "test",
4215            "program",
4216            "show",
4217            "--buffers",
4218            "--buffer-authority",
4219            &authority_keypair.pubkey().to_string(),
4220        ]);
4221        assert_eq!(
4222            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4223            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4224                account_pubkey: None,
4225                authority_pubkey: authority_keypair.pubkey(),
4226                get_programs: false,
4227                get_buffers: true,
4228                all: false,
4229                use_lamports_unit: false,
4230            }))
4231        );
4232
4233        let test_command = test_commands.clone().get_matches_from(vec![
4234            "test",
4235            "program",
4236            "show",
4237            "--buffers",
4238            "--buffer-authority",
4239            &authority_keypair_file,
4240        ]);
4241        assert_eq!(
4242            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4243            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4244                account_pubkey: None,
4245                authority_pubkey: authority_keypair.pubkey(),
4246                get_programs: false,
4247                get_buffers: true,
4248                all: false,
4249                use_lamports_unit: false,
4250            }))
4251        );
4252    }
4253
4254    #[test]
4255    #[allow(clippy::cognitive_complexity)]
4256    fn test_cli_parse_close() {
4257        let test_commands = get_clap_app("test", "desc", "version");
4258
4259        let default_keypair = Keypair::new();
4260        let keypair_file = make_tmp_path("keypair_file");
4261        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4262        let default_signer = DefaultSigner::new("", &keypair_file);
4263
4264        // defaults
4265        let buffer_pubkey = Pubkey::new_unique();
4266        let recipient_pubkey = Pubkey::new_unique();
4267        let authority_keypair = Keypair::new();
4268        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4269
4270        let test_command = test_commands.clone().get_matches_from(vec![
4271            "test",
4272            "program",
4273            "close",
4274            &buffer_pubkey.to_string(),
4275        ]);
4276        assert_eq!(
4277            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4278            CliCommandInfo {
4279                command: CliCommand::Program(ProgramCliCommand::Close {
4280                    account_pubkey: Some(buffer_pubkey),
4281                    recipient_pubkey: default_keypair.pubkey(),
4282                    authority_index: 0,
4283                    use_lamports_unit: false,
4284                    bypass_warning: false,
4285                }),
4286                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4287            }
4288        );
4289
4290        // with bypass-warning
4291        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4292        let test_command = test_commands.clone().get_matches_from(vec![
4293            "test",
4294            "program",
4295            "close",
4296            &buffer_pubkey.to_string(),
4297            "--bypass-warning",
4298        ]);
4299        assert_eq!(
4300            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4301            CliCommandInfo {
4302                command: CliCommand::Program(ProgramCliCommand::Close {
4303                    account_pubkey: Some(buffer_pubkey),
4304                    recipient_pubkey: default_keypair.pubkey(),
4305                    authority_index: 0,
4306                    use_lamports_unit: false,
4307                    bypass_warning: true,
4308                }),
4309                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4310            }
4311        );
4312
4313        // with authority
4314        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4315        let test_command = test_commands.clone().get_matches_from(vec![
4316            "test",
4317            "program",
4318            "close",
4319            &buffer_pubkey.to_string(),
4320            "--buffer-authority",
4321            &authority_keypair_file,
4322        ]);
4323        assert_eq!(
4324            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4325            CliCommandInfo {
4326                command: CliCommand::Program(ProgramCliCommand::Close {
4327                    account_pubkey: Some(buffer_pubkey),
4328                    recipient_pubkey: default_keypair.pubkey(),
4329                    authority_index: 1,
4330                    use_lamports_unit: false,
4331                    bypass_warning: false,
4332                }),
4333                signers: vec![
4334                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4335                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4336                ],
4337            }
4338        );
4339
4340        // with recipient
4341        let test_command = test_commands.clone().get_matches_from(vec![
4342            "test",
4343            "program",
4344            "close",
4345            &buffer_pubkey.to_string(),
4346            "--recipient",
4347            &recipient_pubkey.to_string(),
4348        ]);
4349        assert_eq!(
4350            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4351            CliCommandInfo {
4352                command: CliCommand::Program(ProgramCliCommand::Close {
4353                    account_pubkey: Some(buffer_pubkey),
4354                    recipient_pubkey,
4355                    authority_index: 0,
4356                    use_lamports_unit: false,
4357                    bypass_warning: false,
4358                }),
4359                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4360            }
4361        );
4362
4363        // --buffers and lamports
4364        let test_command = test_commands.clone().get_matches_from(vec![
4365            "test",
4366            "program",
4367            "close",
4368            "--buffers",
4369            "--lamports",
4370        ]);
4371        assert_eq!(
4372            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4373            CliCommandInfo {
4374                command: CliCommand::Program(ProgramCliCommand::Close {
4375                    account_pubkey: None,
4376                    recipient_pubkey: default_keypair.pubkey(),
4377                    authority_index: 0,
4378                    use_lamports_unit: true,
4379                    bypass_warning: false,
4380                }),
4381                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4382            }
4383        );
4384    }
4385
4386    #[test]
4387    fn test_cli_parse_extend_program() {
4388        let test_commands = get_clap_app("test", "desc", "version");
4389
4390        let default_keypair = Keypair::new();
4391        let keypair_file = make_tmp_path("keypair_file");
4392        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4393        let default_signer = DefaultSigner::new("", &keypair_file);
4394
4395        // defaults
4396        let program_pubkey = Pubkey::new_unique();
4397        let additional_bytes = 100;
4398
4399        let test_command = test_commands.clone().get_matches_from(vec![
4400            "test",
4401            "program",
4402            "extend",
4403            &program_pubkey.to_string(),
4404            &additional_bytes.to_string(),
4405        ]);
4406        assert_eq!(
4407            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4408            CliCommandInfo {
4409                command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4410                    program_pubkey,
4411                    additional_bytes
4412                }),
4413                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4414            }
4415        );
4416    }
4417
4418    #[test]
4419    fn test_cli_parse_migrate_program() {
4420        let test_commands = get_clap_app("test", "desc", "version");
4421
4422        let default_keypair = Keypair::new();
4423        let keypair_file = make_tmp_path("keypair_file");
4424        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4425        let default_signer = DefaultSigner::new("", &keypair_file);
4426
4427        let program_pubkey = Pubkey::new_unique();
4428        let authority_keypair = Keypair::new();
4429        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4430        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4431
4432        let test_command = test_commands.clone().get_matches_from(vec![
4433            "test",
4434            "program",
4435            "migrate",
4436            &program_pubkey.to_string(),
4437            "--authority",
4438            &authority_keypair_file.to_string(),
4439            "--with-compute-unit-price",
4440            "1",
4441        ]);
4442        assert_eq!(
4443            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4444            CliCommandInfo {
4445                command: CliCommand::Program(ProgramCliCommand::MigrateProgram {
4446                    program_pubkey,
4447                    authority_signer_index: 1,
4448                    compute_unit_price: Some(1),
4449                }),
4450                signers: vec![
4451                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4452                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4453                ],
4454            }
4455        );
4456    }
4457
4458    #[test]
4459    fn test_cli_keypair_file() {
4460        solana_logger::setup();
4461
4462        let default_keypair = Keypair::new();
4463        let program_pubkey = Keypair::new();
4464        let deploy_path = make_tmp_path("deploy");
4465        let mut program_location = PathBuf::from(deploy_path.clone());
4466        program_location.push("noop");
4467        program_location.set_extension("so");
4468        let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
4469        pathbuf.push("tests");
4470        pathbuf.push("fixtures");
4471        pathbuf.push("noop");
4472        pathbuf.set_extension("so");
4473        let program_keypair_location = program_location.with_file_name("noop-keypair.json");
4474        std::fs::create_dir_all(deploy_path).unwrap();
4475        std::fs::copy(pathbuf, program_location.as_os_str()).unwrap();
4476        write_keypair_file(&program_pubkey, program_keypair_location).unwrap();
4477
4478        let config = CliConfig {
4479            rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
4480            command: CliCommand::Program(ProgramCliCommand::Deploy {
4481                program_location: Some(program_location.to_str().unwrap().to_string()),
4482                fee_payer_signer_index: 0,
4483                buffer_signer_index: None,
4484                buffer_pubkey: None,
4485                program_signer_index: None,
4486                program_pubkey: None,
4487                upgrade_authority_signer_index: 0,
4488                is_final: false,
4489                max_len: None,
4490                skip_fee_check: false,
4491                compute_unit_price: None,
4492                max_sign_attempts: 5,
4493                auto_extend: true,
4494                use_rpc: false,
4495                skip_feature_verification: true,
4496            }),
4497            signers: vec![&default_keypair],
4498            output_format: OutputFormat::JsonCompact,
4499            ..CliConfig::default()
4500        };
4501
4502        let result = process_command(&config);
4503        let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
4504        let program_id = json
4505            .as_object()
4506            .unwrap()
4507            .get("programId")
4508            .unwrap()
4509            .as_str()
4510            .unwrap();
4511
4512        assert_eq!(
4513            program_id.parse::<Pubkey>().unwrap(),
4514            program_pubkey.pubkey()
4515        );
4516    }
4517}