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