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