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