|
| 1 | +# Account Comparison |
| 2 | + |
| 3 | +Side-by-side implementation of Solana accounts vs compressed accounts. Shows equivalent create and update operations for both account types using identical data structures. |
| 4 | + |
| 5 | +## Summary |
| 6 | + |
| 7 | +- Demonstrates equivalent PDA and compressed account patterns with identical seeds `["account", user.key()]` |
| 8 | +- Compressed accounts use `LightAccount::new_init` for creation and `LightAccount::new_mut` for updates |
| 9 | +- Updates require client to pass existing state because on-chain storage is a Poseidon hash |
| 10 | +- All fields marked `#[hash]` are included in Poseidon hash computation |
| 11 | + |
| 12 | +## Source Structure |
| 13 | + |
| 14 | +``` |
| 15 | +programs/account-comparison/src/ |
| 16 | + lib.rs # Program entrypoint, instructions, account structs |
| 17 | +programs/account-comparison/tests/ |
| 18 | + test_solana_account.rs # LiteSVM tests for standard accounts |
| 19 | + test_compressed_account.rs # Light Protocol tests for compressed accounts |
| 20 | +``` |
| 21 | + |
| 22 | +## Accounts |
| 23 | + |
| 24 | +### AccountData (Solana PDA) |
| 25 | + |
| 26 | +| Field | Type | Offset | |
| 27 | +|-------|------|--------| |
| 28 | +| discriminator | `[u8; 8]` | 0..8 | |
| 29 | +| user | `Pubkey` | 8..40 | |
| 30 | +| name | `String` | 40..168 | |
| 31 | +| data | `[u8; 128]` | 168..232 | |
| 32 | + |
| 33 | +- **Seeds**: `["account", user.key()]` |
| 34 | +- **Discriminator**: 8 bytes, SHA256("account:AccountData")[0..8] |
| 35 | +- **Space**: 232 bytes |
| 36 | + |
| 37 | +### CompressedAccountData (LightAccount) |
| 38 | + |
| 39 | +```rust |
| 40 | +#[derive(LightDiscriminator, LightHasher)] |
| 41 | +pub struct CompressedAccountData { |
| 42 | + #[hash] pub user: Pubkey, |
| 43 | + #[hash] pub name: String, |
| 44 | + #[hash] pub data: [u8; 128], |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +- **Address seeds**: `["account", user.key()]` |
| 49 | +- **Discriminator**: `LightDiscriminator` derive generates from struct name |
| 50 | +- **Hashing**: Poseidon hash of all `#[hash]` fields (user, name, data) |
| 51 | + |
| 52 | +### CPI Signer |
| 53 | + |
| 54 | +```rust |
| 55 | +const CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("FYX4GmKJYzSiycc7XZKf12NGXNE9siSx1cJubYJniHcv"); |
| 56 | +``` |
| 57 | + |
| 58 | +## Instructions |
| 59 | + |
| 60 | +| # | Instruction | Accounts | Parameters | Logic | |
| 61 | +|---|-------------|----------|------------|-------| |
| 62 | +| 0 | `create_account` | `user` (signer, mut), `account` (init PDA), `system_program` | `name: String` | Initializes PDA with seeds `["account", user]`, sets `data = [1u8; 128]` | |
| 63 | +| 1 | `update_data` | `user` (signer, mut), `account` (mut, has_one = user) | `data: [u8; 128]` | Overwrites account.data | |
| 64 | +| 2 | `create_compressed_account` | `user` (signer, mut) + remaining_accounts | `name`, `proof`, `address_tree_info`, `output_tree_index` | Validates ADDRESS_TREE_V2, derives address, calls `LightAccount::new_init`, invokes light-system-program | |
| 65 | +| 3 | `update_compressed_account` | `user` (signer, mut) + remaining_accounts | `new_data`, `existing_data`, `name`, `proof`, `account_meta` | Reconstructs state via `LightAccount::new_mut`, verifies user ownership, invokes light-system-program | |
| 66 | + |
| 67 | +## Security |
| 68 | + |
| 69 | +| Check | Location | Description | |
| 70 | +|-------|----------|-------------| |
| 71 | +| Address tree validation | `create_compressed_account` | Verifies `address_tree_pubkey.to_bytes() == ADDRESS_TREE_V2` | |
| 72 | +| Owner verification | `update_compressed_account` | Asserts `compressed_account.user == ctx.accounts.user.key()` | |
| 73 | +| PDA constraint | `update_data` | Anchor `has_one = user` constraint | |
| 74 | +| Signer requirement | All instructions | User must sign transaction | |
| 75 | + |
| 76 | +## Errors |
| 77 | + |
| 78 | +| Error | Message | |
| 79 | +|-------|---------| |
| 80 | +| `CustomError::Unauthorized` | "No authority to perform this action" | |
| 81 | +| `ProgramError::InvalidAccountData` | Returned when address tree validation fails | |
0 commit comments