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