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