solana_cli/
program.rs

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