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