Account Discriminators
Discriminators identify which struct type owns an account's byte layout. They block type confusion attacks where a malicious account is interpreted as the wrong type.
Recipe
if data[..8] != Account::DISCRIMINATOR { return Err(ProgramError::InvalidAccountData); }When to reach for this:
- Multiple account types in one program.
- Migrating layouts across versions.
- Matching Anchor account validation behavior.
Working Example
pub const VAULT_DISC: [u8; 8] = *b"vault___";
pub fn load_vault(data: &[u8]) -> Result<Vault, ProgramError> {
if data.len() < 8 || data[..8] != VAULT_DISC { return Err(ProgramError::InvalidAccountData); }
Vault::deserialize(&mut &data[8..]).map_err(|_| ProgramError::InvalidAccountData)
}What this demonstrates:
- 8-byte prefix checked before Borsh body.
- Constant discriminator per type.
- Wrong discriminator rejects early.
Deep Dive
Anchor Discriminators
SHA256("account:TypeName")[:8] in 0.32.1.
Native
Pick unique constants; document in IDL.
Rust Notes
// Include disc in SPACE: 8 + bodyGotchas
- No discriminator - Token account parsed as custom state.. Fix: Always prefix types.
- Duplicate discriminators - Two types collide.. Fix: Unique per struct.
- Forgot disc in SPACE - Serialize truncates.. Fix: Add 8 bytes to LEN.
- Client initializes wrong type - Init handler must write disc.. Fix: Set at create only.
- Upgrade disc change - Bricks accounts.. Fix: Never change live disc.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Owner-only typing | Single account type per program | Multi-type programs |
| Version byte only | Simpler | Weaker type safety |
| Anchor auto disc | Default | Native manual |
FAQs
8 bytes required?
Anchor convention; native can differ if consistent.
Disc in Borsh?
Usually prepended manually or Anchor wrapper.
Init sets disc?
Yes first write.
Close clears disc?
Zero entire data.
Disc in IDL?
Anchor includes account names.
Brute force disc?
Not security boundary - layout match matters.
Token account disc?
Token program has own layout - different.
Multiple versions same type?
Use version field after disc.
Test wrong disc?
LiteSVM negative test.
Hash disc?
Anchor internal - reproducible.
Pub in SDK?
Export constants for clients.
Zero disc?
Valid bytes but avoid all-zero ambiguity.
Related
- Borsh Structs - Layouts
- Account Initialization Patterns - Init
- Reinitialization Attacks - Confusion attacks
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.