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