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