Skip to main content

solana_cli/
program.rs

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