1use pinocchio::{
2 address::{address_eq, Address},
3 error::ProgramError,
4 AccountView,
5};
6use pinocchio_log::log;
7
8use crate::{
9 error::DlpError,
10 pda::{
11 self, program_config_from_program_id,
12 validator_fees_vault_pda_from_validator,
13 },
14};
15
16#[macro_export]
18macro_rules! require {
19 ($cond:expr, $error:expr) => {{
20 if !$cond {
21 let expr = stringify!($cond);
22 pinocchio_log::log!("require!({}) failed.", expr);
23 return Err($error.into());
24 }
25 }};
26}
27
28#[macro_export]
30macro_rules! require_signer {
31 ($info: expr) => {{
32 if !$info.is_signer() {
33 log!("require_signer!({}): ", stringify!($info));
34 $info.address().log();
35 return Err(ProgramError::MissingRequiredSignature);
36 }
37 }};
38}
39
40#[macro_export]
42macro_rules! require_eq_keys {
43 ( $key1:expr, $key2:expr, $error:expr) => {{
44 if !pinocchio::address::address_eq($key1, $key2) {
45 pinocchio_log::log!(
46 "require_eq_keys!({}, {}) failed: ",
47 stringify!($key1),
48 stringify!($key2)
49 );
50 $key1.log();
51 $key2.log();
52 return Err($error.into());
53 }
54 }};
55}
56
57#[macro_export]
59macro_rules! require_eq {
60 ( $val1:expr, $val2:expr, $error:expr) => {{
61 if !($val1 == $val2) {
62 pinocchio_log::log!(
63 "require_eq!({}, {}) failed: {} == {}",
64 stringify!($val1),
65 stringify!($val2),
66 $val1,
67 $val2
68 );
69 return Err($error.into());
70 }
71 }};
72}
73
74#[macro_export]
76macro_rules! require_le {
77 ( $val1:expr, $val2:expr, $error:expr) => {{
78 if !($val1 <= $val2) {
79 pinocchio_log::log!(
80 "require_le!({}, {}) failed: {} <= {}",
81 stringify!($val1),
82 stringify!($val2),
83 $val1,
84 $val2
85 );
86 return Err($error.into());
87 }
88 }};
89}
90
91#[macro_export]
93macro_rules! require_lt {
94 ( $val1:expr, $val2:expr, $error:expr) => {{
95 if !($val1 < $val2) {
96 pinocchio_log::log!(
97 "require_lt!({}, {}) failed: {} < {}",
98 stringify!($val1),
99 stringify!($val2),
100 $val1,
101 $val2
102 );
103 return Err($error.into());
104 }
105 }};
106}
107
108#[macro_export]
110macro_rules! require_ge {
111 ( $val1:expr, $val2:expr, $error:expr) => {{
112 if !($val1 >= $val2) {
113 pinocchio_log::log!(
114 "require_ge!({}, {}) failed: {} >= {}",
115 stringify!($val1),
116 stringify!($val2),
117 $val1,
118 $val2
119 );
120 return Err($error.into());
121 }
122 }};
123}
124
125#[macro_export]
127macro_rules! require_gt {
128 ( $val1:expr, $val2:expr, $error:expr) => {{
129 if !($val1 > $val2) {
130 pinocchio_log::log!(
131 "require_gt!({}, {}) failed: {} > {}",
132 stringify!($val1),
133 stringify!($val2),
134 $val1,
135 $val2
136 );
137 return Err($error.into());
138 }
139 }};
140}
141
142#[macro_export]
143macro_rules! require_n_accounts {
144 ( $accounts:expr, $n:literal) => {{
145 match $accounts.len().cmp(&$n) {
146 core::cmp::Ordering::Less => {
147 pinocchio_log::log!(
148 "Need {} accounts, but got less ({}) accounts",
149 $n,
150 $accounts.len()
151 );
152 return Err(
153 pinocchio::error::ProgramError::NotEnoughAccountKeys,
154 );
155 }
156 core::cmp::Ordering::Equal => {
157 TryInto::<&[_; $n]>::try_into($accounts)
158 .map_err(|_| $crate::error::DlpError::InfallibleError)?
159 }
160 core::cmp::Ordering::Greater => {
161 pinocchio_log::log!(
162 "Need {} accounts, but got more ({}) accounts",
163 $n,
164 $accounts.len()
165 );
166 return Err($crate::error::DlpError::TooManyAccountKeys.into());
167 }
168 }
169 }};
170}
171
172#[macro_export]
173macro_rules! require_n_accounts_with_optionals {
174 ( $accounts:expr, $n:literal) => {{
175 match $accounts.len().cmp(&$n) {
176 core::cmp::Ordering::Less => {
177 pinocchio_log::log!(
178 "Need {} accounts, but got less ({}) accounts",
179 $n,
180 $accounts.len()
181 );
182 return Err(
183 pinocchio::error::ProgramError::NotEnoughAccountKeys,
184 );
185 }
186 _ => {
187 let (exact, optionals) = $accounts.split_at($n);
188
189 (
190 TryInto::<&[_; $n]>::try_into(exact).map_err(|_| {
191 $crate::error::DlpError::InfallibleError
192 })?,
193 optionals,
194 )
195 }
196 }
197 }};
198}
199
200#[macro_export]
201macro_rules! require_some {
202 ($option:expr, $error:expr) => {{
203 match $option {
204 Some(val) => val,
205 None => return Err($error.into()),
206 }
207 }};
208}
209
210#[macro_export]
217macro_rules! require_owned_by {
218 ($info: expr, $owner: expr) => {{
219 if !address_eq(unsafe { $info.owner() }, $owner) {
220 pinocchio_log::log!(
221 "require_owned_by!({}, {})",
222 stringify!($info),
223 stringify!($owner)
224 );
225 $info.address().log();
226 $owner.log();
227 return Err(ProgramError::InvalidAccountOwner);
228 }
229 }};
230}
231
232#[macro_export]
241macro_rules! require_initialized_pda {
242 ($info:expr, $seeds: expr, $program_id: expr, $is_writable: expr) => {{
243 let pda = match pinocchio::Address::create_program_address($seeds, $program_id) {
244 Ok(pda) => pda,
245 Err(_) => {
246 log!(
247 "require_initialized_pda!({}, {}, {}, {}); create_program_address failed",
248 stringify!($info),
249 stringify!($seeds),
250 stringify!($program_id),
251 stringify!($is_writable),
252 );
253 return Err(ProgramError::InvalidSeeds);
254 }
255 };
256 if !address_eq($info.address(), &pda) {
257 log!(
258 "require_initialized_pda!({}, {}, {}, {}); address_eq failed",
259 stringify!($info),
260 stringify!($seeds),
261 stringify!($program_id),
262 stringify!($is_writable)
263 );
264 $info.address().log();
265 $program_id.log();
266 return Err(ProgramError::InvalidSeeds);
267 }
268
269 require_owned_by!($info, $program_id);
270
271 if $is_writable && !$info.is_writable() {
272 log!(
273 "require_initialized_pda!({}, {}, {}, {}); is_writable expectation failed",
274 stringify!($info),
275 stringify!($seeds),
276 stringify!($program_id),
277 stringify!($is_writable)
278 );
279 $info.address().log();
280 return Err(ProgramError::Immutable);
281 }
282 }};
283}
284
285#[macro_export]
286macro_rules! require_initialized_pda_fast {
287 ($info:expr, $seeds: expr, $is_writable: expr) => {{
288 let pda = solana_sha256_hasher::hashv($seeds).to_bytes();
289 if !address_eq($info.address(), &pda.into()) {
290 log!(
291 "require_initialized_pda!({}, {}, {}); address_eq failed",
292 stringify!($info),
293 stringify!($seeds),
294 stringify!($is_writable)
295 );
296 $info.address().log();
297 return Err(ProgramError::InvalidSeeds);
298 }
299
300 require_owned_by!($info, &$crate::fast::ID);
301
302 if $is_writable && !$info.is_writable() {
303 log!(
304 "require_initialized_pda!({}, {}, {}); is_writable expectation failed",
305 stringify!($info),
306 stringify!($seeds),
307 stringify!($is_writable)
308 );
309 $info.address().log();
310 return Err(ProgramError::Immutable);
311 }
312 }};
313}
314
315#[macro_export]
316macro_rules! require_pda {
317 ($info:expr, $seeds: expr, $program_id: expr, $is_writable: expr) => {{
318 let pda = match pinocchio::Address::create_program_address($seeds, $program_id) {
319 Ok(pda) => pda,
320 Err(_) => {
321 log!(
322 "require_pda!({}, {}, {}, {}); create_program_address failed",
323 stringify!($info),
324 stringify!($seeds),
325 stringify!($program_id),
326 stringify!($is_writable),
327 );
328 return Err(ProgramError::InvalidSeeds);
329 }
330 };
331 if !address_eq($info.address(), &pda) {
332 log!(
333 "require_pda!({}, {}, {}, {}); address_eq failed",
334 stringify!($info),
335 stringify!($seeds),
336 stringify!($program_id),
337 stringify!($is_writable)
338 );
339 $info.address().log();
340 $program_id.log();
341 return Err(ProgramError::InvalidSeeds);
342 }
343
344 if $is_writable && !$info.is_writable() {
345 log!(
346 "require_pda!({}, {}, {}, {}); is_writable expectation failed",
347 stringify!($info),
348 stringify!($seeds),
349 stringify!($program_id),
350 stringify!($is_writable)
351 );
352 $info.address().log();
353 return Err(ProgramError::Immutable);
354 }
355 }};
356}
357
358#[inline(always)]
361pub fn require_owned_pda(
362 info: &AccountView,
363 owner: &Address,
364 label: &str,
365) -> Result<(), ProgramError> {
366 if !address_eq(unsafe { info.owner() }, owner) {
367 log!("Invalid account owner for {}:", label);
368 info.address().log();
369 unsafe { info.owner() }.log();
370 owner.log();
371 return Err(ProgramError::InvalidAccountOwner);
372 }
373 Ok(())
374}
375
376#[inline(always)]
379pub fn require_signer(
380 info: &AccountView,
381 label: &str,
382) -> Result<(), ProgramError> {
383 if !info.is_signer() {
384 log!("Account needs to be signer {}: ", label);
385 info.address().log();
386 return Err(ProgramError::MissingRequiredSignature);
387 }
388
389 Ok(())
390}
391
392#[inline(always)]
395pub fn require_pda(
396 info: &AccountView,
397 seeds: &[&[u8]],
398 program_id: &Address,
399 is_writable: bool,
400 label: &str,
401) -> Result<u8, ProgramError> {
402 let pda = Address::find_program_address(seeds, program_id);
403
404 if !address_eq(info.address(), &pda.0) {
405 log!("Invalid seeds for {}: ", label);
406 info.address().log();
407 return Err(ProgramError::InvalidSeeds);
408 }
409
410 if is_writable && !info.is_writable() {
411 log!("Account needs to be writable. Label: {}", label);
412 info.address().log();
413 return Err(ProgramError::Immutable);
414 }
415
416 Ok(pda.1)
417}
418
419pub fn is_uninitialized_account(info: &AccountView) -> bool {
423 address_eq(unsafe { info.owner() }, &pinocchio_system::ID)
424 && info.is_data_empty()
425}
426
427#[inline(always)]
432pub fn require_uninitialized_account(
433 info: &AccountView,
434 is_writable: bool,
435 ctx: impl RequireUninitializedAccountCtx,
436) -> Result<(), ProgramError> {
437 if !address_eq(unsafe { info.owner() }, &pinocchio_system::id()) {
438 log!(
439 "Invalid owner for account. Label: {}; account and owner: ",
440 ctx.label()
441 );
442 info.address().log();
443 unsafe { info.owner() }.log();
444 return Err(ctx.invalid_account_owner());
445 }
446
447 if !info.is_data_empty() {
448 log!(
449 "Account needs to be uninitialized. Label: {}, account: ",
450 ctx.label(),
451 );
452 info.address().log();
453 return Err(ctx.account_already_initialized());
454 }
455
456 if is_writable && !info.is_writable() {
457 log!(
458 "Account needs to be writable. label: {}, account: ",
459 ctx.label()
460 );
461 info.address().log();
462 return Err(ctx.immutable());
463 }
464
465 Ok(())
466}
467
468#[inline(always)]
472pub fn require_uninitialized_pda(
473 info: &AccountView,
474 seeds: &[&[u8]],
475 program_id: &Address,
476 is_writable: bool,
477 ctx: impl RequireUninitializedAccountCtx,
478) -> Result<u8, ProgramError> {
479 let pda = Address::find_program_address(seeds, program_id);
480
481 if !address_eq(info.address(), &pda.0) {
482 log!("Invalid seeds for account {}: ", ctx.label());
483 info.address().log();
484 return Err(ctx.invalid_seeds());
485 }
486
487 require_uninitialized_account(info, is_writable, ctx)?;
488 Ok(pda.1)
489}
490
491pub fn require_initialized_pda(
496 info: &AccountView,
497 seeds: &[&[u8]],
498 program_id: &Address,
499 is_writable: bool,
500 label: &str,
501) -> Result<u8, ProgramError> {
502 let pda = Address::find_program_address(seeds, program_id);
503 if !address_eq(info.address(), &pda.0) {
504 log!("Invalid seeds (label: {}) for account ", label);
505 info.address().log();
506 return Err(ProgramError::InvalidSeeds);
507 }
508
509 require_owned_pda(info, program_id, label)?;
510
511 if is_writable && !info.is_writable() {
512 log!("Account needs to be writable. label: {}, account: ", label);
513 info.address().log();
514 return Err(ProgramError::Immutable);
515 }
516
517 Ok(pda.1)
518}
519
520#[inline(always)]
524#[allow(dead_code)]
525pub fn require_program(
526 info: &AccountView,
527 key: &Address,
528 label: &str,
529) -> Result<(), ProgramError> {
530 if !address_eq(info.address(), key) {
531 log!("Invalid program account {}: ", label);
532 info.address().log();
533 return Err(ProgramError::IncorrectProgramId);
534 }
535
536 if !info.executable() {
537 log!("{} program is not executable: ", label);
538 info.address().log();
539 return Err(ProgramError::InvalidAccountData);
540 }
541
542 Ok(())
543}
544
545pub fn require_initialized_protocol_fees_vault(
548 fees_vault: &AccountView,
549 is_writable: bool,
550) -> Result<(), ProgramError> {
551 require_initialized_pda(
552 fees_vault,
553 &[b"fees-vault"],
554 &crate::fast::ID,
555 is_writable,
556 "protocol fees vault",
557 )?;
558 Ok(())
559}
560
561pub fn require_initialized_validator_fees_vault(
565 validator: &AccountView,
566 validator_fees_vault: &AccountView,
567 is_writable: bool,
568) -> Result<(), ProgramError> {
569 let pda = validator_fees_vault_pda_from_validator(
570 &validator.address().to_bytes().into(),
571 )
572 .to_bytes()
573 .into();
574 if !address_eq(validator_fees_vault.address(), &pda) {
575 log!("Invalid validator fees vault PDA, expected: ");
576 pda.log();
577 log!("but got: ");
578 validator_fees_vault.address().log();
579 return Err(DlpError::InvalidAuthority.into());
580 }
581 require_initialized_pda(
582 validator_fees_vault,
583 &[pda::VALIDATOR_FEES_VAULT_TAG, validator.address().as_ref()],
584 &crate::fast::ID,
585 is_writable,
586 "validator fees vault",
587 )?;
588 Ok(())
589}
590
591pub fn require_program_config(
594 program_config: &AccountView,
595 program: &Address,
596 is_writable: bool,
597) -> Result<bool, ProgramError> {
598 let pda = program_config_from_program_id(&program.to_bytes().into());
599 if !address_eq(&pda.to_bytes().into(), program_config.address()) {
600 log!("Invalid program config PDA, expected: ");
601 pda.log();
602 log!("but got: ");
603 program_config.address().log();
604 return Err(DlpError::InvalidAuthority.into());
605 }
606 require_pda(
607 program_config,
608 &[pda::PROGRAM_CONFIG_TAG, program.as_ref()],
609 &crate::fast::ID,
610 is_writable,
611 "program config",
612 )?;
613 Ok(!address_eq(
614 unsafe { program_config.owner() },
615 &pinocchio_system::ID,
616 ))
617}
618
619pub fn require_initialized_delegation_record(
622 delegated_account: &AccountView,
623 delegation_record: &AccountView,
624 is_writable: bool,
625) -> Result<(), ProgramError> {
626 require_initialized_pda(
627 delegation_record,
628 &[
629 pda::DELEGATION_RECORD_TAG,
630 delegated_account.address().as_ref(),
631 ],
632 &crate::fast::ID,
633 is_writable,
634 "delegation record",
635 )?;
636 Ok(())
637}
638
639pub fn require_initialized_delegation_metadata(
642 delegated_account: &AccountView,
643 delegation_metadata: &AccountView,
644 is_writable: bool,
645) -> Result<(), ProgramError> {
646 require_initialized_pda(
647 delegation_metadata,
648 &[
649 pda::DELEGATION_METADATA_TAG,
650 delegated_account.address().as_ref(),
651 ],
652 &crate::fast::ID,
653 is_writable,
654 "delegation metadata",
655 )?;
656 Ok(())
657}
658
659pub fn require_initialized_commit_state(
662 delegated_account: &AccountView,
663 commit_state: &AccountView,
664 is_writable: bool,
665) -> Result<(), ProgramError> {
666 require_initialized_pda(
667 commit_state,
668 &[pda::COMMIT_STATE_TAG, delegated_account.address().as_ref()],
669 &crate::fast::ID,
670 is_writable,
671 "commit state",
672 )?;
673 Ok(())
674}
675
676pub fn require_initialized_commit_record(
679 delegated_account: &AccountView,
680 commit_record: &AccountView,
681 is_writable: bool,
682) -> Result<(), ProgramError> {
683 require_initialized_pda(
684 commit_record,
685 &[pda::COMMIT_RECORD_TAG, delegated_account.address().as_ref()],
686 &crate::fast::ID,
687 is_writable,
688 "commit record",
689 )?;
690 Ok(())
691}
692
693pub trait RequireUninitializedAccountCtx {
699 fn label(&self) -> &str;
700 fn invalid_seeds(&self) -> ProgramError;
701 fn invalid_account_owner(&self) -> ProgramError;
702 fn account_already_initialized(&self) -> ProgramError;
703 fn immutable(&self) -> ProgramError;
704}
705
706macro_rules! define_uninitialized_ctx {
707 (
708 $name:ident,
709 label = $label:expr,
710 invalid_seeds = $seeds:expr,
711 invalid_account_owner = $owner:expr,
712 account_already_initialized = $already_init:expr,
713 immutable = $immutable:expr
714 ) => {
715 pub struct $name;
716
717 impl $crate::requires::RequireUninitializedAccountCtx for $name {
718 fn label(&self) -> &str {
719 $label
720 }
721
722 fn invalid_seeds(&self) -> pinocchio::error::ProgramError {
723 $seeds.into()
724 }
725
726 fn invalid_account_owner(&self) -> pinocchio::error::ProgramError {
727 $owner.into()
728 }
729
730 fn account_already_initialized(
731 &self,
732 ) -> pinocchio::error::ProgramError {
733 $already_init.into()
734 }
735
736 fn immutable(&self) -> pinocchio::error::ProgramError {
737 $immutable.into()
738 }
739 }
740 };
741}
742
743define_uninitialized_ctx!(
744 CommitStateAccountCtx,
745 label = "commit state account",
746 invalid_seeds = DlpError::CommitStateInvalidSeeds,
747 invalid_account_owner = DlpError::CommitStateInvalidAccountOwner,
748 account_already_initialized = DlpError::CommitStateAlreadyInitialized,
749 immutable = DlpError::CommitStateImmutable
750);
751
752define_uninitialized_ctx!(
753 CommitRecordCtx,
754 label = "commit record",
755 invalid_seeds = DlpError::CommitRecordInvalidSeeds,
756 invalid_account_owner = DlpError::CommitRecordInvalidAccountOwner,
757 account_already_initialized = DlpError::CommitRecordAlreadyInitialized,
758 immutable = DlpError::CommitRecordImmutable
759);
760
761define_uninitialized_ctx!(
762 DelegationRecordCtx,
763 label = "delegation record",
764 invalid_seeds = DlpError::DelegationRecordInvalidSeeds,
765 invalid_account_owner = DlpError::DelegationRecordInvalidAccountOwner,
766 account_already_initialized = DlpError::DelegationRecordAlreadyInitialized,
767 immutable = DlpError::DelegationRecordImmutable
768);
769
770define_uninitialized_ctx!(
771 DelegationMetadataCtx,
772 label = "delegation metadata",
773 invalid_seeds = DlpError::DelegationMetadataInvalidSeeds,
774 invalid_account_owner = DlpError::DelegationMetadataInvalidAccountOwner,
775 account_already_initialized =
776 DlpError::DelegationMetadataAlreadyInitialized,
777 immutable = DlpError::DelegationMetadataImmutable
778);
779
780define_uninitialized_ctx!(
781 UndelegateBufferCtx,
782 label = "undelegate buffer",
783 invalid_seeds = DlpError::UndelegateBufferInvalidSeeds,
784 invalid_account_owner = DlpError::UndelegateBufferInvalidAccountOwner,
785 account_already_initialized = DlpError::UndelegateBufferAlreadyInitialized,
786 immutable = DlpError::UndelegateBufferImmutable
787);
788
789pub fn require_authorization(
790 program_data: &AccountView,
791 admin: &AccountView,
792) -> Result<(), ProgramError> {
793 #[cfg(feature = "unit_test_config")]
794 {
795 let _ = program_data;
796
797 require_eq_keys!(
798 &Address::from(
799 crate::consts::DEFAULT_VALIDATOR_IDENTITY.to_bytes()
800 ),
801 admin.address(),
802 ProgramError::IncorrectAuthority
803 );
804 Ok(())
805 }
806
807 #[cfg(not(feature = "unit_test_config"))]
808 {
809 require_eq_keys!(
811 &crate::consts::DELEGATION_PROGRAM_DATA_ID,
812 program_data.address(),
813 ProgramError::IncorrectAuthority
814 );
815
816 let offset_of_upgrade_authority_address =
819 4 + 8 ;
822
823 const PROGRAM_DATA: u8 = 3;
825 const OPTION_SOME: u8 = 1;
826
827 let data = program_data.try_borrow()?;
831 if data.len() >= offset_of_upgrade_authority_address + 33
832 && data[0] == PROGRAM_DATA
833 && data[offset_of_upgrade_authority_address] == OPTION_SOME
834 {
835 let bytes = &data[offset_of_upgrade_authority_address + 1
836 ..offset_of_upgrade_authority_address + 33];
837
838 let upgrade_authority_address =
839 unsafe { &*(bytes.as_ptr() as *const Address) };
840
841 require_eq_keys!(
842 upgrade_authority_address,
843 admin.address(),
844 ProgramError::IncorrectAuthority
845 );
846
847 Ok(())
848 } else {
849 Err(ProgramError::InvalidAccountData)
850 }
851 }
852}