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