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 local_verifier::verify,
14 },
15 agave_feature_set::{FEATURE_NAMES, FeatureSet},
16 bip39::{Language, Mnemonic},
17 clap::{App, AppSettings, Arg, ArgMatches, SubCommand},
18 log::*,
19 solana_account_decoder::{UiAccount, UiAccountEncoding, UiDataSliceConfig},
20 solana_clap_utils::{
21 self,
22 compute_budget::{ComputeUnitLimit, compute_unit_price_arg},
23 fee_payer::{FEE_PAYER_ARG, fee_payer_arg},
24 hidden_unless_forced,
25 input_parsers::*,
26 input_validators::*,
27 keypair::*,
28 offline::{DUMP_TRANSACTION_MESSAGE, OfflineArgs, SIGN_ONLY_ARG},
29 },
30 solana_cli_output::{
31 CliProgram, CliProgramAccountType, CliProgramAuthority, CliProgramBuffer, CliProgramId,
32 CliUpgradeableBuffer, CliUpgradeableBuffers, CliUpgradeableProgram,
33 CliUpgradeableProgramClosed, CliUpgradeableProgramExtended, CliUpgradeablePrograms,
34 ReturnSignersConfig, return_signers_with_config,
35 },
36 solana_client::send_and_confirm_transactions_in_parallel::{
37 SendAndConfirmConfigV3, SendTransport, send_and_confirm_transactions_in_parallel_v3,
38 },
39 solana_commitment_config::CommitmentConfig,
40 solana_instruction::{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::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 verify(
3068 executable.get_text_bytes().1,
3069 executable.get_sbpf_version(),
3070 executable.get_loader().get_function_registry(),
3071 )
3072 .map_err(|err| format!("Verifier error: {err} (local pre-flight)").into())
3073}
3074
3075fn explain_elf_error(err: &EbpfError, program_data: &[u8], config: &Config) -> String {
3078 let EbpfError::ElfError(ElfError::UnsupportedSBPFVersion) = err else {
3082 return format!("{err} (local pre-flight)");
3083 };
3084
3085 fn sbpf_version_label(version: SBPFVersion) -> &'static str {
3086 match version {
3087 SBPFVersion::V0 => "v0",
3088 SBPFVersion::V1 => "v1",
3089 SBPFVersion::V2 => "v2",
3090 SBPFVersion::V3 => "v3",
3091 SBPFVersion::V4 => "v4",
3092 SBPFVersion::Reserved => "reserved",
3093 }
3094 }
3095
3096 let min = sbpf_version_label(*config.enabled_sbpf_versions.start());
3097 let max = sbpf_version_label(*config.enabled_sbpf_versions.end());
3098 match get_sbpf_version(program_data) {
3099 Ok(version) => {
3100 let detected = sbpf_version_label(version);
3101 format!(
3102 "program targets SBPF {detected}, which this CLI does not have enabled (enabled: \
3103 {min}–{max}). Rebuild the program targeting {max}."
3104 )
3105 }
3106 Err(error) => format!("could not read SBPF version: {error} (local pre-flight)"),
3107 }
3108}
3109
3110async fn check_payer(
3111 rpc_client: &RpcClient,
3112 config: &CliConfig<'_>,
3113 fee_payer_pubkey: Pubkey,
3114 balance_needed: u64,
3115 initial_message: &Option<Message>,
3116 write_messages: &[Message],
3117 final_message: &Option<Message>,
3118) -> Result<(), Box<dyn std::error::Error>> {
3119 let mut fee = Saturating(0);
3120 if let Some(message) = initial_message {
3121 fee += rpc_client.get_fee_for_message(message).await?;
3122 }
3123 if let Some(message) = write_messages.first() {
3125 fee += rpc_client
3126 .get_fee_for_message(message)
3127 .await?
3128 .saturating_mul(write_messages.len() as u64);
3129 }
3130 if let Some(message) = final_message {
3131 fee += rpc_client.get_fee_for_message(message).await?;
3132 }
3133 check_account_for_spend_and_fee_with_commitment(
3134 rpc_client,
3135 &fee_payer_pubkey,
3136 balance_needed,
3137 fee.0,
3138 config.commitment,
3139 )
3140 .await?;
3141 Ok(())
3142}
3143
3144fn dedup_signers<'a>(signers: &[&'a dyn Signer]) -> Vec<&'a dyn Signer> {
3145 let mut seen = Vec::with_capacity(signers.len());
3146 signers
3147 .iter()
3148 .filter(|signer| {
3149 let pubkey = signer.pubkey();
3150 let is_new = !seen.contains(&pubkey);
3151 if is_new {
3152 seen.push(pubkey);
3153 }
3154 is_new
3155 })
3156 .copied()
3157 .collect()
3158}
3159
3160#[allow(clippy::too_many_arguments)]
3161async fn send_deploy_messages(
3162 rpc_client: Arc<RpcClient>,
3163 config: &CliConfig<'_>,
3164 initial_message: Option<Message>,
3165 mut write_messages: Vec<Message>,
3166 final_message: Option<Message>,
3167 fee_payer_signer: &dyn Signer,
3168 initial_signer: Option<&dyn Signer>,
3169 write_signer: Option<&dyn Signer>,
3170 final_signers: Option<&[&dyn Signer]>,
3171 max_sign_attempts: usize,
3172 use_rpc: bool,
3173 compute_unit_limit: &ComputeUnitLimit,
3174) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
3175 if let Some(mut message) = initial_message {
3176 if let Some(initial_signer) = initial_signer {
3177 trace!("Preparing the required accounts");
3178 simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)
3179 .await?;
3180 let mut initial_transaction = Transaction::new_unsigned(message.clone());
3181 let blockhash = rpc_client.get_latest_blockhash().await?;
3182
3183 if message.header.num_required_signatures == 3 {
3188 initial_transaction.try_sign(
3189 &[fee_payer_signer, initial_signer, write_signer.unwrap()],
3190 blockhash,
3191 )?;
3192 } else if message.header.num_required_signatures == 2 {
3193 initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
3194 } else {
3195 initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
3196 }
3197 let result = rpc_client
3198 .send_and_confirm_transaction_with_spinner_and_config(
3199 &initial_transaction,
3200 config.commitment,
3201 config.send_transaction_config,
3202 )
3203 .await;
3204 log_instruction_custom_error::<SystemError>(result, config)
3205 .map_err(|err| format!("Account allocation failed: {err}"))?;
3206 } else {
3207 return Err("Buffer account not created yet, must provide a key pair".into());
3208 }
3209 }
3210
3211 if !write_messages.is_empty()
3212 && let Some(write_signer) = write_signer
3213 {
3214 trace!("Writing program data");
3215
3216 {
3220 let mut message = write_messages[0].clone();
3221 if let UpdateComputeUnitLimitResult::UpdatedInstructionIndex(ix_index) =
3222 simulate_and_update_compute_unit_limit(
3223 compute_unit_limit,
3224 &rpc_client,
3225 &mut message,
3226 )
3227 .await?
3228 {
3229 for msg in &mut write_messages {
3230 assert_eq!(msg.program_id(ix_index), Some(&compute_budget::id()));
3236 msg.instructions[ix_index]
3237 .data
3238 .clone_from(&message.instructions[ix_index].data);
3239 }
3240 }
3241
3242 let _tpu_client;
3244 let cancel_token = CancellationToken::new();
3245 let transport = if use_rpc {
3246 SendTransport::Rpc(config.send_transaction_config)
3247 } else {
3248 let node_address_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) =
3259 ClientBuilder::new(Box::new(node_address_service))
3260 .cancel_token(cancel_token.clone())
3261 .bind_socket(bind_socket)
3262 .broadcaster(NonblockingBroadcaster)
3263 .build()
3264 .expect("Failed to build TPU client");
3265 _tpu_client = client;
3266 SendTransport::Tpu(transaction_sender)
3267 };
3268
3269 let versioned_write_messages = write_messages.into_iter().map(VersionedMessage::Legacy);
3270
3271 let transaction_errors = send_and_confirm_transactions_in_parallel_v3(
3272 rpc_client.clone(),
3273 transport,
3274 versioned_write_messages,
3275 &dedup_signers(&[fee_payer_signer, write_signer]),
3276 SendAndConfirmConfigV3 {
3277 with_spinner: true,
3278 max_sign_attempts: NonZeroUsize::new(max_sign_attempts)
3279 .ok_or("--max-sign-attempts must be at least 1")?,
3280 check_interval: CHECK_INTERVAL,
3281 send_interval: SEND_INTERVAL,
3282 },
3283 )
3284 .await
3285 .map_err(|err| format!("Data writes to account failed: {err}"))?
3286 .into_iter()
3287 .flatten()
3288 .collect::<Vec<_>>();
3289
3290 if !transaction_errors.is_empty() {
3291 for transaction_error in &transaction_errors {
3292 error!("{transaction_error:?}");
3293 }
3294 return Err(
3295 format!("{} write transactions failed", transaction_errors.len()).into(),
3296 );
3297 }
3298 }
3299 }
3300
3301 if let Some(mut message) = final_message
3302 && let Some(final_signers) = final_signers
3303 {
3304 trace!("Deploying program");
3305
3306 simulate_and_update_compute_unit_limit(compute_unit_limit, &rpc_client, &mut message)
3307 .await?;
3308 let mut final_tx = Transaction::new_unsigned(message);
3309 let blockhash = rpc_client.get_latest_blockhash().await?;
3310 let mut signers = final_signers.to_vec();
3311 signers.push(fee_payer_signer);
3312 final_tx.try_sign(&signers, blockhash)?;
3313 let result = rpc_client
3314 .send_and_confirm_transaction_with_spinner_and_config(
3315 &final_tx,
3316 config.commitment,
3317 config.send_transaction_config,
3318 )
3319 .await
3320 .map_err(|e| format!("Deploying program failed: {e}"))?;
3321 return Ok(Some(result));
3322 }
3323
3324 Ok(None)
3325}
3326
3327fn create_ephemeral_keypair()
3328-> Result<(usize, bip39::Mnemonic, Keypair), Box<dyn std::error::Error>> {
3329 const WORDS: usize = 12;
3330 let mnemonic = Mnemonic::generate_in(Language::English, WORDS)?;
3331 let seed = mnemonic.to_seed("");
3332 let new_keypair = keypair_from_seed(&seed)?;
3333
3334 Ok((WORDS, mnemonic, new_keypair))
3335}
3336
3337fn report_ephemeral_mnemonic(words: usize, mnemonic: bip39::Mnemonic, ephemeral_pubkey: &Pubkey) {
3338 let phrase = mnemonic.to_string();
3339 let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
3340 eprintln!("{divider}\nRecover the intermediate account's ephemeral keypair file with");
3341 eprintln!("`solana-keygen recover` and the following {words}-word seed phrase:");
3342 eprintln!("{divider}\n{phrase}\n{divider}");
3343 eprintln!("To resume a deploy, pass the recovered keypair as the");
3344 eprintln!("[BUFFER_SIGNER] to `solana program deploy` or `solana program write-buffer'.");
3345 eprintln!("Or to recover the account's lamports, use:");
3346 eprintln!("{divider}\nsolana program close {ephemeral_pubkey}\n{divider}");
3347}
3348
3349async fn fetch_feature_set(
3350 rpc_client: &RpcClient,
3351) -> Result<FeatureSet, Box<dyn std::error::Error>> {
3352 let mut feature_set = FeatureSet::default();
3353 for feature_ids in FEATURE_NAMES
3354 .keys()
3355 .cloned()
3356 .collect::<Vec<Pubkey>>()
3357 .chunks(MAX_MULTIPLE_ACCOUNTS)
3358 {
3359 rpc_client
3360 .get_multiple_accounts(feature_ids)
3361 .await?
3362 .into_iter()
3363 .zip(feature_ids)
3364 .for_each(|(account, feature_id)| {
3365 let activation_slot = account.and_then(status_from_account);
3366
3367 if let Some(CliFeatureStatus::Active(slot)) = activation_slot {
3368 feature_set.activate(feature_id, slot);
3369 }
3370 });
3371 }
3372
3373 Ok(feature_set)
3374}
3375
3376#[cfg(test)]
3377mod tests {
3378 use {
3379 super::*,
3380 crate::{
3381 clap_app::get_clap_app,
3382 cli::{parse_command, process_command},
3383 },
3384 serde_json::Value,
3385 solana_cli_output::OutputFormat,
3386 solana_hash::Hash,
3387 solana_keypair::write_keypair_file,
3388 solana_sbpf::elf_parser::consts::{
3389 EI_OSABI, ELFCLASS64, ELFDATA2LSB, ELFMAG, ELFOSABI_NONE, EV_CURRENT,
3390 },
3391 };
3392
3393 fn make_tmp_path(name: &str) -> String {
3394 let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
3395 let keypair = Keypair::new();
3396
3397 let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
3398
3399 let _ignored = std::fs::remove_dir_all(&path);
3401 let _ignored = std::fs::remove_file(&path);
3403
3404 path
3405 }
3406
3407 #[test]
3408 #[allow(clippy::cognitive_complexity)]
3409 fn test_cli_parse_deploy() {
3410 let test_commands = get_clap_app("test", "desc", "version");
3411
3412 let default_keypair = Keypair::new();
3413 let keypair_file = make_tmp_path("keypair_file");
3414 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3415 let default_signer = DefaultSigner::new("", &keypair_file);
3416
3417 let test_command = test_commands.clone().get_matches_from(vec![
3418 "test",
3419 "program",
3420 "deploy",
3421 "/Users/test/program.so",
3422 ]);
3423 assert_eq!(
3424 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3425 CliCommandInfo {
3426 command: CliCommand::Program(ProgramCliCommand::Deploy {
3427 program_location: Some("/Users/test/program.so".to_string()),
3428 fee_payer_signer_index: 0,
3429 buffer_signer_index: None,
3430 buffer_pubkey: None,
3431 program_signer_index: None,
3432 program_pubkey: None,
3433 upgrade_authority_signer_index: 0,
3434 is_final: false,
3435 max_len: None,
3436 skip_fee_check: false,
3437 compute_unit_price: None,
3438 max_sign_attempts: 5,
3439 auto_extend: true,
3440 use_rpc: false,
3441 skip_feature_verification: false,
3442 }),
3443 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3444 }
3445 );
3446
3447 let test_command = test_commands.clone().get_matches_from(vec![
3448 "test",
3449 "program",
3450 "deploy",
3451 "/Users/test/program.so",
3452 "--max-len",
3453 "42",
3454 ]);
3455 assert_eq!(
3456 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3457 CliCommandInfo {
3458 command: CliCommand::Program(ProgramCliCommand::Deploy {
3459 program_location: Some("/Users/test/program.so".to_string()),
3460 fee_payer_signer_index: 0,
3461 buffer_signer_index: None,
3462 buffer_pubkey: None,
3463 program_signer_index: None,
3464 program_pubkey: None,
3465 upgrade_authority_signer_index: 0,
3466 is_final: false,
3467 max_len: Some(42),
3468 skip_fee_check: false,
3469 compute_unit_price: None,
3470 max_sign_attempts: 5,
3471 auto_extend: true,
3472 use_rpc: false,
3473 skip_feature_verification: false,
3474 }),
3475 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3476 }
3477 );
3478
3479 let buffer_keypair = Keypair::new();
3480 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3481 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3482 let test_command = test_commands.clone().get_matches_from(vec![
3483 "test",
3484 "program",
3485 "deploy",
3486 "--buffer",
3487 &buffer_keypair_file,
3488 ]);
3489 assert_eq!(
3490 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3491 CliCommandInfo {
3492 command: CliCommand::Program(ProgramCliCommand::Deploy {
3493 program_location: None,
3494 fee_payer_signer_index: 0,
3495 buffer_signer_index: Some(1),
3496 buffer_pubkey: Some(buffer_keypair.pubkey()),
3497 program_signer_index: None,
3498 program_pubkey: None,
3499 upgrade_authority_signer_index: 0,
3500 is_final: false,
3501 max_len: None,
3502 skip_fee_check: false,
3503 compute_unit_price: None,
3504 max_sign_attempts: 5,
3505 auto_extend: true,
3506 use_rpc: false,
3507 skip_feature_verification: false,
3508 }),
3509 signers: vec![
3510 Box::new(read_keypair_file(&keypair_file).unwrap()),
3511 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3512 ],
3513 }
3514 );
3515
3516 let program_pubkey = Pubkey::new_unique();
3517 let test = test_commands.clone().get_matches_from(vec![
3518 "test",
3519 "program",
3520 "deploy",
3521 "/Users/test/program.so",
3522 "--program-id",
3523 &program_pubkey.to_string(),
3524 ]);
3525 assert_eq!(
3526 parse_command(&test, &default_signer, &mut None).unwrap(),
3527 CliCommandInfo {
3528 command: CliCommand::Program(ProgramCliCommand::Deploy {
3529 program_location: Some("/Users/test/program.so".to_string()),
3530 fee_payer_signer_index: 0,
3531 buffer_signer_index: None,
3532 buffer_pubkey: None,
3533 program_signer_index: None,
3534 program_pubkey: Some(program_pubkey),
3535 upgrade_authority_signer_index: 0,
3536 is_final: false,
3537 max_len: None,
3538 skip_fee_check: false,
3539 compute_unit_price: None,
3540 max_sign_attempts: 5,
3541 auto_extend: true,
3542 use_rpc: false,
3543 skip_feature_verification: false,
3544 }),
3545 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3546 }
3547 );
3548
3549 let program_keypair = Keypair::new();
3550 let program_keypair_file = make_tmp_path("program_keypair_file");
3551 write_keypair_file(&program_keypair, &program_keypair_file).unwrap();
3552 let test = test_commands.clone().get_matches_from(vec![
3553 "test",
3554 "program",
3555 "deploy",
3556 "/Users/test/program.so",
3557 "--program-id",
3558 &program_keypair_file,
3559 ]);
3560 assert_eq!(
3561 parse_command(&test, &default_signer, &mut None).unwrap(),
3562 CliCommandInfo {
3563 command: CliCommand::Program(ProgramCliCommand::Deploy {
3564 program_location: Some("/Users/test/program.so".to_string()),
3565 fee_payer_signer_index: 0,
3566 buffer_signer_index: None,
3567 buffer_pubkey: None,
3568 program_signer_index: Some(1),
3569 program_pubkey: Some(program_keypair.pubkey()),
3570 upgrade_authority_signer_index: 0,
3571 is_final: false,
3572 max_len: None,
3573 skip_fee_check: false,
3574 compute_unit_price: None,
3575 max_sign_attempts: 5,
3576 auto_extend: true,
3577 use_rpc: false,
3578 skip_feature_verification: false,
3579 }),
3580 signers: vec![
3581 Box::new(read_keypair_file(&keypair_file).unwrap()),
3582 Box::new(read_keypair_file(&program_keypair_file).unwrap()),
3583 ],
3584 }
3585 );
3586
3587 let authority_keypair = Keypair::new();
3588 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3589 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3590 let test_command = test_commands.clone().get_matches_from(vec![
3591 "test",
3592 "program",
3593 "deploy",
3594 "/Users/test/program.so",
3595 "--upgrade-authority",
3596 &authority_keypair_file,
3597 ]);
3598 assert_eq!(
3599 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3600 CliCommandInfo {
3601 command: CliCommand::Program(ProgramCliCommand::Deploy {
3602 program_location: Some("/Users/test/program.so".to_string()),
3603 fee_payer_signer_index: 0,
3604 buffer_signer_index: None,
3605 buffer_pubkey: None,
3606 program_signer_index: None,
3607 program_pubkey: None,
3608 upgrade_authority_signer_index: 1,
3609 is_final: false,
3610 max_len: None,
3611 skip_fee_check: false,
3612 compute_unit_price: None,
3613 max_sign_attempts: 5,
3614 auto_extend: true,
3615 use_rpc: false,
3616 skip_feature_verification: false,
3617 }),
3618 signers: vec![
3619 Box::new(read_keypair_file(&keypair_file).unwrap()),
3620 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3621 ],
3622 }
3623 );
3624
3625 let test_command = test_commands.clone().get_matches_from(vec![
3626 "test",
3627 "program",
3628 "deploy",
3629 "/Users/test/program.so",
3630 "--final",
3631 ]);
3632 assert_eq!(
3633 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3634 CliCommandInfo {
3635 command: CliCommand::Program(ProgramCliCommand::Deploy {
3636 program_location: Some("/Users/test/program.so".to_string()),
3637 fee_payer_signer_index: 0,
3638 buffer_signer_index: None,
3639 buffer_pubkey: None,
3640 program_signer_index: None,
3641 program_pubkey: None,
3642 upgrade_authority_signer_index: 0,
3643 is_final: true,
3644 max_len: None,
3645 skip_fee_check: false,
3646 compute_unit_price: None,
3647 max_sign_attempts: 5,
3648 auto_extend: true,
3649 use_rpc: false,
3650 skip_feature_verification: false,
3651 }),
3652 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3653 }
3654 );
3655
3656 let test_command = test_commands.clone().get_matches_from(vec![
3657 "test",
3658 "program",
3659 "deploy",
3660 "/Users/test/program.so",
3661 "--max-sign-attempts",
3662 "1",
3663 ]);
3664 assert_eq!(
3665 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3666 CliCommandInfo {
3667 command: CliCommand::Program(ProgramCliCommand::Deploy {
3668 program_location: Some("/Users/test/program.so".to_string()),
3669 fee_payer_signer_index: 0,
3670 buffer_signer_index: None,
3671 buffer_pubkey: None,
3672 program_signer_index: None,
3673 program_pubkey: None,
3674 upgrade_authority_signer_index: 0,
3675 is_final: false,
3676 max_len: None,
3677 skip_fee_check: false,
3678 compute_unit_price: None,
3679 max_sign_attempts: 1,
3680 auto_extend: true,
3681 use_rpc: false,
3682 skip_feature_verification: false,
3683 }),
3684 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3685 }
3686 );
3687
3688 let test_command = test_commands.clone().get_matches_from(vec![
3689 "test",
3690 "program",
3691 "deploy",
3692 "/Users/test/program.so",
3693 "--use-rpc",
3694 ]);
3695 assert_eq!(
3696 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3697 CliCommandInfo {
3698 command: CliCommand::Program(ProgramCliCommand::Deploy {
3699 program_location: Some("/Users/test/program.so".to_string()),
3700 fee_payer_signer_index: 0,
3701 buffer_signer_index: None,
3702 buffer_pubkey: None,
3703 program_signer_index: None,
3704 program_pubkey: None,
3705 upgrade_authority_signer_index: 0,
3706 is_final: false,
3707 max_len: None,
3708 skip_fee_check: false,
3709 compute_unit_price: None,
3710 max_sign_attempts: 5,
3711 auto_extend: true,
3712 use_rpc: true,
3713 skip_feature_verification: false,
3714 }),
3715 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3716 }
3717 );
3718
3719 let test_command = test_commands.clone().get_matches_from(vec![
3720 "test",
3721 "program",
3722 "deploy",
3723 "/Users/test/program.so",
3724 "--skip-feature-verify",
3725 ]);
3726 assert_eq!(
3727 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3728 CliCommandInfo {
3729 command: CliCommand::Program(ProgramCliCommand::Deploy {
3730 program_location: Some("/Users/test/program.so".to_string()),
3731 fee_payer_signer_index: 0,
3732 buffer_signer_index: None,
3733 buffer_pubkey: None,
3734 program_signer_index: None,
3735 program_pubkey: None,
3736 upgrade_authority_signer_index: 0,
3737 is_final: false,
3738 max_len: None,
3739 skip_fee_check: false,
3740 compute_unit_price: None,
3741 max_sign_attempts: 5,
3742 auto_extend: true,
3743 use_rpc: false,
3744 skip_feature_verification: true,
3745 }),
3746 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3747 }
3748 );
3749 }
3750
3751 #[test]
3752 fn test_cli_parse_upgrade() {
3753 let test_commands = get_clap_app("test", "desc", "version");
3754
3755 let default_keypair = Keypair::new();
3756 let keypair_file = make_tmp_path("keypair_file");
3757 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3758 let default_signer = DefaultSigner::new("", &keypair_file);
3759
3760 let program_key = Pubkey::new_unique();
3761 let buffer_key = Pubkey::new_unique();
3762 let test_command = test_commands.clone().get_matches_from(vec![
3763 "test",
3764 "program",
3765 "upgrade",
3766 format!("{buffer_key}").as_str(),
3767 format!("{program_key}").as_str(),
3768 "--skip-feature-verify",
3769 ]);
3770 assert_eq!(
3771 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3772 CliCommandInfo {
3773 command: CliCommand::Program(ProgramCliCommand::Upgrade {
3774 fee_payer_signer_index: 0,
3775 program_pubkey: program_key,
3776 buffer_pubkey: buffer_key,
3777 upgrade_authority_signer_index: 0,
3778 sign_only: false,
3779 dump_transaction_message: false,
3780 blockhash_query: BlockhashQuery::default(),
3781 skip_feature_verification: true,
3782 }),
3783 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3784 }
3785 );
3786 }
3787
3788 #[test]
3789 #[allow(clippy::cognitive_complexity)]
3790 fn test_cli_parse_write_buffer() {
3791 let test_commands = get_clap_app("test", "desc", "version");
3792
3793 let default_keypair = Keypair::new();
3794 let keypair_file = make_tmp_path("keypair_file");
3795 write_keypair_file(&default_keypair, &keypair_file).unwrap();
3796 let default_signer = DefaultSigner::new("", &keypair_file);
3797
3798 let test_command = test_commands.clone().get_matches_from(vec![
3800 "test",
3801 "program",
3802 "write-buffer",
3803 "/Users/test/program.so",
3804 ]);
3805 assert_eq!(
3806 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3807 CliCommandInfo {
3808 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3809 program_location: "/Users/test/program.so".to_string(),
3810 fee_payer_signer_index: 0,
3811 buffer_signer_index: None,
3812 buffer_pubkey: None,
3813 buffer_authority_signer_index: 0,
3814 max_len: None,
3815 skip_fee_check: false,
3816 compute_unit_price: None,
3817 max_sign_attempts: 5,
3818 use_rpc: false,
3819 skip_feature_verification: false,
3820 }),
3821 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3822 }
3823 );
3824
3825 let test_command = test_commands.clone().get_matches_from(vec![
3827 "test",
3828 "program",
3829 "write-buffer",
3830 "/Users/test/program.so",
3831 "--max-len",
3832 "42",
3833 ]);
3834 assert_eq!(
3835 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3836 CliCommandInfo {
3837 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3838 program_location: "/Users/test/program.so".to_string(),
3839 fee_payer_signer_index: 0,
3840 buffer_signer_index: None,
3841 buffer_pubkey: None,
3842 buffer_authority_signer_index: 0,
3843 max_len: Some(42),
3844 skip_fee_check: false,
3845 compute_unit_price: None,
3846 max_sign_attempts: 5,
3847 use_rpc: false,
3848 skip_feature_verification: false,
3849 }),
3850 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3851 }
3852 );
3853
3854 let buffer_keypair = Keypair::new();
3856 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3857 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3858 let test_command = test_commands.clone().get_matches_from(vec![
3859 "test",
3860 "program",
3861 "write-buffer",
3862 "/Users/test/program.so",
3863 "--buffer",
3864 &buffer_keypair_file,
3865 ]);
3866 assert_eq!(
3867 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3868 CliCommandInfo {
3869 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3870 program_location: "/Users/test/program.so".to_string(),
3871 fee_payer_signer_index: 0,
3872 buffer_signer_index: Some(1),
3873 buffer_pubkey: Some(buffer_keypair.pubkey()),
3874 buffer_authority_signer_index: 0,
3875 max_len: None,
3876 skip_fee_check: false,
3877 compute_unit_price: None,
3878 max_sign_attempts: 5,
3879 use_rpc: false,
3880 skip_feature_verification: false,
3881 }),
3882 signers: vec![
3883 Box::new(read_keypair_file(&keypair_file).unwrap()),
3884 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3885 ],
3886 }
3887 );
3888
3889 let authority_keypair = Keypair::new();
3891 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3892 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3893 let test_command = test_commands.clone().get_matches_from(vec![
3894 "test",
3895 "program",
3896 "write-buffer",
3897 "/Users/test/program.so",
3898 "--buffer-authority",
3899 &authority_keypair_file,
3900 ]);
3901 assert_eq!(
3902 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3903 CliCommandInfo {
3904 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3905 program_location: "/Users/test/program.so".to_string(),
3906 fee_payer_signer_index: 0,
3907 buffer_signer_index: None,
3908 buffer_pubkey: None,
3909 buffer_authority_signer_index: 1,
3910 max_len: None,
3911 skip_fee_check: false,
3912 compute_unit_price: None,
3913 max_sign_attempts: 5,
3914 use_rpc: false,
3915 skip_feature_verification: false,
3916 }),
3917 signers: vec![
3918 Box::new(read_keypair_file(&keypair_file).unwrap()),
3919 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3920 ],
3921 }
3922 );
3923
3924 let buffer_keypair = Keypair::new();
3926 let buffer_keypair_file = make_tmp_path("buffer_keypair_file");
3927 write_keypair_file(&buffer_keypair, &buffer_keypair_file).unwrap();
3928 let authority_keypair = Keypair::new();
3929 let authority_keypair_file = make_tmp_path("authority_keypair_file");
3930 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
3931 let test_command = test_commands.clone().get_matches_from(vec![
3932 "test",
3933 "program",
3934 "write-buffer",
3935 "/Users/test/program.so",
3936 "--buffer",
3937 &buffer_keypair_file,
3938 "--buffer-authority",
3939 &authority_keypair_file,
3940 ]);
3941 assert_eq!(
3942 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3943 CliCommandInfo {
3944 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3945 program_location: "/Users/test/program.so".to_string(),
3946 fee_payer_signer_index: 0,
3947 buffer_signer_index: Some(1),
3948 buffer_pubkey: Some(buffer_keypair.pubkey()),
3949 buffer_authority_signer_index: 2,
3950 max_len: None,
3951 skip_fee_check: false,
3952 compute_unit_price: None,
3953 max_sign_attempts: 5,
3954 use_rpc: false,
3955 skip_feature_verification: false,
3956 }),
3957 signers: vec![
3958 Box::new(read_keypair_file(&keypair_file).unwrap()),
3959 Box::new(read_keypair_file(&buffer_keypair_file).unwrap()),
3960 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
3961 ],
3962 }
3963 );
3964
3965 let test_command = test_commands.clone().get_matches_from(vec![
3967 "test",
3968 "program",
3969 "write-buffer",
3970 "/Users/test/program.so",
3971 "--max-sign-attempts",
3972 "10",
3973 ]);
3974 assert_eq!(
3975 parse_command(&test_command, &default_signer, &mut None).unwrap(),
3976 CliCommandInfo {
3977 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
3978 program_location: "/Users/test/program.so".to_string(),
3979 fee_payer_signer_index: 0,
3980 buffer_signer_index: None,
3981 buffer_pubkey: None,
3982 buffer_authority_signer_index: 0,
3983 max_len: None,
3984 skip_fee_check: false,
3985 compute_unit_price: None,
3986 max_sign_attempts: 10,
3987 use_rpc: false,
3988 skip_feature_verification: false
3989 }),
3990 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
3991 }
3992 );
3993
3994 let test_command = test_commands.clone().get_matches_from(vec![
3996 "test",
3997 "program",
3998 "write-buffer",
3999 "/Users/test/program.so",
4000 "--skip-feature-verify",
4001 ]);
4002 assert_eq!(
4003 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4004 CliCommandInfo {
4005 command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
4006 program_location: "/Users/test/program.so".to_string(),
4007 fee_payer_signer_index: 0,
4008 buffer_signer_index: None,
4009 buffer_pubkey: None,
4010 buffer_authority_signer_index: 0,
4011 max_len: None,
4012 skip_fee_check: false,
4013 compute_unit_price: None,
4014 max_sign_attempts: 5,
4015 use_rpc: false,
4016 skip_feature_verification: true,
4017 }),
4018 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4019 }
4020 );
4021 }
4022
4023 #[test]
4024 #[allow(clippy::cognitive_complexity)]
4025 fn test_cli_parse_set_upgrade_authority() {
4026 let test_commands = get_clap_app("test", "desc", "version");
4027
4028 let default_keypair = Keypair::new();
4029 let keypair_file = make_tmp_path("keypair_file");
4030 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4031 let default_signer = DefaultSigner::new("", &keypair_file);
4032
4033 let program_pubkey = Pubkey::new_unique();
4034 let new_authority_pubkey = Pubkey::new_unique();
4035 let blockhash = Hash::new_unique();
4036
4037 let test_command = test_commands.clone().get_matches_from(vec![
4038 "test",
4039 "program",
4040 "set-upgrade-authority",
4041 &program_pubkey.to_string(),
4042 "--new-upgrade-authority",
4043 &new_authority_pubkey.to_string(),
4044 "--skip-new-upgrade-authority-signer-check",
4045 "--sign-only",
4046 "--dump-transaction-message",
4047 "--blockhash",
4048 blockhash.to_string().as_str(),
4049 ]);
4050 assert_eq!(
4051 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4052 CliCommandInfo {
4053 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4054 program_pubkey,
4055 upgrade_authority_index: Some(0),
4056 new_upgrade_authority: Some(new_authority_pubkey),
4057 sign_only: true,
4058 dump_transaction_message: true,
4059 blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4060 }),
4061 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4062 }
4063 );
4064
4065 let program_pubkey = Pubkey::new_unique();
4066 let new_authority_pubkey = Keypair::new();
4067 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4068 write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
4069 let test_command = test_commands.clone().get_matches_from(vec![
4070 "test",
4071 "program",
4072 "set-upgrade-authority",
4073 &program_pubkey.to_string(),
4074 "--new-upgrade-authority",
4075 &new_authority_pubkey_file,
4076 "--skip-new-upgrade-authority-signer-check",
4077 ]);
4078 assert_eq!(
4079 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4080 CliCommandInfo {
4081 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4082 program_pubkey,
4083 upgrade_authority_index: Some(0),
4084 new_upgrade_authority: Some(new_authority_pubkey.pubkey()),
4085 sign_only: false,
4086 dump_transaction_message: false,
4087 blockhash_query: BlockhashQuery::default(),
4088 }),
4089 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4090 }
4091 );
4092
4093 let blockhash = Hash::new_unique();
4094 let program_pubkey = Pubkey::new_unique();
4095 let new_authority_pubkey = Keypair::new();
4096 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4097 write_keypair_file(&new_authority_pubkey, &new_authority_pubkey_file).unwrap();
4098 let test_command = test_commands.clone().get_matches_from(vec![
4099 "test",
4100 "program",
4101 "set-upgrade-authority",
4102 &program_pubkey.to_string(),
4103 "--new-upgrade-authority",
4104 &new_authority_pubkey_file,
4105 "--sign-only",
4106 "--dump-transaction-message",
4107 "--blockhash",
4108 blockhash.to_string().as_str(),
4109 ]);
4110 assert_eq!(
4111 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4112 CliCommandInfo {
4113 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthorityChecked {
4114 program_pubkey,
4115 upgrade_authority_index: 0,
4116 new_upgrade_authority_index: 1,
4117 sign_only: true,
4118 dump_transaction_message: true,
4119 blockhash_query: BlockhashQuery::new(Some(blockhash), true, None),
4120 }),
4121 signers: vec![
4122 Box::new(read_keypair_file(&keypair_file).unwrap()),
4123 Box::new(read_keypair_file(&new_authority_pubkey_file).unwrap()),
4124 ],
4125 }
4126 );
4127
4128 let program_pubkey = Pubkey::new_unique();
4129 let new_authority_pubkey = Keypair::new();
4130 let new_authority_pubkey_file = make_tmp_path("authority_keypair_file");
4131 write_keypair_file(&new_authority_pubkey, new_authority_pubkey_file).unwrap();
4132 let test_command = test_commands.clone().get_matches_from(vec![
4133 "test",
4134 "program",
4135 "set-upgrade-authority",
4136 &program_pubkey.to_string(),
4137 "--final",
4138 ]);
4139 assert_eq!(
4140 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4141 CliCommandInfo {
4142 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4143 program_pubkey,
4144 upgrade_authority_index: Some(0),
4145 new_upgrade_authority: None,
4146 sign_only: false,
4147 dump_transaction_message: false,
4148 blockhash_query: BlockhashQuery::default(),
4149 }),
4150 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4151 }
4152 );
4153
4154 let program_pubkey = Pubkey::new_unique();
4155 let authority = Keypair::new();
4156 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4157 write_keypair_file(&authority, &authority_keypair_file).unwrap();
4158 let test_command = test_commands.clone().get_matches_from(vec![
4159 "test",
4160 "program",
4161 "set-upgrade-authority",
4162 &program_pubkey.to_string(),
4163 "--upgrade-authority",
4164 &authority_keypair_file,
4165 "--final",
4166 ]);
4167 assert_eq!(
4168 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4169 CliCommandInfo {
4170 command: CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority {
4171 program_pubkey,
4172 upgrade_authority_index: Some(1),
4173 new_upgrade_authority: None,
4174 sign_only: false,
4175 dump_transaction_message: false,
4176 blockhash_query: BlockhashQuery::default(),
4177 }),
4178 signers: vec![
4179 Box::new(read_keypair_file(&keypair_file).unwrap()),
4180 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4181 ],
4182 }
4183 );
4184 }
4185
4186 #[test]
4187 #[allow(clippy::cognitive_complexity)]
4188 fn test_cli_parse_set_buffer_authority() {
4189 let test_commands = get_clap_app("test", "desc", "version");
4190
4191 let default_keypair = Keypair::new();
4192 let keypair_file = make_tmp_path("keypair_file");
4193 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4194 let default_signer = DefaultSigner::new("", &keypair_file);
4195
4196 let buffer_pubkey = Pubkey::new_unique();
4197 let new_authority_pubkey = Pubkey::new_unique();
4198 let test_command = test_commands.clone().get_matches_from(vec![
4199 "test",
4200 "program",
4201 "set-buffer-authority",
4202 &buffer_pubkey.to_string(),
4203 "--new-buffer-authority",
4204 &new_authority_pubkey.to_string(),
4205 ]);
4206 assert_eq!(
4207 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4208 CliCommandInfo {
4209 command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4210 buffer_pubkey,
4211 buffer_authority_index: Some(0),
4212 new_buffer_authority: new_authority_pubkey,
4213 }),
4214 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4215 }
4216 );
4217
4218 let buffer_pubkey = Pubkey::new_unique();
4219 let new_authority_keypair = Keypair::new();
4220 let new_authority_keypair_file = make_tmp_path("authority_keypair_file");
4221 write_keypair_file(&new_authority_keypair, &new_authority_keypair_file).unwrap();
4222 let test_command = test_commands.clone().get_matches_from(vec![
4223 "test",
4224 "program",
4225 "set-buffer-authority",
4226 &buffer_pubkey.to_string(),
4227 "--new-buffer-authority",
4228 &new_authority_keypair_file,
4229 ]);
4230 assert_eq!(
4231 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4232 CliCommandInfo {
4233 command: CliCommand::Program(ProgramCliCommand::SetBufferAuthority {
4234 buffer_pubkey,
4235 buffer_authority_index: Some(0),
4236 new_buffer_authority: new_authority_keypair.pubkey(),
4237 }),
4238 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4239 }
4240 );
4241 }
4242
4243 #[test]
4244 #[allow(clippy::cognitive_complexity)]
4245 fn test_cli_parse_show() {
4246 let test_commands = get_clap_app("test", "desc", "version");
4247
4248 let default_keypair = Keypair::new();
4249 let keypair_file = make_tmp_path("keypair_file");
4250 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4251 let default_signer = DefaultSigner::new("", &keypair_file);
4252
4253 let buffer_pubkey = Pubkey::new_unique();
4255 let authority_keypair = Keypair::new();
4256 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4257 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4258
4259 let test_command = test_commands.clone().get_matches_from(vec![
4260 "test",
4261 "program",
4262 "show",
4263 &buffer_pubkey.to_string(),
4264 ]);
4265 assert_eq!(
4266 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4267 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4268 account_pubkey: Some(buffer_pubkey),
4269 authority_pubkey: default_keypair.pubkey(),
4270 get_programs: false,
4271 get_buffers: false,
4272 all: false,
4273 use_lamports_unit: false,
4274 }))
4275 );
4276
4277 let test_command = test_commands.clone().get_matches_from(vec![
4278 "test",
4279 "program",
4280 "show",
4281 "--programs",
4282 "--all",
4283 "--lamports",
4284 ]);
4285 assert_eq!(
4286 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4287 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4288 account_pubkey: None,
4289 authority_pubkey: default_keypair.pubkey(),
4290 get_programs: true,
4291 get_buffers: false,
4292 all: true,
4293 use_lamports_unit: true,
4294 }))
4295 );
4296
4297 let test_command = test_commands.clone().get_matches_from(vec![
4298 "test",
4299 "program",
4300 "show",
4301 "--buffers",
4302 "--all",
4303 "--lamports",
4304 ]);
4305 assert_eq!(
4306 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4307 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4308 account_pubkey: None,
4309 authority_pubkey: default_keypair.pubkey(),
4310 get_programs: false,
4311 get_buffers: true,
4312 all: true,
4313 use_lamports_unit: true,
4314 }))
4315 );
4316
4317 let test_command = test_commands.clone().get_matches_from(vec![
4318 "test",
4319 "program",
4320 "show",
4321 "--buffers",
4322 "--buffer-authority",
4323 &authority_keypair.pubkey().to_string(),
4324 ]);
4325 assert_eq!(
4326 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4327 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4328 account_pubkey: None,
4329 authority_pubkey: authority_keypair.pubkey(),
4330 get_programs: false,
4331 get_buffers: true,
4332 all: false,
4333 use_lamports_unit: false,
4334 }))
4335 );
4336
4337 let test_command = test_commands.clone().get_matches_from(vec![
4338 "test",
4339 "program",
4340 "show",
4341 "--buffers",
4342 "--buffer-authority",
4343 &authority_keypair_file,
4344 ]);
4345 assert_eq!(
4346 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4347 CliCommandInfo::without_signers(CliCommand::Program(ProgramCliCommand::Show {
4348 account_pubkey: None,
4349 authority_pubkey: authority_keypair.pubkey(),
4350 get_programs: false,
4351 get_buffers: true,
4352 all: false,
4353 use_lamports_unit: false,
4354 }))
4355 );
4356 }
4357
4358 #[test]
4359 #[allow(clippy::cognitive_complexity)]
4360 fn test_cli_parse_close() {
4361 let test_commands = get_clap_app("test", "desc", "version");
4362
4363 let default_keypair = Keypair::new();
4364 let keypair_file = make_tmp_path("keypair_file");
4365 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4366 let default_signer = DefaultSigner::new("", &keypair_file);
4367
4368 let buffer_pubkey = Pubkey::new_unique();
4370 let recipient_pubkey = Pubkey::new_unique();
4371 let authority_keypair = Keypair::new();
4372 let authority_keypair_file = make_tmp_path("authority_keypair_file");
4373
4374 let test_command = test_commands.clone().get_matches_from(vec![
4375 "test",
4376 "program",
4377 "close",
4378 &buffer_pubkey.to_string(),
4379 ]);
4380 assert_eq!(
4381 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4382 CliCommandInfo {
4383 command: CliCommand::Program(ProgramCliCommand::Close {
4384 account_pubkey: Some(buffer_pubkey),
4385 recipient_pubkey: default_keypair.pubkey(),
4386 authority_index: 0,
4387 use_lamports_unit: false,
4388 bypass_warning: false,
4389 }),
4390 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4391 }
4392 );
4393
4394 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4396 let test_command = test_commands.clone().get_matches_from(vec![
4397 "test",
4398 "program",
4399 "close",
4400 &buffer_pubkey.to_string(),
4401 "--bypass-warning",
4402 ]);
4403 assert_eq!(
4404 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4405 CliCommandInfo {
4406 command: CliCommand::Program(ProgramCliCommand::Close {
4407 account_pubkey: Some(buffer_pubkey),
4408 recipient_pubkey: default_keypair.pubkey(),
4409 authority_index: 0,
4410 use_lamports_unit: false,
4411 bypass_warning: true,
4412 }),
4413 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4414 }
4415 );
4416
4417 write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap();
4419 let test_command = test_commands.clone().get_matches_from(vec![
4420 "test",
4421 "program",
4422 "close",
4423 &buffer_pubkey.to_string(),
4424 "--buffer-authority",
4425 &authority_keypair_file,
4426 ]);
4427 assert_eq!(
4428 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4429 CliCommandInfo {
4430 command: CliCommand::Program(ProgramCliCommand::Close {
4431 account_pubkey: Some(buffer_pubkey),
4432 recipient_pubkey: default_keypair.pubkey(),
4433 authority_index: 1,
4434 use_lamports_unit: false,
4435 bypass_warning: false,
4436 }),
4437 signers: vec![
4438 Box::new(read_keypair_file(&keypair_file).unwrap()),
4439 Box::new(read_keypair_file(&authority_keypair_file).unwrap()),
4440 ],
4441 }
4442 );
4443
4444 let test_command = test_commands.clone().get_matches_from(vec![
4446 "test",
4447 "program",
4448 "close",
4449 &buffer_pubkey.to_string(),
4450 "--recipient",
4451 &recipient_pubkey.to_string(),
4452 ]);
4453 assert_eq!(
4454 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4455 CliCommandInfo {
4456 command: CliCommand::Program(ProgramCliCommand::Close {
4457 account_pubkey: Some(buffer_pubkey),
4458 recipient_pubkey,
4459 authority_index: 0,
4460 use_lamports_unit: false,
4461 bypass_warning: false,
4462 }),
4463 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4464 }
4465 );
4466
4467 let test_command = test_commands.clone().get_matches_from(vec![
4469 "test",
4470 "program",
4471 "close",
4472 "--buffers",
4473 "--lamports",
4474 ]);
4475 assert_eq!(
4476 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4477 CliCommandInfo {
4478 command: CliCommand::Program(ProgramCliCommand::Close {
4479 account_pubkey: None,
4480 recipient_pubkey: default_keypair.pubkey(),
4481 authority_index: 0,
4482 use_lamports_unit: true,
4483 bypass_warning: false,
4484 }),
4485 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap()),],
4486 }
4487 );
4488 }
4489
4490 #[test]
4491 fn test_cli_parse_extend_program() {
4492 let test_commands = get_clap_app("test", "desc", "version");
4493
4494 let default_keypair = Keypair::new();
4495 let keypair_file = make_tmp_path("keypair_file");
4496 write_keypair_file(&default_keypair, &keypair_file).unwrap();
4497 let default_signer = DefaultSigner::new("", &keypair_file);
4498
4499 let program_pubkey = Pubkey::new_unique();
4501 let additional_bytes = 100;
4502
4503 let test_command = test_commands.clone().get_matches_from(vec![
4504 "test",
4505 "program",
4506 "extend",
4507 &program_pubkey.to_string(),
4508 &additional_bytes.to_string(),
4509 ]);
4510 assert_eq!(
4511 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4512 CliCommandInfo {
4513 command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4514 program_pubkey,
4515 payer_signer_index: 0,
4516 additional_bytes
4517 }),
4518 signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
4519 }
4520 );
4521
4522 let payer_keypair = Keypair::new();
4524 let payer_keypair_file = make_tmp_path("payer_keypair_file");
4525 write_keypair_file(&payer_keypair, &payer_keypair_file).unwrap();
4526 let test_command = test_commands.clone().get_matches_from(vec![
4527 "test",
4528 "program",
4529 "extend",
4530 &program_pubkey.to_string(),
4531 &additional_bytes.to_string(),
4532 "--payer",
4533 &payer_keypair_file,
4534 ]);
4535 assert_eq!(
4536 parse_command(&test_command, &default_signer, &mut None).unwrap(),
4537 CliCommandInfo {
4538 command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
4539 program_pubkey,
4540 payer_signer_index: 1,
4541 additional_bytes
4542 }),
4543 signers: vec![
4544 Box::new(read_keypair_file(&keypair_file).unwrap()),
4545 Box::new(read_keypair_file(&payer_keypair_file).unwrap()),
4546 ],
4547 }
4548 );
4549 }
4550
4551 #[tokio::test]
4552 async fn test_cli_keypair_file() {
4553 agave_logger::setup();
4554
4555 let default_keypair = Keypair::new();
4556 let program_pubkey = Keypair::new();
4557 let deploy_path = make_tmp_path("deploy");
4558 let mut program_location = PathBuf::from(deploy_path.clone());
4559 program_location.push("noop");
4560 program_location.set_extension("so");
4561 let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
4562 pathbuf.push("tests");
4563 pathbuf.push("fixtures");
4564 pathbuf.push("noop");
4565 pathbuf.set_extension("so");
4566 let program_keypair_location = program_location.with_file_name("noop-keypair.json");
4567 std::fs::create_dir_all(deploy_path).unwrap();
4568 std::fs::copy(pathbuf, program_location.as_os_str()).unwrap();
4569 write_keypair_file(&program_pubkey, program_keypair_location).unwrap();
4570
4571 let config = CliConfig {
4572 rpc_client: Some(Arc::new(RpcClient::new_mock("".to_string()))),
4573 command: CliCommand::Program(ProgramCliCommand::Deploy {
4574 program_location: Some(program_location.to_str().unwrap().to_string()),
4575 fee_payer_signer_index: 0,
4576 buffer_signer_index: None,
4577 buffer_pubkey: None,
4578 program_signer_index: None,
4579 program_pubkey: None,
4580 upgrade_authority_signer_index: 0,
4581 is_final: false,
4582 max_len: None,
4583 skip_fee_check: false,
4584 compute_unit_price: None,
4585 max_sign_attempts: 5,
4586 auto_extend: true,
4587 use_rpc: false,
4588 skip_feature_verification: true,
4589 }),
4590 signers: vec![&default_keypair],
4591 output_format: OutputFormat::JsonCompact,
4592 ..CliConfig::default()
4593 };
4594
4595 let result = process_command(&config).await;
4596 let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
4597 let program_id = json
4598 .as_object()
4599 .unwrap()
4600 .get("programId")
4601 .unwrap()
4602 .as_str()
4603 .unwrap();
4604
4605 assert_eq!(
4606 program_id.parse::<Pubkey>().unwrap(),
4607 program_pubkey.pubkey()
4608 );
4609 }
4610
4611 fn fake_elf(e_flags: u32) -> Vec<u8> {
4614 let mut bytes = vec![0u8; 64];
4615 bytes[..4].copy_from_slice(&ELFMAG);
4616 bytes[4] = ELFCLASS64;
4617 bytes[5] = ELFDATA2LSB;
4618 bytes[6] = EV_CURRENT as u8;
4619 bytes[EI_OSABI as usize] = ELFOSABI_NONE;
4620 bytes[48..52].copy_from_slice(&e_flags.to_le_bytes());
4621 bytes
4622 }
4623
4624 fn deploy_config(versions: std::ops::RangeInclusive<SBPFVersion>) -> Config {
4625 Config {
4626 enabled_sbpf_versions: versions,
4627 ..Config::default()
4628 }
4629 }
4630
4631 #[test]
4632 fn test_explain_unsupported_sbpf_version() {
4633 let elf = fake_elf(1); let config = deploy_config(SBPFVersion::V3..=SBPFVersion::V3);
4635 let err = EbpfError::ElfError(ElfError::UnsupportedSBPFVersion);
4636 let msg = explain_elf_error(&err, &elf, &config);
4637 assert!(msg.contains("SBPF v1"), "got: {msg}");
4638 assert!(msg.contains("enabled: v3"), "got: {msg}");
4639 }
4640}