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