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::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).into())
3064}
3065
3066/// Turns an `EbpfError` from local ELF verification into a concise error
3067/// message and a remediation hint.
3068fn explain_elf_error(err: &EbpfError, program_data: &[u8], config: &Config) -> String {
3069    // `UnsupportedSBPFVersion` is special-cased here. Richer, per-field
3070    // diagnostics for malformed headers require changes to `sbpf::ElfError`;
3071    // tracked by https://github.com/anza-xyz/sbpf/issues/204.
3072    let EbpfError::ElfError(ElfError::UnsupportedSBPFVersion) = err else {
3073        return format!("{err} (local pre-flight)");
3074    };
3075
3076    fn sbpf_version_label(version: SBPFVersion) -> &'static str {
3077        match version {
3078            SBPFVersion::V0 => "v0",
3079            SBPFVersion::V1 => "v1",
3080            SBPFVersion::V2 => "v2",
3081            SBPFVersion::V3 => "v3",
3082            SBPFVersion::V4 => "v4",
3083            SBPFVersion::Reserved => "reserved",
3084        }
3085    }
3086
3087    let min = sbpf_version_label(*config.enabled_sbpf_versions.start());
3088    let max = sbpf_version_label(*config.enabled_sbpf_versions.end());
3089    match get_sbpf_version(program_data) {
3090        Ok(version) => {
3091            let detected = sbpf_version_label(version);
3092            format!(
3093                "program targets SBPF {detected}, which this CLI does not have enabled (enabled: \
3094                 {min}–{max}). Rebuild the program targeting {max}."
3095            )
3096        }
3097        Err(error) => format!("could not read SBPF version: {error} (local pre-flight)"),
3098    }
3099}
3100
3101async fn check_payer(
3102    rpc_client: &RpcClient,
3103    config: &CliConfig<'_>,
3104    fee_payer_pubkey: Pubkey,
3105    balance_needed: u64,
3106    initial_message: &Option<Message>,
3107    write_messages: &[Message],
3108    final_message: &Option<Message>,
3109) -> Result<(), Box<dyn std::error::Error>> {
3110    let mut fee = Saturating(0);
3111    if let Some(message) = initial_message {
3112        fee += rpc_client.get_fee_for_message(message).await?;
3113    }
3114    // Assume all write messages cost the same
3115    if let Some(message) = write_messages.first() {
3116        fee += rpc_client
3117            .get_fee_for_message(message)
3118            .await?
3119            .saturating_mul(write_messages.len() as u64);
3120    }
3121    if let Some(message) = final_message {
3122        fee += rpc_client.get_fee_for_message(message).await?;
3123    }
3124    check_account_for_spend_and_fee_with_commitment(
3125        rpc_client,
3126        &fee_payer_pubkey,
3127        balance_needed,
3128        fee.0,
3129        config.commitment,
3130    )
3131    .await?;
3132    Ok(())
3133}
3134
3135fn dedup_signers<'a>(signers: &[&'a dyn Signer]) -> Vec<&'a dyn Signer> {
3136    let mut seen = Vec::with_capacity(signers.len());
3137    signers
3138        .iter()
3139        .filter(|signer| {
3140            let pubkey = signer.pubkey();
3141            let is_new = !seen.contains(&pubkey);
3142            if is_new {
3143                seen.push(pubkey);
3144            }
3145            is_new
3146        })
3147        .copied()
3148        .collect()
3149}
3150
3151#[allow(clippy::too_many_arguments)]
3152async fn send_deploy_messages(
3153    rpc_client: Arc<RpcClient>,
3154    config: &CliConfig<'_>,
3155    initial_message: Option<Message>,
3156    mut write_messages: Vec<Message>,
3157    final_message: Option<Message>,
3158    fee_payer_signer: &dyn Signer,
3159    initial_signer: Option<&dyn Signer>,
3160    write_signer: Option<&dyn Signer>,
3161    final_signers: Option<&[&dyn Signer]>,
3162    max_sign_attempts: usize,
3163    use_rpc: bool,
3164    compute_unit_limit: &ComputeUnitLimit,
3165) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
3166    if let Some(mut message) = initial_message {
3167        if let Some(initial_signer) = initial_signer {
3168            trace!("Preparing the required accounts");
3169            simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)
3170                .await?;
3171            let mut initial_transaction = Transaction::new_unsigned(message.clone());
3172            let blockhash = rpc_client.get_latest_blockhash().await?;
3173
3174            // Most of the initial_transaction combinations require both the fee-payer and new program
3175            // account to sign the transaction. One (transfer) only requires the fee-payer signature.
3176            // This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
3177            // extraneous signature.
3178            if message.header.num_required_signatures == 3 {
3179                initial_transaction.try_sign(
3180                    &[fee_payer_signer, initial_signer, write_signer.unwrap()],
3181                    blockhash,
3182                )?;
3183            } else if message.header.num_required_signatures == 2 {
3184                initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
3185            } else {
3186                initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
3187            }
3188            let result = rpc_client
3189                .send_and_confirm_transaction_with_spinner_and_config(
3190                    &initial_transaction,
3191                    config.commitment,
3192                    config.send_transaction_config,
3193                )
3194                .await;
3195            log_instruction_custom_error::<SystemError>(result, config)
3196                .map_err(|err| format!("Account allocation failed: {err}"))?;
3197        } else {
3198            return Err("Buffer account not created yet, must provide a key pair".into());
3199        }
3200    }
3201
3202    if !write_messages.is_empty()
3203        && let Some(write_signer) = write_signer
3204    {
3205        trace!("Writing program data");
3206
3207        // Simulate the first write message to get the number of compute units
3208        // consumed and then reuse that value as the compute unit limit for all
3209        // write messages.
3210        {
3211            let mut message = write_messages[0].clone();
3212            if let UpdateComputeUnitLimitResult::UpdatedInstructionIndex(ix_index) =
3213                simulate_and_update_compute_unit_limit(
3214                    compute_unit_limit,
3215                    &rpc_client,
3216                    &mut message,
3217                )
3218                .await?
3219            {
3220                for msg in &mut write_messages {
3221                    // Write messages are all assumed to be identical except
3222                    // the program data being written. But just in case that
3223                    // assumption is broken, assert that we are only ever
3224                    // changing the instruction data for a compute budget
3225                    // instruction.
3226                    assert_eq!(msg.program_id(ix_index), Some(&compute_budget::id()));
3227                    msg.instructions[ix_index]
3228                        .data
3229                        .clone_from(&message.instructions[ix_index].data);
3230                }
3231            }
3232
3233            // Holds the scheduler alive for the duration of the send.
3234            let _tpu_client;
3235            let cancel_token = CancellationToken::new();
3236            let transport = if use_rpc {
3237                SendTransport::Rpc(config.send_transaction_config)
3238            } else {
3239                let node_address_service = WebsocketNodeAddressService::run(
3240                    rpc_client.clone(),
3241                    config.websocket_url.clone(),
3242                    LeaderTpuCacheServiceConfig::default(),
3243                    cancel_token.child_token(),
3244                )
3245                .await?;
3246
3247                let bind_socket = bind_to_unspecified()?;
3248
3249                let (transaction_sender, client) =
3250                    ClientBuilder::new(Box::new(node_address_service))
3251                        .cancel_token(cancel_token.clone())
3252                        .bind_socket(bind_socket)
3253                        .broadcaster(NonblockingBroadcaster)
3254                        .build()
3255                        .expect("Failed to build TPU client");
3256                _tpu_client = client;
3257                SendTransport::Tpu(transaction_sender)
3258            };
3259
3260            let versioned_write_messages = write_messages.into_iter().map(VersionedMessage::Legacy);
3261
3262            let transaction_errors = send_and_confirm_transactions_in_parallel_v3(
3263                rpc_client.clone(),
3264                transport,
3265                versioned_write_messages,
3266                &dedup_signers(&[fee_payer_signer, write_signer]),
3267                SendAndConfirmConfigV3 {
3268                    with_spinner: true,
3269                    max_sign_attempts: NonZeroUsize::new(max_sign_attempts)
3270                        .ok_or("--max-sign-attempts must be at least 1")?,
3271                    check_interval: CHECK_INTERVAL,
3272                    send_interval: SEND_INTERVAL,
3273                },
3274            )
3275            .await
3276            .map_err(|err| format!("Data writes to account failed: {err}"))?
3277            .into_iter()
3278            .flatten()
3279            .collect::<Vec<_>>();
3280
3281            if !transaction_errors.is_empty() {
3282                for transaction_error in &transaction_errors {
3283                    error!("{transaction_error:?}");
3284                }
3285                return Err(
3286                    format!("{} write transactions failed", transaction_errors.len()).into(),
3287                );
3288            }
3289        }
3290    }
3291
3292    if let Some(mut message) = final_message
3293        && let Some(final_signers) = final_signers
3294    {
3295        trace!("Deploying program");
3296
3297        simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)
3298            .await?;
3299        let mut final_tx = Transaction::new_unsigned(message);
3300        let blockhash = rpc_client.get_latest_blockhash().await?;
3301        let mut signers = final_signers.to_vec();
3302        signers.push(fee_payer_signer);
3303        final_tx.try_sign(&signers, blockhash)?;
3304        let result = rpc_client
3305            .send_and_confirm_transaction_with_spinner_and_config(
3306                &final_tx,
3307                config.commitment,
3308                config.send_transaction_config,
3309            )
3310            .await
3311            .map_err(|e| format!("Deploying program failed: {e}"))?;
3312        return Ok(Some(result));
3313    }
3314
3315    Ok(None)
3316}
3317
3318fn create_ephemeral_keypair()
3319-> Result<(usize, bip39::Mnemonic, Keypair), Box<dyn std::error::Error>> {
3320    const WORDS: usize = 12;
3321    let mnemonic = Mnemonic::generate_in(Language::English, WORDS)?;
3322    let seed = mnemonic.to_seed("");
3323    let new_keypair = keypair_from_seed(&seed)?;
3324
3325    Ok((WORDS, mnemonic, new_keypair))
3326}
3327
3328fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic, ephemeral_pubkey: &Pubkey) {
3329    let phrase = mnemonic.to_string();
3330    let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
3331    eprintln!("{divider}\nRecover the intermediate account's ephemeral keypair file with");
3332    eprintln!("`solana-keygen recover` and the following {words}-word seed phrase:");
3333    eprintln!("{divider}\n{phrase}\n{divider}");
3334    eprintln!("To resume a deploy, pass the recovered keypair as the");
3335    eprintln!("[BUFFER_SIGNER] to `solana program deploy` or `solana program write-buffer'.");
3336    eprintln!("Or to recover the account's lamports, use:");
3337    eprintln!("{divider}\nsolana program close {ephemeral_pubkey}\n{divider}");
3338}
3339
3340async fn fetch_feature_set(
3341    rpc_client: &RpcClient,
3342) -> Result<FeatureSet, Box<dyn std::error::Error>> {
3343    let mut feature_set = FeatureSet::default();
3344    for feature_ids in FEATURE_NAMES
3345        .keys()
3346        .cloned()
3347        .collect::<Vec<Pubkey>>()
3348        .chunks(MAX_MULTIPLE_ACCOUNTS)
3349    {
3350        rpc_client
3351            .get_multiple_accounts(feature_ids)
3352            .await?
3353            .into_iter()
3354            .zip(feature_ids)
3355            .for_each(|(account, feature_id)| {
3356                let activation_slot = account.and_then(status_from_account);
3357
3358                if let Some(CliFeatureStatus::Active(slot)) = activation_slot {
3359                    feature_set.activate(feature_id, slot);
3360                }
3361            });
3362    }
3363
3364    Ok(feature_set)
3365}
3366
3367#[cfg(test)]
3368mod tests {
3369    use {
3370        super::*,
3371        crate::{
3372            clap_app::get_clap_app,
3373            cli::{parse_command, process_command},
3374        },
3375        serde_json::Value,
3376        solana_cli_output::OutputFormat,
3377        solana_hash::Hash,
3378        solana_keypair::write_keypair_file,
3379        solana_sbpf::elf_parser::consts::{
3380            EI_OSABI, ELFCLASS64, ELFDATA2LSB, ELFMAG, ELFOSABI_NONE, EV_CURRENT,
3381        },
3382    };
3383
3384    fn make_tmp_path(name: &str) -> String {
3385        let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
3386        let keypair = Keypair::new();
3387
3388        let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
3389
3390        // whack any possible collision
3391        let _ignored = std::fs::remove_dir_all(&path);
3392        // whack any possible collision
3393        let _ignored = std::fs::remove_file(&path);
3394
3395        path
3396    }
3397
3398    #[test]
3399    #[allow(clippy::cognitive_complexity)]
3400    fn test_cli_parse_deploy() {
3401        let test_commands = get_clap_app("test", "desc", "version");
3402
3403        let default_keypair = Keypair::new();
3404        let keypair_file = make_tmp_path("keypair_file");
3405        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3406        let default_signer = DefaultSigner::new("", &keypair_file);
3407
3408        let test_command = test_commands.clone().get_matches_from(vec![
3409            "test",
3410            "program",
3411            "deploy",
3412            "/Users/test/program.so",
3413        ]);
3414        assert_eq!(
3415            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3416            CliCommandInfo {
3417                command: CliCommand::Program(ProgramCliCommand::Deploy {
3418                    program_location: Some("/Users/test/program.so".to_string()),
3419                    fee_payer_signer_index: 0,
3420                    buffer_signer_index: None,
3421                    buffer_pubkey: None,
3422                    program_signer_index: None,
3423                    program_pubkey: None,
3424                    upgrade_authority_signer_index: 0,
3425                    is_final: false,
3426                    max_len: None,
3427                    skip_fee_check: false,
3428                    compute_unit_price: None,
3429                    max_sign_attempts: 5,
3430                    auto_extend: true,
3431                    use_rpc: false,
3432                    skip_feature_verification: false,
3433                }),
3434                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3435            }
3436        );
3437
3438        let test_command = test_commands.clone().get_matches_from(vec![
3439            "test",
3440            "program",
3441            "deploy",
3442            "/Users/test/program.so",
3443            "--max-len",
3444            "42",
3445        ]);
3446        assert_eq!(
3447            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3448            CliCommandInfo {
3449                command: CliCommand::Program(ProgramCliCommand::Deploy {
3450                    program_location: Some("/Users/test/program.so".to_string()),
3451                    fee_payer_signer_index: 0,
3452                    buffer_signer_index: None,
3453                    buffer_pubkey: None,
3454                    program_signer_index: None,
3455                    program_pubkey: None,
3456                    upgrade_authority_signer_index: 0,
3457                    is_final: false,
3458                    max_len: Some(42),
3459                    skip_fee_check: false,
3460                    compute_unit_price: None,
3461                    max_sign_attempts: 5,
3462                    auto_extend: true,
3463                    use_rpc: false,
3464                    skip_feature_verification: false,
3465                }),
3466                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3467            }
3468        );
3469
3470        let buffer_keypair = Keypair::new();
3471        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3472        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3473        let test_command = test_commands.clone().get_matches_from(vec![
3474            "test",
3475            "program",
3476            "deploy",
3477            "--buffer",
3478            &buffer_keypair_file,
3479        ]);
3480        assert_eq!(
3481            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3482            CliCommandInfo {
3483                command: CliCommand::Program(ProgramCliCommand::Deploy {
3484                    program_location: None,
3485                    fee_payer_signer_index: 0,
3486                    buffer_signer_index: Some(1),
3487                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3488                    program_signer_index: None,
3489                    program_pubkey: None,
3490                    upgrade_authority_signer_index: 0,
3491                    is_final: false,
3492                    max_len: None,
3493                    skip_fee_check: false,
3494                    compute_unit_price: None,
3495                    max_sign_attempts: 5,
3496                    auto_extend: true,
3497                    use_rpc: false,
3498                    skip_feature_verification: false,
3499                }),
3500                signers: vec![
3501                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3502                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3503                ],
3504            }
3505        );
3506
3507        let program_pubkey = Pubkey::new_unique();
3508        let test = test_commands.clone().get_matches_from(vec![
3509            "test",
3510            "program",
3511            "deploy",
3512            "/Users/test/program.so",
3513            "--program-id",
3514            &program_pubkey.to_string(),
3515        ]);
3516        assert_eq!(
3517            parse_command(&test, &default_signer, &mut None).unwrap(),
3518            CliCommandInfo {
3519                command: CliCommand::Program(ProgramCliCommand::Deploy {
3520                    program_location: Some("/Users/test/program.so".to_string()),
3521                    fee_payer_signer_index: 0,
3522                    buffer_signer_index: None,
3523                    buffer_pubkey: None,
3524                    program_signer_index: None,
3525                    program_pubkey: Some(program_pubkey),
3526                    upgrade_authority_signer_index: 0,
3527                    is_final: false,
3528                    max_len: None,
3529                    skip_fee_check: false,
3530                    compute_unit_price: None,
3531                    max_sign_attempts: 5,
3532                    auto_extend: true,
3533                    use_rpc: false,
3534                    skip_feature_verification: false,
3535                }),
3536                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3537            }
3538        );
3539
3540        let program_keypair = Keypair::new();
3541        let program_keypair_file = make_tmp_path("program_keypair_file");
3542        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
3543        let test = test_commands.clone().get_matches_from(vec![
3544            "test",
3545            "program",
3546            "deploy",
3547            "/Users/test/program.so",
3548            "--program-id",
3549            &program_keypair_file,
3550        ]);
3551        assert_eq!(
3552            parse_command(&test, &default_signer, &mut None).unwrap(),
3553            CliCommandInfo {
3554                command: CliCommand::Program(ProgramCliCommand::Deploy {
3555                    program_location: Some("/Users/test/program.so".to_string()),
3556                    fee_payer_signer_index: 0,
3557                    buffer_signer_index: None,
3558                    buffer_pubkey: None,
3559                    program_signer_index: Some(1),
3560                    program_pubkey: Some(program_keypair.pubkey()),
3561                    upgrade_authority_signer_index: 0,
3562                    is_final: false,
3563                    max_len: None,
3564                    skip_fee_check: false,
3565                    compute_unit_price: None,
3566                    max_sign_attempts: 5,
3567                    auto_extend: true,
3568                    use_rpc: false,
3569                    skip_feature_verification: false,
3570                }),
3571                signers: vec![
3572                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3573                    Box::new(read_keypair_file(&program_keypair_file).unwrap()),
3574                ],
3575            }
3576        );
3577
3578        let authority_keypair = Keypair::new();
3579        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3580        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3581        let test_command = test_commands.clone().get_matches_from(vec![
3582            "test",
3583            "program",
3584            "deploy",
3585            "/Users/test/program.so",
3586            "--upgrade-authority",
3587            &authority_keypair_file,
3588        ]);
3589        assert_eq!(
3590            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3591            CliCommandInfo {
3592                command: CliCommand::Program(ProgramCliCommand::Deploy {
3593                    program_location: Some("/Users/test/program.so".to_string()),
3594                    fee_payer_signer_index: 0,
3595                    buffer_signer_index: None,
3596                    buffer_pubkey: None,
3597                    program_signer_index: None,
3598                    program_pubkey: None,
3599                    upgrade_authority_signer_index: 1,
3600                    is_final: false,
3601                    max_len: None,
3602                    skip_fee_check: false,
3603                    compute_unit_price: None,
3604                    max_sign_attempts: 5,
3605                    auto_extend: true,
3606                    use_rpc: false,
3607                    skip_feature_verification: false,
3608                }),
3609                signers: vec![
3610                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3611                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3612                ],
3613            }
3614        );
3615
3616        let test_command = test_commands.clone().get_matches_from(vec![
3617            "test",
3618            "program",
3619            "deploy",
3620            "/Users/test/program.so",
3621            "--final",
3622        ]);
3623        assert_eq!(
3624            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3625            CliCommandInfo {
3626                command: CliCommand::Program(ProgramCliCommand::Deploy {
3627                    program_location: Some("/Users/test/program.so".to_string()),
3628                    fee_payer_signer_index: 0,
3629                    buffer_signer_index: None,
3630                    buffer_pubkey: None,
3631                    program_signer_index: None,
3632                    program_pubkey: None,
3633                    upgrade_authority_signer_index: 0,
3634                    is_final: true,
3635                    max_len: None,
3636                    skip_fee_check: false,
3637                    compute_unit_price: None,
3638                    max_sign_attempts: 5,
3639                    auto_extend: true,
3640                    use_rpc: false,
3641                    skip_feature_verification: false,
3642                }),
3643                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3644            }
3645        );
3646
3647        let test_command = test_commands.clone().get_matches_from(vec![
3648            "test",
3649            "program",
3650            "deploy",
3651            "/Users/test/program.so",
3652            "--max-sign-attempts",
3653            "1",
3654        ]);
3655        assert_eq!(
3656            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3657            CliCommandInfo {
3658                command: CliCommand::Program(ProgramCliCommand::Deploy {
3659                    program_location: Some("/Users/test/program.so".to_string()),
3660                    fee_payer_signer_index: 0,
3661                    buffer_signer_index: None,
3662                    buffer_pubkey: None,
3663                    program_signer_index: None,
3664                    program_pubkey: None,
3665                    upgrade_authority_signer_index: 0,
3666                    is_final: false,
3667                    max_len: None,
3668                    skip_fee_check: false,
3669                    compute_unit_price: None,
3670                    max_sign_attempts: 1,
3671                    auto_extend: true,
3672                    use_rpc: false,
3673                    skip_feature_verification: false,
3674                }),
3675                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3676            }
3677        );
3678
3679        let test_command = test_commands.clone().get_matches_from(vec![
3680            "test",
3681            "program",
3682            "deploy",
3683            "/Users/test/program.so",
3684            "--use-rpc",
3685        ]);
3686        assert_eq!(
3687            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3688            CliCommandInfo {
3689                command: CliCommand::Program(ProgramCliCommand::Deploy {
3690                    program_location: Some("/Users/test/program.so".to_string()),
3691                    fee_payer_signer_index: 0,
3692                    buffer_signer_index: None,
3693                    buffer_pubkey: None,
3694                    program_signer_index: None,
3695                    program_pubkey: None,
3696                    upgrade_authority_signer_index: 0,
3697                    is_final: false,
3698                    max_len: None,
3699                    skip_fee_check: false,
3700                    compute_unit_price: None,
3701                    max_sign_attempts: 5,
3702                    auto_extend: true,
3703                    use_rpc: true,
3704                    skip_feature_verification: false,
3705                }),
3706                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3707            }
3708        );
3709
3710        let test_command = test_commands.clone().get_matches_from(vec![
3711            "test",
3712            "program",
3713            "deploy",
3714            "/Users/test/program.so",
3715            "--skip-feature-verify",
3716        ]);
3717        assert_eq!(
3718            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3719            CliCommandInfo {
3720                command: CliCommand::Program(ProgramCliCommand::Deploy {
3721                    program_location: Some("/Users/test/program.so".to_string()),
3722                    fee_payer_signer_index: 0,
3723                    buffer_signer_index: None,
3724                    buffer_pubkey: None,
3725                    program_signer_index: None,
3726                    program_pubkey: None,
3727                    upgrade_authority_signer_index: 0,
3728                    is_final: false,
3729                    max_len: None,
3730                    skip_fee_check: false,
3731                    compute_unit_price: None,
3732                    max_sign_attempts: 5,
3733                    auto_extend: true,
3734                    use_rpc: false,
3735                    skip_feature_verification: true,
3736                }),
3737                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3738            }
3739        );
3740    }
3741
3742    #[test]
3743    fn test_cli_parse_upgrade() {
3744        let test_commands = get_clap_app("test", "desc", "version");
3745
3746        let default_keypair = Keypair::new();
3747        let keypair_file = make_tmp_path("keypair_file");
3748        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3749        let default_signer = DefaultSigner::new("", &keypair_file);
3750
3751        let program_key = Pubkey::new_unique();
3752        let buffer_key = Pubkey::new_unique();
3753        let test_command = test_commands.clone().get_matches_from(vec![
3754            "test",
3755            "program",
3756            "upgrade",
3757            format!("{buffer_key}").as_str(),
3758            format!("{program_key}").as_str(),
3759            "--skip-feature-verify",
3760        ]);
3761        assert_eq!(
3762            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3763            CliCommandInfo {
3764                command: CliCommand::Program(ProgramCliCommand::Upgrade {
3765                    fee_payer_signer_index: 0,
3766                    program_pubkey: program_key,
3767                    buffer_pubkey: buffer_key,
3768                    upgrade_authority_signer_index: 0,
3769                    sign_only: false,
3770                    dump_transaction_message: false,
3771                    blockhash_query: BlockhashQuery::default(),
3772                    skip_feature_verification: true,
3773                }),
3774                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3775            }
3776        );
3777    }
3778
3779    #[test]
3780    #[allow(clippy::cognitive_complexity)]
3781    fn test_cli_parse_write_buffer() {
3782        let test_commands = get_clap_app("test", "desc", "version");
3783
3784        let default_keypair = Keypair::new();
3785        let keypair_file = make_tmp_path("keypair_file");
3786        write_keypair_file(&default_keypair, &keypair_file).unwrap();
3787        let default_signer = DefaultSigner::new("", &keypair_file);
3788
3789        // defaults
3790        let test_command = test_commands.clone().get_matches_from(vec![
3791            "test",
3792            "program",
3793            "write-buffer",
3794            "/Users/test/program.so",
3795        ]);
3796        assert_eq!(
3797            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3798            CliCommandInfo {
3799                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3800                    program_location: "/Users/test/program.so".to_string(),
3801                    fee_payer_signer_index: 0,
3802                    buffer_signer_index: None,
3803                    buffer_pubkey: None,
3804                    buffer_authority_signer_index: 0,
3805                    max_len: None,
3806                    skip_fee_check: false,
3807                    compute_unit_price: None,
3808                    max_sign_attempts: 5,
3809                    use_rpc: false,
3810                    skip_feature_verification: false,
3811                }),
3812                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3813            }
3814        );
3815
3816        // specify max len
3817        let test_command = test_commands.clone().get_matches_from(vec![
3818            "test",
3819            "program",
3820            "write-buffer",
3821            "/Users/test/program.so",
3822            "--max-len",
3823            "42",
3824        ]);
3825        assert_eq!(
3826            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3827            CliCommandInfo {
3828                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3829                    program_location: "/Users/test/program.so".to_string(),
3830                    fee_payer_signer_index: 0,
3831                    buffer_signer_index: None,
3832                    buffer_pubkey: None,
3833                    buffer_authority_signer_index: 0,
3834                    max_len: Some(42),
3835                    skip_fee_check: false,
3836                    compute_unit_price: None,
3837                    max_sign_attempts: 5,
3838                    use_rpc: false,
3839                    skip_feature_verification: false,
3840                }),
3841                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3842            }
3843        );
3844
3845        // specify buffer
3846        let buffer_keypair = Keypair::new();
3847        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3848        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3849        let test_command = test_commands.clone().get_matches_from(vec![
3850            "test",
3851            "program",
3852            "write-buffer",
3853            "/Users/test/program.so",
3854            "--buffer",
3855            &buffer_keypair_file,
3856        ]);
3857        assert_eq!(
3858            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3859            CliCommandInfo {
3860                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3861                    program_location: "/Users/test/program.so".to_string(),
3862                    fee_payer_signer_index: 0,
3863                    buffer_signer_index: Some(1),
3864                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3865                    buffer_authority_signer_index: 0,
3866                    max_len: None,
3867                    skip_fee_check: false,
3868                    compute_unit_price: None,
3869                    max_sign_attempts: 5,
3870                    use_rpc: false,
3871                    skip_feature_verification: false,
3872                }),
3873                signers: vec![
3874                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3875                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3876                ],
3877            }
3878        );
3879
3880        // specify authority
3881        let authority_keypair = Keypair::new();
3882        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3883        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3884        let test_command = test_commands.clone().get_matches_from(vec![
3885            "test",
3886            "program",
3887            "write-buffer",
3888            "/Users/test/program.so",
3889            "--buffer-authority",
3890            &authority_keypair_file,
3891        ]);
3892        assert_eq!(
3893            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3894            CliCommandInfo {
3895                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3896                    program_location: "/Users/test/program.so".to_string(),
3897                    fee_payer_signer_index: 0,
3898                    buffer_signer_index: None,
3899                    buffer_pubkey: None,
3900                    buffer_authority_signer_index: 1,
3901                    max_len: None,
3902                    skip_fee_check: false,
3903                    compute_unit_price: None,
3904                    max_sign_attempts: 5,
3905                    use_rpc: false,
3906                    skip_feature_verification: false,
3907                }),
3908                signers: vec![
3909                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3910                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3911                ],
3912            }
3913        );
3914
3915        // specify both buffer and authority
3916        let buffer_keypair = Keypair::new();
3917        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3918        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3919        let authority_keypair = Keypair::new();
3920        let authority_keypair_file = make_tmp_path("authority_keypair_file");
3921        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3922        let test_command = test_commands.clone().get_matches_from(vec![
3923            "test",
3924            "program",
3925            "write-buffer",
3926            "/Users/test/program.so",
3927            "--buffer",
3928            &buffer_keypair_file,
3929            "--buffer-authority",
3930            &authority_keypair_file,
3931        ]);
3932        assert_eq!(
3933            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3934            CliCommandInfo {
3935                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3936                    program_location: "/Users/test/program.so".to_string(),
3937                    fee_payer_signer_index: 0,
3938                    buffer_signer_index: Some(1),
3939                    buffer_pubkey: Some(buffer_keypair.pubkey()),
3940                    buffer_authority_signer_index: 2,
3941                    max_len: None,
3942                    skip_fee_check: false,
3943                    compute_unit_price: None,
3944                    max_sign_attempts: 5,
3945                    use_rpc: false,
3946                    skip_feature_verification: false,
3947                }),
3948                signers: vec![
3949                    Box::new(read_keypair_file(&keypair_file).unwrap()),
3950                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3951                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3952                ],
3953            }
3954        );
3955
3956        // specify max sign attempts
3957        let test_command = test_commands.clone().get_matches_from(vec![
3958            "test",
3959            "program",
3960            "write-buffer",
3961            "/Users/test/program.so",
3962            "--max-sign-attempts",
3963            "10",
3964        ]);
3965        assert_eq!(
3966            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3967            CliCommandInfo {
3968                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3969                    program_location: "/Users/test/program.so".to_string(),
3970                    fee_payer_signer_index: 0,
3971                    buffer_signer_index: None,
3972                    buffer_pubkey: None,
3973                    buffer_authority_signer_index: 0,
3974                    max_len: None,
3975                    skip_fee_check: false,
3976                    compute_unit_price: None,
3977                    max_sign_attempts: 10,
3978                    use_rpc: false,
3979                    skip_feature_verification: false
3980                }),
3981                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3982            }
3983        );
3984
3985        // skip feature verification
3986        let test_command = test_commands.clone().get_matches_from(vec![
3987            "test",
3988            "program",
3989            "write-buffer",
3990            "/Users/test/program.so",
3991            "--skip-feature-verify",
3992        ]);
3993        assert_eq!(
3994            parse_command(&test_command, &default_signer, &mut None).unwrap(),
3995            CliCommandInfo {
3996                command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3997                    program_location: "/Users/test/program.so".to_string(),
3998                    fee_payer_signer_index: 0,
3999                    buffer_signer_index: None,
4000                    buffer_pubkey: None,
4001                    buffer_authority_signer_index: 0,
4002                    max_len: None,
4003                    skip_fee_check: false,
4004                    compute_unit_price: None,
4005                    max_sign_attempts: 5,
4006                    use_rpc: false,
4007                    skip_feature_verification: true,
4008                }),
4009                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4010            }
4011        );
4012    }
4013
4014    #[test]
4015    #[allow(clippy::cognitive_complexity)]
4016    fn test_cli_parse_set_upgrade_authority() {
4017        let test_commands = get_clap_app("test", "desc", "version");
4018
4019        let default_keypair = Keypair::new();
4020        let keypair_file = make_tmp_path("keypair_file");
4021        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4022        let default_signer = DefaultSigner::new("", &keypair_file);
4023
4024        let program_pubkey = Pubkey::new_unique();
4025        let new_authority_pubkey = Pubkey::new_unique();
4026        let blockhash = Hash::new_unique();
4027
4028        let test_command = test_commands.clone().get_matches_from(vec![
4029            "test",
4030            "program",
4031            "set-upgrade-authority",
4032            &program_pubkey.to_string(),
4033            "--new-upgrade-authority",
4034            &new_authority_pubkey.to_string(),
4035            "--skip-new-upgrade-authority-signer-check",
4036            "--sign-only",
4037            "--dump-transaction-message",
4038            "--blockhash",
4039            blockhash.to_string().as_str(),
4040        ]);
4041        assert_eq!(
4042            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4043            CliCommandInfo {
4044                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4045                    program_pubkey,
4046                    upgrade_authority_index: Some(0),
4047                    new_upgrade_authority: Some(new_authority_pubkey),
4048                    sign_only: true,
4049                    dump_transaction_message: true,
4050                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4051                }),
4052                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4053            }
4054        );
4055
4056        let program_pubkey = Pubkey::new_unique();
4057        let new_authority_pubkey = Keypair::new();
4058        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4059        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
4060        let test_command = test_commands.clone().get_matches_from(vec![
4061            "test",
4062            "program",
4063            "set-upgrade-authority",
4064            &program_pubkey.to_string(),
4065            "--new-upgrade-authority",
4066            &new_authority_pubkey_file,
4067            "--skip-new-upgrade-authority-signer-check",
4068        ]);
4069        assert_eq!(
4070            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4071            CliCommandInfo {
4072                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4073                    program_pubkey,
4074                    upgrade_authority_index: Some(0),
4075                    new_upgrade_authority: Some(new_authority_pubkey.pubkey()),
4076                    sign_only: false,
4077                    dump_transaction_message: false,
4078                    blockhash_query: BlockhashQuery::default(),
4079                }),
4080                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4081            }
4082        );
4083
4084        let blockhash = Hash::new_unique();
4085        let program_pubkey = Pubkey::new_unique();
4086        let new_authority_pubkey = Keypair::new();
4087        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4088        write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
4089        let test_command = test_commands.clone().get_matches_from(vec![
4090            "test",
4091            "program",
4092            "set-upgrade-authority",
4093            &program_pubkey.to_string(),
4094            "--new-upgrade-authority",
4095            &new_authority_pubkey_file,
4096            "--sign-only",
4097            "--dump-transaction-message",
4098            "--blockhash",
4099            blockhash.to_string().as_str(),
4100        ]);
4101        assert_eq!(
4102            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4103            CliCommandInfo {
4104                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
4105                    program_pubkey,
4106                    upgrade_authority_index: 0,
4107                    new_upgrade_authority_index: 1,
4108                    sign_only: true,
4109                    dump_transaction_message: true,
4110                    blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4111                }),
4112                signers: vec![
4113                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4114                    Box::new(read_keypair_file(&new_authority_pubkey_file).unwrap()),
4115                ],
4116            }
4117        );
4118
4119        let program_pubkey = Pubkey::new_unique();
4120        let new_authority_pubkey = Keypair::new();
4121        let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4122        write_keypair_file(&new_authority_pubkey, new_authority_pubkey_file).unwrap();
4123        let test_command = test_commands.clone().get_matches_from(vec![
4124            "test",
4125            "program",
4126            "set-upgrade-authority",
4127            &program_pubkey.to_string(),
4128            "--final",
4129        ]);
4130        assert_eq!(
4131            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4132            CliCommandInfo {
4133                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4134                    program_pubkey,
4135                    upgrade_authority_index: Some(0),
4136                    new_upgrade_authority: None,
4137                    sign_only: false,
4138                    dump_transaction_message: false,
4139                    blockhash_query: BlockhashQuery::default(),
4140                }),
4141                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4142            }
4143        );
4144
4145        let program_pubkey = Pubkey::new_unique();
4146        let authority = Keypair::new();
4147        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4148        write_keypair_file(&authority, &authority_keypair_file).unwrap();
4149        let test_command = test_commands.clone().get_matches_from(vec![
4150            "test",
4151            "program",
4152            "set-upgrade-authority",
4153            &program_pubkey.to_string(),
4154            "--upgrade-authority",
4155            &authority_keypair_file,
4156            "--final",
4157        ]);
4158        assert_eq!(
4159            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4160            CliCommandInfo {
4161                command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4162                    program_pubkey,
4163                    upgrade_authority_index: Some(1),
4164                    new_upgrade_authority: None,
4165                    sign_only: false,
4166                    dump_transaction_message: false,
4167                    blockhash_query: BlockhashQuery::default(),
4168                }),
4169                signers: vec![
4170                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4171                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4172                ],
4173            }
4174        );
4175    }
4176
4177    #[test]
4178    #[allow(clippy::cognitive_complexity)]
4179    fn test_cli_parse_set_buffer_authority() {
4180        let test_commands = get_clap_app("test", "desc", "version");
4181
4182        let default_keypair = Keypair::new();
4183        let keypair_file = make_tmp_path("keypair_file");
4184        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4185        let default_signer = DefaultSigner::new("", &keypair_file);
4186
4187        let buffer_pubkey = Pubkey::new_unique();
4188        let new_authority_pubkey = Pubkey::new_unique();
4189        let test_command = test_commands.clone().get_matches_from(vec![
4190            "test",
4191            "program",
4192            "set-buffer-authority",
4193            &buffer_pubkey.to_string(),
4194            "--new-buffer-authority",
4195            &new_authority_pubkey.to_string(),
4196        ]);
4197        assert_eq!(
4198            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4199            CliCommandInfo {
4200                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4201                    buffer_pubkey,
4202                    buffer_authority_index: Some(0),
4203                    new_buffer_authority: new_authority_pubkey,
4204                }),
4205                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4206            }
4207        );
4208
4209        let buffer_pubkey = Pubkey::new_unique();
4210        let new_authority_keypair = Keypair::new();
4211        let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
4212        write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
4213        let test_command = test_commands.clone().get_matches_from(vec![
4214            "test",
4215            "program",
4216            "set-buffer-authority",
4217            &buffer_pubkey.to_string(),
4218            "--new-buffer-authority",
4219            &new_authority_keypair_file,
4220        ]);
4221        assert_eq!(
4222            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4223            CliCommandInfo {
4224                command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4225                    buffer_pubkey,
4226                    buffer_authority_index: Some(0),
4227                    new_buffer_authority: new_authority_keypair.pubkey(),
4228                }),
4229                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4230            }
4231        );
4232    }
4233
4234    #[test]
4235    #[allow(clippy::cognitive_complexity)]
4236    fn test_cli_parse_show() {
4237        let test_commands = get_clap_app("test", "desc", "version");
4238
4239        let default_keypair = Keypair::new();
4240        let keypair_file = make_tmp_path("keypair_file");
4241        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4242        let default_signer = DefaultSigner::new("", &keypair_file);
4243
4244        // defaults
4245        let buffer_pubkey = Pubkey::new_unique();
4246        let authority_keypair = Keypair::new();
4247        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4248        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4249
4250        let test_command = test_commands.clone().get_matches_from(vec![
4251            "test",
4252            "program",
4253            "show",
4254            &buffer_pubkey.to_string(),
4255        ]);
4256        assert_eq!(
4257            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4258            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4259                account_pubkey: Some(buffer_pubkey),
4260                authority_pubkey: default_keypair.pubkey(),
4261                get_programs: false,
4262                get_buffers: false,
4263                all: false,
4264                use_lamports_unit: false,
4265            }))
4266        );
4267
4268        let test_command = test_commands.clone().get_matches_from(vec![
4269            "test",
4270            "program",
4271            "show",
4272            "--programs",
4273            "--all",
4274            "--lamports",
4275        ]);
4276        assert_eq!(
4277            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4278            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4279                account_pubkey: None,
4280                authority_pubkey: default_keypair.pubkey(),
4281                get_programs: true,
4282                get_buffers: false,
4283                all: true,
4284                use_lamports_unit: true,
4285            }))
4286        );
4287
4288        let test_command = test_commands.clone().get_matches_from(vec![
4289            "test",
4290            "program",
4291            "show",
4292            "--buffers",
4293            "--all",
4294            "--lamports",
4295        ]);
4296        assert_eq!(
4297            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4298            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4299                account_pubkey: None,
4300                authority_pubkey: default_keypair.pubkey(),
4301                get_programs: false,
4302                get_buffers: true,
4303                all: true,
4304                use_lamports_unit: true,
4305            }))
4306        );
4307
4308        let test_command = test_commands.clone().get_matches_from(vec![
4309            "test",
4310            "program",
4311            "show",
4312            "--buffers",
4313            "--buffer-authority",
4314            &authority_keypair.pubkey().to_string(),
4315        ]);
4316        assert_eq!(
4317            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4318            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4319                account_pubkey: None,
4320                authority_pubkey: authority_keypair.pubkey(),
4321                get_programs: false,
4322                get_buffers: true,
4323                all: false,
4324                use_lamports_unit: false,
4325            }))
4326        );
4327
4328        let test_command = test_commands.clone().get_matches_from(vec![
4329            "test",
4330            "program",
4331            "show",
4332            "--buffers",
4333            "--buffer-authority",
4334            &authority_keypair_file,
4335        ]);
4336        assert_eq!(
4337            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4338            CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4339                account_pubkey: None,
4340                authority_pubkey: authority_keypair.pubkey(),
4341                get_programs: false,
4342                get_buffers: true,
4343                all: false,
4344                use_lamports_unit: false,
4345            }))
4346        );
4347    }
4348
4349    #[test]
4350    #[allow(clippy::cognitive_complexity)]
4351    fn test_cli_parse_close() {
4352        let test_commands = get_clap_app("test", "desc", "version");
4353
4354        let default_keypair = Keypair::new();
4355        let keypair_file = make_tmp_path("keypair_file");
4356        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4357        let default_signer = DefaultSigner::new("", &keypair_file);
4358
4359        // defaults
4360        let buffer_pubkey = Pubkey::new_unique();
4361        let recipient_pubkey = Pubkey::new_unique();
4362        let authority_keypair = Keypair::new();
4363        let authority_keypair_file = make_tmp_path("authority_keypair_file");
4364
4365        let test_command = test_commands.clone().get_matches_from(vec![
4366            "test",
4367            "program",
4368            "close",
4369            &buffer_pubkey.to_string(),
4370        ]);
4371        assert_eq!(
4372            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4373            CliCommandInfo {
4374                command: CliCommand::Program(ProgramCliCommand::Close {
4375                    account_pubkey: Some(buffer_pubkey),
4376                    recipient_pubkey: default_keypair.pubkey(),
4377                    authority_index: 0,
4378                    use_lamports_unit: false,
4379                    bypass_warning: false,
4380                }),
4381                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4382            }
4383        );
4384
4385        // with bypass-warning
4386        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4387        let test_command = test_commands.clone().get_matches_from(vec![
4388            "test",
4389            "program",
4390            "close",
4391            &buffer_pubkey.to_string(),
4392            "--bypass-warning",
4393        ]);
4394        assert_eq!(
4395            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4396            CliCommandInfo {
4397                command: CliCommand::Program(ProgramCliCommand::Close {
4398                    account_pubkey: Some(buffer_pubkey),
4399                    recipient_pubkey: default_keypair.pubkey(),
4400                    authority_index: 0,
4401                    use_lamports_unit: false,
4402                    bypass_warning: true,
4403                }),
4404                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4405            }
4406        );
4407
4408        // with authority
4409        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4410        let test_command = test_commands.clone().get_matches_from(vec![
4411            "test",
4412            "program",
4413            "close",
4414            &buffer_pubkey.to_string(),
4415            "--buffer-authority",
4416            &authority_keypair_file,
4417        ]);
4418        assert_eq!(
4419            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4420            CliCommandInfo {
4421                command: CliCommand::Program(ProgramCliCommand::Close {
4422                    account_pubkey: Some(buffer_pubkey),
4423                    recipient_pubkey: default_keypair.pubkey(),
4424                    authority_index: 1,
4425                    use_lamports_unit: false,
4426                    bypass_warning: false,
4427                }),
4428                signers: vec![
4429                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4430                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4431                ],
4432            }
4433        );
4434
4435        // with recipient
4436        let test_command = test_commands.clone().get_matches_from(vec![
4437            "test",
4438            "program",
4439            "close",
4440            &buffer_pubkey.to_string(),
4441            "--recipient",
4442            &recipient_pubkey.to_string(),
4443        ]);
4444        assert_eq!(
4445            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4446            CliCommandInfo {
4447                command: CliCommand::Program(ProgramCliCommand::Close {
4448                    account_pubkey: Some(buffer_pubkey),
4449                    recipient_pubkey,
4450                    authority_index: 0,
4451                    use_lamports_unit: false,
4452                    bypass_warning: false,
4453                }),
4454                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4455            }
4456        );
4457
4458        // --buffers and lamports
4459        let test_command = test_commands.clone().get_matches_from(vec![
4460            "test",
4461            "program",
4462            "close",
4463            "--buffers",
4464            "--lamports",
4465        ]);
4466        assert_eq!(
4467            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4468            CliCommandInfo {
4469                command: CliCommand::Program(ProgramCliCommand::Close {
4470                    account_pubkey: None,
4471                    recipient_pubkey: default_keypair.pubkey(),
4472                    authority_index: 0,
4473                    use_lamports_unit: true,
4474                    bypass_warning: false,
4475                }),
4476                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4477            }
4478        );
4479    }
4480
4481    #[test]
4482    fn test_cli_parse_extend_program() {
4483        let test_commands = get_clap_app("test", "desc", "version");
4484
4485        let default_keypair = Keypair::new();
4486        let keypair_file = make_tmp_path("keypair_file");
4487        write_keypair_file(&default_keypair, &keypair_file).unwrap();
4488        let default_signer = DefaultSigner::new("", &keypair_file);
4489
4490        // defaults
4491        let program_pubkey = Pubkey::new_unique();
4492        let additional_bytes = 100;
4493
4494        let test_command = test_commands.clone().get_matches_from(vec![
4495            "test",
4496            "program",
4497            "extend",
4498            &program_pubkey.to_string(),
4499            &additional_bytes.to_string(),
4500        ]);
4501        assert_eq!(
4502            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4503            CliCommandInfo {
4504                command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4505                    program_pubkey,
4506                    payer_signer_index: 0,
4507                    additional_bytes
4508                }),
4509                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4510            }
4511        );
4512
4513        // with payer
4514        let payer_keypair = Keypair::new();
4515        let payer_keypair_file = make_tmp_path("payer_keypair_file");
4516        write_keypair_file(&payer_keypair, &payer_keypair_file).unwrap();
4517        let test_command = test_commands.clone().get_matches_from(vec![
4518            "test",
4519            "program",
4520            "extend",
4521            &program_pubkey.to_string(),
4522            &additional_bytes.to_string(),
4523            "--payer",
4524            &payer_keypair_file,
4525        ]);
4526        assert_eq!(
4527            parse_command(&test_command, &default_signer, &mut None).unwrap(),
4528            CliCommandInfo {
4529                command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4530                    program_pubkey,
4531                    payer_signer_index: 1,
4532                    additional_bytes
4533                }),
4534                signers: vec![
4535                    Box::new(read_keypair_file(&keypair_file).unwrap()),
4536                    Box::new(read_keypair_file(&payer_keypair_file).unwrap()),
4537                ],
4538            }
4539        );
4540    }
4541
4542    #[tokio::test]
4543    async fn test_cli_keypair_file() {
4544        agave_logger::setup();
4545
4546        let default_keypair = Keypair::new();
4547        let program_pubkey = Keypair::new();
4548        let deploy_path = make_tmp_path("deploy");
4549        let mut program_location = PathBuf::from(deploy_path.clone());
4550        program_location.push("noop");
4551        program_location.set_extension("so");
4552        let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
4553        pathbuf.push("tests");
4554        pathbuf.push("fixtures");
4555        pathbuf.push("noop");
4556        pathbuf.set_extension("so");
4557        let program_keypair_location = program_location.with_file_name("noop-keypair.json");
4558        std::fs::create_dir_all(deploy_path).unwrap();
4559        std::fs::copy(pathbuf, program_location.as_os_str()).unwrap();
4560        write_keypair_file(&program_pubkey, program_keypair_location).unwrap();
4561
4562        let config = CliConfig {
4563            rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
4564            command: CliCommand::Program(ProgramCliCommand::Deploy {
4565                program_location: Some(program_location.to_str().unwrap().to_string()),
4566                fee_payer_signer_index: 0,
4567                buffer_signer_index: None,
4568                buffer_pubkey: None,
4569                program_signer_index: None,
4570                program_pubkey: None,
4571                upgrade_authority_signer_index: 0,
4572                is_final: false,
4573                max_len: None,
4574                skip_fee_check: false,
4575                compute_unit_price: None,
4576                max_sign_attempts: 5,
4577                auto_extend: true,
4578                use_rpc: false,
4579                skip_feature_verification: true,
4580            }),
4581            signers: vec![&default_keypair],
4582            output_format: OutputFormat::JsonCompact,
4583            ..CliConfig::default()
4584        };
4585
4586        let result = process_command(&config).await;
4587        let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
4588        let program_id = json
4589            .as_object()
4590            .unwrap()
4591            .get("programId")
4592            .unwrap()
4593            .as_str()
4594            .unwrap();
4595
4596        assert_eq!(
4597            program_id.parse::<Pubkey>().unwrap(),
4598            program_pubkey.pubkey()
4599        );
4600    }
4601
4602    /// Minimal ELF64 header buffer with a valid `e_ident` and `e_flags`
4603    /// (which encodes the SBPF version).
4604    fn fake_elf(e_flags: u32) -> Vec<u8> {
4605        let mut bytes = vec![0u8; 64];
4606        bytes[..4].copy_from_slice(&ELFMAG);
4607        bytes[4] = ELFCLASS64;
4608        bytes[5] = ELFDATA2LSB;
4609        bytes[6] = EV_CURRENT as u8;
4610        bytes[EI_OSABI as usize] = ELFOSABI_NONE;
4611        bytes[48..52].copy_from_slice(&e_flags.to_le_bytes());
4612        bytes
4613    }
4614
4615    fn deploy_config(versions: std::ops::RangeInclusive<SBPFVersion>) -> Config {
4616        Config {
4617            enabled_sbpf_versions: versions,
4618            ..Config::default()
4619        }
4620    }
4621
4622    #[test]
4623    fn test_explain_unsupported_sbpf_version() {
4624        let elf = fake_elf(1); // e_flags=1 -> SBPF v1
4625        let config = deploy_config(SBPFVersion::V3..=SBPFVersion::V3);
4626        let err = EbpfError::ElfError(ElfError::UnsupportedSBPFVersion);
4627        let msg = explain_elf_error(&err, &elf, &config);
4628        assert!(msg.contains("SBPF v1"), "got: {msg}");
4629        assert!(msg.contains("enabled: v3"), "got: {msg}");
4630    }
4631}