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