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