solana_cli/
program_v4.rs

1use {
2    crate::{
3        checks::*,
4        cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult},
5        compute_budget::{
6            simulate_and_update_compute_unit_limit, ComputeUnitConfig, WithComputeUnitConfig,
7        },
8        feature::{status_from_account, CliFeatureStatus},
9        program::calculate_max_chunk_size,
10    },
11    clap::{value_t, App, AppSettings, Arg, ArgMatches, SubCommand},
12    log::*,
13    solana_account::Account,
14    solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
15    solana_clap_utils::{
16        compute_budget::{compute_unit_price_arg, ComputeUnitLimit},
17        input_parsers::{pubkey_of, pubkey_of_signer, signer_of},
18        input_validators::{is_valid_pubkey, is_valid_signer},
19        keypair::{DefaultSigner, SignerIndex},
20        offline::{OfflineArgs, DUMP_TRANSACTION_MESSAGE, SIGN_ONLY_ARG},
21    },
22    solana_cli_output::{
23        return_signers_with_config, CliProgramId, CliProgramV4, CliProgramsV4, ReturnSignersConfig,
24    },
25    solana_client::{
26        connection_cache::ConnectionCache,
27        send_and_confirm_transactions_in_parallel::{
28            send_and_confirm_transactions_in_parallel_blocking_v2, SendAndConfirmConfigV2,
29        },
30        tpu_client::{TpuClient, TpuClientConfig},
31    },
32    solana_compute_budget::compute_budget::ComputeBudget,
33    solana_feature_set::{FeatureSet, FEATURE_NAMES},
34    solana_instruction::Instruction,
35    solana_loader_v4_interface::{
36        instruction,
37        state::{
38            LoaderV4State,
39            LoaderV4Status::{self, Retracted},
40        },
41    },
42    solana_message::Message,
43    solana_program_runtime::invoke_context::InvokeContext,
44    solana_pubkey::Pubkey,
45    solana_remote_wallet::remote_wallet::RemoteWalletManager,
46    solana_rpc_client::rpc_client::RpcClient,
47    solana_rpc_client_api::{
48        config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
49        filter::{Memcmp, RpcFilterType},
50        request::MAX_MULTIPLE_ACCOUNTS,
51    },
52    solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
53    solana_sbpf::{elf::Executable, verifier::RequisiteVerifier},
54    solana_sdk_ids::{loader_v4, system_program},
55    solana_signer::Signer,
56    solana_system_interface::{instruction as system_instruction, MAX_PERMITTED_DATA_LENGTH},
57    solana_transaction::Transaction,
58    std::{
59        cmp::Ordering,
60        fs::File,
61        io::{Read, Write},
62        mem::size_of,
63        num::Saturating,
64        ops::Range,
65        rc::Rc,
66        sync::Arc,
67    },
68};
69
70#[derive(Debug, PartialEq, Eq, Default)]
71pub struct AdditionalCliConfig {
72    pub use_rpc: bool,
73    pub sign_only: bool,
74    pub dump_transaction_message: bool,
75    pub blockhash_query: BlockhashQuery,
76    pub compute_unit_price: Option<u64>,
77}
78
79impl AdditionalCliConfig {
80    fn from_matches(matches: &ArgMatches<'_>) -> Self {
81        Self {
82            use_rpc: matches.is_present("use-rpc"),
83            sign_only: matches.is_present(SIGN_ONLY_ARG.name),
84            dump_transaction_message: matches.is_present(DUMP_TRANSACTION_MESSAGE.name),
85            blockhash_query: BlockhashQuery::new_from_matches(matches),
86            compute_unit_price: value_t!(matches, "compute_unit_price", u64).ok(),
87        }
88    }
89}
90
91#[derive(Debug, PartialEq, Eq)]
92pub enum ProgramV4CliCommand {
93    Deploy {
94        additional_cli_config: AdditionalCliConfig,
95        program_address: Pubkey,
96        buffer_address: Option<Pubkey>,
97        upload_signer_index: Option<SignerIndex>,
98        authority_signer_index: SignerIndex,
99        path_to_elf: Option<String>,
100        upload_range: Range<Option<usize>>,
101    },
102    Close {
103        additional_cli_config: AdditionalCliConfig,
104        program_address: Pubkey,
105        authority_signer_index: SignerIndex,
106    },
107    TransferAuthority {
108        additional_cli_config: AdditionalCliConfig,
109        program_address: Pubkey,
110        authority_signer_index: SignerIndex,
111        new_authority_signer_index: SignerIndex,
112    },
113    Finalize {
114        additional_cli_config: AdditionalCliConfig,
115        program_address: Pubkey,
116        authority_signer_index: SignerIndex,
117        next_version_signer_index: SignerIndex,
118    },
119    Show {
120        account_pubkey: Option<Pubkey>,
121        authority: Pubkey,
122        all: bool,
123    },
124    Dump {
125        account_pubkey: Option<Pubkey>,
126        output_location: String,
127    },
128}
129
130pub trait ProgramV4SubCommands {
131    fn program_v4_subcommands(self) -> Self;
132}
133
134impl ProgramV4SubCommands for App<'_, '_> {
135    fn program_v4_subcommands(self) -> Self {
136        self.subcommand(
137            SubCommand::with_name("program-v4")
138                .about("Program V4 management")
139                .setting(AppSettings::SubcommandRequiredElseHelp)
140                .subcommand(
141                    SubCommand::with_name("deploy")
142                        .about("Deploy a new or redeploy an existing program")
143                        .arg(
144                            Arg::with_name("path-to-elf")
145                                .index(1)
146                                .value_name("PATH-TO-ELF")
147                                .takes_value(true)
148                                .help("/path/to/program.so"),
149                        )
150                        .arg(
151                            Arg::with_name("start-offset")
152                                .long("start-offset")
153                                .value_name("START_OFFSET")
154                                .takes_value(true)
155                                .help("Optionally starts writing at this byte offset"),
156                        )
157                        .arg(
158                            Arg::with_name("end-offset")
159                                .long("end-offset")
160                                .value_name("END_OFFSET")
161                                .takes_value(true)
162                                .help("Optionally stops writing after this byte offset"),
163                        )
164                        .arg(
165                            Arg::with_name("program-keypair")
166                                .long("program-keypair")
167                                .value_name("PROGRAM_SIGNER")
168                                .takes_value(true)
169                                .validator(is_valid_signer)
170                                .help(
171                                    "Program account signer for deploying a new program",
172                                ),
173                        )
174                        .arg(
175                            Arg::with_name("program-id")
176                                .long("program-id")
177                                .value_name("PROGRAM_ID")
178                                .takes_value(true)
179                                .help("Program address for redeploying an existing program"),
180                        )
181                        .arg(
182                            Arg::with_name("buffer")
183                                .long("buffer")
184                                .value_name("BUFFER_SIGNER")
185                                .takes_value(true)
186                                .validator(is_valid_signer)
187                                .help(
188                                    "Optional intermediate buffer account to write data to",
189                                ),
190                        )
191                        .arg(
192                            Arg::with_name("authority")
193                                .long("authority")
194                                .value_name("AUTHORITY_SIGNER")
195                                .takes_value(true)
196                                .validator(is_valid_signer)
197                                .help(
198                                    "Program authority [default: the default configured keypair]",
199                                ),
200                        )
201                        .arg(Arg::with_name("use-rpc").long("use-rpc").help(
202                            "Send transactions to the configured RPC instead of validator TPUs",
203                        ))
204                        .offline_args()
205                        .arg(compute_unit_price_arg()),
206                )
207                .subcommand(
208                    SubCommand::with_name("close")
209                        .about("Close a program and delete the account")
210                        .arg(
211                            Arg::with_name("program-id")
212                                .long("program-id")
213                                .value_name("PROGRAM_ID")
214                                .takes_value(true)
215                                .help("Executable program's address"),
216                        )
217                        .arg(
218                            Arg::with_name("authority")
219                                .long("authority")
220                                .value_name("AUTHORITY_SIGNER")
221                                .takes_value(true)
222                                .validator(is_valid_signer)
223                                .help(
224                                    "Program authority [default: the default configured keypair]",
225                                ),
226                        )
227                        .offline_args()
228                        .arg(compute_unit_price_arg()),
229                )
230                .subcommand(
231                    SubCommand::with_name("transfer-authority")
232                        .about("Transfer the authority of a program to a different address")
233                        .arg(
234                            Arg::with_name("program-id")
235                                .long("program-id")
236                                .value_name("PROGRAM_ID")
237                                .takes_value(true)
238                                .help("Executable program's address"),
239                        )
240                        .arg(
241                            Arg::with_name("authority")
242                                .long("authority")
243                                .value_name("AUTHORITY_SIGNER")
244                                .takes_value(true)
245                                .validator(is_valid_signer)
246                                .help(
247                                    "Current program authority [default: the default configured keypair]",
248                                ),
249                        )
250                        .arg(
251                            Arg::with_name("new-authority")
252                                .long("new-authority")
253                                .value_name("NEW_AUTHORITY_SIGNER")
254                                .takes_value(true)
255                                .required(true)
256                                .validator(is_valid_signer)
257                                .help(
258                                    "New program authority",
259                                ),
260                        )
261                        .offline_args()
262                        .arg(compute_unit_price_arg()),
263                )
264                .subcommand(
265                    SubCommand::with_name("finalize")
266                        .about("Finalize a program to make it immutable")
267                        .arg(
268                            Arg::with_name("program-id")
269                                .long("program-id")
270                                .value_name("PROGRAM_ID")
271                                .takes_value(true)
272                                .help("Executable program's address"),
273                        )
274                        .arg(
275                            Arg::with_name("authority")
276                                .long("authority")
277                                .value_name("AUTHORITY_SIGNER")
278                                .takes_value(true)
279                                .validator(is_valid_signer)
280                                .help(
281                                    "Program authority [default: the default configured keypair]",
282                                ),
283                        )
284                        .arg(
285                            Arg::with_name("next-version")
286                                .long("next-version")
287                                .value_name("NEXT_VERSION")
288                                .takes_value(true)
289                                .validator(is_valid_signer)
290                                .help(
291                                    "Reserves the address and links it as the programs next-version, which is a hint that frontends can show to users",
292                                ),
293                        )
294                        .offline_args()
295                        .arg(compute_unit_price_arg()),
296                )
297                .subcommand(
298                    SubCommand::with_name("show")
299                        .about("Display information about a buffer or program")
300                        .arg(
301                            Arg::with_name("account")
302                                .index(1)
303                                .value_name("ACCOUNT_ADDRESS")
304                                .takes_value(true)
305                                .help("Address of the program to show"),
306                        )
307                        .arg(
308                            Arg::with_name("all")
309                                .long("all")
310                                .conflicts_with("account")
311                                .conflicts_with("authority")
312                                .help("Show accounts for all authorities"),
313                        )
314                        .arg(pubkey!(
315                            Arg::with_name("authority")
316                                .long("authority")
317                                .value_name("AUTHORITY")
318                                .conflicts_with("all"),
319                            "Authority [default: the default configured keypair]."
320                        )),
321                )
322                .subcommand(
323                    SubCommand::with_name("dump")
324                        .about("Write the program data to a file")
325                        .arg(
326                            Arg::with_name("account")
327                                .index(1)
328                                .value_name("ACCOUNT_ADDRESS")
329                                .takes_value(true)
330                                .required(true)
331                                .help("Address of the buffer or program"),
332                        )
333                        .arg(
334                            Arg::with_name("output_location")
335                                .index(2)
336                                .value_name("OUTPUT_FILEPATH")
337                                .takes_value(true)
338                                .required(true)
339                                .help("/path/to/program.so"),
340                        ),
341                ),
342        )
343    }
344}
345
346pub fn parse_program_v4_subcommand(
347    matches: &ArgMatches<'_>,
348    default_signer: &DefaultSigner,
349    wallet_manager: &mut Option<Rc<RemoteWalletManager>>,
350) -> Result<CliCommandInfo, CliError> {
351    let (subcommand, sub_matches) = matches.subcommand();
352    let response = match (subcommand, sub_matches) {
353        ("deploy", Some(matches)) => {
354            let mut bulk_signers = vec![Some(
355                default_signer.signer_from_path(matches, wallet_manager)?,
356            )];
357
358            let path_to_elf = matches
359                .value_of("path-to-elf")
360                .map(|location| location.to_string());
361
362            let program_address = pubkey_of(matches, "program-id");
363            let program_pubkey = if let Ok((program_signer, Some(program_pubkey))) =
364                signer_of(matches, "program-keypair", wallet_manager)
365            {
366                bulk_signers.push(program_signer);
367                Some(program_pubkey)
368            } else {
369                pubkey_of_signer(matches, "program-keypair", wallet_manager)?
370            };
371
372            let buffer_pubkey = if let Ok((buffer_signer, Some(buffer_pubkey))) =
373                signer_of(matches, "buffer", wallet_manager)
374            {
375                bulk_signers.push(buffer_signer);
376                Some(buffer_pubkey)
377            } else {
378                pubkey_of_signer(matches, "buffer", wallet_manager)?
379            };
380
381            let (authority, authority_pubkey) = signer_of(matches, "authority", wallet_manager)?;
382            bulk_signers.push(authority);
383
384            let signer_info =
385                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
386            let program_signer_index = signer_info.index_of_or_none(program_pubkey);
387            let buffer_signer_index = signer_info.index_of_or_none(buffer_pubkey);
388            let upload_signer_index = buffer_signer_index.or(program_signer_index);
389            let authority_signer_index = signer_info
390                .index_of(authority_pubkey)
391                .expect("Authority signer is missing");
392            assert!(
393                program_address.is_some() != program_signer_index.is_some(),
394                "Requires either --program-keypair or --program-id",
395            );
396
397            CliCommandInfo {
398                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
399                    additional_cli_config: AdditionalCliConfig::from_matches(matches),
400                    program_address: program_address.or(program_pubkey).unwrap(),
401                    buffer_address: buffer_pubkey,
402                    upload_signer_index,
403                    authority_signer_index,
404                    path_to_elf,
405                    upload_range: value_t!(matches, "start-offset", usize).ok()
406                        ..value_t!(matches, "end-offset", usize).ok(),
407                }),
408                signers: signer_info.signers,
409            }
410        }
411        ("close", Some(matches)) => {
412            let mut bulk_signers = vec![Some(
413                default_signer.signer_from_path(matches, wallet_manager)?,
414            )];
415
416            let (authority, authority_pubkey) = signer_of(matches, "authority", wallet_manager)?;
417            bulk_signers.push(authority);
418
419            let signer_info =
420                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
421
422            CliCommandInfo {
423                command: CliCommand::ProgramV4(ProgramV4CliCommand::Close {
424                    additional_cli_config: AdditionalCliConfig::from_matches(matches),
425                    program_address: pubkey_of(matches, "program-id")
426                        .expect("Program address is missing"),
427                    authority_signer_index: signer_info
428                        .index_of(authority_pubkey)
429                        .expect("Authority signer is missing"),
430                }),
431                signers: signer_info.signers,
432            }
433        }
434        ("transfer-authority", Some(matches)) => {
435            let mut bulk_signers = vec![Some(
436                default_signer.signer_from_path(matches, wallet_manager)?,
437            )];
438
439            let (authority, authority_pubkey) = signer_of(matches, "authority", wallet_manager)?;
440            bulk_signers.push(authority);
441
442            let (new_authority, new_authority_pubkey) =
443                signer_of(matches, "new-authority", wallet_manager)?;
444            bulk_signers.push(new_authority);
445
446            let signer_info =
447                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
448
449            CliCommandInfo {
450                command: CliCommand::ProgramV4(ProgramV4CliCommand::TransferAuthority {
451                    additional_cli_config: AdditionalCliConfig::from_matches(matches),
452                    program_address: pubkey_of(matches, "program-id")
453                        .expect("Program address is missing"),
454                    authority_signer_index: signer_info
455                        .index_of(authority_pubkey)
456                        .expect("Authority signer is missing"),
457                    new_authority_signer_index: signer_info
458                        .index_of(new_authority_pubkey)
459                        .expect("Authority signer is missing"),
460                }),
461                signers: signer_info.signers,
462            }
463        }
464        ("finalize", Some(matches)) => {
465            let mut bulk_signers = vec![Some(
466                default_signer.signer_from_path(matches, wallet_manager)?,
467            )];
468
469            let (authority, authority_pubkey) = signer_of(matches, "authority", wallet_manager)?;
470            bulk_signers.push(authority);
471
472            if let Ok((next_version, _next_version_pubkey)) =
473                signer_of(matches, "next-version", wallet_manager)
474            {
475                bulk_signers.push(next_version);
476            }
477
478            let signer_info =
479                default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
480            let authority_signer_index = signer_info
481                .index_of(authority_pubkey)
482                .expect("Authority signer is missing");
483
484            CliCommandInfo {
485                command: CliCommand::ProgramV4(ProgramV4CliCommand::Finalize {
486                    additional_cli_config: AdditionalCliConfig::from_matches(matches),
487                    program_address: pubkey_of(matches, "program-id")
488                        .expect("Program address is missing"),
489                    authority_signer_index,
490                    next_version_signer_index: pubkey_of(matches, "next-version")
491                        .and_then(|pubkey| signer_info.index_of(Some(pubkey)))
492                        .unwrap_or(authority_signer_index),
493                }),
494                signers: signer_info.signers,
495            }
496        }
497        ("show", Some(matches)) => {
498            let authority =
499                if let Some(authority) = pubkey_of_signer(matches, "authority", wallet_manager)? {
500                    authority
501                } else {
502                    default_signer
503                        .signer_from_path(matches, wallet_manager)?
504                        .pubkey()
505                };
506
507            CliCommandInfo::without_signers(CliCommand::ProgramV4(ProgramV4CliCommand::Show {
508                account_pubkey: pubkey_of(matches, "account"),
509                authority,
510                all: matches.is_present("all"),
511            }))
512        }
513        ("dump", Some(matches)) => {
514            CliCommandInfo::without_signers(CliCommand::ProgramV4(ProgramV4CliCommand::Dump {
515                account_pubkey: pubkey_of(matches, "account"),
516                output_location: matches.value_of("output_location").unwrap().to_string(),
517            }))
518        }
519        _ => unreachable!(),
520    };
521    Ok(response)
522}
523
524pub fn process_program_v4_subcommand(
525    rpc_client: Arc<RpcClient>,
526    config: &CliConfig,
527    program_subcommand: &ProgramV4CliCommand,
528) -> ProcessResult {
529    match program_subcommand {
530        ProgramV4CliCommand::Deploy {
531            additional_cli_config,
532            program_address,
533            buffer_address,
534            upload_signer_index,
535            authority_signer_index,
536            path_to_elf,
537            upload_range,
538        } => {
539            let mut program_data = Vec::new();
540            if let Some(path_to_elf) = path_to_elf {
541                let mut file = File::open(path_to_elf)
542                    .map_err(|err| format!("Unable to open program file: {err}"))?;
543                file.read_to_end(&mut program_data)
544                    .map_err(|err| format!("Unable to read program file: {err}"))?;
545            }
546            process_deploy_program(
547                rpc_client,
548                config,
549                additional_cli_config,
550                program_address,
551                buffer_address.as_ref(),
552                upload_signer_index.as_ref(),
553                authority_signer_index,
554                &program_data,
555                upload_range.clone(),
556            )
557        }
558        ProgramV4CliCommand::Close {
559            additional_cli_config,
560            program_address,
561            authority_signer_index,
562        } => process_close_program(
563            rpc_client,
564            config,
565            additional_cli_config,
566            authority_signer_index,
567            program_address,
568        ),
569        ProgramV4CliCommand::TransferAuthority {
570            additional_cli_config,
571            program_address,
572            authority_signer_index,
573            new_authority_signer_index,
574        } => process_transfer_authority_of_program(
575            rpc_client,
576            config,
577            additional_cli_config,
578            authority_signer_index,
579            new_authority_signer_index,
580            program_address,
581        ),
582        ProgramV4CliCommand::Finalize {
583            additional_cli_config,
584            program_address,
585            authority_signer_index,
586            next_version_signer_index,
587        } => process_finalize_program(
588            rpc_client,
589            config,
590            additional_cli_config,
591            authority_signer_index,
592            next_version_signer_index,
593            program_address,
594        ),
595        ProgramV4CliCommand::Show {
596            account_pubkey,
597            authority,
598            all,
599        } => process_show(rpc_client, config, *account_pubkey, *authority, *all),
600        ProgramV4CliCommand::Dump {
601            account_pubkey,
602            output_location,
603        } => process_dump(rpc_client, config, *account_pubkey, output_location),
604    }
605}
606
607// This function can be used for the following use-cases
608// * Deploy a new program
609//   - buffer_address must be `None`
610//   - upload_signer_index must be `Some(program_signer_index)`
611// * Redeploy an exisiting program using the original program account
612//   - buffer_address must be `None`
613//   - upload_signer_index must be `None`
614// * Redeploy an exisiting program using a buffer account
615//   - buffer_address must be `Some(buffer_signer.pubkey())`
616//   - upload_signer_index must be `Some(buffer_signer_index)`
617pub fn process_deploy_program(
618    rpc_client: Arc<RpcClient>,
619    config: &CliConfig,
620    additional_cli_config: &AdditionalCliConfig,
621    program_address: &Pubkey,
622    buffer_address: Option<&Pubkey>,
623    upload_signer_index: Option<&SignerIndex>,
624    auth_signer_index: &SignerIndex,
625    program_data: &[u8],
626    upload_range: Range<Option<usize>>,
627) -> ProcessResult {
628    let payer_pubkey = config.signers[0].pubkey();
629    let authority_pubkey = config.signers[*auth_signer_index].pubkey();
630
631    // Check requested command makes sense given the on-chain state
632    let program_account = rpc_client
633        .get_account_with_commitment(program_address, config.commitment)?
634        .value;
635    let program_account_exists = program_account.is_some();
636    if buffer_address.is_some() != upload_signer_index.is_some() {
637        // Deploy new program
638        if program_account_exists {
639            return Err("Program account does exist already. Did you perhaps intent to redeploy an existing program instead? Then use --program-id instead of --program-keypair.".into());
640        }
641    } else {
642        // Redeploy an existing program
643        if !program_account_exists {
644            return Err("Program account does not exist. Did you perhaps intent to deploy a new program instead? Then use --program-keypair instead of --program-id.".into());
645        }
646    }
647
648    // Download feature set
649    let mut feature_set = FeatureSet::default();
650    for feature_ids in FEATURE_NAMES
651        .keys()
652        .cloned()
653        .collect::<Vec<Pubkey>>()
654        .chunks(MAX_MULTIPLE_ACCOUNTS)
655    {
656        rpc_client
657            .get_multiple_accounts(feature_ids)?
658            .into_iter()
659            .zip(feature_ids)
660            .for_each(|(account, feature_id)| {
661                let activation_slot = account.and_then(status_from_account);
662
663                if let Some(CliFeatureStatus::Active(slot)) = activation_slot {
664                    feature_set.activate(feature_id, slot);
665                }
666            });
667    }
668    let program_runtime_environment =
669        solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1(
670            &feature_set,
671            &ComputeBudget::default(),
672            true,
673            false,
674        )
675        .unwrap();
676
677    // Verify the program
678    let upload_range =
679        upload_range.start.unwrap_or(0)..upload_range.end.unwrap_or(program_data.len());
680    const MAX_LEN: usize =
681        (MAX_PERMITTED_DATA_LENGTH as usize).saturating_sub(LoaderV4State::program_data_offset());
682    if program_data.len() > MAX_LEN {
683        return Err(format!(
684            "Program length {} exeeds maximum length {}",
685            program_data.len(),
686            MAX_LEN,
687        )
688        .into());
689    }
690    if upload_range.end > program_data.len() {
691        return Err(format!(
692            "Range end {} exeeds program length {}",
693            upload_range.end,
694            program_data.len(),
695        )
696        .into());
697    }
698    let executable =
699        Executable::<InvokeContext>::from_elf(program_data, Arc::new(program_runtime_environment))
700            .map_err(|err| format!("ELF error: {err}"))?;
701    executable
702        .verify::<RequisiteVerifier>()
703        .map_err(|err| format!("ELF error: {err}"))?;
704
705    // Create and add retract message
706    let mut initial_messages = Vec::default();
707    let mut retract_instruction = None;
708    if let Some(program_account) = program_account.as_ref() {
709        retract_instruction =
710            build_retract_instruction(program_account, program_address, &authority_pubkey)?;
711    }
712
713    let lamports_required = rpc_client.get_minimum_balance_for_rent_exemption(
714        LoaderV4State::program_data_offset().saturating_add(program_data.len()),
715    )?;
716    let upload_address = buffer_address.unwrap_or(program_address);
717    let (existing_lamports, upload_account) = if let Some(buffer_address) = buffer_address {
718        let buffer_account = rpc_client
719            .get_account_with_commitment(buffer_address, config.commitment)?
720            .value;
721        (0, buffer_account)
722    } else {
723        (
724            program_account
725                .as_ref()
726                .map(|account| account.lamports)
727                .unwrap_or(0),
728            program_account,
729        )
730    };
731    if upload_account.is_none() {
732        // Create and add create_buffer message
733        initial_messages.push(instruction::create_buffer(
734            &payer_pubkey,
735            upload_address,
736            lamports_required,
737            &authority_pubkey,
738            program_data.len() as u32,
739            &payer_pubkey,
740        ));
741    }
742
743    let mut write_messages = vec![];
744    if upload_range.is_empty() {
745        if upload_account.is_none() {
746            return Err(format!(
747                "No ELF was provided or uploaded to the account {:?}",
748                upload_address,
749            )
750            .into());
751        }
752    } else {
753        // Create and add set_program_length message
754        if let Some(upload_account) = upload_account.as_ref() {
755            let (set_program_length_instructions, _lamports_required) =
756                build_set_program_length_instructions(
757                    rpc_client.clone(),
758                    config,
759                    auth_signer_index,
760                    upload_account,
761                    upload_address,
762                    program_data.len() as u32,
763                )?;
764            if !set_program_length_instructions.is_empty() {
765                initial_messages.push(set_program_length_instructions);
766            }
767        }
768
769        // Create and add write messages
770        let first_write_message = Message::new(
771            &[instruction::write(
772                upload_address,
773                &authority_pubkey,
774                0,
775                Vec::new(),
776            )],
777            Some(&payer_pubkey),
778        );
779        let chunk_size = calculate_max_chunk_size(first_write_message);
780        for (chunk, i) in program_data[upload_range.clone()]
781            .chunks(chunk_size)
782            .zip(0usize..)
783        {
784            write_messages.push(vec![instruction::write(
785                upload_address,
786                &authority_pubkey,
787                (upload_range.start as u32).saturating_add(i.saturating_mul(chunk_size) as u32),
788                chunk.to_vec(),
789            )]);
790        }
791    }
792
793    // Create and add deploy messages
794    let final_messages = vec![if buffer_address.is_some() {
795        // Redeploy with a buffer account
796        let mut instructions = Vec::default();
797        if let Some(retract_instruction) = retract_instruction {
798            instructions.push(retract_instruction);
799        }
800        instructions.push(instruction::deploy_from_source(
801            program_address,
802            &authority_pubkey,
803            upload_address,
804        ));
805        instructions
806    } else {
807        // Deploy new program or redeploy without a buffer account
808        if let Some(retract_instruction) = retract_instruction {
809            initial_messages.insert(0, vec![retract_instruction]);
810        }
811        vec![instruction::deploy(program_address, &authority_pubkey)]
812    }];
813
814    send_messages(
815        rpc_client,
816        config,
817        additional_cli_config,
818        auth_signer_index,
819        initial_messages,
820        write_messages,
821        final_messages,
822        upload_signer_index,
823        lamports_required.saturating_sub(existing_lamports),
824        config.output_format.formatted_string(&CliProgramId {
825            program_id: program_address.to_string(),
826            signature: None,
827        }),
828    )
829}
830
831fn process_close_program(
832    rpc_client: Arc<RpcClient>,
833    config: &CliConfig,
834    additional_cli_config: &AdditionalCliConfig,
835    auth_signer_index: &SignerIndex,
836    program_address: &Pubkey,
837) -> ProcessResult {
838    let payer_pubkey = config.signers[0].pubkey();
839    let authority_pubkey = config.signers[*auth_signer_index].pubkey();
840
841    let Some(program_account) = rpc_client
842        .get_account_with_commitment(program_address, config.commitment)?
843        .value
844    else {
845        return Err("Program account does not exist".into());
846    };
847
848    let retract_instruction =
849        build_retract_instruction(&program_account, program_address, &authority_pubkey)?;
850
851    let mut instructions = Vec::default();
852    if let Some(retract_instruction) = retract_instruction {
853        instructions.push(retract_instruction);
854    }
855    let set_program_length_instruction =
856        instruction::set_program_length(program_address, &authority_pubkey, 0, &payer_pubkey);
857    instructions.push(set_program_length_instruction);
858    let messages = vec![instructions];
859
860    send_messages(
861        rpc_client,
862        config,
863        additional_cli_config,
864        auth_signer_index,
865        messages,
866        Vec::default(),
867        Vec::default(),
868        None,
869        0,
870        config.output_format.formatted_string(&CliProgramId {
871            program_id: program_address.to_string(),
872            signature: None,
873        }),
874    )
875}
876
877fn process_transfer_authority_of_program(
878    rpc_client: Arc<RpcClient>,
879    config: &CliConfig,
880    additional_cli_config: &AdditionalCliConfig,
881    auth_signer_index: &SignerIndex,
882    new_auth_signer_index: &SignerIndex,
883    program_address: &Pubkey,
884) -> ProcessResult {
885    let authority_pubkey = config.signers[*auth_signer_index].pubkey();
886    let new_authority_pubkey = config.signers[*new_auth_signer_index].pubkey();
887
888    let messages = vec![vec![instruction::transfer_authority(
889        program_address,
890        &authority_pubkey,
891        &new_authority_pubkey,
892    )]];
893
894    send_messages(
895        rpc_client,
896        config,
897        additional_cli_config,
898        auth_signer_index,
899        messages,
900        Vec::default(),
901        Vec::default(),
902        None,
903        0,
904        config.output_format.formatted_string(&CliProgramId {
905            program_id: program_address.to_string(),
906            signature: None,
907        }),
908    )
909}
910
911fn process_finalize_program(
912    rpc_client: Arc<RpcClient>,
913    config: &CliConfig,
914    additional_cli_config: &AdditionalCliConfig,
915    auth_signer_index: &SignerIndex,
916    next_version_signer_index: &SignerIndex,
917    program_address: &Pubkey,
918) -> ProcessResult {
919    let authority_pubkey = config.signers[*auth_signer_index].pubkey();
920    let next_version_pubkey = config.signers[*next_version_signer_index].pubkey();
921
922    let messages = vec![vec![instruction::finalize(
923        program_address,
924        &authority_pubkey,
925        &next_version_pubkey,
926    )]];
927
928    send_messages(
929        rpc_client,
930        config,
931        additional_cli_config,
932        auth_signer_index,
933        messages,
934        Vec::default(),
935        Vec::default(),
936        None,
937        0,
938        config.output_format.formatted_string(&CliProgramId {
939            program_id: program_address.to_string(),
940            signature: None,
941        }),
942    )
943}
944
945fn process_show(
946    rpc_client: Arc<RpcClient>,
947    config: &CliConfig,
948    program_address: Option<Pubkey>,
949    authority: Pubkey,
950    all: bool,
951) -> ProcessResult {
952    if let Some(program_address) = program_address {
953        if let Some(account) = rpc_client
954            .get_account_with_commitment(&program_address, config.commitment)?
955            .value
956        {
957            if loader_v4::check_id(&account.owner) {
958                if let Ok(state) = solana_loader_v4_program::get_state(&account.data) {
959                    let status = match state.status {
960                        LoaderV4Status::Retracted => "retracted",
961                        LoaderV4Status::Deployed => "deployed",
962                        LoaderV4Status::Finalized => "finalized",
963                    };
964                    Ok(config.output_format.formatted_string(&CliProgramV4 {
965                        program_id: program_address.to_string(),
966                        owner: account.owner.to_string(),
967                        authority: state.authority_address_or_next_version.to_string(),
968                        last_deploy_slot: state.slot,
969                        data_len: account
970                            .data
971                            .len()
972                            .saturating_sub(LoaderV4State::program_data_offset()),
973                        status: status.to_string(),
974                    }))
975                } else {
976                    Err(format!("{program_address} SBF program state is invalid").into())
977                }
978            } else {
979                Err(format!("{program_address} is not an SBF program").into())
980            }
981        } else {
982            Err(format!("Unable to find the account {program_address}").into())
983        }
984    } else {
985        let authority_pubkey = if all { None } else { Some(authority) };
986        let programs = get_programs(rpc_client, config, authority_pubkey)?;
987        Ok(config.output_format.formatted_string(&programs))
988    }
989}
990
991pub fn process_dump(
992    rpc_client: Arc<RpcClient>,
993    config: &CliConfig,
994    account_pubkey: Option<Pubkey>,
995    output_location: &str,
996) -> ProcessResult {
997    if let Some(account_pubkey) = account_pubkey {
998        if let Some(account) = rpc_client
999            .get_account_with_commitment(&account_pubkey, config.commitment)?
1000            .value
1001        {
1002            if loader_v4::check_id(&account.owner) {
1003                let mut f = File::create(output_location)?;
1004                f.write_all(&account.data[LoaderV4State::program_data_offset()..])?;
1005                Ok(format!("Wrote program to {output_location}"))
1006            } else {
1007                Err(format!("{account_pubkey} is not an SBF program").into())
1008            }
1009        } else {
1010            Err(format!("Unable to find the account {account_pubkey}").into())
1011        }
1012    } else {
1013        Err("No account specified".into())
1014    }
1015}
1016
1017#[allow(clippy::too_many_arguments)]
1018fn send_messages(
1019    rpc_client: Arc<RpcClient>,
1020    config: &CliConfig,
1021    additional_cli_config: &AdditionalCliConfig,
1022    auth_signer_index: &SignerIndex,
1023    initial_messages: Vec<Vec<Instruction>>,
1024    write_messages: Vec<Vec<Instruction>>,
1025    final_messages: Vec<Vec<Instruction>>,
1026    program_signer_index: Option<&SignerIndex>,
1027    balance_needed: u64,
1028    ok_result: String,
1029) -> ProcessResult {
1030    let payer_pubkey = config.signers[0].pubkey();
1031    let program_signer = program_signer_index.map(|index| config.signers[*index]);
1032    let blockhash = additional_cli_config
1033        .blockhash_query
1034        .get_blockhash(&rpc_client, config.commitment)?;
1035    let compute_unit_config = ComputeUnitConfig {
1036        compute_unit_price: additional_cli_config.compute_unit_price,
1037        compute_unit_limit: ComputeUnitLimit::Simulated,
1038    };
1039    let simulate_messages = |message_prototypes: Vec<Vec<Instruction>>| {
1040        let mut messages = Vec::with_capacity(message_prototypes.len());
1041        for instructions in message_prototypes.into_iter() {
1042            let mut message = Message::new_with_blockhash(
1043                &instructions.with_compute_unit_config(&compute_unit_config),
1044                Some(&payer_pubkey),
1045                &blockhash,
1046            );
1047            simulate_and_update_compute_unit_limit(
1048                &ComputeUnitLimit::Simulated,
1049                &rpc_client,
1050                &mut message,
1051            )?;
1052            messages.push(message);
1053        }
1054        Ok::<Vec<solana_message::Message>, Box<dyn std::error::Error>>(messages)
1055    };
1056    let initial_messages = simulate_messages(initial_messages)?;
1057    let write_messages = simulate_messages(write_messages)?;
1058    let final_messages = simulate_messages(final_messages)?;
1059
1060    let mut fee = Saturating(0);
1061    for message in initial_messages.iter() {
1062        fee += rpc_client.get_fee_for_message(message)?;
1063    }
1064    for message in final_messages.iter() {
1065        fee += rpc_client.get_fee_for_message(message)?;
1066    }
1067    // Assume all write messages cost the same
1068    if let Some(message) = write_messages.first() {
1069        fee += rpc_client
1070            .get_fee_for_message(message)?
1071            .saturating_mul(write_messages.len() as u64);
1072    }
1073    check_account_for_spend_and_fee_with_commitment(
1074        &rpc_client,
1075        &payer_pubkey,
1076        balance_needed,
1077        fee.0,
1078        config.commitment,
1079    )?;
1080
1081    let send_or_return_message = |message: Message, signers: &[&dyn Signer]| {
1082        let mut tx = Transaction::new_unsigned(message);
1083        tx.try_sign(signers, blockhash)?;
1084        if additional_cli_config.sign_only {
1085            return_signers_with_config(
1086                &tx,
1087                &config.output_format,
1088                &ReturnSignersConfig {
1089                    dump_transaction_message: additional_cli_config.dump_transaction_message,
1090                },
1091            )
1092        } else {
1093            rpc_client
1094                .send_and_confirm_transaction_with_spinner_and_config(
1095                    &tx,
1096                    config.commitment,
1097                    config.send_transaction_config,
1098                )
1099                .map_err(|err| format!("Failed to send message: {err}").into())
1100                .map(|_| String::new())
1101        }
1102    };
1103
1104    for message in initial_messages.into_iter() {
1105        let signers: &[_] = if message.account_keys.contains(&system_program::id()) {
1106            // The initial message that creates and initializes the account
1107            // has up to 3 signatures (payer, program, and authority).
1108            if let Some(initial_signer) = program_signer {
1109                &[
1110                    config.signers[0],
1111                    initial_signer,
1112                    config.signers[*auth_signer_index],
1113                ]
1114            } else {
1115                return Err("Buffer account not created yet, must provide a key pair".into());
1116            }
1117        } else {
1118            // All other messages have up to 2 signatures (payer, and authority).
1119            &[config.signers[0], config.signers[*auth_signer_index]]
1120        };
1121        let result = send_or_return_message(message, signers)?;
1122        if additional_cli_config.sign_only {
1123            return Ok(result);
1124        }
1125    }
1126
1127    if !write_messages.is_empty() {
1128        let connection_cache = if config.use_quic {
1129            ConnectionCache::new_quic("connection_cache_cli_program_v4_quic", 1)
1130        } else {
1131            ConnectionCache::with_udp("connection_cache_cli_program_v4_udp", 1)
1132        };
1133        let transaction_errors = match connection_cache {
1134            ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
1135                rpc_client.clone(),
1136                &config.websocket_url,
1137                TpuClientConfig::default(),
1138                cache,
1139            )?
1140            .send_and_confirm_messages_with_spinner(
1141                &write_messages,
1142                &[config.signers[0], config.signers[*auth_signer_index]],
1143            ),
1144            ConnectionCache::Quic(cache) => {
1145                let tpu_client_fut =
1146                    solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
1147                        rpc_client.get_inner_client().clone(),
1148                        &config.websocket_url,
1149                        solana_client::tpu_client::TpuClientConfig::default(),
1150                        cache,
1151                    );
1152                let tpu_client = (!additional_cli_config.use_rpc).then(|| {
1153                    rpc_client
1154                        .runtime()
1155                        .block_on(tpu_client_fut)
1156                        .expect("Should return a valid tpu client")
1157                });
1158
1159                send_and_confirm_transactions_in_parallel_blocking_v2(
1160                    rpc_client.clone(),
1161                    tpu_client,
1162                    &write_messages,
1163                    &[config.signers[0], config.signers[*auth_signer_index]],
1164                    SendAndConfirmConfigV2 {
1165                        resign_txs_count: Some(5),
1166                        with_spinner: true,
1167                        rpc_send_transaction_config: config.send_transaction_config,
1168                    },
1169                )
1170            }
1171        }
1172        .map_err(|err| format!("Data writes to account failed: {err}"))?
1173        .into_iter()
1174        .flatten()
1175        .collect::<Vec<_>>();
1176
1177        if !transaction_errors.is_empty() {
1178            for transaction_error in &transaction_errors {
1179                error!("{:?}", transaction_error);
1180            }
1181            return Err(format!("{} write transactions failed", transaction_errors.len()).into());
1182        }
1183    }
1184
1185    for message in final_messages.into_iter() {
1186        let result = send_or_return_message(
1187            message,
1188            &[config.signers[0], config.signers[*auth_signer_index]],
1189        )?;
1190        if additional_cli_config.sign_only {
1191            return Ok(result);
1192        }
1193    }
1194
1195    Ok(ok_result)
1196}
1197
1198fn build_retract_instruction(
1199    account: &Account,
1200    buffer_address: &Pubkey,
1201    authority: &Pubkey,
1202) -> Result<Option<Instruction>, Box<dyn std::error::Error>> {
1203    if !loader_v4::check_id(&account.owner) {
1204        return Err("Buffer account passed is already in use by another program".into());
1205    }
1206
1207    if let Ok(LoaderV4State {
1208        slot: _,
1209        authority_address_or_next_version,
1210        status,
1211    }) = solana_loader_v4_program::get_state(&account.data)
1212    {
1213        if authority != authority_address_or_next_version {
1214            return Err(
1215                "Program authority does not match with the provided authority address".into(),
1216            );
1217        }
1218
1219        match status {
1220            Retracted => Ok(None),
1221            LoaderV4Status::Deployed => Ok(Some(instruction::retract(buffer_address, authority))),
1222            LoaderV4Status::Finalized => Err("Program is immutable".into()),
1223        }
1224    } else {
1225        Err("Program account's state could not be deserialized".into())
1226    }
1227}
1228
1229fn build_set_program_length_instructions(
1230    rpc_client: Arc<RpcClient>,
1231    config: &CliConfig,
1232    auth_signer_index: &SignerIndex,
1233    account: &Account,
1234    buffer_address: &Pubkey,
1235    program_data_length: u32,
1236) -> Result<(Vec<Instruction>, u64), Box<dyn std::error::Error>> {
1237    if !loader_v4::check_id(&account.owner) {
1238        return Err("Buffer account passed is already in use by another program".into());
1239    }
1240
1241    let payer_pubkey = config.signers[0].pubkey();
1242    let authority_pubkey = config.signers[*auth_signer_index].pubkey();
1243
1244    if !account.data.is_empty() {
1245        if let Ok(LoaderV4State {
1246            slot: _,
1247            authority_address_or_next_version,
1248            status,
1249        }) = solana_loader_v4_program::get_state(&account.data)
1250        {
1251            if &authority_pubkey != authority_address_or_next_version {
1252                return Err(
1253                    "Program authority does not match with the provided authority address".into(),
1254                );
1255            }
1256
1257            if matches!(status, LoaderV4Status::Finalized) {
1258                return Err("Program is immutable".into());
1259            }
1260        } else {
1261            return Err("Program account's state could not be deserialized".into());
1262        }
1263    }
1264
1265    let set_program_length_instruction = instruction::set_program_length(
1266        buffer_address,
1267        &authority_pubkey,
1268        program_data_length,
1269        &payer_pubkey,
1270    );
1271
1272    let expected_account_data_len =
1273        LoaderV4State::program_data_offset().saturating_add(program_data_length as usize);
1274
1275    let lamports_required =
1276        rpc_client.get_minimum_balance_for_rent_exemption(expected_account_data_len)?;
1277
1278    match account.data.len().cmp(&expected_account_data_len) {
1279        Ordering::Less => {
1280            if account.lamports < lamports_required {
1281                let extra_lamports_required = lamports_required.saturating_sub(account.lamports);
1282                Ok((
1283                    vec![
1284                        system_instruction::transfer(
1285                            &payer_pubkey,
1286                            buffer_address,
1287                            extra_lamports_required,
1288                        ),
1289                        set_program_length_instruction,
1290                    ],
1291                    extra_lamports_required,
1292                ))
1293            } else {
1294                Ok((vec![set_program_length_instruction], 0))
1295            }
1296        }
1297        Ordering::Equal => {
1298            if account.lamports < lamports_required {
1299                return Err("Program account has less lamports than required for its size".into());
1300            }
1301            Ok((vec![], 0))
1302        }
1303        Ordering::Greater => {
1304            if account.lamports < lamports_required {
1305                return Err("Program account has less lamports than required for its size".into());
1306            }
1307            Ok((vec![set_program_length_instruction], 0))
1308        }
1309    }
1310}
1311
1312fn get_accounts_with_filter(
1313    rpc_client: Arc<RpcClient>,
1314    _config: &CliConfig,
1315    filters: Vec<RpcFilterType>,
1316    length: usize,
1317) -> Result<Vec<(Pubkey, Account)>, Box<dyn std::error::Error>> {
1318    let results = rpc_client.get_program_accounts_with_config(
1319        &loader_v4::id(),
1320        RpcProgramAccountsConfig {
1321            filters: Some(filters),
1322            account_config: RpcAccountInfoConfig {
1323                encoding: Some(UiAccountEncoding::Base64),
1324                data_slice: Some(UiDataSliceConfig { offset: 0, length }),
1325                ..RpcAccountInfoConfig::default()
1326            },
1327            ..RpcProgramAccountsConfig::default()
1328        },
1329    )?;
1330    Ok(results)
1331}
1332
1333fn get_programs(
1334    rpc_client: Arc<RpcClient>,
1335    config: &CliConfig,
1336    authority_pubkey: Option<Pubkey>,
1337) -> Result<CliProgramsV4, Box<dyn std::error::Error>> {
1338    let filters = if let Some(authority_pubkey) = authority_pubkey {
1339        vec![
1340            (RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1341                size_of::<u64>(),
1342                authority_pubkey.as_ref(),
1343            ))),
1344        ]
1345    } else {
1346        vec![]
1347    };
1348
1349    let results = get_accounts_with_filter(
1350        rpc_client,
1351        config,
1352        filters,
1353        LoaderV4State::program_data_offset(),
1354    )?;
1355
1356    let mut programs = vec![];
1357    for (program, account) in results.iter() {
1358        if let Ok(state) = solana_loader_v4_program::get_state(&account.data) {
1359            let status = match state.status {
1360                LoaderV4Status::Retracted => "retracted",
1361                LoaderV4Status::Deployed => "deployed",
1362                LoaderV4Status::Finalized => "finalized",
1363            };
1364            programs.push(CliProgramV4 {
1365                program_id: program.to_string(),
1366                owner: account.owner.to_string(),
1367                authority: state.authority_address_or_next_version.to_string(),
1368                last_deploy_slot: state.slot,
1369                status: status.to_string(),
1370                data_len: account
1371                    .data
1372                    .len()
1373                    .saturating_sub(LoaderV4State::program_data_offset()),
1374            });
1375        } else {
1376            return Err(format!("Error parsing Program account {program}").into());
1377        }
1378    }
1379    Ok(CliProgramsV4 { programs })
1380}
1381
1382#[cfg(test)]
1383mod tests {
1384    use {
1385        super::*,
1386        crate::{clap_app::get_clap_app, cli::parse_command},
1387        serde_json::json,
1388        solana_keypair::{keypair_from_seed, read_keypair_file, write_keypair_file, Keypair},
1389        solana_rpc_client_api::{
1390            request::RpcRequest,
1391            response::{Response, RpcResponseContext},
1392        },
1393        std::collections::HashMap,
1394    };
1395
1396    fn program_authority() -> solana_keypair::Keypair {
1397        keypair_from_seed(&[3u8; 32]).unwrap()
1398    }
1399
1400    fn rpc_client_no_existing_program() -> RpcClient {
1401        RpcClient::new_mock("succeeds".to_string())
1402    }
1403
1404    fn rpc_client_with_program_data(data: &str, loader_is_owner: bool) -> RpcClient {
1405        let owner = if loader_is_owner {
1406            "LoaderV411111111111111111111111111111111111"
1407        } else {
1408            "Vote111111111111111111111111111111111111111"
1409        };
1410        let account_info_response = json!(Response {
1411            context: RpcResponseContext {
1412                slot: 1,
1413                api_version: None
1414            },
1415            value: json!({
1416                "data": [data, "base64"],
1417                "lamports": 42,
1418                "owner": owner,
1419                "executable": true,
1420                "rentEpoch": 1,
1421            }),
1422        });
1423        let mut mocks = HashMap::new();
1424        mocks.insert(RpcRequest::GetAccountInfo, account_info_response);
1425        RpcClient::new_mock_with_mocks("".to_string(), mocks)
1426    }
1427
1428    fn rpc_client_wrong_account_owner() -> RpcClient {
1429        rpc_client_with_program_data(
1430            "AAAAAAAAAADtSSjGKNHCxurpAziQWZVhKVknOlxj+TY2wUYUrIc30QAAAAAAAAAA",
1431            false,
1432        )
1433    }
1434
1435    fn rpc_client_wrong_authority() -> RpcClient {
1436        rpc_client_with_program_data(
1437            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
1438            true,
1439        )
1440    }
1441
1442    fn rpc_client_with_program_retracted() -> RpcClient {
1443        rpc_client_with_program_data(
1444            "AAAAAAAAAADtSSjGKNHCxurpAziQWZVhKVknOlxj+TY2wUYUrIc30QAAAAAAAAAA",
1445            true,
1446        )
1447    }
1448
1449    fn rpc_client_with_program_deployed() -> RpcClient {
1450        rpc_client_with_program_data(
1451            "AAAAAAAAAADtSSjGKNHCxurpAziQWZVhKVknOlxj+TY2wUYUrIc30QEAAAAAAAAA",
1452            true,
1453        )
1454    }
1455
1456    fn rpc_client_with_program_finalized() -> RpcClient {
1457        rpc_client_with_program_data(
1458            "AAAAAAAAAADtSSjGKNHCxurpAziQWZVhKVknOlxj+TY2wUYUrIc30QIAAAAAAAAA",
1459            true,
1460        )
1461    }
1462
1463    #[test]
1464    fn test_deploy() {
1465        let mut config = CliConfig::default();
1466        let mut program_data = Vec::new();
1467        let mut file = File::open("tests/fixtures/noop.so").unwrap();
1468        file.read_to_end(&mut program_data).unwrap();
1469
1470        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1471        let program_signer = keypair_from_seed(&[2u8; 32]).unwrap();
1472        let authority_signer = program_authority();
1473
1474        config.signers.push(&payer);
1475        config.signers.push(&program_signer);
1476        config.signers.push(&authority_signer);
1477
1478        assert!(process_deploy_program(
1479            Arc::new(rpc_client_no_existing_program()),
1480            &config,
1481            &AdditionalCliConfig::default(),
1482            &program_signer.pubkey(),
1483            None,
1484            Some(&1),
1485            &2,
1486            &program_data,
1487            None..None,
1488        )
1489        .is_ok());
1490
1491        assert!(process_deploy_program(
1492            Arc::new(rpc_client_wrong_account_owner()),
1493            &config,
1494            &AdditionalCliConfig::default(),
1495            &program_signer.pubkey(),
1496            None,
1497            Some(&1),
1498            &2,
1499            &program_data,
1500            None..None,
1501        )
1502        .is_err());
1503
1504        assert!(process_deploy_program(
1505            Arc::new(rpc_client_with_program_deployed()),
1506            &config,
1507            &AdditionalCliConfig::default(),
1508            &program_signer.pubkey(),
1509            None,
1510            Some(&1),
1511            &2,
1512            &program_data,
1513            None..None,
1514        )
1515        .is_err());
1516    }
1517
1518    #[test]
1519    fn test_redeploy() {
1520        let mut config = CliConfig::default();
1521        let mut program_data = Vec::new();
1522        let mut file = File::open("tests/fixtures/noop.so").unwrap();
1523        file.read_to_end(&mut program_data).unwrap();
1524
1525        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1526        let program_address = Pubkey::new_unique();
1527        let authority_signer = program_authority();
1528
1529        config.signers.push(&payer);
1530        config.signers.push(&authority_signer);
1531
1532        // Redeploying a non-existent program should fail
1533        assert!(process_deploy_program(
1534            Arc::new(rpc_client_no_existing_program()),
1535            &config,
1536            &AdditionalCliConfig::default(),
1537            &program_address,
1538            None,
1539            None,
1540            &1,
1541            &program_data,
1542            None..None,
1543        )
1544        .is_err());
1545
1546        assert!(process_deploy_program(
1547            Arc::new(rpc_client_with_program_retracted()),
1548            &config,
1549            &AdditionalCliConfig::default(),
1550            &program_address,
1551            None,
1552            None,
1553            &1,
1554            &program_data,
1555            None..None,
1556        )
1557        .is_ok());
1558
1559        assert!(process_deploy_program(
1560            Arc::new(rpc_client_with_program_deployed()),
1561            &config,
1562            &AdditionalCliConfig::default(),
1563            &program_address,
1564            None,
1565            None,
1566            &1,
1567            &program_data,
1568            None..None,
1569        )
1570        .is_ok());
1571
1572        assert!(process_deploy_program(
1573            Arc::new(rpc_client_with_program_finalized()),
1574            &config,
1575            &AdditionalCliConfig::default(),
1576            &program_address,
1577            None,
1578            None,
1579            &1,
1580            &program_data,
1581            None..None,
1582        )
1583        .is_err());
1584
1585        assert!(process_deploy_program(
1586            Arc::new(rpc_client_wrong_account_owner()),
1587            &config,
1588            &AdditionalCliConfig::default(),
1589            &program_address,
1590            None,
1591            None,
1592            &1,
1593            &program_data,
1594            None..None,
1595        )
1596        .is_err());
1597
1598        assert!(process_deploy_program(
1599            Arc::new(rpc_client_wrong_authority()),
1600            &config,
1601            &AdditionalCliConfig::default(),
1602            &program_address,
1603            None,
1604            None,
1605            &1,
1606            &program_data,
1607            None..None,
1608        )
1609        .is_err());
1610    }
1611
1612    #[test]
1613    fn test_redeploy_from_source() {
1614        let mut config = CliConfig::default();
1615        let mut program_data = Vec::new();
1616        let mut file = File::open("tests/fixtures/noop.so").unwrap();
1617        file.read_to_end(&mut program_data).unwrap();
1618
1619        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1620        let buffer_signer = keypair_from_seed(&[2u8; 32]).unwrap();
1621        let program_address = Pubkey::new_unique();
1622        let authority_signer = program_authority();
1623
1624        config.signers.push(&payer);
1625        config.signers.push(&buffer_signer);
1626        config.signers.push(&authority_signer);
1627
1628        // Redeploying a non-existent program should fail
1629        assert!(process_deploy_program(
1630            Arc::new(rpc_client_no_existing_program()),
1631            &config,
1632            &AdditionalCliConfig::default(),
1633            &program_address,
1634            Some(&buffer_signer.pubkey()),
1635            Some(&1),
1636            &2,
1637            &program_data,
1638            None..None,
1639        )
1640        .is_err());
1641
1642        assert!(process_deploy_program(
1643            Arc::new(rpc_client_wrong_account_owner()),
1644            &config,
1645            &AdditionalCliConfig::default(),
1646            &program_address,
1647            Some(&buffer_signer.pubkey()),
1648            Some(&1),
1649            &2,
1650            &program_data,
1651            None..None,
1652        )
1653        .is_err());
1654
1655        assert!(process_deploy_program(
1656            Arc::new(rpc_client_wrong_authority()),
1657            &config,
1658            &AdditionalCliConfig::default(),
1659            &program_address,
1660            Some(&buffer_signer.pubkey()),
1661            Some(&1),
1662            &2,
1663            &program_data,
1664            None..None,
1665        )
1666        .is_err());
1667    }
1668
1669    #[test]
1670    fn test_close() {
1671        let mut config = CliConfig::default();
1672
1673        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1674        let program_signer = keypair_from_seed(&[2u8; 32]).unwrap();
1675        let authority_signer = program_authority();
1676
1677        config.signers.push(&payer);
1678        config.signers.push(&authority_signer);
1679
1680        assert!(process_close_program(
1681            Arc::new(rpc_client_no_existing_program()),
1682            &config,
1683            &AdditionalCliConfig::default(),
1684            &1,
1685            &program_signer.pubkey(),
1686        )
1687        .is_err());
1688
1689        assert!(process_close_program(
1690            Arc::new(rpc_client_with_program_retracted()),
1691            &config,
1692            &AdditionalCliConfig::default(),
1693            &1,
1694            &program_signer.pubkey(),
1695        )
1696        .is_ok());
1697
1698        assert!(process_close_program(
1699            Arc::new(rpc_client_with_program_deployed()),
1700            &config,
1701            &AdditionalCliConfig::default(),
1702            &1,
1703            &program_signer.pubkey(),
1704        )
1705        .is_ok());
1706
1707        assert!(process_close_program(
1708            Arc::new(rpc_client_with_program_finalized()),
1709            &config,
1710            &AdditionalCliConfig::default(),
1711            &1,
1712            &program_signer.pubkey(),
1713        )
1714        .is_err());
1715
1716        assert!(process_close_program(
1717            Arc::new(rpc_client_wrong_account_owner()),
1718            &config,
1719            &AdditionalCliConfig::default(),
1720            &1,
1721            &program_signer.pubkey(),
1722        )
1723        .is_err());
1724
1725        assert!(process_close_program(
1726            Arc::new(rpc_client_wrong_authority()),
1727            &config,
1728            &AdditionalCliConfig::default(),
1729            &1,
1730            &program_signer.pubkey(),
1731        )
1732        .is_err());
1733    }
1734
1735    #[test]
1736    fn test_transfer_authority() {
1737        let mut config = CliConfig::default();
1738
1739        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1740        let program_signer = keypair_from_seed(&[2u8; 32]).unwrap();
1741        let authority_signer = program_authority();
1742        let new_authority_signer = program_authority();
1743
1744        config.signers.push(&payer);
1745        config.signers.push(&authority_signer);
1746        config.signers.push(&new_authority_signer);
1747
1748        assert!(process_transfer_authority_of_program(
1749            Arc::new(rpc_client_with_program_deployed()),
1750            &config,
1751            &AdditionalCliConfig::default(),
1752            &1,
1753            &2,
1754            &program_signer.pubkey(),
1755        )
1756        .is_ok());
1757    }
1758
1759    #[test]
1760    fn test_finalize() {
1761        let mut config = CliConfig::default();
1762
1763        let payer = keypair_from_seed(&[1u8; 32]).unwrap();
1764        let program_signer = keypair_from_seed(&[2u8; 32]).unwrap();
1765        let authority_signer = program_authority();
1766        let next_version_signer = keypair_from_seed(&[4u8; 32]).unwrap();
1767
1768        config.signers.push(&payer);
1769        config.signers.push(&authority_signer);
1770        config.signers.push(&next_version_signer);
1771
1772        assert!(process_finalize_program(
1773            Arc::new(rpc_client_with_program_deployed()),
1774            &config,
1775            &AdditionalCliConfig::default(),
1776            &1,
1777            &2,
1778            &program_signer.pubkey(),
1779        )
1780        .is_ok());
1781    }
1782
1783    fn make_tmp_path(name: &str) -> String {
1784        let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
1785        let keypair = Keypair::new();
1786
1787        let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
1788
1789        // whack any possible collision
1790        let _ignored = std::fs::remove_dir_all(&path);
1791        // whack any possible collision
1792        let _ignored = std::fs::remove_file(&path);
1793
1794        path
1795    }
1796
1797    #[test]
1798    #[allow(clippy::cognitive_complexity)]
1799    fn test_cli_parse_deploy() {
1800        let test_commands = get_clap_app("test", "desc", "version");
1801
1802        let default_keypair = Keypair::new();
1803        let keypair_file = make_tmp_path("keypair_file");
1804        write_keypair_file(&default_keypair, &keypair_file).unwrap();
1805        let default_signer = DefaultSigner::new("", &keypair_file);
1806
1807        let program_keypair = Keypair::new();
1808        let program_keypair_file = make_tmp_path("program_keypair_file");
1809        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
1810
1811        let buffer_keypair = Keypair::new();
1812        let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
1813        write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
1814
1815        let authority_keypair = Keypair::new();
1816        let authority_keypair_file = make_tmp_path("authority_keypair_file");
1817        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
1818
1819        let test_command = test_commands.clone().get_matches_from(vec![
1820            "test",
1821            "program-v4",
1822            "deploy",
1823            "/Users/test/program.so",
1824            "--program-keypair",
1825            &program_keypair_file,
1826        ]);
1827        assert_eq!(
1828            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1829            CliCommandInfo {
1830                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1831                    additional_cli_config: AdditionalCliConfig::default(),
1832                    program_address: program_keypair.pubkey(),
1833                    buffer_address: None,
1834                    upload_signer_index: Some(1),
1835                    authority_signer_index: 0,
1836                    path_to_elf: Some("/Users/test/program.so".to_string()),
1837                    upload_range: None..None,
1838                }),
1839                signers: vec![
1840                    Box::new(read_keypair_file(&keypair_file).unwrap()),
1841                    Box::new(read_keypair_file(&program_keypair_file).unwrap()),
1842                ],
1843            }
1844        );
1845
1846        let test_command = test_commands.clone().get_matches_from(vec![
1847            "test",
1848            "program-v4",
1849            "deploy",
1850            "/Users/test/program.so",
1851            "--program-keypair",
1852            &program_keypair_file,
1853            "--authority",
1854            &authority_keypair_file,
1855        ]);
1856        assert_eq!(
1857            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1858            CliCommandInfo {
1859                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1860                    additional_cli_config: AdditionalCliConfig::default(),
1861                    program_address: program_keypair.pubkey(),
1862                    buffer_address: None,
1863                    upload_signer_index: Some(1),
1864                    authority_signer_index: 2,
1865                    path_to_elf: Some("/Users/test/program.so".to_string()),
1866                    upload_range: None..None,
1867                }),
1868                signers: vec![
1869                    Box::new(read_keypair_file(&keypair_file).unwrap()),
1870                    Box::new(read_keypair_file(&program_keypair_file).unwrap()),
1871                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
1872                ],
1873            }
1874        );
1875
1876        let test_command = test_commands.clone().get_matches_from(vec![
1877            "test",
1878            "program-v4",
1879            "deploy",
1880            "/Users/test/program.so",
1881            "--program-id",
1882            &program_keypair_file,
1883            "--authority",
1884            &authority_keypair_file,
1885        ]);
1886        assert_eq!(
1887            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1888            CliCommandInfo {
1889                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1890                    additional_cli_config: AdditionalCliConfig::default(),
1891                    program_address: program_keypair.pubkey(),
1892                    buffer_address: None,
1893                    upload_signer_index: None,
1894                    authority_signer_index: 1,
1895                    path_to_elf: Some("/Users/test/program.so".to_string()),
1896                    upload_range: None..None,
1897                }),
1898                signers: vec![
1899                    Box::new(read_keypair_file(&keypair_file).unwrap()),
1900                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
1901                ],
1902            }
1903        );
1904
1905        let test_command = test_commands.clone().get_matches_from(vec![
1906            "test",
1907            "program-v4",
1908            "deploy",
1909            "/Users/test/program.so",
1910            "--program-id",
1911            &program_keypair_file,
1912            "--buffer",
1913            &buffer_keypair_file,
1914            "--authority",
1915            &authority_keypair_file,
1916        ]);
1917        assert_eq!(
1918            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1919            CliCommandInfo {
1920                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1921                    additional_cli_config: AdditionalCliConfig::default(),
1922                    program_address: program_keypair.pubkey(),
1923                    buffer_address: Some(buffer_keypair.pubkey()),
1924                    upload_signer_index: Some(1),
1925                    authority_signer_index: 2,
1926                    path_to_elf: Some("/Users/test/program.so".to_string()),
1927                    upload_range: None..None,
1928                }),
1929                signers: vec![
1930                    Box::new(read_keypair_file(&keypair_file).unwrap()),
1931                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
1932                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
1933                ],
1934            }
1935        );
1936
1937        let test_command = test_commands.clone().get_matches_from(vec![
1938            "test",
1939            "program-v4",
1940            "deploy",
1941            "--program-id",
1942            &program_keypair_file,
1943            "--buffer",
1944            &buffer_keypair_file,
1945            "--authority",
1946            &authority_keypair_file,
1947        ]);
1948        assert_eq!(
1949            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1950            CliCommandInfo {
1951                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1952                    additional_cli_config: AdditionalCliConfig::default(),
1953                    program_address: program_keypair.pubkey(),
1954                    buffer_address: Some(buffer_keypair.pubkey()),
1955                    upload_signer_index: Some(1),
1956                    authority_signer_index: 2,
1957                    path_to_elf: None,
1958                    upload_range: None..None,
1959                }),
1960                signers: vec![
1961                    Box::new(read_keypair_file(&keypair_file).unwrap()),
1962                    Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
1963                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
1964                ],
1965            }
1966        );
1967
1968        let test_command = test_commands.clone().get_matches_from(vec![
1969            "test",
1970            "program-v4",
1971            "deploy",
1972            "/Users/test/program.so",
1973            "--start-offset",
1974            "16",
1975            "--end-offset",
1976            "32",
1977            "--program-id",
1978            &program_keypair_file,
1979            "--use-rpc",
1980            "--with-compute-unit-price",
1981            "1",
1982        ]);
1983        assert_eq!(
1984            parse_command(&test_command, &default_signer, &mut None).unwrap(),
1985            CliCommandInfo {
1986                command: CliCommand::ProgramV4(ProgramV4CliCommand::Deploy {
1987                    additional_cli_config: AdditionalCliConfig {
1988                        use_rpc: true,
1989                        sign_only: false,
1990                        dump_transaction_message: false,
1991                        blockhash_query: BlockhashQuery::default(),
1992                        compute_unit_price: Some(1),
1993                    },
1994                    program_address: program_keypair.pubkey(),
1995                    buffer_address: None,
1996                    upload_signer_index: None,
1997                    authority_signer_index: 0,
1998                    path_to_elf: Some("/Users/test/program.so".to_string()),
1999                    upload_range: Some(16)..Some(32),
2000                }),
2001                signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
2002            }
2003        );
2004    }
2005
2006    #[test]
2007    #[allow(clippy::cognitive_complexity)]
2008    fn test_cli_parse_close() {
2009        let test_commands = get_clap_app("test", "desc", "version");
2010
2011        let default_keypair = Keypair::new();
2012        let keypair_file = make_tmp_path("keypair_file");
2013        write_keypair_file(&default_keypair, &keypair_file).unwrap();
2014        let default_signer = DefaultSigner::new("", &keypair_file);
2015
2016        let program_keypair = Keypair::new();
2017        let program_keypair_file = make_tmp_path("program_keypair_file");
2018        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
2019
2020        let authority_keypair = Keypair::new();
2021        let authority_keypair_file = make_tmp_path("authority_keypair_file");
2022        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
2023
2024        let test_command = test_commands.clone().get_matches_from(vec![
2025            "test",
2026            "program-v4",
2027            "close",
2028            "--program-id",
2029            &program_keypair_file,
2030            "--authority",
2031            &authority_keypair_file,
2032        ]);
2033        assert_eq!(
2034            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2035            CliCommandInfo {
2036                command: CliCommand::ProgramV4(ProgramV4CliCommand::Close {
2037                    additional_cli_config: AdditionalCliConfig::default(),
2038                    program_address: program_keypair.pubkey(),
2039                    authority_signer_index: 1,
2040                }),
2041                signers: vec![
2042                    Box::new(read_keypair_file(&keypair_file).unwrap()),
2043                    Box::new(read_keypair_file(&authority_keypair_file).unwrap())
2044                ],
2045            }
2046        );
2047    }
2048
2049    #[test]
2050    #[allow(clippy::cognitive_complexity)]
2051    fn test_cli_parse_transfer_authority() {
2052        let test_commands = get_clap_app("test", "desc", "version");
2053
2054        let default_keypair = Keypair::new();
2055        let keypair_file = make_tmp_path("keypair_file");
2056        write_keypair_file(&default_keypair, &keypair_file).unwrap();
2057        let default_signer = DefaultSigner::new("", &keypair_file);
2058
2059        let program_keypair = Keypair::new();
2060        let program_keypair_file = make_tmp_path("program_keypair_file");
2061        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
2062
2063        let authority_keypair = Keypair::new();
2064        let authority_keypair_file = make_tmp_path("authority_keypair_file");
2065        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
2066
2067        let new_authority_keypair = Keypair::new();
2068        let new_authority_keypair_file = make_tmp_path("new_authority_keypair_file");
2069        write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
2070
2071        let test_command = test_commands.clone().get_matches_from(vec![
2072            "test",
2073            "program-v4",
2074            "transfer-authority",
2075            "--program-id",
2076            &program_keypair_file,
2077            "--authority",
2078            &authority_keypair_file,
2079            "--new-authority",
2080            &new_authority_keypair_file,
2081        ]);
2082        assert_eq!(
2083            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2084            CliCommandInfo {
2085                command: CliCommand::ProgramV4(ProgramV4CliCommand::TransferAuthority {
2086                    additional_cli_config: AdditionalCliConfig::default(),
2087                    program_address: program_keypair.pubkey(),
2088                    authority_signer_index: 1,
2089                    new_authority_signer_index: 2,
2090                }),
2091                signers: vec![
2092                    Box::new(read_keypair_file(&keypair_file).unwrap()),
2093                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
2094                    Box::new(read_keypair_file(&new_authority_keypair_file).unwrap()),
2095                ],
2096            }
2097        );
2098    }
2099
2100    #[test]
2101    #[allow(clippy::cognitive_complexity)]
2102    fn test_cli_parse_finalize() {
2103        let test_commands = get_clap_app("test", "desc", "version");
2104
2105        let default_keypair = Keypair::new();
2106        let keypair_file = make_tmp_path("keypair_file");
2107        write_keypair_file(&default_keypair, &keypair_file).unwrap();
2108        let default_signer = DefaultSigner::new("", &keypair_file);
2109
2110        let program_keypair = Keypair::new();
2111        let program_keypair_file = make_tmp_path("program_keypair_file");
2112        write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
2113
2114        let authority_keypair = Keypair::new();
2115        let authority_keypair_file = make_tmp_path("authority_keypair_file");
2116        write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
2117
2118        let next_version_keypair = Keypair::new();
2119        let next_version_keypair_file = make_tmp_path("next_version_keypair_file");
2120        write_keypair_file(&next_version_keypair, &next_version_keypair_file).unwrap();
2121
2122        let test_command = test_commands.clone().get_matches_from(vec![
2123            "test",
2124            "program-v4",
2125            "finalize",
2126            "--program-id",
2127            &program_keypair_file,
2128            "--authority",
2129            &authority_keypair_file,
2130            "--next-version",
2131            &next_version_keypair_file,
2132        ]);
2133        assert_eq!(
2134            parse_command(&test_command, &default_signer, &mut None).unwrap(),
2135            CliCommandInfo {
2136                command: CliCommand::ProgramV4(ProgramV4CliCommand::Finalize {
2137                    additional_cli_config: AdditionalCliConfig::default(),
2138                    program_address: program_keypair.pubkey(),
2139                    authority_signer_index: 1,
2140                    next_version_signer_index: 2,
2141                }),
2142                signers: vec![
2143                    Box::new(read_keypair_file(&keypair_file).unwrap()),
2144                    Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
2145                    Box::new(read_keypair_file(&next_version_keypair_file).unwrap()),
2146                ],
2147            }
2148        );
2149    }
2150}