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