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 agave_feature_set::{FeatureSet, FEATURE_NAMES},
15 bip39::{Language, Mnemonic, MnemonicType, Seed},
16 clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
17 log::*,
18 solana_account::{state_traits::StateMut, Account},
19 solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
20 solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1,
21 solana_clap_utils::{
22 self,
23 compute_budget::{compute_unit_price_arg, ComputeUnitLimit},
24 fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
25 hidden_unless_forced,
26 input_parsers::*,
27 input_validators::*,
28 keypair::*,
29 offline::{OfflineArgs, DUMP_TRANSACTION_MESSAGE, SIGN_ONLY_ARG},
30 },
31 solana_cli_output::{
32 return_signers_with_config, CliProgram, CliProgramAccountType, CliProgramAuthority,
33 CliProgramBuffer, CliProgramId, CliUpgradeableBuffer, CliUpgradeableBuffers,
34 CliUpgradeableProgram, CliUpgradeableProgramClosed, CliUpgradeableProgramExtended,
35 CliUpgradeableProgramMigrated, CliUpgradeablePrograms, ReturnSignersConfig,
36 },
37 solana_client::{
38 connection_cache::ConnectionCache,
39 send_and_confirm_transactions_in_parallel::{
40 send_and_confirm_transactions_in_parallel_blocking_v2, SendAndConfirmConfigV2,
41 },
42 tpu_client::{TpuClient, TpuClientConfig},
43 },
44 solana_commitment_config::CommitmentConfig,
45 solana_compute_budget::compute_budget::ComputeBudget,
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 && feature_set.is_active(&agave_feature_set::enable_loader_v4::id())
1391 {
1392 warn!("Loader-v4 is available now. Please migrate your program.");
1393 }
1394
1395 let (program_data, program_len, buffer_program_data) =
1396 if let Some(program_location) = program_location {
1397 let program_data = read_and_verify_elf(program_location, feature_set)?;
1398 let program_len = program_data.len();
1399
1400 let buffer_program_data = if buffer_provided {
1402 fetch_buffer_program_data(
1403 &rpc_client,
1404 config,
1405 Some(program_len),
1406 buffer_pubkey,
1407 upgrade_authority_signer.pubkey(),
1408 )?
1409 } else {
1410 None
1411 };
1412
1413 (program_data, program_len, buffer_program_data)
1414 } else if buffer_provided {
1415 let buffer_program_data = fetch_verified_buffer_program_data(
1416 &rpc_client,
1417 config,
1418 buffer_pubkey,
1419 upgrade_authority_signer.pubkey(),
1420 feature_set,
1421 )?;
1422
1423 (vec![], buffer_program_data.len(), Some(buffer_program_data))
1424 } else {
1425 return Err("Program location required if buffer not supplied".into());
1426 };
1427
1428 let program_data_max_len = if let Some(len) = max_len {
1429 if program_len > len {
1430 return Err(
1431 "Max length specified not large enough to accommodate desired program".into(),
1432 );
1433 }
1434 len
1435 } else {
1436 program_len
1437 };
1438
1439 let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1440 UpgradeableLoaderState::size_of_programdata(program_data_max_len),
1441 )?;
1442
1443 let result = if do_initial_deploy {
1444 if program_signer.is_none() {
1445 return Err(
1446 "Initial deployments require a keypair be provided for the program id".into(),
1447 );
1448 }
1449 do_process_program_deploy(
1450 rpc_client.clone(),
1451 config,
1452 &program_data,
1453 program_len,
1454 program_data_max_len,
1455 min_rent_exempt_program_data_balance,
1456 fee_payer_signer,
1457 &[program_signer.unwrap(), upgrade_authority_signer],
1458 buffer_signer,
1459 &buffer_pubkey,
1460 buffer_program_data,
1461 upgrade_authority_signer,
1462 skip_fee_check,
1463 compute_unit_price,
1464 max_sign_attempts,
1465 use_rpc,
1466 )
1467 } else {
1468 do_process_program_upgrade(
1469 rpc_client.clone(),
1470 config,
1471 &program_data,
1472 program_len,
1473 min_rent_exempt_program_data_balance,
1474 fee_payer_signer,
1475 &program_pubkey,
1476 upgrade_authority_signer,
1477 &buffer_pubkey,
1478 buffer_signer,
1479 buffer_program_data,
1480 skip_fee_check,
1481 compute_unit_price,
1482 max_sign_attempts,
1483 auto_extend,
1484 use_rpc,
1485 )
1486 };
1487 if result.is_ok() && is_final {
1488 process_set_authority(
1489 &rpc_client,
1490 config,
1491 Some(program_pubkey),
1492 None,
1493 Some(upgrade_authority_signer_index),
1494 None,
1495 false,
1496 false,
1497 &BlockhashQuery::default(),
1498 )?;
1499 }
1500 if result.is_err() && !buffer_provided {
1501 report_ephemeral_mnemonic(buffer_words, buffer_mnemonic, &buffer_pubkey);
1504 }
1505 result
1506}
1507
1508fn fetch_verified_buffer_program_data(
1509 rpc_client: &RpcClient,
1510 config: &CliConfig,
1511 buffer_pubkey: Pubkey,
1512 buffer_authority: Pubkey,
1513 feature_set: FeatureSet,
1514) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1515 let Some(buffer_program_data) =
1516 fetch_buffer_program_data(rpc_client, config, None, buffer_pubkey, buffer_authority)?
1517 else {
1518 return Err(format!("Buffer account {buffer_pubkey} not found").into());
1519 };
1520
1521 verify_elf(&buffer_program_data, feature_set).map_err(|err| {
1522 format!(
1523 "Buffer account {buffer_pubkey} has invalid program data: {:?}",
1524 err
1525 )
1526 })?;
1527
1528 Ok(buffer_program_data)
1529}
1530
1531fn fetch_buffer_program_data(
1532 rpc_client: &RpcClient,
1533 config: &CliConfig,
1534 min_program_len: Option<usize>,
1535 buffer_pubkey: Pubkey,
1536 buffer_authority: Pubkey,
1537) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
1538 let Some(mut account) = rpc_client
1539 .get_account_with_commitment(&buffer_pubkey, config.commitment)?
1540 .value
1541 else {
1542 return Ok(None);
1543 };
1544
1545 if !bpf_loader_upgradeable::check_id(&account.owner) {
1546 return Err(format!(
1547 "Buffer account {buffer_pubkey} is not owned by the BPF Upgradeable Loader",
1548 )
1549 .into());
1550 }
1551
1552 if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1553 if authority_address.is_none() {
1554 return Err(format!("Buffer {buffer_pubkey} is immutable").into());
1555 }
1556 if authority_address != Some(buffer_authority) {
1557 return Err(format!(
1558 "Buffer's authority {:?} does not match authority provided {}",
1559 authority_address, buffer_authority
1560 )
1561 .into());
1562 }
1563 } else {
1564 return Err(format!("{buffer_pubkey} is not an upgradeable loader buffer account").into());
1565 }
1566
1567 if let Some(min_program_len) = min_program_len {
1568 let min_buffer_data_len = UpgradeableLoaderState::size_of_buffer(min_program_len);
1569 if account.data.len() < min_buffer_data_len {
1570 return Err(format!(
1571 "Buffer account data size ({}) is smaller than the minimum size ({})",
1572 account.data.len(),
1573 min_buffer_data_len
1574 )
1575 .into());
1576 }
1577 }
1578
1579 let buffer_program_data = account
1580 .data
1581 .split_off(UpgradeableLoaderState::size_of_buffer_metadata());
1582
1583 Ok(Some(buffer_program_data))
1584}
1585
1586#[allow(clippy::too_many_arguments)]
1588fn process_program_upgrade(
1589 rpc_client: Arc<RpcClient>,
1590 config: &CliConfig,
1591 fee_payer_signer_index: SignerIndex,
1592 program_id: Pubkey,
1593 buffer_pubkey: Pubkey,
1594 upgrade_authority_signer_index: SignerIndex,
1595 sign_only: bool,
1596 dump_transaction_message: bool,
1597 blockhash_query: &BlockhashQuery,
1598 skip_feature_verification: bool,
1599) -> ProcessResult {
1600 let fee_payer_signer = config.signers[fee_payer_signer_index];
1601 let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
1602
1603 let blockhash = blockhash_query.get_blockhash(&rpc_client, config.commitment)?;
1604 let message = Message::new_with_blockhash(
1605 &[loader_v3_instruction::upgrade(
1606 &program_id,
1607 &buffer_pubkey,
1608 &upgrade_authority_signer.pubkey(),
1609 &fee_payer_signer.pubkey(),
1610 )],
1611 Some(&fee_payer_signer.pubkey()),
1612 &blockhash,
1613 );
1614
1615 if sign_only {
1616 let mut tx = Transaction::new_unsigned(message);
1617 let signers = &[fee_payer_signer, upgrade_authority_signer];
1618 tx.try_partial_sign(signers, blockhash)?;
1621 return_signers_with_config(
1622 &tx,
1623 &config.output_format,
1624 &ReturnSignersConfig {
1625 dump_transaction_message,
1626 },
1627 )
1628 } else {
1629 let feature_set = if skip_feature_verification {
1630 FeatureSet::all_enabled()
1631 } else {
1632 fetch_feature_set(&rpc_client)?
1633 };
1634
1635 fetch_verified_buffer_program_data(
1636 &rpc_client,
1637 config,
1638 buffer_pubkey,
1639 upgrade_authority_signer.pubkey(),
1640 feature_set,
1641 )?;
1642
1643 let fee = rpc_client.get_fee_for_message(&message)?;
1644 check_account_for_spend_and_fee_with_commitment(
1645 &rpc_client,
1646 &fee_payer_signer.pubkey(),
1647 0,
1648 fee,
1649 config.commitment,
1650 )?;
1651 let mut tx = Transaction::new_unsigned(message);
1652 let signers = &[fee_payer_signer, upgrade_authority_signer];
1653 tx.try_sign(signers, blockhash)?;
1654 let final_tx_sig = rpc_client
1655 .send_and_confirm_transaction_with_spinner_and_config(
1656 &tx,
1657 config.commitment,
1658 config.send_transaction_config,
1659 )
1660 .map_err(|e| format!("Upgrading program failed: {e}"))?;
1661 let program_id = CliProgramId {
1662 program_id: program_id.to_string(),
1663 signature: Some(final_tx_sig.to_string()),
1664 };
1665 Ok(config.output_format.formatted_string(&program_id))
1666 }
1667}
1668
1669#[allow(clippy::too_many_arguments)]
1670fn process_write_buffer(
1671 rpc_client: Arc<RpcClient>,
1672 config: &CliConfig,
1673 program_location: &str,
1674 fee_payer_signer_index: SignerIndex,
1675 buffer_signer_index: Option<SignerIndex>,
1676 buffer_pubkey: Option<Pubkey>,
1677 buffer_authority_signer_index: SignerIndex,
1678 max_len: Option<usize>,
1679 skip_fee_check: bool,
1680 compute_unit_price: Option<u64>,
1681 max_sign_attempts: usize,
1682 use_rpc: bool,
1683 skip_feature_verification: bool,
1684) -> ProcessResult {
1685 let fee_payer_signer = config.signers[fee_payer_signer_index];
1686 let buffer_authority = config.signers[buffer_authority_signer_index];
1687
1688 let feature_set = if skip_feature_verification {
1689 FeatureSet::all_enabled()
1690 } else {
1691 fetch_feature_set(&rpc_client)?
1692 };
1693
1694 let program_data = read_and_verify_elf(program_location, feature_set)?;
1695 let program_len = program_data.len();
1696
1697 let (words, mnemonic, buffer_keypair) = create_ephemeral_keypair()?;
1699 let (buffer_signer, buffer_pubkey) = if let Some(i) = buffer_signer_index {
1700 (Some(config.signers[i]), config.signers[i].pubkey())
1701 } else if let Some(pubkey) = buffer_pubkey {
1702 (None, pubkey)
1703 } else {
1704 (
1705 Some(&buffer_keypair as &dyn Signer),
1706 buffer_keypair.pubkey(),
1707 )
1708 };
1709
1710 let buffer_program_data = fetch_buffer_program_data(
1711 &rpc_client,
1712 config,
1713 Some(program_len),
1714 buffer_pubkey,
1715 buffer_authority.pubkey(),
1716 )?;
1717
1718 let buffer_data_max_len = if let Some(len) = max_len {
1719 len
1720 } else {
1721 program_data.len()
1722 };
1723 let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(
1724 UpgradeableLoaderState::size_of_programdata(buffer_data_max_len),
1725 )?;
1726
1727 let result = do_process_write_buffer(
1728 rpc_client,
1729 config,
1730 &program_data,
1731 program_data.len(),
1732 min_rent_exempt_program_data_balance,
1733 fee_payer_signer,
1734 buffer_signer,
1735 &buffer_pubkey,
1736 buffer_program_data,
1737 buffer_authority,
1738 skip_fee_check,
1739 compute_unit_price,
1740 max_sign_attempts,
1741 use_rpc,
1742 );
1743 if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
1744 report_ephemeral_mnemonic(words, mnemonic, &buffer_pubkey);
1745 }
1746 result
1747}
1748
1749fn process_set_authority(
1750 rpc_client: &RpcClient,
1751 config: &CliConfig,
1752 program_pubkey: Option<Pubkey>,
1753 buffer_pubkey: Option<Pubkey>,
1754 authority: Option<SignerIndex>,
1755 new_authority: Option<Pubkey>,
1756 sign_only: bool,
1757 dump_transaction_message: bool,
1758 blockhash_query: &BlockhashQuery,
1759) -> ProcessResult {
1760 let authority_signer = if let Some(index) = authority {
1761 config.signers[index]
1762 } else {
1763 return Err("Set authority requires the current authority".into());
1764 };
1765
1766 trace!("Set a new authority");
1767 let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1768
1769 let mut tx = if let Some(ref pubkey) = program_pubkey {
1770 Transaction::new_unsigned(Message::new(
1771 &[loader_v3_instruction::set_upgrade_authority(
1772 pubkey,
1773 &authority_signer.pubkey(),
1774 new_authority.as_ref(),
1775 )],
1776 Some(&config.signers[0].pubkey()),
1777 ))
1778 } else if let Some(pubkey) = buffer_pubkey {
1779 if let Some(ref new_authority) = new_authority {
1780 Transaction::new_unsigned(Message::new(
1781 &[loader_v3_instruction::set_buffer_authority(
1782 &pubkey,
1783 &authority_signer.pubkey(),
1784 new_authority,
1785 )],
1786 Some(&config.signers[0].pubkey()),
1787 ))
1788 } else {
1789 return Err("Buffer authority cannot be None".into());
1790 }
1791 } else {
1792 return Err("Program or Buffer not provided".into());
1793 };
1794
1795 let signers = &[config.signers[0], authority_signer];
1796
1797 if sign_only {
1798 tx.try_partial_sign(signers, blockhash)?;
1799 return_signers_with_config(
1800 &tx,
1801 &config.output_format,
1802 &ReturnSignersConfig {
1803 dump_transaction_message,
1804 },
1805 )
1806 } else {
1807 tx.try_sign(signers, blockhash)?;
1808 rpc_client
1809 .send_and_confirm_transaction_with_spinner_and_config(
1810 &tx,
1811 config.commitment,
1812 config.send_transaction_config,
1813 )
1814 .map_err(|e| format!("Setting authority failed: {e}"))?;
1815
1816 let authority = CliProgramAuthority {
1817 authority: new_authority
1818 .map(|pubkey| pubkey.to_string())
1819 .unwrap_or_else(|| "none".to_string()),
1820 account_type: if program_pubkey.is_some() {
1821 CliProgramAccountType::Program
1822 } else {
1823 CliProgramAccountType::Buffer
1824 },
1825 };
1826 Ok(config.output_format.formatted_string(&authority))
1827 }
1828}
1829
1830fn process_set_authority_checked(
1831 rpc_client: &RpcClient,
1832 config: &CliConfig,
1833 program_pubkey: Pubkey,
1834 authority_index: SignerIndex,
1835 new_authority_index: SignerIndex,
1836 sign_only: bool,
1837 dump_transaction_message: bool,
1838 blockhash_query: &BlockhashQuery,
1839) -> ProcessResult {
1840 let authority_signer = config.signers[authority_index];
1841 let new_authority_signer = config.signers[new_authority_index];
1842
1843 trace!("Set a new (checked) authority");
1844 let blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?;
1845
1846 let mut tx = Transaction::new_unsigned(Message::new(
1847 &[loader_v3_instruction::set_upgrade_authority_checked(
1848 &program_pubkey,
1849 &authority_signer.pubkey(),
1850 &new_authority_signer.pubkey(),
1851 )],
1852 Some(&config.signers[0].pubkey()),
1853 ));
1854
1855 let signers = &[config.signers[0], authority_signer, new_authority_signer];
1856 if sign_only {
1857 tx.try_partial_sign(signers, blockhash)?;
1858 return_signers_with_config(
1859 &tx,
1860 &config.output_format,
1861 &ReturnSignersConfig {
1862 dump_transaction_message,
1863 },
1864 )
1865 } else {
1866 tx.try_sign(signers, blockhash)?;
1867 rpc_client
1868 .send_and_confirm_transaction_with_spinner_and_config(
1869 &tx,
1870 config.commitment,
1871 config.send_transaction_config,
1872 )
1873 .map_err(|e| format!("Setting authority failed: {e}"))?;
1874
1875 let authority = CliProgramAuthority {
1876 authority: new_authority_signer.pubkey().to_string(),
1877 account_type: CliProgramAccountType::Program,
1878 };
1879 Ok(config.output_format.formatted_string(&authority))
1880 }
1881}
1882
1883const ACCOUNT_TYPE_SIZE: usize = 4;
1884const SLOT_SIZE: usize = size_of::<u64>();
1885const OPTION_SIZE: usize = 1;
1886const PUBKEY_LEN: usize = 32;
1887
1888fn get_buffers(
1889 rpc_client: &RpcClient,
1890 authority_pubkey: Option<Pubkey>,
1891 use_lamports_unit: bool,
1892) -> Result<CliUpgradeableBuffers, Box<dyn std::error::Error>> {
1893 let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1894 0,
1895 &[1, 0, 0, 0],
1896 ))];
1897 if let Some(authority_pubkey) = authority_pubkey {
1898 filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1899 ACCOUNT_TYPE_SIZE,
1900 &[1],
1901 )));
1902 filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1903 ACCOUNT_TYPE_SIZE + OPTION_SIZE,
1904 authority_pubkey.as_ref(),
1905 )));
1906 }
1907
1908 let results = get_accounts_with_filter(
1909 rpc_client,
1910 filters,
1911 ACCOUNT_TYPE_SIZE + OPTION_SIZE + PUBKEY_LEN,
1912 )?;
1913
1914 let mut buffers = vec![];
1915 for (address, account) in results.iter() {
1916 if let Ok(UpgradeableLoaderState::Buffer { authority_address }) = account.state() {
1917 buffers.push(CliUpgradeableBuffer {
1918 address: address.to_string(),
1919 authority: authority_address
1920 .map(|pubkey| pubkey.to_string())
1921 .unwrap_or_else(|| "none".to_string()),
1922 data_len: 0,
1923 lamports: account.lamports,
1924 use_lamports_unit,
1925 });
1926 } else {
1927 return Err(format!("Error parsing Buffer account {address}").into());
1928 }
1929 }
1930 Ok(CliUpgradeableBuffers {
1931 buffers,
1932 use_lamports_unit,
1933 })
1934}
1935
1936fn get_programs(
1937 rpc_client: &RpcClient,
1938 authority_pubkey: Option<Pubkey>,
1939 use_lamports_unit: bool,
1940) -> Result<CliUpgradeablePrograms, Box<dyn std::error::Error>> {
1941 let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1942 0,
1943 &[3, 0, 0, 0],
1944 ))];
1945 if let Some(authority_pubkey) = authority_pubkey {
1946 filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1947 ACCOUNT_TYPE_SIZE + SLOT_SIZE,
1948 &[1],
1949 )));
1950 filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
1951 ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE,
1952 authority_pubkey.as_ref(),
1953 )));
1954 }
1955
1956 let results = get_accounts_with_filter(
1957 rpc_client,
1958 filters,
1959 ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE + PUBKEY_LEN,
1960 )?;
1961
1962 let mut programs = vec![];
1963 for (programdata_address, programdata_account) in results.iter() {
1964 if let Ok(UpgradeableLoaderState::ProgramData {
1965 slot,
1966 upgrade_authority_address,
1967 }) = programdata_account.state()
1968 {
1969 let mut bytes = vec![2, 0, 0, 0];
1970 bytes.extend_from_slice(programdata_address.as_ref());
1971 let filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &bytes))];
1972
1973 let results = get_accounts_with_filter(rpc_client, filters, 0)?;
1974 if results.len() != 1 {
1975 return Err(format!(
1976 "Error: More than one Program associated with ProgramData account \
1977 {programdata_address}"
1978 )
1979 .into());
1980 }
1981 programs.push(CliUpgradeableProgram {
1982 program_id: results[0].0.to_string(),
1983 owner: programdata_account.owner.to_string(),
1984 programdata_address: programdata_address.to_string(),
1985 authority: upgrade_authority_address
1986 .map(|pubkey| pubkey.to_string())
1987 .unwrap_or_else(|| "none".to_string()),
1988 last_deploy_slot: slot,
1989 data_len: programdata_account
1990 .data
1991 .len()
1992 .saturating_sub(UpgradeableLoaderState::size_of_programdata_metadata()),
1993 lamports: programdata_account.lamports,
1994 use_lamports_unit,
1995 });
1996 } else {
1997 return Err(format!("Error parsing ProgramData account {programdata_address}").into());
1998 }
1999 }
2000 Ok(CliUpgradeablePrograms {
2001 programs,
2002 use_lamports_unit,
2003 })
2004}
2005
2006fn get_accounts_with_filter(
2007 rpc_client: &RpcClient,
2008 filters: Vec<RpcFilterType>,
2009 length: usize,
2010) -> Result<Vec<(Pubkey, Account)>, Box<dyn std::error::Error>> {
2011 let results = rpc_client.get_program_accounts_with_config(
2012 &bpf_loader_upgradeable::id(),
2013 RpcProgramAccountsConfig {
2014 filters: Some(filters),
2015 account_config: RpcAccountInfoConfig {
2016 encoding: Some(UiAccountEncoding::Base64),
2017 data_slice: Some(UiDataSliceConfig { offset: 0, length }),
2018 ..RpcAccountInfoConfig::default()
2019 },
2020 ..RpcProgramAccountsConfig::default()
2021 },
2022 )?;
2023 Ok(results)
2024}
2025
2026fn process_show(
2027 rpc_client: &RpcClient,
2028 config: &CliConfig,
2029 account_pubkey: Option<Pubkey>,
2030 authority_pubkey: Pubkey,
2031 programs: bool,
2032 buffers: bool,
2033 all: bool,
2034 use_lamports_unit: bool,
2035) -> ProcessResult {
2036 if let Some(account_pubkey) = account_pubkey {
2037 if let Some(account) = rpc_client
2038 .get_account_with_commitment(&account_pubkey, config.commitment)?
2039 .value
2040 {
2041 if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
2042 Ok(config.output_format.formatted_string(&CliProgram {
2043 program_id: account_pubkey.to_string(),
2044 owner: account.owner.to_string(),
2045 data_len: account.data.len(),
2046 }))
2047 } else if account.owner == bpf_loader_upgradeable::id() {
2048 if let Ok(UpgradeableLoaderState::Program {
2049 programdata_address,
2050 }) = account.state()
2051 {
2052 if let Some(programdata_account) = rpc_client
2053 .get_account_with_commitment(&programdata_address, config.commitment)?
2054 .value
2055 {
2056 if let Ok(UpgradeableLoaderState::ProgramData {
2057 upgrade_authority_address,
2058 slot,
2059 }) = programdata_account.state()
2060 {
2061 Ok(config
2062 .output_format
2063 .formatted_string(&CliUpgradeableProgram {
2064 program_id: account_pubkey.to_string(),
2065 owner: account.owner.to_string(),
2066 programdata_address: programdata_address.to_string(),
2067 authority: upgrade_authority_address
2068 .map(|pubkey| pubkey.to_string())
2069 .unwrap_or_else(|| "none".to_string()),
2070 last_deploy_slot: slot,
2071 data_len: programdata_account.data.len().saturating_sub(
2072 UpgradeableLoaderState::size_of_programdata_metadata(),
2073 ),
2074 lamports: programdata_account.lamports,
2075 use_lamports_unit,
2076 }))
2077 } else {
2078 Err(format!("Program {account_pubkey} has been closed").into())
2079 }
2080 } else {
2081 Err(format!("Program {account_pubkey} has been closed").into())
2082 }
2083 } else if let Ok(UpgradeableLoaderState::Buffer { authority_address }) =
2084 account.state()
2085 {
2086 Ok(config
2087 .output_format
2088 .formatted_string(&CliUpgradeableBuffer {
2089 address: account_pubkey.to_string(),
2090 authority: authority_address
2091 .map(|pubkey| pubkey.to_string())
2092 .unwrap_or_else(|| "none".to_string()),
2093 data_len: account
2094 .data
2095 .len()
2096 .saturating_sub(UpgradeableLoaderState::size_of_buffer_metadata()),
2097 lamports: account.lamports,
2098 use_lamports_unit,
2099 }))
2100 } else {
2101 Err(format!(
2102 "{account_pubkey} is not an upgradeable loader Buffer or Program account"
2103 )
2104 .into())
2105 }
2106 } else {
2107 Err(format!("{account_pubkey} is not an SBF program").into())
2108 }
2109 } else {
2110 Err(format!("Unable to find the account {account_pubkey}").into())
2111 }
2112 } else if programs {
2113 let authority_pubkey = if all { None } else { Some(authority_pubkey) };
2114 let programs = get_programs(rpc_client, authority_pubkey, use_lamports_unit)?;
2115 Ok(config.output_format.formatted_string(&programs))
2116 } else if buffers {
2117 let authority_pubkey = if all { None } else { Some(authority_pubkey) };
2118 let buffers = get_buffers(rpc_client, authority_pubkey, use_lamports_unit)?;
2119 Ok(config.output_format.formatted_string(&buffers))
2120 } else {
2121 Err("Invalid parameters".to_string().into())
2122 }
2123}
2124
2125fn process_dump(
2126 rpc_client: &RpcClient,
2127 config: &CliConfig,
2128 account_pubkey: Option<Pubkey>,
2129 output_location: &str,
2130) -> ProcessResult {
2131 if let Some(account_pubkey) = account_pubkey {
2132 if let Some(account) = rpc_client
2133 .get_account_with_commitment(&account_pubkey, config.commitment)?
2134 .value
2135 {
2136 if account.owner == bpf_loader::id() || account.owner == bpf_loader_deprecated::id() {
2137 let mut f = File::create(output_location)?;
2138 f.write_all(&account.data)?;
2139 Ok(format!("Wrote program to {output_location}"))
2140 } else if account.owner == bpf_loader_upgradeable::id() {
2141 if let Ok(UpgradeableLoaderState::Program {
2142 programdata_address,
2143 }) = account.state()
2144 {
2145 if let Some(programdata_account) = rpc_client
2146 .get_account_with_commitment(&programdata_address, config.commitment)?
2147 .value
2148 {
2149 if let Ok(UpgradeableLoaderState::ProgramData { .. }) =
2150 programdata_account.state()
2151 {
2152 let offset = UpgradeableLoaderState::size_of_programdata_metadata();
2153 let program_data = &programdata_account.data[offset..];
2154 let mut f = File::create(output_location)?;
2155 f.write_all(program_data)?;
2156 Ok(format!("Wrote program to {output_location}"))
2157 } else {
2158 Err(format!("Program {account_pubkey} has been closed").into())
2159 }
2160 } else {
2161 Err(format!("Program {account_pubkey} has been closed").into())
2162 }
2163 } else if let Ok(UpgradeableLoaderState::Buffer { .. }) = account.state() {
2164 let offset = UpgradeableLoaderState::size_of_buffer_metadata();
2165 let program_data = &account.data[offset..];
2166 let mut f = File::create(output_location)?;
2167 f.write_all(program_data)?;
2168 Ok(format!("Wrote program to {output_location}"))
2169 } else {
2170 Err(format!(
2171 "{account_pubkey} is not an upgradeable loader buffer or program account"
2172 )
2173 .into())
2174 }
2175 } else {
2176 Err(format!("{account_pubkey} is not an SBF program").into())
2177 }
2178 } else {
2179 Err(format!("Unable to find the account {account_pubkey}").into())
2180 }
2181 } else {
2182 Err("No account specified".into())
2183 }
2184}
2185
2186fn close(
2187 rpc_client: &RpcClient,
2188 config: &CliConfig,
2189 account_pubkey: &Pubkey,
2190 recipient_pubkey: &Pubkey,
2191 authority_signer: &dyn Signer,
2192 program_pubkey: Option<&Pubkey>,
2193) -> Result<(), Box<dyn std::error::Error>> {
2194 let blockhash = rpc_client.get_latest_blockhash()?;
2195
2196 let mut tx = Transaction::new_unsigned(Message::new(
2197 &[loader_v3_instruction::close_any(
2198 account_pubkey,
2199 recipient_pubkey,
2200 Some(&authority_signer.pubkey()),
2201 program_pubkey,
2202 )],
2203 Some(&config.signers[0].pubkey()),
2204 ));
2205
2206 tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
2207 let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2208 &tx,
2209 config.commitment,
2210 config.send_transaction_config,
2211 );
2212 if let Err(err) = result {
2213 if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2214 _,
2215 InstructionError::InvalidInstructionData,
2216 )) = err.kind()
2217 {
2218 return Err("Closing a buffer account is not supported by the cluster".into());
2219 } else if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2220 _,
2221 InstructionError::InvalidArgument,
2222 )) = err.kind()
2223 {
2224 return Err("Closing a program account is not supported by the cluster".into());
2225 } else {
2226 return Err(format!("Close failed: {err}").into());
2227 }
2228 }
2229 Ok(())
2230}
2231
2232fn process_close(
2233 rpc_client: &RpcClient,
2234 config: &CliConfig,
2235 account_pubkey: Option<Pubkey>,
2236 recipient_pubkey: Pubkey,
2237 authority_index: SignerIndex,
2238 use_lamports_unit: bool,
2239 bypass_warning: bool,
2240) -> ProcessResult {
2241 let authority_signer = config.signers[authority_index];
2242
2243 if let Some(account_pubkey) = account_pubkey {
2244 if let Some(account) = rpc_client
2245 .get_account_with_commitment(&account_pubkey, config.commitment)?
2246 .value
2247 {
2248 match account.state() {
2249 Ok(UpgradeableLoaderState::Buffer { authority_address }) => {
2250 if authority_address != Some(authority_signer.pubkey()) {
2251 return Err(format!(
2252 "Buffer account authority {:?} does not match {:?}",
2253 authority_address,
2254 Some(authority_signer.pubkey())
2255 )
2256 .into());
2257 } else {
2258 close(
2259 rpc_client,
2260 config,
2261 &account_pubkey,
2262 &recipient_pubkey,
2263 authority_signer,
2264 None,
2265 )?;
2266 }
2267 Ok(config
2268 .output_format
2269 .formatted_string(&CliUpgradeableBuffers {
2270 buffers: vec![CliUpgradeableBuffer {
2271 address: account_pubkey.to_string(),
2272 authority: authority_address
2273 .map(|pubkey| pubkey.to_string())
2274 .unwrap_or_else(|| "none".to_string()),
2275 data_len: 0,
2276 lamports: account.lamports,
2277 use_lamports_unit,
2278 }],
2279 use_lamports_unit,
2280 }))
2281 }
2282 Ok(UpgradeableLoaderState::Program {
2283 programdata_address: programdata_pubkey,
2284 }) => {
2285 if let Some(account) = rpc_client
2286 .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2287 .value
2288 {
2289 if let Ok(UpgradeableLoaderState::ProgramData {
2290 slot: _,
2291 upgrade_authority_address: authority_pubkey,
2292 }) = account.state()
2293 {
2294 if authority_pubkey != Some(authority_signer.pubkey()) {
2295 Err(format!(
2296 "Program authority {:?} does not match {:?}",
2297 authority_pubkey,
2298 Some(authority_signer.pubkey())
2299 )
2300 .into())
2301 } else {
2302 if !bypass_warning {
2303 return Err(String::from(CLOSE_PROGRAM_WARNING).into());
2304 }
2305 close(
2306 rpc_client,
2307 config,
2308 &programdata_pubkey,
2309 &recipient_pubkey,
2310 authority_signer,
2311 Some(&account_pubkey),
2312 )?;
2313 Ok(config.output_format.formatted_string(
2314 &CliUpgradeableProgramClosed {
2315 program_id: account_pubkey.to_string(),
2316 lamports: account.lamports,
2317 use_lamports_unit,
2318 },
2319 ))
2320 }
2321 } else {
2322 Err(format!("Program {account_pubkey} has been closed").into())
2323 }
2324 } else {
2325 Err(format!("Program {account_pubkey} has been closed").into())
2326 }
2327 }
2328 _ => Err(format!("{account_pubkey} is not a Program or Buffer account").into()),
2329 }
2330 } else {
2331 Err(format!("Unable to find the account {account_pubkey}").into())
2332 }
2333 } else {
2334 let buffers = get_buffers(
2335 rpc_client,
2336 Some(authority_signer.pubkey()),
2337 use_lamports_unit,
2338 )?;
2339
2340 let mut closed = vec![];
2341 for buffer in buffers.buffers.iter() {
2342 if close(
2343 rpc_client,
2344 config,
2345 &Pubkey::from_str(&buffer.address)?,
2346 &recipient_pubkey,
2347 authority_signer,
2348 None,
2349 )
2350 .is_ok()
2351 {
2352 closed.push(buffer.clone());
2353 }
2354 }
2355 Ok(config
2356 .output_format
2357 .formatted_string(&CliUpgradeableBuffers {
2358 buffers: closed,
2359 use_lamports_unit,
2360 }))
2361 }
2362}
2363
2364fn process_extend_program(
2365 rpc_client: &RpcClient,
2366 config: &CliConfig,
2367 program_pubkey: Pubkey,
2368 additional_bytes: u32,
2369) -> ProcessResult {
2370 let payer_pubkey = config.signers[0].pubkey();
2371
2372 if additional_bytes == 0 {
2373 return Err("Additional bytes must be greater than zero".into());
2374 }
2375
2376 let program_account = match rpc_client
2377 .get_account_with_commitment(&program_pubkey, config.commitment)?
2378 .value
2379 {
2380 Some(program_account) => Ok(program_account),
2381 None => Err(format!("Unable to find program {program_pubkey}")),
2382 }?;
2383
2384 if !bpf_loader_upgradeable::check_id(&program_account.owner) {
2385 return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2386 }
2387
2388 let programdata_pubkey = match program_account.state() {
2389 Ok(UpgradeableLoaderState::Program {
2390 programdata_address: programdata_pubkey,
2391 }) => Ok(programdata_pubkey),
2392 _ => Err(format!(
2393 "Account {program_pubkey} is not an upgradeable program"
2394 )),
2395 }?;
2396
2397 let programdata_account = match rpc_client
2398 .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2399 .value
2400 {
2401 Some(programdata_account) => Ok(programdata_account),
2402 None => Err(format!("Program {program_pubkey} is closed")),
2403 }?;
2404
2405 let upgrade_authority_address = match programdata_account.state() {
2406 Ok(UpgradeableLoaderState::ProgramData {
2407 slot: _,
2408 upgrade_authority_address,
2409 }) => Ok(upgrade_authority_address),
2410 _ => Err(format!("Program {program_pubkey} is closed")),
2411 }?;
2412
2413 match upgrade_authority_address {
2414 None => Err(format!("Program {program_pubkey} is not upgradeable")),
2415 _ => Ok(()),
2416 }?;
2417
2418 let blockhash = rpc_client.get_latest_blockhash()?;
2419
2420 let mut tx = Transaction::new_unsigned(Message::new(
2421 &[loader_v3_instruction::extend_program(
2422 &program_pubkey,
2423 Some(&payer_pubkey),
2424 additional_bytes,
2425 )],
2426 Some(&payer_pubkey),
2427 ));
2428
2429 tx.try_sign(&[config.signers[0]], blockhash)?;
2430 let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2431 &tx,
2432 config.commitment,
2433 config.send_transaction_config,
2434 );
2435 if let Err(err) = result {
2436 if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2437 _,
2438 InstructionError::InvalidInstructionData,
2439 )) = err.kind()
2440 {
2441 return Err("Extending a program is not supported by the cluster".into());
2442 } else {
2443 return Err(format!("Extend program failed: {err}").into());
2444 }
2445 }
2446
2447 Ok(config
2448 .output_format
2449 .formatted_string(&CliUpgradeableProgramExtended {
2450 program_id: program_pubkey.to_string(),
2451 additional_bytes,
2452 }))
2453}
2454
2455fn process_migrate_program(
2456 rpc_client: &RpcClient,
2457 config: &CliConfig,
2458 program_pubkey: Pubkey,
2459 authority_signer_index: SignerIndex,
2460 compute_unit_price: Option<u64>,
2461) -> ProcessResult {
2462 let payer_pubkey = config.signers[0].pubkey();
2463 let authority_signer = config.signers[authority_signer_index];
2464
2465 let program_account = match rpc_client
2466 .get_account_with_commitment(&program_pubkey, config.commitment)?
2467 .value
2468 {
2469 Some(program_account) => Ok(program_account),
2470 None => Err(format!("Unable to find program {program_pubkey}")),
2471 }?;
2472
2473 if !bpf_loader_upgradeable::check_id(&program_account.owner) {
2474 return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2475 }
2476
2477 let Ok(UpgradeableLoaderState::Program {
2478 programdata_address: programdata_pubkey,
2479 }) = program_account.state()
2480 else {
2481 return Err(format!("Account {program_pubkey} is not an upgradeable program").into());
2482 };
2483
2484 let Some(programdata_account) = rpc_client
2485 .get_account_with_commitment(&programdata_pubkey, config.commitment)?
2486 .value
2487 else {
2488 return Err(format!("Program {program_pubkey} is closed").into());
2489 };
2490
2491 let upgrade_authority_address = match programdata_account.state() {
2492 Ok(UpgradeableLoaderState::ProgramData {
2493 slot: _slot,
2494 upgrade_authority_address,
2495 }) => upgrade_authority_address,
2496 _ => None,
2497 };
2498
2499 if authority_signer.pubkey() != upgrade_authority_address.unwrap_or(program_pubkey) {
2500 return Err(format!(
2501 "Upgrade authority {:?} does not match {:?}",
2502 upgrade_authority_address,
2503 Some(authority_signer.pubkey())
2504 )
2505 .into());
2506 }
2507
2508 let blockhash = rpc_client.get_latest_blockhash()?;
2509 let mut message = Message::new(
2510 &vec![loader_v3_instruction::migrate_program(
2511 &programdata_pubkey,
2512 &program_pubkey,
2513 &authority_signer.pubkey(),
2514 )]
2515 .with_compute_unit_config(&ComputeUnitConfig {
2516 compute_unit_price,
2517 compute_unit_limit: ComputeUnitLimit::Simulated,
2518 }),
2519 Some(&payer_pubkey),
2520 );
2521 simulate_and_update_compute_unit_limit(&ComputeUnitLimit::Simulated, rpc_client, &mut message)?;
2522
2523 let mut tx = Transaction::new_unsigned(message);
2524 tx.try_sign(&[config.signers[0], config.signers[1]], blockhash)?;
2525 let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
2526 &tx,
2527 config.commitment,
2528 config.send_transaction_config,
2529 );
2530 if let Err(err) = result {
2531 if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
2532 _,
2533 InstructionError::InvalidInstructionData,
2534 )) = err.kind()
2535 {
2536 return Err("Migrating a program is not supported by the cluster".into());
2537 } else {
2538 return Err(format!("Migrate program failed: {err}").into());
2539 }
2540 }
2541
2542 Ok(config
2543 .output_format
2544 .formatted_string(&CliUpgradeableProgramMigrated {
2545 program_id: program_pubkey.to_string(),
2546 }))
2547}
2548
2549pub fn calculate_max_chunk_size(baseline_msg: Message) -> usize {
2550 let tx_size = bincode::serialized_size(&Transaction {
2551 signatures: vec![
2552 Signature::default();
2553 baseline_msg.header.num_required_signatures as usize
2554 ],
2555 message: baseline_msg,
2556 })
2557 .unwrap() as usize;
2558 PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1)
2560}
2561
2562#[allow(clippy::too_many_arguments)]
2563fn do_process_program_deploy(
2564 rpc_client: Arc<RpcClient>,
2565 config: &CliConfig,
2566 program_data: &[u8], program_len: usize,
2568 program_data_max_len: usize,
2569 min_rent_exempt_program_data_balance: u64,
2570 fee_payer_signer: &dyn Signer,
2571 program_signers: &[&dyn Signer],
2572 buffer_signer: Option<&dyn Signer>,
2573 buffer_pubkey: &Pubkey,
2574 buffer_program_data: Option<Vec<u8>>,
2575 buffer_authority_signer: &dyn Signer,
2576 skip_fee_check: bool,
2577 compute_unit_price: Option<u64>,
2578 max_sign_attempts: usize,
2579 use_rpc: bool,
2580) -> ProcessResult {
2581 let blockhash = rpc_client.get_latest_blockhash()?;
2582 let compute_unit_limit = ComputeUnitLimit::Simulated;
2583
2584 let (initial_instructions, balance_needed, buffer_program_data) =
2585 if let Some(buffer_program_data) = buffer_program_data {
2586 (vec![], 0, buffer_program_data)
2587 } else {
2588 (
2589 loader_v3_instruction::create_buffer(
2590 &fee_payer_signer.pubkey(),
2591 buffer_pubkey,
2592 &buffer_authority_signer.pubkey(),
2593 min_rent_exempt_program_data_balance,
2594 program_len,
2595 )?,
2596 min_rent_exempt_program_data_balance,
2597 vec![0; program_len],
2598 )
2599 };
2600
2601 let initial_message = if !initial_instructions.is_empty() {
2602 Some(Message::new_with_blockhash(
2603 &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2604 compute_unit_price,
2605 compute_unit_limit,
2606 }),
2607 Some(&fee_payer_signer.pubkey()),
2608 &blockhash,
2609 ))
2610 } else {
2611 None
2612 };
2613
2614 let create_msg = |offset: u32, bytes: Vec<u8>| {
2616 let instruction = loader_v3_instruction::write(
2617 buffer_pubkey,
2618 &buffer_authority_signer.pubkey(),
2619 offset,
2620 bytes,
2621 );
2622
2623 let instructions = vec![instruction].with_compute_unit_config(&ComputeUnitConfig {
2624 compute_unit_price,
2625 compute_unit_limit,
2626 });
2627 Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2628 };
2629
2630 let mut write_messages = vec![];
2631 let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2632 for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2633 let offset = i.saturating_mul(chunk_size);
2634 if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2635 write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2636 }
2637 }
2638
2639 let final_message = {
2641 #[allow(deprecated)]
2642 let instructions = loader_v3_instruction::deploy_with_max_program_len(
2643 &fee_payer_signer.pubkey(),
2644 &program_signers[0].pubkey(),
2645 buffer_pubkey,
2646 &program_signers[1].pubkey(),
2647 rpc_client
2648 .get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_program())?,
2649 program_data_max_len,
2650 )?
2651 .with_compute_unit_config(&ComputeUnitConfig {
2652 compute_unit_price,
2653 compute_unit_limit,
2654 });
2655
2656 Some(Message::new_with_blockhash(
2657 &instructions,
2658 Some(&fee_payer_signer.pubkey()),
2659 &blockhash,
2660 ))
2661 };
2662
2663 if !skip_fee_check {
2664 check_payer(
2665 &rpc_client,
2666 config,
2667 fee_payer_signer.pubkey(),
2668 balance_needed,
2669 &initial_message,
2670 &write_messages,
2671 &final_message,
2672 )?;
2673 }
2674
2675 let final_tx_sig = send_deploy_messages(
2676 rpc_client,
2677 config,
2678 initial_message,
2679 write_messages,
2680 final_message,
2681 fee_payer_signer,
2682 buffer_signer,
2683 Some(buffer_authority_signer),
2684 Some(program_signers),
2685 max_sign_attempts,
2686 use_rpc,
2687 &compute_unit_limit,
2688 )?;
2689
2690 let program_id = CliProgramId {
2691 program_id: program_signers[0].pubkey().to_string(),
2692 signature: final_tx_sig.as_ref().map(ToString::to_string),
2693 };
2694 Ok(config.output_format.formatted_string(&program_id))
2695}
2696
2697#[allow(clippy::too_many_arguments)]
2698fn do_process_write_buffer(
2699 rpc_client: Arc<RpcClient>,
2700 config: &CliConfig,
2701 program_data: &[u8], program_len: usize,
2703 min_rent_exempt_program_data_balance: u64,
2704 fee_payer_signer: &dyn Signer,
2705 buffer_signer: Option<&dyn Signer>,
2706 buffer_pubkey: &Pubkey,
2707 buffer_program_data: Option<Vec<u8>>,
2708 buffer_authority_signer: &dyn Signer,
2709 skip_fee_check: bool,
2710 compute_unit_price: Option<u64>,
2711 max_sign_attempts: usize,
2712 use_rpc: bool,
2713) -> ProcessResult {
2714 let blockhash = rpc_client.get_latest_blockhash()?;
2715 let compute_unit_limit = ComputeUnitLimit::Simulated;
2716
2717 let (initial_instructions, balance_needed, buffer_program_data) =
2718 if let Some(buffer_program_data) = buffer_program_data {
2719 (vec![], 0, buffer_program_data)
2720 } else {
2721 (
2722 loader_v3_instruction::create_buffer(
2723 &fee_payer_signer.pubkey(),
2724 buffer_pubkey,
2725 &buffer_authority_signer.pubkey(),
2726 min_rent_exempt_program_data_balance,
2727 program_len,
2728 )?,
2729 min_rent_exempt_program_data_balance,
2730 vec![0; program_len],
2731 )
2732 };
2733
2734 let initial_message = if !initial_instructions.is_empty() {
2735 Some(Message::new_with_blockhash(
2736 &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2737 compute_unit_price,
2738 compute_unit_limit,
2739 }),
2740 Some(&fee_payer_signer.pubkey()),
2741 &blockhash,
2742 ))
2743 } else {
2744 None
2745 };
2746
2747 let create_msg = |offset: u32, bytes: Vec<u8>| {
2749 let instruction = loader_v3_instruction::write(
2750 buffer_pubkey,
2751 &buffer_authority_signer.pubkey(),
2752 offset,
2753 bytes,
2754 );
2755
2756 let instructions = vec![instruction].with_compute_unit_config(&ComputeUnitConfig {
2757 compute_unit_price,
2758 compute_unit_limit,
2759 });
2760 Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2761 };
2762
2763 let mut write_messages = vec![];
2764 let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2765 for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2766 let offset = i.saturating_mul(chunk_size);
2767 if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2768 write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2769 }
2770 }
2771
2772 if !skip_fee_check {
2773 check_payer(
2774 &rpc_client,
2775 config,
2776 fee_payer_signer.pubkey(),
2777 balance_needed,
2778 &initial_message,
2779 &write_messages,
2780 &None,
2781 )?;
2782 }
2783
2784 let _final_tx_sig = send_deploy_messages(
2785 rpc_client,
2786 config,
2787 initial_message,
2788 write_messages,
2789 None,
2790 fee_payer_signer,
2791 buffer_signer,
2792 Some(buffer_authority_signer),
2793 None,
2794 max_sign_attempts,
2795 use_rpc,
2796 &compute_unit_limit,
2797 )?;
2798
2799 let buffer = CliProgramBuffer {
2800 buffer: buffer_pubkey.to_string(),
2801 };
2802 Ok(config.output_format.formatted_string(&buffer))
2803}
2804
2805#[allow(clippy::too_many_arguments)]
2806fn do_process_program_upgrade(
2807 rpc_client: Arc<RpcClient>,
2808 config: &CliConfig,
2809 program_data: &[u8], program_len: usize,
2811 min_rent_exempt_program_data_balance: u64,
2812 fee_payer_signer: &dyn Signer,
2813 program_id: &Pubkey,
2814 upgrade_authority: &dyn Signer,
2815 buffer_pubkey: &Pubkey,
2816 buffer_signer: Option<&dyn Signer>,
2817 buffer_program_data: Option<Vec<u8>>,
2818 skip_fee_check: bool,
2819 compute_unit_price: Option<u64>,
2820 max_sign_attempts: usize,
2821 auto_extend: bool,
2822 use_rpc: bool,
2823) -> ProcessResult {
2824 let blockhash = rpc_client.get_latest_blockhash()?;
2825 let compute_unit_limit = ComputeUnitLimit::Simulated;
2826
2827 let (initial_message, write_messages, balance_needed) = if let Some(buffer_signer) =
2828 buffer_signer
2829 {
2830 let (mut initial_instructions, balance_needed, buffer_program_data) =
2831 if let Some(buffer_program_data) = buffer_program_data {
2832 (vec![], 0, buffer_program_data)
2833 } else {
2834 (
2835 loader_v3_instruction::create_buffer(
2836 &fee_payer_signer.pubkey(),
2837 &buffer_signer.pubkey(),
2838 &upgrade_authority.pubkey(),
2839 min_rent_exempt_program_data_balance,
2840 program_len,
2841 )?,
2842 min_rent_exempt_program_data_balance,
2843 vec![0; program_len],
2844 )
2845 };
2846
2847 if auto_extend {
2848 extend_program_data_if_needed(
2849 &mut initial_instructions,
2850 &rpc_client,
2851 config.commitment,
2852 &fee_payer_signer.pubkey(),
2853 program_id,
2854 program_len,
2855 )?;
2856 }
2857
2858 let initial_message = if !initial_instructions.is_empty() {
2859 Some(Message::new_with_blockhash(
2860 &initial_instructions.with_compute_unit_config(&ComputeUnitConfig {
2861 compute_unit_price,
2862 compute_unit_limit: ComputeUnitLimit::Simulated,
2863 }),
2864 Some(&fee_payer_signer.pubkey()),
2865 &blockhash,
2866 ))
2867 } else {
2868 None
2869 };
2870
2871 let buffer_signer_pubkey = buffer_signer.pubkey();
2872 let upgrade_authority_pubkey = upgrade_authority.pubkey();
2873 let create_msg = |offset: u32, bytes: Vec<u8>| {
2874 let instructions = vec![loader_v3_instruction::write(
2875 &buffer_signer_pubkey,
2876 &upgrade_authority_pubkey,
2877 offset,
2878 bytes,
2879 )]
2880 .with_compute_unit_config(&ComputeUnitConfig {
2881 compute_unit_price,
2882 compute_unit_limit,
2883 });
2884 Message::new_with_blockhash(&instructions, Some(&fee_payer_signer.pubkey()), &blockhash)
2885 };
2886
2887 let mut write_messages = vec![];
2889 let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new()));
2890 for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) {
2891 let offset = i.saturating_mul(chunk_size);
2892 if chunk != &buffer_program_data[offset..offset.saturating_add(chunk.len())] {
2893 write_messages.push(create_msg(offset as u32, chunk.to_vec()));
2894 }
2895 }
2896
2897 (initial_message, write_messages, balance_needed)
2898 } else {
2899 (None, vec![], 0)
2900 };
2901
2902 let final_instructions = vec![loader_v3_instruction::upgrade(
2904 program_id,
2905 buffer_pubkey,
2906 &upgrade_authority.pubkey(),
2907 &fee_payer_signer.pubkey(),
2908 )]
2909 .with_compute_unit_config(&ComputeUnitConfig {
2910 compute_unit_price,
2911 compute_unit_limit,
2912 });
2913 let final_message = Message::new_with_blockhash(
2914 &final_instructions,
2915 Some(&fee_payer_signer.pubkey()),
2916 &blockhash,
2917 );
2918 let final_message = Some(final_message);
2919
2920 if !skip_fee_check {
2921 check_payer(
2922 &rpc_client,
2923 config,
2924 fee_payer_signer.pubkey(),
2925 balance_needed,
2926 &initial_message,
2927 &write_messages,
2928 &final_message,
2929 )?;
2930 }
2931
2932 let final_tx_sig = send_deploy_messages(
2933 rpc_client,
2934 config,
2935 initial_message,
2936 write_messages,
2937 final_message,
2938 fee_payer_signer,
2939 buffer_signer,
2940 Some(upgrade_authority),
2941 Some(&[upgrade_authority]),
2942 max_sign_attempts,
2943 use_rpc,
2944 &compute_unit_limit,
2945 )?;
2946
2947 let program_id = CliProgramId {
2948 program_id: program_id.to_string(),
2949 signature: final_tx_sig.as_ref().map(ToString::to_string),
2950 };
2951 Ok(config.output_format.formatted_string(&program_id))
2952}
2953
2954fn extend_program_data_if_needed(
2957 initial_instructions: &mut Vec<Instruction>,
2958 rpc_client: &RpcClient,
2959 commitment: CommitmentConfig,
2960 fee_payer: &Pubkey,
2961 program_id: &Pubkey,
2962 program_len: usize,
2963) -> Result<(), Box<dyn std::error::Error>> {
2964 let program_data_address = get_program_data_address(program_id);
2965
2966 let Some(program_data_account) = rpc_client
2967 .get_account_with_commitment(&program_data_address, commitment)?
2968 .value
2969 else {
2970 return Ok(());
2972 };
2973
2974 let required_len = UpgradeableLoaderState::size_of_programdata(program_len);
2975 let max_permitted_data_length = usize::try_from(MAX_PERMITTED_DATA_LENGTH).unwrap();
2976 if required_len > max_permitted_data_length {
2977 let max_program_len = max_permitted_data_length
2978 .saturating_sub(UpgradeableLoaderState::size_of_programdata(0));
2979 return Err(format!(
2980 "New program ({program_id}) data account is too big: {required_len}.\n\
2981 Maximum program size: {max_program_len}.",
2982 )
2983 .into());
2984 }
2985
2986 let current_len = program_data_account.data.len();
2987 let additional_bytes = required_len.saturating_sub(current_len);
2988 if additional_bytes == 0 {
2989 return Ok(());
2991 }
2992
2993 let additional_bytes =
2994 u32::try_from(additional_bytes).expect("`u32` is big enough to hold an account size");
2995 initial_instructions.push(loader_v3_instruction::extend_program(
2996 program_id,
2997 Some(fee_payer),
2998 additional_bytes,
2999 ));
3000
3001 Ok(())
3002}
3003
3004fn read_and_verify_elf(
3005 program_location: &str,
3006 feature_set: FeatureSet,
3007) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
3008 let mut file = File::open(program_location)
3009 .map_err(|err| format!("Unable to open program file: {err}"))?;
3010 let mut program_data = Vec::new();
3011 file.read_to_end(&mut program_data)
3012 .map_err(|err| format!("Unable to read program file: {err}"))?;
3013
3014 verify_elf(&program_data, feature_set)?;
3015
3016 Ok(program_data)
3017}
3018
3019fn verify_elf(
3020 program_data: &[u8],
3021 feature_set: FeatureSet,
3022) -> Result<(), Box<dyn std::error::Error>> {
3023 let program_runtime_environment =
3025 create_program_runtime_environment_v1(&feature_set, &ComputeBudget::default(), true, false)
3026 .unwrap();
3027 let executable =
3028 Executable::<InvokeContext>::from_elf(program_data, Arc::new(program_runtime_environment))
3029 .map_err(|err| format!("ELF error: {err}"))?;
3030
3031 executable
3032 .verify::<RequisiteVerifier>()
3033 .map_err(|err| format!("ELF error: {err}").into())
3034}
3035
3036fn check_payer(
3037 rpc_client: &RpcClient,
3038 config: &CliConfig,
3039 fee_payer_pubkey: Pubkey,
3040 balance_needed: u64,
3041 initial_message: &Option<Message>,
3042 write_messages: &[Message],
3043 final_message: &Option<Message>,
3044) -> Result<(), Box<dyn std::error::Error>> {
3045 let mut fee = Saturating(0);
3046 if let Some(message) = initial_message {
3047 fee += rpc_client.get_fee_for_message(message)?;
3048 }
3049 if let Some(message) = write_messages.first() {
3051 fee += rpc_client
3052 .get_fee_for_message(message)?
3053 .saturating_mul(write_messages.len() as u64);
3054 }
3055 if let Some(message) = final_message {
3056 fee += rpc_client.get_fee_for_message(message)?;
3057 }
3058 check_account_for_spend_and_fee_with_commitment(
3059 rpc_client,
3060 &fee_payer_pubkey,
3061 balance_needed,
3062 fee.0,
3063 config.commitment,
3064 )?;
3065 Ok(())
3066}
3067
3068#[allow(clippy::too_many_arguments)]
3069fn send_deploy_messages(
3070 rpc_client: Arc<RpcClient>,
3071 config: &CliConfig,
3072 initial_message: Option<Message>,
3073 mut write_messages: Vec<Message>,
3074 final_message: Option<Message>,
3075 fee_payer_signer: &dyn Signer,
3076 initial_signer: Option<&dyn Signer>,
3077 write_signer: Option<&dyn Signer>,
3078 final_signers: Option<&[&dyn Signer]>,
3079 max_sign_attempts: usize,
3080 use_rpc: bool,
3081 compute_unit_limit: &ComputeUnitLimit,
3082) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
3083 if let Some(mut message) = initial_message {
3084 if let Some(initial_signer) = initial_signer {
3085 trace!("Preparing the required accounts");
3086 simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)?;
3087 let mut initial_transaction = Transaction::new_unsigned(message.clone());
3088 let blockhash = rpc_client.get_latest_blockhash()?;
3089
3090 if message.header.num_required_signatures == 2 {
3095 initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
3096 } else {
3097 initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
3098 }
3099 let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
3100 &initial_transaction,
3101 config.commitment,
3102 config.send_transaction_config,
3103 );
3104 log_instruction_custom_error::<SystemError>(result, config)
3105 .map_err(|err| format!("Account allocation failed: {err}"))?;
3106 } else {
3107 return Err("Buffer account not created yet, must provide a key pair".into());
3108 }
3109 }
3110
3111 if !write_messages.is_empty() {
3112 if let Some(write_signer) = write_signer {
3113 trace!("Writing program data");
3114
3115 {
3119 let mut message = write_messages[0].clone();
3120 if let UpdateComputeUnitLimitResult::UpdatedInstructionIndex(ix_index) =
3121 simulate_and_update_compute_unit_limit(
3122 compute_unit_limit,
3123 &rpc_client,
3124 &mut message,
3125 )?
3126 {
3127 for msg in &mut write_messages {
3128 assert_eq!(msg.program_id(ix_index), Some(&compute_budget::id()));
3134 msg.instructions[ix_index]
3135 .data
3136 .clone_from(&message.instructions[ix_index].data);
3137 }
3138 }
3139 }
3140
3141 let connection_cache = if config.use_quic {
3142 ConnectionCache::new_quic("connection_cache_cli_program_quic", 1)
3143 } else {
3144 ConnectionCache::with_udp("connection_cache_cli_program_udp", 1)
3145 };
3146 let transaction_errors = match connection_cache {
3147 ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
3148 rpc_client.clone(),
3149 &config.websocket_url,
3150 TpuClientConfig::default(),
3151 cache,
3152 )?
3153 .send_and_confirm_messages_with_spinner(
3154 &write_messages,
3155 &[fee_payer_signer, write_signer],
3156 ),
3157 ConnectionCache::Quic(cache) => {
3158 let tpu_client_fut = solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
3159 rpc_client.get_inner_client().clone(),
3160 config.websocket_url.as_str(),
3161 solana_client::tpu_client::TpuClientConfig::default(),
3162 cache,
3163 );
3164 let tpu_client = (!use_rpc).then(|| rpc_client
3165 .runtime()
3166 .block_on(tpu_client_fut)
3167 .expect("Should return a valid tpu client")
3168 );
3169
3170 send_and_confirm_transactions_in_parallel_blocking_v2(
3171 rpc_client.clone(),
3172 tpu_client,
3173 &write_messages,
3174 &[fee_payer_signer, write_signer],
3175 SendAndConfirmConfigV2 {
3176 resign_txs_count: Some(max_sign_attempts),
3177 with_spinner: true,
3178 rpc_send_transaction_config: config.send_transaction_config,
3179 },
3180 )
3181 },
3182 }
3183 .map_err(|err| format!("Data writes to account failed: {err}"))?
3184 .into_iter()
3185 .flatten()
3186 .collect::<Vec<_>>();
3187
3188 if !transaction_errors.is_empty() {
3189 for transaction_error in &transaction_errors {
3190 error!("{:?}", transaction_error);
3191 }
3192 return Err(
3193 format!("{} write transactions failed", transaction_errors.len()).into(),
3194 );
3195 }
3196 }
3197 }
3198
3199 if let Some(mut message) = final_message {
3200 if let Some(final_signers) = final_signers {
3201 trace!("Deploying program");
3202
3203 simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)?;
3204 let mut final_tx = Transaction::new_unsigned(message);
3205 let blockhash = rpc_client.get_latest_blockhash()?;
3206 let mut signers = final_signers.to_vec();
3207 signers.push(fee_payer_signer);
3208 final_tx.try_sign(&signers, blockhash)?;
3209 return Ok(Some(
3210 rpc_client
3211 .send_and_confirm_transaction_with_spinner_and_config(
3212 &final_tx,
3213 config.commitment,
3214 config.send_transaction_config,
3215 )
3216 .map_err(|e| format!("Deploying program failed: {e}"))?,
3217 ));
3218 }
3219 }
3220
3221 Ok(None)
3222}
3223
3224fn create_ephemeral_keypair(
3225) -> Result<(usize, bip39::Mnemonic, Keypair), Box<dyn std::error::Error>> {
3226 const WORDS: usize = 12;
3227 let mnemonic = Mnemonic::new(MnemonicType::for_word_count(WORDS)?, Language::English);
3228 let seed = Seed::new(&mnemonic, "");
3229 let new_keypair = keypair_from_seed(seed.as_bytes())?;
3230
3231 Ok((WORDS, mnemonic, new_keypair))
3232}
3233
3234fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic, ephemeral_pubkey: &Pubkey) {
3235 let phrase: &str = mnemonic.phrase();
3236 let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
3237 eprintln!("{divider}\nRecover the intermediate account's ephemeral keypair file with");
3238 eprintln!("`solana-keygen recover` and the following {words}-word seed phrase:");
3239 eprintln!("{divider}\n{phrase}\n{divider}");
3240 eprintln!("To resume a deploy, pass the recovered keypair as the");
3241 eprintln!("[BUFFER_SIGNER] to `solana program deploy` or `solana program write-buffer'.");
3242 eprintln!("Or to recover the account's lamports, use:");
3243 eprintln!("{divider}\nsolana program close {ephemeral_pubkey}\n{divider}");
3244}
3245
3246fn fetch_feature_set(rpc_client: &RpcClient) -> Result<FeatureSet, Box<dyn std::error::Error>> {
3247 let mut feature_set = FeatureSet::default();
3248 for feature_ids in FEATURE_NAMES
3249 .keys()
3250 .cloned()
3251 .collect::<Vec<Pubkey>>()
3252 .chunks(MAX_MULTIPLE_ACCOUNTS)
3253 {
3254 rpc_client
3255 .get_multiple_accounts(feature_ids)?
3256 .into_iter()
3257 .zip(feature_ids)
3258 .for_each(|(account, feature_id)| {
3259 let activation_slot = account.and_then(status_from_account);
3260
3261 if let Some(CliFeatureStatus::Active(slot)) = activation_slot {
3262 feature_set.activate(feature_id, slot);
3263 }
3264 });
3265 }
3266
3267 Ok(feature_set)
3268}
3269
3270#[cfg(test)]
3271mod tests {
3272 use {
3273 super::*,
3274 crate::{
3275 clap_app::get_clap_app,
3276 cli::{parse_command, process_command},
3277 },
3278 serde_json::Value,
3279 solana_cli_output::OutputFormat,
3280 solana_hash::Hash,
3281 solana_keypair::write_keypair_file,
3282 };
3283
3284 fn make_tmp_path(name: &str) -> String {
3285 let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
3286 let keypair = Keypair::new();
3287
3288 let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
3289
3290 let _ignored = std::fs::remove_dir_all(&path);
3292 let _ignored = std::fs::remove_file(&path);
3294
3295 path
3296 }
3297
3298 #[test]
3299 #[allow(clippy::cognitive_complexity)]
3300 fn test_cli_parse_deploy() {
3301 let test_commands = get_clap_app("test", "desc", "version");
3302
3303 let default_keypair = Keypair::new();
3304 let keypair_file = make_tmp_path("keypair_file");
3305 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3306 let default_signer = DefaultSigner::new("", &keypair_file);
3307
3308 let test_command = test_commands.clone().get_matches_from(vec![
3309 "test",
3310 "program",
3311 "deploy",
3312 "/Users/test/program.so",
3313 ]);
3314 assert_eq!(
3315 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3316 CliCommandInfo {
3317 command: CliCommand::Program(ProgramCliCommand::Deploy {
3318 program_location: Some("/Users/test/program.so".to_string()),
3319 fee_payer_signer_index: 0,
3320 buffer_signer_index: None,
3321 buffer_pubkey: None,
3322 program_signer_index: None,
3323 program_pubkey: None,
3324 upgrade_authority_signer_index: 0,
3325 is_final: false,
3326 max_len: None,
3327 skip_fee_check: false,
3328 compute_unit_price: None,
3329 max_sign_attempts: 5,
3330 auto_extend: true,
3331 use_rpc: false,
3332 skip_feature_verification: false,
3333 }),
3334 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3335 }
3336 );
3337
3338 let test_command = test_commands.clone().get_matches_from(vec![
3339 "test",
3340 "program",
3341 "deploy",
3342 "/Users/test/program.so",
3343 "--max-len",
3344 "42",
3345 ]);
3346 assert_eq!(
3347 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3348 CliCommandInfo {
3349 command: CliCommand::Program(ProgramCliCommand::Deploy {
3350 program_location: Some("/Users/test/program.so".to_string()),
3351 fee_payer_signer_index: 0,
3352 buffer_signer_index: None,
3353 buffer_pubkey: None,
3354 program_signer_index: None,
3355 program_pubkey: None,
3356 upgrade_authority_signer_index: 0,
3357 is_final: false,
3358 max_len: Some(42),
3359 skip_fee_check: false,
3360 compute_unit_price: None,
3361 max_sign_attempts: 5,
3362 auto_extend: true,
3363 use_rpc: false,
3364 skip_feature_verification: false,
3365 }),
3366 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3367 }
3368 );
3369
3370 let buffer_keypair = Keypair::new();
3371 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3372 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3373 let test_command = test_commands.clone().get_matches_from(vec![
3374 "test",
3375 "program",
3376 "deploy",
3377 "--buffer",
3378 &buffer_keypair_file,
3379 ]);
3380 assert_eq!(
3381 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3382 CliCommandInfo {
3383 command: CliCommand::Program(ProgramCliCommand::Deploy {
3384 program_location: None,
3385 fee_payer_signer_index: 0,
3386 buffer_signer_index: Some(1),
3387 buffer_pubkey: Some(buffer_keypair.pubkey()),
3388 program_signer_index: None,
3389 program_pubkey: None,
3390 upgrade_authority_signer_index: 0,
3391 is_final: false,
3392 max_len: None,
3393 skip_fee_check: false,
3394 compute_unit_price: None,
3395 max_sign_attempts: 5,
3396 auto_extend: true,
3397 use_rpc: false,
3398 skip_feature_verification: false,
3399 }),
3400 signers: vec![
3401 Box::new(read_keypair_file(&keypair_file).unwrap()),
3402 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3403 ],
3404 }
3405 );
3406
3407 let program_pubkey = Pubkey::new_unique();
3408 let test = test_commands.clone().get_matches_from(vec![
3409 "test",
3410 "program",
3411 "deploy",
3412 "/Users/test/program.so",
3413 "--program-id",
3414 &program_pubkey.to_string(),
3415 ]);
3416 assert_eq!(
3417 parse_command(&test, &default_signer, &mut None).unwrap(),
3418 CliCommandInfo {
3419 command: CliCommand::Program(ProgramCliCommand::Deploy {
3420 program_location: Some("/Users/test/program.so".to_string()),
3421 fee_payer_signer_index: 0,
3422 buffer_signer_index: None,
3423 buffer_pubkey: None,
3424 program_signer_index: None,
3425 program_pubkey: Some(program_pubkey),
3426 upgrade_authority_signer_index: 0,
3427 is_final: false,
3428 max_len: None,
3429 skip_fee_check: false,
3430 compute_unit_price: None,
3431 max_sign_attempts: 5,
3432 auto_extend: true,
3433 use_rpc: false,
3434 skip_feature_verification: false,
3435 }),
3436 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3437 }
3438 );
3439
3440 let program_keypair = Keypair::new();
3441 let program_keypair_file = make_tmp_path("program_keypair_file");
3442 write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
3443 let test = test_commands.clone().get_matches_from(vec![
3444 "test",
3445 "program",
3446 "deploy",
3447 "/Users/test/program.so",
3448 "--program-id",
3449 &program_keypair_file,
3450 ]);
3451 assert_eq!(
3452 parse_command(&test, &default_signer, &mut None).unwrap(),
3453 CliCommandInfo {
3454 command: CliCommand::Program(ProgramCliCommand::Deploy {
3455 program_location: Some("/Users/test/program.so".to_string()),
3456 fee_payer_signer_index: 0,
3457 buffer_signer_index: None,
3458 buffer_pubkey: None,
3459 program_signer_index: Some(1),
3460 program_pubkey: Some(program_keypair.pubkey()),
3461 upgrade_authority_signer_index: 0,
3462 is_final: false,
3463 max_len: None,
3464 skip_fee_check: false,
3465 compute_unit_price: None,
3466 max_sign_attempts: 5,
3467 auto_extend: true,
3468 use_rpc: false,
3469 skip_feature_verification: false,
3470 }),
3471 signers: vec![
3472 Box::new(read_keypair_file(&keypair_file).unwrap()),
3473 Box::new(read_keypair_file(&program_keypair_file).unwrap()),
3474 ],
3475 }
3476 );
3477
3478 let authority_keypair = Keypair::new();
3479 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3480 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3481 let test_command = test_commands.clone().get_matches_from(vec![
3482 "test",
3483 "program",
3484 "deploy",
3485 "/Users/test/program.so",
3486 "--upgrade-authority",
3487 &authority_keypair_file,
3488 ]);
3489 assert_eq!(
3490 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3491 CliCommandInfo {
3492 command: CliCommand::Program(ProgramCliCommand::Deploy {
3493 program_location: Some("/Users/test/program.so".to_string()),
3494 fee_payer_signer_index: 0,
3495 buffer_signer_index: None,
3496 buffer_pubkey: None,
3497 program_signer_index: None,
3498 program_pubkey: None,
3499 upgrade_authority_signer_index: 1,
3500 is_final: false,
3501 max_len: None,
3502 skip_fee_check: false,
3503 compute_unit_price: None,
3504 max_sign_attempts: 5,
3505 auto_extend: true,
3506 use_rpc: false,
3507 skip_feature_verification: false,
3508 }),
3509 signers: vec![
3510 Box::new(read_keypair_file(&keypair_file).unwrap()),
3511 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3512 ],
3513 }
3514 );
3515
3516 let test_command = test_commands.clone().get_matches_from(vec![
3517 "test",
3518 "program",
3519 "deploy",
3520 "/Users/test/program.so",
3521 "--final",
3522 ]);
3523 assert_eq!(
3524 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3525 CliCommandInfo {
3526 command: CliCommand::Program(ProgramCliCommand::Deploy {
3527 program_location: Some("/Users/test/program.so".to_string()),
3528 fee_payer_signer_index: 0,
3529 buffer_signer_index: None,
3530 buffer_pubkey: None,
3531 program_signer_index: None,
3532 program_pubkey: None,
3533 upgrade_authority_signer_index: 0,
3534 is_final: true,
3535 max_len: None,
3536 skip_fee_check: false,
3537 compute_unit_price: None,
3538 max_sign_attempts: 5,
3539 auto_extend: true,
3540 use_rpc: false,
3541 skip_feature_verification: false,
3542 }),
3543 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3544 }
3545 );
3546
3547 let test_command = test_commands.clone().get_matches_from(vec![
3548 "test",
3549 "program",
3550 "deploy",
3551 "/Users/test/program.so",
3552 "--max-sign-attempts",
3553 "1",
3554 ]);
3555 assert_eq!(
3556 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3557 CliCommandInfo {
3558 command: CliCommand::Program(ProgramCliCommand::Deploy {
3559 program_location: Some("/Users/test/program.so".to_string()),
3560 fee_payer_signer_index: 0,
3561 buffer_signer_index: None,
3562 buffer_pubkey: None,
3563 program_signer_index: None,
3564 program_pubkey: None,
3565 upgrade_authority_signer_index: 0,
3566 is_final: false,
3567 max_len: None,
3568 skip_fee_check: false,
3569 compute_unit_price: None,
3570 max_sign_attempts: 1,
3571 auto_extend: true,
3572 use_rpc: false,
3573 skip_feature_verification: false,
3574 }),
3575 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3576 }
3577 );
3578
3579 let test_command = test_commands.clone().get_matches_from(vec![
3580 "test",
3581 "program",
3582 "deploy",
3583 "/Users/test/program.so",
3584 "--use-rpc",
3585 ]);
3586 assert_eq!(
3587 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3588 CliCommandInfo {
3589 command: CliCommand::Program(ProgramCliCommand::Deploy {
3590 program_location: Some("/Users/test/program.so".to_string()),
3591 fee_payer_signer_index: 0,
3592 buffer_signer_index: None,
3593 buffer_pubkey: None,
3594 program_signer_index: None,
3595 program_pubkey: None,
3596 upgrade_authority_signer_index: 0,
3597 is_final: false,
3598 max_len: None,
3599 skip_fee_check: false,
3600 compute_unit_price: None,
3601 max_sign_attempts: 5,
3602 auto_extend: true,
3603 use_rpc: true,
3604 skip_feature_verification: false,
3605 }),
3606 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3607 }
3608 );
3609
3610 let test_command = test_commands.clone().get_matches_from(vec![
3611 "test",
3612 "program",
3613 "deploy",
3614 "/Users/test/program.so",
3615 "--skip-feature-verify",
3616 ]);
3617 assert_eq!(
3618 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3619 CliCommandInfo {
3620 command: CliCommand::Program(ProgramCliCommand::Deploy {
3621 program_location: Some("/Users/test/program.so".to_string()),
3622 fee_payer_signer_index: 0,
3623 buffer_signer_index: None,
3624 buffer_pubkey: None,
3625 program_signer_index: None,
3626 program_pubkey: None,
3627 upgrade_authority_signer_index: 0,
3628 is_final: false,
3629 max_len: None,
3630 skip_fee_check: false,
3631 compute_unit_price: None,
3632 max_sign_attempts: 5,
3633 auto_extend: true,
3634 use_rpc: false,
3635 skip_feature_verification: true,
3636 }),
3637 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3638 }
3639 );
3640 }
3641
3642 #[test]
3643 fn test_cli_parse_upgrade() {
3644 let test_commands = get_clap_app("test", "desc", "version");
3645
3646 let default_keypair = Keypair::new();
3647 let keypair_file = make_tmp_path("keypair_file");
3648 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3649 let default_signer = DefaultSigner::new("", &keypair_file);
3650
3651 let program_key = Pubkey::new_unique();
3652 let buffer_key = Pubkey::new_unique();
3653 let test_command = test_commands.clone().get_matches_from(vec![
3654 "test",
3655 "program",
3656 "upgrade",
3657 format!("{}", buffer_key).as_str(),
3658 format!("{}", program_key).as_str(),
3659 "--skip-feature-verify",
3660 ]);
3661 assert_eq!(
3662 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3663 CliCommandInfo {
3664 command: CliCommand::Program(ProgramCliCommand::Upgrade {
3665 fee_payer_signer_index: 0,
3666 program_pubkey: program_key,
3667 buffer_pubkey: buffer_key,
3668 upgrade_authority_signer_index: 0,
3669 sign_only: false,
3670 dump_transaction_message: false,
3671 blockhash_query: BlockhashQuery::default(),
3672 skip_feature_verification: true,
3673 }),
3674 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3675 }
3676 );
3677 }
3678
3679 #[test]
3680 #[allow(clippy::cognitive_complexity)]
3681 fn test_cli_parse_write_buffer() {
3682 let test_commands = get_clap_app("test", "desc", "version");
3683
3684 let default_keypair = Keypair::new();
3685 let keypair_file = make_tmp_path("keypair_file");
3686 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3687 let default_signer = DefaultSigner::new("", &keypair_file);
3688
3689 let test_command = test_commands.clone().get_matches_from(vec![
3691 "test",
3692 "program",
3693 "write-buffer",
3694 "/Users/test/program.so",
3695 ]);
3696 assert_eq!(
3697 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3698 CliCommandInfo {
3699 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3700 program_location: "/Users/test/program.so".to_string(),
3701 fee_payer_signer_index: 0,
3702 buffer_signer_index: None,
3703 buffer_pubkey: None,
3704 buffer_authority_signer_index: 0,
3705 max_len: None,
3706 skip_fee_check: false,
3707 compute_unit_price: None,
3708 max_sign_attempts: 5,
3709 use_rpc: false,
3710 skip_feature_verification: false,
3711 }),
3712 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3713 }
3714 );
3715
3716 let test_command = test_commands.clone().get_matches_from(vec![
3718 "test",
3719 "program",
3720 "write-buffer",
3721 "/Users/test/program.so",
3722 "--max-len",
3723 "42",
3724 ]);
3725 assert_eq!(
3726 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3727 CliCommandInfo {
3728 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3729 program_location: "/Users/test/program.so".to_string(),
3730 fee_payer_signer_index: 0,
3731 buffer_signer_index: None,
3732 buffer_pubkey: None,
3733 buffer_authority_signer_index: 0,
3734 max_len: Some(42),
3735 skip_fee_check: false,
3736 compute_unit_price: None,
3737 max_sign_attempts: 5,
3738 use_rpc: false,
3739 skip_feature_verification: false,
3740 }),
3741 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3742 }
3743 );
3744
3745 let buffer_keypair = Keypair::new();
3747 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3748 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3749 let test_command = test_commands.clone().get_matches_from(vec![
3750 "test",
3751 "program",
3752 "write-buffer",
3753 "/Users/test/program.so",
3754 "--buffer",
3755 &buffer_keypair_file,
3756 ]);
3757 assert_eq!(
3758 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3759 CliCommandInfo {
3760 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3761 program_location: "/Users/test/program.so".to_string(),
3762 fee_payer_signer_index: 0,
3763 buffer_signer_index: Some(1),
3764 buffer_pubkey: Some(buffer_keypair.pubkey()),
3765 buffer_authority_signer_index: 0,
3766 max_len: None,
3767 skip_fee_check: false,
3768 compute_unit_price: None,
3769 max_sign_attempts: 5,
3770 use_rpc: false,
3771 skip_feature_verification: false,
3772 }),
3773 signers: vec![
3774 Box::new(read_keypair_file(&keypair_file).unwrap()),
3775 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3776 ],
3777 }
3778 );
3779
3780 let authority_keypair = Keypair::new();
3782 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3783 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3784 let test_command = test_commands.clone().get_matches_from(vec![
3785 "test",
3786 "program",
3787 "write-buffer",
3788 "/Users/test/program.so",
3789 "--buffer-authority",
3790 &authority_keypair_file,
3791 ]);
3792 assert_eq!(
3793 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3794 CliCommandInfo {
3795 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3796 program_location: "/Users/test/program.so".to_string(),
3797 fee_payer_signer_index: 0,
3798 buffer_signer_index: None,
3799 buffer_pubkey: None,
3800 buffer_authority_signer_index: 1,
3801 max_len: None,
3802 skip_fee_check: false,
3803 compute_unit_price: None,
3804 max_sign_attempts: 5,
3805 use_rpc: false,
3806 skip_feature_verification: false,
3807 }),
3808 signers: vec![
3809 Box::new(read_keypair_file(&keypair_file).unwrap()),
3810 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3811 ],
3812 }
3813 );
3814
3815 let buffer_keypair = Keypair::new();
3817 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3818 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3819 let authority_keypair = Keypair::new();
3820 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3821 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3822 let test_command = test_commands.clone().get_matches_from(vec![
3823 "test",
3824 "program",
3825 "write-buffer",
3826 "/Users/test/program.so",
3827 "--buffer",
3828 &buffer_keypair_file,
3829 "--buffer-authority",
3830 &authority_keypair_file,
3831 ]);
3832 assert_eq!(
3833 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3834 CliCommandInfo {
3835 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3836 program_location: "/Users/test/program.so".to_string(),
3837 fee_payer_signer_index: 0,
3838 buffer_signer_index: Some(1),
3839 buffer_pubkey: Some(buffer_keypair.pubkey()),
3840 buffer_authority_signer_index: 2,
3841 max_len: None,
3842 skip_fee_check: false,
3843 compute_unit_price: None,
3844 max_sign_attempts: 5,
3845 use_rpc: false,
3846 skip_feature_verification: false,
3847 }),
3848 signers: vec![
3849 Box::new(read_keypair_file(&keypair_file).unwrap()),
3850 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3851 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3852 ],
3853 }
3854 );
3855
3856 let test_command = test_commands.clone().get_matches_from(vec![
3858 "test",
3859 "program",
3860 "write-buffer",
3861 "/Users/test/program.so",
3862 "--max-sign-attempts",
3863 "10",
3864 ]);
3865 assert_eq!(
3866 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3867 CliCommandInfo {
3868 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3869 program_location: "/Users/test/program.so".to_string(),
3870 fee_payer_signer_index: 0,
3871 buffer_signer_index: None,
3872 buffer_pubkey: None,
3873 buffer_authority_signer_index: 0,
3874 max_len: None,
3875 skip_fee_check: false,
3876 compute_unit_price: None,
3877 max_sign_attempts: 10,
3878 use_rpc: false,
3879 skip_feature_verification: false
3880 }),
3881 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3882 }
3883 );
3884
3885 let test_command = test_commands.clone().get_matches_from(vec![
3887 "test",
3888 "program",
3889 "write-buffer",
3890 "/Users/test/program.so",
3891 "--skip-feature-verify",
3892 ]);
3893 assert_eq!(
3894 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3895 CliCommandInfo {
3896 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3897 program_location: "/Users/test/program.so".to_string(),
3898 fee_payer_signer_index: 0,
3899 buffer_signer_index: None,
3900 buffer_pubkey: None,
3901 buffer_authority_signer_index: 0,
3902 max_len: None,
3903 skip_fee_check: false,
3904 compute_unit_price: None,
3905 max_sign_attempts: 5,
3906 use_rpc: false,
3907 skip_feature_verification: true,
3908 }),
3909 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3910 }
3911 );
3912 }
3913
3914 #[test]
3915 #[allow(clippy::cognitive_complexity)]
3916 fn test_cli_parse_set_upgrade_authority() {
3917 let test_commands = get_clap_app("test", "desc", "version");
3918
3919 let default_keypair = Keypair::new();
3920 let keypair_file = make_tmp_path("keypair_file");
3921 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3922 let default_signer = DefaultSigner::new("", &keypair_file);
3923
3924 let program_pubkey = Pubkey::new_unique();
3925 let new_authority_pubkey = Pubkey::new_unique();
3926 let blockhash = Hash::new_unique();
3927
3928 let test_command = test_commands.clone().get_matches_from(vec![
3929 "test",
3930 "program",
3931 "set-upgrade-authority",
3932 &program_pubkey.to_string(),
3933 "--new-upgrade-authority",
3934 &new_authority_pubkey.to_string(),
3935 "--skip-new-upgrade-authority-signer-check",
3936 "--sign-only",
3937 "--dump-transaction-message",
3938 "--blockhash",
3939 blockhash.to_string().as_str(),
3940 ]);
3941 assert_eq!(
3942 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3943 CliCommandInfo {
3944 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3945 program_pubkey,
3946 upgrade_authority_index: Some(0),
3947 new_upgrade_authority: Some(new_authority_pubkey),
3948 sign_only: true,
3949 dump_transaction_message: true,
3950 blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
3951 }),
3952 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3953 }
3954 );
3955
3956 let program_pubkey = Pubkey::new_unique();
3957 let new_authority_pubkey = Keypair::new();
3958 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3959 write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3960 let test_command = test_commands.clone().get_matches_from(vec![
3961 "test",
3962 "program",
3963 "set-upgrade-authority",
3964 &program_pubkey.to_string(),
3965 "--new-upgrade-authority",
3966 &new_authority_pubkey_file,
3967 "--skip-new-upgrade-authority-signer-check",
3968 ]);
3969 assert_eq!(
3970 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3971 CliCommandInfo {
3972 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
3973 program_pubkey,
3974 upgrade_authority_index: Some(0),
3975 new_upgrade_authority: Some(new_authority_pubkey.pubkey()),
3976 sign_only: false,
3977 dump_transaction_message: false,
3978 blockhash_query: BlockhashQuery::default(),
3979 }),
3980 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3981 }
3982 );
3983
3984 let blockhash = Hash::new_unique();
3985 let program_pubkey = Pubkey::new_unique();
3986 let new_authority_pubkey = Keypair::new();
3987 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
3988 write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
3989 let test_command = test_commands.clone().get_matches_from(vec![
3990 "test",
3991 "program",
3992 "set-upgrade-authority",
3993 &program_pubkey.to_string(),
3994 "--new-upgrade-authority",
3995 &new_authority_pubkey_file,
3996 "--sign-only",
3997 "--dump-transaction-message",
3998 "--blockhash",
3999 blockhash.to_string().as_str(),
4000 ]);
4001 assert_eq!(
4002 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4003 CliCommandInfo {
4004 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
4005 program_pubkey,
4006 upgrade_authority_index: 0,
4007 new_upgrade_authority_index: 1,
4008 sign_only: true,
4009 dump_transaction_message: true,
4010 blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4011 }),
4012 signers: vec![
4013 Box::new(read_keypair_file(&keypair_file).unwrap()),
4014 Box::new(read_keypair_file(&new_authority_pubkey_file).unwrap()),
4015 ],
4016 }
4017 );
4018
4019 let program_pubkey = Pubkey::new_unique();
4020 let new_authority_pubkey = Keypair::new();
4021 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4022 write_keypair_file(&new_authority_pubkey, new_authority_pubkey_file).unwrap();
4023 let test_command = test_commands.clone().get_matches_from(vec![
4024 "test",
4025 "program",
4026 "set-upgrade-authority",
4027 &program_pubkey.to_string(),
4028 "--final",
4029 ]);
4030 assert_eq!(
4031 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4032 CliCommandInfo {
4033 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4034 program_pubkey,
4035 upgrade_authority_index: Some(0),
4036 new_upgrade_authority: None,
4037 sign_only: false,
4038 dump_transaction_message: false,
4039 blockhash_query: BlockhashQuery::default(),
4040 }),
4041 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4042 }
4043 );
4044
4045 let program_pubkey = Pubkey::new_unique();
4046 let authority = Keypair::new();
4047 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4048 write_keypair_file(&authority, &authority_keypair_file).unwrap();
4049 let test_command = test_commands.clone().get_matches_from(vec![
4050 "test",
4051 "program",
4052 "set-upgrade-authority",
4053 &program_pubkey.to_string(),
4054 "--upgrade-authority",
4055 &authority_keypair_file,
4056 "--final",
4057 ]);
4058 assert_eq!(
4059 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4060 CliCommandInfo {
4061 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4062 program_pubkey,
4063 upgrade_authority_index: Some(1),
4064 new_upgrade_authority: None,
4065 sign_only: false,
4066 dump_transaction_message: false,
4067 blockhash_query: BlockhashQuery::default(),
4068 }),
4069 signers: vec![
4070 Box::new(read_keypair_file(&keypair_file).unwrap()),
4071 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4072 ],
4073 }
4074 );
4075 }
4076
4077 #[test]
4078 #[allow(clippy::cognitive_complexity)]
4079 fn test_cli_parse_set_buffer_authority() {
4080 let test_commands = get_clap_app("test", "desc", "version");
4081
4082 let default_keypair = Keypair::new();
4083 let keypair_file = make_tmp_path("keypair_file");
4084 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4085 let default_signer = DefaultSigner::new("", &keypair_file);
4086
4087 let buffer_pubkey = Pubkey::new_unique();
4088 let new_authority_pubkey = Pubkey::new_unique();
4089 let test_command = test_commands.clone().get_matches_from(vec![
4090 "test",
4091 "program",
4092 "set-buffer-authority",
4093 &buffer_pubkey.to_string(),
4094 "--new-buffer-authority",
4095 &new_authority_pubkey.to_string(),
4096 ]);
4097 assert_eq!(
4098 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4099 CliCommandInfo {
4100 command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4101 buffer_pubkey,
4102 buffer_authority_index: Some(0),
4103 new_buffer_authority: new_authority_pubkey,
4104 }),
4105 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4106 }
4107 );
4108
4109 let buffer_pubkey = Pubkey::new_unique();
4110 let new_authority_keypair = Keypair::new();
4111 let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
4112 write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
4113 let test_command = test_commands.clone().get_matches_from(vec![
4114 "test",
4115 "program",
4116 "set-buffer-authority",
4117 &buffer_pubkey.to_string(),
4118 "--new-buffer-authority",
4119 &new_authority_keypair_file,
4120 ]);
4121 assert_eq!(
4122 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4123 CliCommandInfo {
4124 command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4125 buffer_pubkey,
4126 buffer_authority_index: Some(0),
4127 new_buffer_authority: new_authority_keypair.pubkey(),
4128 }),
4129 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4130 }
4131 );
4132 }
4133
4134 #[test]
4135 #[allow(clippy::cognitive_complexity)]
4136 fn test_cli_parse_show() {
4137 let test_commands = get_clap_app("test", "desc", "version");
4138
4139 let default_keypair = Keypair::new();
4140 let keypair_file = make_tmp_path("keypair_file");
4141 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4142 let default_signer = DefaultSigner::new("", &keypair_file);
4143
4144 let buffer_pubkey = Pubkey::new_unique();
4146 let authority_keypair = Keypair::new();
4147 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4148 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4149
4150 let test_command = test_commands.clone().get_matches_from(vec![
4151 "test",
4152 "program",
4153 "show",
4154 &buffer_pubkey.to_string(),
4155 ]);
4156 assert_eq!(
4157 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4158 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4159 account_pubkey: Some(buffer_pubkey),
4160 authority_pubkey: default_keypair.pubkey(),
4161 get_programs: false,
4162 get_buffers: false,
4163 all: false,
4164 use_lamports_unit: false,
4165 }))
4166 );
4167
4168 let test_command = test_commands.clone().get_matches_from(vec![
4169 "test",
4170 "program",
4171 "show",
4172 "--programs",
4173 "--all",
4174 "--lamports",
4175 ]);
4176 assert_eq!(
4177 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4178 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4179 account_pubkey: None,
4180 authority_pubkey: default_keypair.pubkey(),
4181 get_programs: true,
4182 get_buffers: false,
4183 all: true,
4184 use_lamports_unit: true,
4185 }))
4186 );
4187
4188 let test_command = test_commands.clone().get_matches_from(vec![
4189 "test",
4190 "program",
4191 "show",
4192 "--buffers",
4193 "--all",
4194 "--lamports",
4195 ]);
4196 assert_eq!(
4197 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4198 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4199 account_pubkey: None,
4200 authority_pubkey: default_keypair.pubkey(),
4201 get_programs: false,
4202 get_buffers: true,
4203 all: true,
4204 use_lamports_unit: true,
4205 }))
4206 );
4207
4208 let test_command = test_commands.clone().get_matches_from(vec![
4209 "test",
4210 "program",
4211 "show",
4212 "--buffers",
4213 "--buffer-authority",
4214 &authority_keypair.pubkey().to_string(),
4215 ]);
4216 assert_eq!(
4217 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4218 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4219 account_pubkey: None,
4220 authority_pubkey: authority_keypair.pubkey(),
4221 get_programs: false,
4222 get_buffers: true,
4223 all: false,
4224 use_lamports_unit: false,
4225 }))
4226 );
4227
4228 let test_command = test_commands.clone().get_matches_from(vec![
4229 "test",
4230 "program",
4231 "show",
4232 "--buffers",
4233 "--buffer-authority",
4234 &authority_keypair_file,
4235 ]);
4236 assert_eq!(
4237 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4238 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4239 account_pubkey: None,
4240 authority_pubkey: authority_keypair.pubkey(),
4241 get_programs: false,
4242 get_buffers: true,
4243 all: false,
4244 use_lamports_unit: false,
4245 }))
4246 );
4247 }
4248
4249 #[test]
4250 #[allow(clippy::cognitive_complexity)]
4251 fn test_cli_parse_close() {
4252 let test_commands = get_clap_app("test", "desc", "version");
4253
4254 let default_keypair = Keypair::new();
4255 let keypair_file = make_tmp_path("keypair_file");
4256 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4257 let default_signer = DefaultSigner::new("", &keypair_file);
4258
4259 let buffer_pubkey = Pubkey::new_unique();
4261 let recipient_pubkey = Pubkey::new_unique();
4262 let authority_keypair = Keypair::new();
4263 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4264
4265 let test_command = test_commands.clone().get_matches_from(vec![
4266 "test",
4267 "program",
4268 "close",
4269 &buffer_pubkey.to_string(),
4270 ]);
4271 assert_eq!(
4272 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4273 CliCommandInfo {
4274 command: CliCommand::Program(ProgramCliCommand::Close {
4275 account_pubkey: Some(buffer_pubkey),
4276 recipient_pubkey: default_keypair.pubkey(),
4277 authority_index: 0,
4278 use_lamports_unit: false,
4279 bypass_warning: false,
4280 }),
4281 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4282 }
4283 );
4284
4285 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4287 let test_command = test_commands.clone().get_matches_from(vec![
4288 "test",
4289 "program",
4290 "close",
4291 &buffer_pubkey.to_string(),
4292 "--bypass-warning",
4293 ]);
4294 assert_eq!(
4295 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4296 CliCommandInfo {
4297 command: CliCommand::Program(ProgramCliCommand::Close {
4298 account_pubkey: Some(buffer_pubkey),
4299 recipient_pubkey: default_keypair.pubkey(),
4300 authority_index: 0,
4301 use_lamports_unit: false,
4302 bypass_warning: true,
4303 }),
4304 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4305 }
4306 );
4307
4308 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4310 let test_command = test_commands.clone().get_matches_from(vec![
4311 "test",
4312 "program",
4313 "close",
4314 &buffer_pubkey.to_string(),
4315 "--buffer-authority",
4316 &authority_keypair_file,
4317 ]);
4318 assert_eq!(
4319 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4320 CliCommandInfo {
4321 command: CliCommand::Program(ProgramCliCommand::Close {
4322 account_pubkey: Some(buffer_pubkey),
4323 recipient_pubkey: default_keypair.pubkey(),
4324 authority_index: 1,
4325 use_lamports_unit: false,
4326 bypass_warning: false,
4327 }),
4328 signers: vec![
4329 Box::new(read_keypair_file(&keypair_file).unwrap()),
4330 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4331 ],
4332 }
4333 );
4334
4335 let test_command = test_commands.clone().get_matches_from(vec![
4337 "test",
4338 "program",
4339 "close",
4340 &buffer_pubkey.to_string(),
4341 "--recipient",
4342 &recipient_pubkey.to_string(),
4343 ]);
4344 assert_eq!(
4345 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4346 CliCommandInfo {
4347 command: CliCommand::Program(ProgramCliCommand::Close {
4348 account_pubkey: Some(buffer_pubkey),
4349 recipient_pubkey,
4350 authority_index: 0,
4351 use_lamports_unit: false,
4352 bypass_warning: false,
4353 }),
4354 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4355 }
4356 );
4357
4358 let test_command = test_commands.clone().get_matches_from(vec![
4360 "test",
4361 "program",
4362 "close",
4363 "--buffers",
4364 "--lamports",
4365 ]);
4366 assert_eq!(
4367 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4368 CliCommandInfo {
4369 command: CliCommand::Program(ProgramCliCommand::Close {
4370 account_pubkey: None,
4371 recipient_pubkey: default_keypair.pubkey(),
4372 authority_index: 0,
4373 use_lamports_unit: true,
4374 bypass_warning: false,
4375 }),
4376 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4377 }
4378 );
4379 }
4380
4381 #[test]
4382 fn test_cli_parse_extend_program() {
4383 let test_commands = get_clap_app("test", "desc", "version");
4384
4385 let default_keypair = Keypair::new();
4386 let keypair_file = make_tmp_path("keypair_file");
4387 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4388 let default_signer = DefaultSigner::new("", &keypair_file);
4389
4390 let program_pubkey = Pubkey::new_unique();
4392 let additional_bytes = 100;
4393
4394 let test_command = test_commands.clone().get_matches_from(vec![
4395 "test",
4396 "program",
4397 "extend",
4398 &program_pubkey.to_string(),
4399 &additional_bytes.to_string(),
4400 ]);
4401 assert_eq!(
4402 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4403 CliCommandInfo {
4404 command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4405 program_pubkey,
4406 additional_bytes
4407 }),
4408 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4409 }
4410 );
4411 }
4412
4413 #[test]
4414 fn test_cli_parse_migrate_program() {
4415 let test_commands = get_clap_app("test", "desc", "version");
4416
4417 let default_keypair = Keypair::new();
4418 let keypair_file = make_tmp_path("keypair_file");
4419 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4420 let default_signer = DefaultSigner::new("", &keypair_file);
4421
4422 let program_pubkey = Pubkey::new_unique();
4423 let authority_keypair = Keypair::new();
4424 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4425 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4426
4427 let test_command = test_commands.clone().get_matches_from(vec![
4428 "test",
4429 "program",
4430 "migrate",
4431 &program_pubkey.to_string(),
4432 "--authority",
4433 &authority_keypair_file.to_string(),
4434 "--with-compute-unit-price",
4435 "1",
4436 ]);
4437 assert_eq!(
4438 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4439 CliCommandInfo {
4440 command: CliCommand::Program(ProgramCliCommand::MigrateProgram {
4441 program_pubkey,
4442 authority_signer_index: 1,
4443 compute_unit_price: Some(1),
4444 }),
4445 signers: vec![
4446 Box::new(read_keypair_file(&keypair_file).unwrap()),
4447 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4448 ],
4449 }
4450 );
4451 }
4452
4453 #[test]
4454 fn test_cli_keypair_file() {
4455 solana_logger::setup();
4456
4457 let default_keypair = Keypair::new();
4458 let program_pubkey = Keypair::new();
4459 let deploy_path = make_tmp_path("deploy");
4460 let mut program_location = PathBuf::from(deploy_path.clone());
4461 program_location.push("noop");
4462 program_location.set_extension("so");
4463 let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
4464 pathbuf.push("tests");
4465 pathbuf.push("fixtures");
4466 pathbuf.push("noop");
4467 pathbuf.set_extension("so");
4468 let program_keypair_location = program_location.with_file_name("noop-keypair.json");
4469 std::fs::create_dir_all(deploy_path).unwrap();
4470 std::fs::copy(pathbuf, program_location.as_os_str()).unwrap();
4471 write_keypair_file(&program_pubkey, program_keypair_location).unwrap();
4472
4473 let config = CliConfig {
4474 rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
4475 command: CliCommand::Program(ProgramCliCommand::Deploy {
4476 program_location: Some(program_location.to_str().unwrap().to_string()),
4477 fee_payer_signer_index: 0,
4478 buffer_signer_index: None,
4479 buffer_pubkey: None,
4480 program_signer_index: None,
4481 program_pubkey: None,
4482 upgrade_authority_signer_index: 0,
4483 is_final: false,
4484 max_len: None,
4485 skip_fee_check: false,
4486 compute_unit_price: None,
4487 max_sign_attempts: 5,
4488 auto_extend: true,
4489 use_rpc: false,
4490 skip_feature_verification: true,
4491 }),
4492 signers: vec![&default_keypair],
4493 output_format: OutputFormat::JsonCompact,
4494 ..CliConfig::default()
4495 };
4496
4497 let result = process_command(&config);
4498 let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
4499 let program_id = json
4500 .as_object()
4501 .unwrap()
4502 .get("programId")
4503 .unwrap()
4504 .as_str()
4505 .unwrap();
4506
4507 assert_eq!(
4508 program_id.parse::<Pubkey>().unwrap(),
4509 program_pubkey.pubkey()
4510 );
4511 }
4512}