mpl_token_auth_rules/state/
rules.rs

1use crate::{
2    error::RuleSetError,
3    payload::Payload,
4    types::Assertable,
5    // TODO: Uncomment this after on-curve sycall available.
6    // utils::is_on_curve,
7    utils::{assert_derivation, compute_merkle_root, is_zeroed},
8};
9use serde::{Deserialize, Serialize};
10#[cfg(feature = "serde-with-feature")]
11use serde_with::{As, DisplayFromStr};
12use solana_program::{
13    account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError,
14    pubkey::Pubkey, system_program,
15};
16use std::collections::{HashMap, HashSet};
17
18#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
19/// Operators that can be used to compare against an `Amount` rule.
20pub enum CompareOp {
21    /// Less Than
22    Lt,
23    /// Less Than or Equal To
24    LtEq,
25    /// Equal To
26    Eq,
27    /// Greater Than or Equal To
28    GtEq,
29    /// Greater Than
30    Gt,
31}
32
33/// Enum representation of Rule failure conditions
34pub enum RuleResult {
35    /// The rule succeeded.
36    Success(ProgramError),
37    /// The rule failed.
38    Failure(ProgramError),
39    /// The program failed to execute the rule.
40    Error(ProgramError),
41}
42
43use RuleResult::*;
44
45#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
46/// The struct containing every type of Rule and its associated data.
47pub enum Rule {
48    /// Group AND, where every rule contained must pass.
49    All {
50        /// The vector of Rules contained under All.
51        rules: Vec<Rule>,
52    },
53    /// Group OR, where at least one rule contained must pass.
54    Any {
55        /// The vector of Rules contained under Any.
56        rules: Vec<Rule>,
57    },
58    /// Negation, where the contained rule must fail.
59    Not {
60        /// The Rule contained under Not.
61        rule: Box<Rule>,
62    },
63    /// An additional signer must be present.  When the `Validate` instruction is called, this rule
64    /// does not require any `Payload` values, but the additional signer account must be provided
65    /// to `Validate` via the `additional_rule_accounts` argument so that whether it is a signer
66    /// can be retrieved from its `AccountInfo` struct.
67    AdditionalSigner {
68        /// The public key that must have also signed the transaction.
69        #[cfg_attr(feature = "serde-with-feature", serde(with = "As::<DisplayFromStr>"))]
70        account: Pubkey,
71    },
72    /// Direct comparison between `Pubkey`s.  When the `Validate` instruction is called, this rule
73    /// requires a `PayloadType` value of `PayloadType::Pubkey`.  The `field` value in the rule is
74    /// used to locate the `Pubkey` in the payload to compare to the `Pubkey` in the rule.
75    PubkeyMatch {
76        /// The public key to be compared against.
77        #[cfg_attr(feature = "serde-with-feature", serde(with = "As::<DisplayFromStr>"))]
78        pubkey: Pubkey,
79        /// The field in the `Payload` to be compared.
80        field: String,
81    },
82    /// The comparing `Pubkey` must be in the list of `Pubkey`s.  When the `Validate` instruction
83    /// is called, this rule requires a `PayloadType` value of `PayloadType::Pubkey`.  The `field`
84    /// value in the Rule is used to locate the `Pubkey` in the payload to compare to the `Pubkey`
85    /// list in the rule.
86    PubkeyListMatch {
87        /// The list of public keys to be compared against.
88        pubkeys: Vec<Pubkey>,
89        /// The field in the `Payload` to be compared.
90        field: String,
91    },
92    /// The comparing `Pubkey` must be a member of the Merkle tree in the rule.  When the
93    /// `Validate` instruction is called, this rule requires `PayloadType` values of
94    /// `PayloadType::Pubkey` and `PayloadType::MerkleProof`.  The `field` values in the Rule are
95    /// used to locate them in the `Payload`.  The `Pubkey` and the proof are used to calculate
96    /// a Merkle root which is compared against the root stored in the rule.
97    PubkeyTreeMatch {
98        /// The root of the Merkle tree.
99        root: [u8; 32],
100        /// The field in the `Payload` to be compared
101        /// when looking for the `Pubkey`.
102        pubkey_field: String,
103        /// The field in the `Payload` to be compared
104        /// when looking for the Merkle proof.
105        proof_field: String,
106    },
107    /// A resulting PDA derivation of seeds must prove the account is a PDA.  When the `Validate`
108    /// instruction is called, this rule requires `PayloadType` values of `PayloadType::Seeds`.
109    /// The `field` values in the Rule are used to locate them in the `Payload`.  The seeds in the
110    /// `Payload` and the program ID stored in the Rule are used to derive the PDA from the
111    /// `Payload`.
112    PDAMatch {
113        /// The program used for the PDA derivation.  If
114        /// `None` then the account owner is used.
115        program: Option<Pubkey>,
116        /// The field in the `Payload` to be compared
117        /// when looking for the PDA.
118        pda_field: String,
119        /// The field in the `Payload` to be compared
120        /// when looking for the seeds.
121        seeds_field: String,
122    },
123    /// The `Pubkey` must be owned by a given program.  When the `Validate` instruction is called,
124    /// this rule requires a `PayloadType` value of `PayloadType::Pubkey`.  The `field` value in
125    /// the rule is used to locate the `Pubkey` in the payload for which the owner must be the
126    /// program in the rule.  Note this same `Pubkey` account must also be provided to `Validate`
127    /// via the `additional_rule_accounts` argument.  This is so that the `Pubkey`'s owner can be
128    /// found from its `AccountInfo` struct.
129    ProgramOwned {
130        /// The program that must own the `Pubkey`.
131        #[cfg_attr(feature = "serde-with-feature", serde(with = "As::<DisplayFromStr>"))]
132        program: Pubkey,
133        /// The field in the `Payload` to be compared.
134        field: String,
135    },
136    /// The `Pubkey` must be owned by a program in the list of `Pubkey`s.  When the `Validate`
137    /// instruction is called, this rule requires a `PayloadType` value of `PayloadType::Pubkey`.
138    /// The `field` value in the rule is used to locate the `Pubkey` in the payload for which the
139    /// owner must be a program in the list in the rule.  Note this same `Pubkey` account must also
140    /// be provided to `Validate` via the `additional_rule_accounts` argument.  This is so that the
141    /// `Pubkey`'s owner can be found from its `AccountInfo` struct.
142    ProgramOwnedList {
143        /// The program that must own the `Pubkey`.
144        programs: Vec<Pubkey>,
145        /// The field in the `Payload` to be compared.
146        field: String,
147    },
148    /// The `Pubkey` must be owned by a member of the Merkle tree in the rule.  When the `Validate`
149    /// instruction is called, this rule requires `PayloadType` values of `PayloadType::Pubkey` and
150    /// `PayloadType::MerkleProof`.  The `field` values in the Rule are used to locate them in the
151    /// `Payload`.  Note this same `Pubkey` account must also be provided to `Validate` via the
152    /// `additional_rule_accounts` argument.  This is so that the `Pubkey`'s owner can be found
153    /// from its `AccountInfo` struct.  The owner and the proof are then used to calculate a Merkle
154    /// root, which is compared against the root stored in the rule.
155    ProgramOwnedTree {
156        /// The root of the Merkle tree.
157        root: [u8; 32],
158        /// The field in the `Payload` to be compared
159        /// when looking for the `Pubkey`.
160        pubkey_field: String,
161        /// The field in the `Payload` to be compared
162        /// when looking for the Merkle proof.
163        proof_field: String,
164    },
165    /// Comparison against the amount of tokens being transferred.   When the `Validate`
166    /// instruction is called, this rule requires a `PayloadType` value of `PayloadType::Amount`.
167    /// The `field` value in the Rule is used to locate the numerical amount in the payload to
168    /// compare to the amount stored in the rule, using the comparison operator stored in the rule.
169    Amount {
170        /// The amount to be compared against.
171        amount: u64,
172        /// The operator to be used in the comparison.
173        operator: CompareOp,
174        /// The field the amount is stored in.
175        field: String,
176    },
177    /// Comparison based on time between operations.  Currently not implemented.  This rule
178    /// is planned check to ensure a certain amount of time has passed.  This rule will make use
179    /// of the `rule_set_state_pda` optional account passed into `Validate`, and will require
180    /// the optional `rule_authority` account to sign.
181    Frequency {
182        /// The authority of the frequency account.
183        #[cfg_attr(feature = "serde-with-feature", serde(with = "As::<DisplayFromStr>"))]
184        authority: Pubkey,
185    },
186    /// The true test if a pubkey can be signed from a client and therefore is a true wallet account.
187    /// The details of this rule are as follows: a wallet is defined as being both owned by the
188    /// System Program and the address is on-curve.  The `field` value in the rule is used to
189    /// locate the `Pubkey` in the payload that must be on-curve and for which the owner must be
190    /// the System Program.  Note this same `Pubkey` account must also be provided to `Validate`
191    /// via the `additional_rule_accounts` argument.  This is so that the `Pubkey`'s owner can be
192    /// found from its `AccountInfo` struct.
193    IsWallet {
194        /// The field in the `Payload` to be checked.
195        field: String,
196    },
197    /// An operation that always succeeds.
198    Pass,
199    /// The `Pubkey` must be owned by a program in the set of `Pubkey`s.  When the `Validate`
200    /// instruction is called, this rule requires a `PayloadType` value of `PayloadType::Pubkey`.
201    /// The `field` value in the rule is used to locate the `Pubkey` in the payload for which the
202    /// owner must be a program in the set in the rule.  Note this same `Pubkey` account must also
203    /// be provided to `Validate` via the `additional_rule_accounts` argument.  This is so that the
204    /// `Pubkey`'s owner can be found from its `AccountInfo` struct.
205    ProgramOwnedSet {
206        /// The program that must own the `Pubkey`.
207        programs: HashSet<Pubkey>,
208        /// The field in the `Payload` to be compared.
209        field: String,
210    },
211    /// A rule that tells the operation finder to use the default namespace rule.
212    Namespace,
213}
214
215impl<'a> Assertable<'a> for Rule {
216    /// The top level validation function which parses an entire rule tree.
217    fn validate(
218        &self,
219        accounts: &HashMap<Pubkey, &AccountInfo>,
220        payload: &Payload,
221        update_rule_state: bool,
222        rule_set_state_pda: &Option<&AccountInfo>,
223        rule_authority: &Option<&AccountInfo>,
224    ) -> ProgramResult {
225        let result = self.low_level_validate(
226            accounts,
227            payload,
228            update_rule_state,
229            rule_set_state_pda,
230            rule_authority,
231        );
232
233        match result {
234            Success(_) => Ok(()),
235            Failure(err) => Err(err),
236            Error(err) => Err(err),
237        }
238    }
239}
240
241impl Rule {
242    /// Lower level validation function which iterates through a rule tree and applies boolean logic to rule results.
243    pub fn low_level_validate(
244        &self,
245        accounts: &HashMap<Pubkey, &AccountInfo>,
246        payload: &Payload,
247        _update_rule_state: bool,
248        _rule_set_state_pda: &Option<&AccountInfo>,
249        rule_authority: &Option<&AccountInfo>,
250    ) -> RuleResult {
251        match self {
252            Rule::All { rules } => {
253                msg!("Validating All");
254                let mut last: Option<ProgramError> = None;
255                for rule in rules {
256                    let result = rule.low_level_validate(
257                        accounts,
258                        payload,
259                        _update_rule_state,
260                        _rule_set_state_pda,
261                        rule_authority,
262                    );
263                    // Return failure on the first failing rule.
264                    match result {
265                        Success(err) => last = Some(err),
266                        _ => return result,
267                    }
268                }
269
270                // Return pass if and only if all rules passed.
271                Success(last.unwrap_or_else(|| RuleSetError::UnexpectedRuleSetFailure.into()))
272            }
273            Rule::Any { rules } => {
274                msg!("Validating Any");
275                let mut last_failure: Option<ProgramError> = None;
276                let mut last_error: Option<ProgramError> = None;
277                for rule in rules {
278                    let result = rule.low_level_validate(
279                        accounts,
280                        payload,
281                        _update_rule_state,
282                        _rule_set_state_pda,
283                        rule_authority,
284                    );
285                    match result {
286                        Success(_) => return result,
287                        Failure(err) => last_failure = Some(err),
288                        Error(err) => last_error = Some(err),
289                    }
290                }
291
292                // Return the last failure if and only if no rules passed and there was at least one failure,
293                // otherwise return the last error
294
295                if let Some(err) = last_failure {
296                    Failure(err)
297                } else if let Some(err) = last_error {
298                    Error(err)
299                } else {
300                    Error(RuleSetError::UnexpectedRuleSetFailure.into())
301                }
302            }
303            Rule::Not { rule } => {
304                msg!("Validating Not");
305                let result = rule.low_level_validate(
306                    accounts,
307                    payload,
308                    _update_rule_state,
309                    _rule_set_state_pda,
310                    rule_authority,
311                );
312
313                // Negate the result.
314                match result {
315                    Success(err) => Failure(err),
316                    Failure(err) => Success(err),
317                    Error(err) => Error(err),
318                }
319            }
320            Rule::AdditionalSigner { account } => {
321                msg!("Validating AdditionalSigner");
322                if let Some(signer) = accounts.get(account) {
323                    if signer.is_signer {
324                        Success(self.to_error())
325                    } else {
326                        Failure(self.to_error())
327                    }
328                } else {
329                    Error(RuleSetError::MissingAccount.into())
330                }
331            }
332            Rule::PubkeyMatch { pubkey, field } => {
333                msg!("Validating PubkeyMatch");
334
335                let key = match payload.get_pubkey(field) {
336                    Some(pubkey) => pubkey,
337                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
338                };
339
340                if key == pubkey {
341                    Success(self.to_error())
342                } else {
343                    Failure(self.to_error())
344                }
345            }
346            Rule::PubkeyListMatch { pubkeys, field } => {
347                msg!("Validating PubkeyListMatch");
348
349                let fields = field.split('|').collect::<Vec<&str>>();
350
351                if fields.len() > 1 {
352                    let new_rule = Rule::Any {
353                        rules: fields
354                            .iter()
355                            .map(|field| Rule::ProgramOwnedList {
356                                programs: pubkeys.clone(),
357                                field: field.to_string(),
358                            })
359                            .collect(),
360                    };
361
362                    return new_rule.low_level_validate(
363                        accounts,
364                        payload,
365                        _update_rule_state,
366                        _rule_set_state_pda,
367                        rule_authority,
368                    );
369                } else {
370                    let key = match payload.get_pubkey(&field.to_owned()) {
371                        Some(pubkey) => pubkey,
372                        _ => return Error(RuleSetError::MissingPayloadValue.into()),
373                    };
374
375                    if pubkeys.iter().any(|pubkey| pubkey == key) {
376                        return Success(self.to_error());
377                    }
378                }
379
380                Failure(self.to_error())
381            }
382            Rule::PubkeyTreeMatch {
383                root,
384                pubkey_field,
385                proof_field,
386            } => {
387                msg!("Validating PubkeyTreeMatch");
388
389                // Get the `Pubkey` we are checking from the payload.
390                let leaf = match payload.get_pubkey(pubkey_field) {
391                    Some(pubkey) => pubkey,
392                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
393                };
394
395                // Get the Merkle proof from the payload.
396                let merkle_proof = match payload.get_merkle_proof(proof_field) {
397                    Some(merkle_proof) => merkle_proof,
398                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
399                };
400
401                // Check if the computed hash (root) is equal to the root in the rule.
402                let computed_root = compute_merkle_root(leaf, merkle_proof);
403                if computed_root == *root {
404                    Success(self.to_error())
405                } else {
406                    Failure(self.to_error())
407                }
408            }
409            Rule::PDAMatch {
410                program,
411                pda_field,
412                seeds_field,
413            } => {
414                msg!("Validating PDAMatch");
415
416                // Get the PDA from the payload.
417                let account = match payload.get_pubkey(pda_field) {
418                    Some(pubkey) => pubkey,
419                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
420                };
421
422                // Get the derivation seeds from the payload.
423                let seeds = match payload.get_seeds(seeds_field) {
424                    Some(seeds) => seeds,
425                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
426                };
427
428                // Get the program ID to use for the PDA derivation from the Rule.
429                let program = match program {
430                    // If the Pubkey is stored in the rule, use that value.
431                    Some(program) => program,
432                    None => {
433                        // If one is not stored, then assume the program ID is the account owner.
434                        match accounts.get(account) {
435                            Some(account) => account.owner,
436                            _ => return Error(RuleSetError::MissingAccount.into()),
437                        }
438                    }
439                };
440
441                // Convert the Vec of Vec into Vec of u8 slices.
442                let vec_of_slices = seeds
443                    .seeds
444                    .iter()
445                    .map(Vec::as_slice)
446                    .collect::<Vec<&[u8]>>();
447
448                if let Ok(_bump) = assert_derivation(program, account, &vec_of_slices) {
449                    Success(self.to_error())
450                } else {
451                    Failure(self.to_error())
452                }
453            }
454            Rule::ProgramOwned { program, field } => {
455                msg!("Validating ProgramOwned");
456
457                let key = match payload.get_pubkey(field) {
458                    Some(pubkey) => pubkey,
459                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
460                };
461
462                if let Some(account) = accounts.get(key) {
463                    let data = match account.data.try_borrow() {
464                        Ok(data) => data,
465                        Err(_) => return Error(ProgramError::AccountBorrowFailed),
466                    };
467
468                    if is_zeroed(&data) {
469                        // Print helpful errors.
470                        if data.len() == 0 {
471                            msg!("Account data is empty");
472                        } else {
473                            msg!("Account data is zeroed");
474                        }
475
476                        // Account must have nonzero data to count as program-owned.
477                        return Error(self.to_error());
478                    } else if *account.owner == *program {
479                        return Success(self.to_error());
480                    }
481                } else {
482                    return Error(RuleSetError::MissingAccount.into());
483                }
484
485                Failure(self.to_error())
486            }
487            Rule::ProgramOwnedList { programs, field } => {
488                msg!("Validating ProgramOwnedList");
489
490                let fields = field.split('|').collect::<Vec<&str>>();
491
492                if fields.len() > 1 {
493                    let new_rule = Rule::Any {
494                        rules: fields
495                            .iter()
496                            .map(|field| Rule::ProgramOwnedList {
497                                programs: programs.clone(),
498                                field: field.to_string(),
499                            })
500                            .collect(),
501                    };
502
503                    return new_rule.low_level_validate(
504                        accounts,
505                        payload,
506                        _update_rule_state,
507                        _rule_set_state_pda,
508                        rule_authority,
509                    );
510                } else {
511                    let key = match payload.get_pubkey(&field.to_string()) {
512                        Some(pubkey) => pubkey,
513                        _ => return Error(RuleSetError::MissingPayloadValue.into()),
514                    };
515
516                    let account = match accounts.get(key) {
517                        Some(account) => account,
518                        _ => return Error(RuleSetError::MissingAccount.into()),
519                    };
520
521                    let data = match account.data.try_borrow() {
522                        Ok(data) => data,
523                        Err(_) => return Error(ProgramError::AccountBorrowFailed),
524                    };
525
526                    if is_zeroed(&data) {
527                        // Print helpful errors.
528                        if data.len() == 0 {
529                            msg!("Account data is empty");
530                        } else {
531                            msg!("Account data is zeroed");
532                        }
533
534                        return Error(RuleSetError::DataIsEmpty.into());
535                    } else if programs.contains(account.owner) {
536                        // Account owner must be in the set.
537                        return Success(self.to_error());
538                    }
539                }
540
541                Failure(self.to_error())
542            }
543            Rule::ProgramOwnedTree {
544                root,
545                pubkey_field,
546                proof_field,
547            } => {
548                msg!("Validating ProgramOwnedTree");
549
550                // Get the `Pubkey` we are checking from the payload.
551                let key = match payload.get_pubkey(pubkey_field) {
552                    Some(pubkey) => pubkey,
553                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
554                };
555
556                // Get the `AccountInfo` struct for the `Pubkey`.
557                let account = match accounts.get(key) {
558                    Some(account) => account,
559                    _ => return Error(RuleSetError::MissingAccount.into()),
560                };
561
562                let data = match account.data.try_borrow() {
563                    Ok(data) => data,
564                    Err(_) => return Error(ProgramError::AccountBorrowFailed),
565                };
566
567                // Account must have nonzero data to count as program-owned.
568                if is_zeroed(&data) {
569                    // Print helpful errors.
570                    if data.len() == 0 {
571                        msg!("Account data is empty");
572                    } else {
573                        msg!("Account data is zeroed");
574                    }
575
576                    return Error(RuleSetError::DataIsEmpty.into());
577                }
578
579                // The account owner is the leaf.
580                let leaf = account.owner;
581
582                // Get the Merkle proof from the payload.
583                let merkle_proof = match payload.get_merkle_proof(proof_field) {
584                    Some(merkle_proof) => merkle_proof,
585                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
586                };
587
588                // Check if the computed hash (root) is equal to the root in the rule.
589                let computed_root = compute_merkle_root(leaf, merkle_proof);
590                if computed_root == *root {
591                    Success(self.to_error())
592                } else {
593                    Failure(self.to_error())
594                }
595            }
596            Rule::Amount {
597                amount: rule_amount,
598                operator,
599                field,
600            } => {
601                msg!("Validating Amount");
602                if let Some(payload_amount) = &payload.get_amount(field) {
603                    let operator_fn = match operator {
604                        CompareOp::Lt => PartialOrd::lt,
605                        CompareOp::LtEq => PartialOrd::le,
606                        CompareOp::Eq => PartialEq::eq,
607                        CompareOp::Gt => PartialOrd::gt,
608                        CompareOp::GtEq => PartialOrd::ge,
609                    };
610
611                    if operator_fn(payload_amount, rule_amount) {
612                        Success(self.to_error())
613                    } else {
614                        Failure(self.to_error())
615                    }
616                } else {
617                    Error(RuleSetError::MissingPayloadValue.into())
618                }
619            }
620            Rule::Frequency { authority } => {
621                msg!("Validating Frequency");
622
623                if let Some(rule_authority) = rule_authority {
624                    // TODO: If it's the wrong account (first condition) the `IsNotASigner`
625                    // is misleading.  Should be improved, perhaps with a `Mismatch` error.
626                    if authority != rule_authority.key || !rule_authority.is_signer {
627                        return Error(RuleSetError::RuleAuthorityIsNotSigner.into());
628                    }
629                } else {
630                    return Error(RuleSetError::MissingAccount.into());
631                }
632
633                Error(RuleSetError::NotImplemented.into())
634            }
635            Rule::Pass => {
636                msg!("Validating Pass");
637                Success(self.to_error())
638            }
639            Rule::IsWallet { field } => {
640                msg!("Validating IsWallet");
641
642                // Get the `Pubkey` we are checking from the payload.
643                let key = match payload.get_pubkey(field) {
644                    Some(pubkey) => pubkey,
645                    _ => return Error(RuleSetError::MissingPayloadValue.into()),
646                };
647
648                // Get the `AccountInfo` struct for the `Pubkey` and verify that
649                // its owner is the System Program.
650                if let Some(account) = accounts.get(key) {
651                    if *account.owner != system_program::ID {
652                        // TODO: Change error return to commented line after on-curve syscall
653                        // available.
654                        return Error(RuleSetError::NotImplemented.into());
655                        //return (false, self.to_error());
656                    }
657                } else {
658                    return Error(RuleSetError::MissingAccount.into());
659                }
660
661                // TODO: Uncomment call to `is_on_curve()` after on-curve sycall available.
662                Error(RuleSetError::NotImplemented.into())
663                //(is_on_curve(key), self.to_error())
664            }
665            Rule::ProgramOwnedSet { programs, field } => {
666                msg!("Validating ProgramOwnedSet");
667
668                let fields = field.split('|').collect::<Vec<&str>>();
669
670                if fields.len() > 1 {
671                    let new_rule = Rule::Any {
672                        rules: fields
673                            .iter()
674                            .map(|field| Rule::ProgramOwnedSet {
675                                programs: programs.clone(),
676                                field: field.to_string(),
677                            })
678                            .collect(),
679                    };
680
681                    return new_rule.low_level_validate(
682                        accounts,
683                        payload,
684                        _update_rule_state,
685                        _rule_set_state_pda,
686                        rule_authority,
687                    );
688                } else {
689                    let key = match payload.get_pubkey(&field.to_string()) {
690                        Some(pubkey) => pubkey,
691                        _ => return Error(RuleSetError::MissingPayloadValue.into()),
692                    };
693
694                    let account = match accounts.get(key) {
695                        Some(account) => account,
696                        _ => return Error(RuleSetError::MissingAccount.into()),
697                    };
698
699                    let data = match account.data.try_borrow() {
700                        Ok(data) => data,
701                        Err(_) => return Error(ProgramError::AccountBorrowFailed),
702                    };
703
704                    if is_zeroed(&data) {
705                        // Print helpful errors.
706                        if data.len() == 0 {
707                            msg!("Account data is empty");
708                        } else {
709                            msg!("Account data is zeroed");
710                        }
711
712                        return Error(RuleSetError::DataIsEmpty.into());
713                    } else if programs.contains(account.owner) {
714                        // Account owner must be in the set.
715                        return Success(self.to_error());
716                    }
717                }
718
719                Failure(self.to_error())
720            }
721            Rule::Namespace => {
722                msg!("Validating Namespace");
723                Failure(self.to_error())
724            }
725        }
726    }
727
728    /// Convert the rule to a corresponding error resulting from the rule failure.
729    pub fn to_error(&self) -> ProgramError {
730        match self {
731            Rule::All { .. }
732            | Rule::Any { .. }
733            | Rule::Not { .. }
734            | Rule::Pass
735            | Rule::Namespace => RuleSetError::UnexpectedRuleSetFailure.into(),
736            Rule::AdditionalSigner { .. } => RuleSetError::AdditionalSignerCheckFailed.into(),
737            Rule::PubkeyMatch { .. } => RuleSetError::PubkeyMatchCheckFailed.into(),
738            Rule::PubkeyListMatch { .. } => RuleSetError::PubkeyListMatchCheckFailed.into(),
739            Rule::PubkeyTreeMatch { .. } => RuleSetError::PubkeyTreeMatchCheckFailed.into(),
740            Rule::PDAMatch { .. } => RuleSetError::PDAMatchCheckFailed.into(),
741            Rule::ProgramOwned { .. } => RuleSetError::ProgramOwnedCheckFailed.into(),
742            Rule::ProgramOwnedList { .. } => RuleSetError::ProgramOwnedListCheckFailed.into(),
743            Rule::ProgramOwnedTree { .. } => RuleSetError::ProgramOwnedTreeCheckFailed.into(),
744            Rule::Amount { .. } => RuleSetError::AmountCheckFailed.into(),
745            Rule::Frequency { .. } => RuleSetError::FrequencyCheckFailed.into(),
746            Rule::IsWallet { .. } => RuleSetError::IsWalletCheckFailed.into(),
747            Rule::ProgramOwnedSet { .. } => RuleSetError::ProgramOwnedSetCheckFailed.into(),
748        }
749    }
750}