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