-
Notifications
You must be signed in to change notification settings - Fork 87
feat: docs light system program #2206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughThis PR adds comprehensive documentation for the Light System Program across 12 markdown files, detailing account layouts (CPI context in V1/V2), instruction specifications (Invoke, InvokeCpi variants), a 19-step processing pipeline, CPI context feature, initialization workflows, and error references. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Review requires verification of documentation accuracy against implementation, cross-referencing across 12 heterogeneous files covering instructions, account layouts, pipeline steps, and error codes; moderate complexity in explaining multi-step flows and data structures. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🤖 Fix all issues with AI agents
In `@programs/system/CLAUDE.md`:
- Line 9: The markdown in CLAUDE.md needs spacing and fence fixes: add a blank
line before the heading that currently reads "Handles SOL
compression/decompression" (and similarly add blank lines before headings at the
other flagged positions), ensure every fenced code block is surrounded by a
blank line (fix blocks at the positions flagged) and add language specifiers
(use "text" for the plain shell/example blocks) to the fenced code blocks
currently missing them; locate these edits by searching for the heading text
"Handles SOL compression/decompression" and the three fenced code blocks shown
in the diff and adjust spacing and fence headers accordingly.
- Around line 150-167: The header currently misstates "The 19-step processing
flow" while the list in the "Processing Pipeline" section (referencing
src/processor/process.rs) shows 12 grouped steps; update the text to either list
all 19 discrete steps from docs/PROCESSING_PIPELINE.md or change the sentence to
indicate this is a high-level summary (e.g., "A high-level 12-step summary of
the 19-step processing flow") and add a clarifying pointer to
docs/PROCESSING_PIPELINE.md; make the change near the "Processing Pipeline"
section heading to ensure consistency between the step count and the enumerated
items.
- Around line 11-24: Update the example transaction flow to match the
CompressedCpiContext state machine: replace the incorrect "execute=true" in the
Program C step with a call to InvokeCpiWithAccountInfo where
first_set_context=false and set_context=false (or omit both since false is
default) to represent the Execute state; reference the CompressedCpiContext
semantics (see CPI_CONTEXT.md) and keep the other steps (Program A:
first_set_context=true, Program B: set_context=true) unchanged.
In `@programs/system/docs/ACCOUNTS.md`:
- Around line 15-114: The documentation's V2 account size math is incorrect:
recompute and update the type sizes for ZPackedReadOnlyAddress,
ZPackedReadOnlyCompressedAccount, CpiContextInAccount, and CpiContextOutAccount
(use the actual sizes from zero_copy.rs and account.rs) and then either (A)
replace the per-type breakdown and total with the corrected totals that sum to
the actual layout, or (B) explicitly state that
DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 (the 14020 constant) includes an additional
reserved buffer beyond the serialized fields; update the Size Calculation
Example and the "Total" line accordingly and reference the actual symbols/types
CpiContextNewAddressParamsAssignedPacked, ZPackedReadOnlyAddress,
ZPackedReadOnlyCompressedAccount, CpiContextInAccount, CpiContextOutAccount and
the constant DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 so readers can verify against
program-libs/compressed-account/src/instruction_data/zero_copy.rs and
programs/system/src/cpi_context/{account.rs,address.rs}.
In `@programs/system/docs/init/INIT_CPI_CONTEXT_ACCOUNT.md`:
- Around line 5-101: The Errors table mixes generic names with actual error
variants returned by our account checks; update the documented errors to use the
real error codes and variant names produced by check_owner and check_signer
(e.g., AccountOwnedByWrongProgram 20001 for associated_merkle_tree ownership and
InvalidSigner 20009 for fee_payer signer), retain InvalidCpiContextOwner 6055
for cpi_context_account ownership, keep NotEnoughAccountKeys and the state-tree
discriminator error (StateMerkleTreeAccountDiscriminatorMismatch 6042), and
remove or replace any “IllegalOwner”/“MissingRequiredSignature” entries so the
table matches the actual return values from check_owner and check_signer in
InitializeCpiContextAccount/cpi_context_account_new.
In `@programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_ACCOUNT_INFO.md`:
- Around line 11-29: The README is missing a dedicated CPI section for
InvokeCpiWithAccountInfo; add a "CPI Invocation" subsection that documents the
target program (program id / entrypoint), required PDA signer seeds and bump
values, how AccountOptions maps to concrete accounts (account mapping
passthrough), how raw instruction data and any contiguous data passthrough are
encoded/decoded for the callee (data passthrough handling), and include a full
end-to-end example that builds the instruction for InvokeCpiWithAccountInfo
using AccountOptions, signs with the PDA (showing seeds + bump), lists the exact
AccountInfo array order, and shows the final invoke_signed call; reference the
instruction name InvokeCpiWithAccountInfo and the AccountOptions encoding so
readers can map flags to accounts.
- Around line 110-147: The fenced code blocks under the "Account Layout
Examples" section (including the "Execute Mode (write_to_cpi_context = false)"
and "CPI Context Write Mode (write_to_cpi_context = true)" examples) need
markdownlint fixes: add a language identifier (e.g., text) after each opening
``` and ensure there is a blank line before and after each fenced block so the
blocks are properly separated from surrounding headings and paragraphs; update
all three blocks shown in the diff accordingly.
- Around line 39-241: The documented InstructionDataInvokeCpiWithAccountInfo
struct is out of sync with the real implementation; update the docs to exactly
match the actual struct in
program-libs/compressed-account/src/instruction_data/with_account_info.rs by
replacing the current fields with the actual ones: include mode:u8 and bump:u8,
invoking_program_id:Pubkey, compress_or_decompress_lamports:u64,
is_compress:bool, with_cpi_context:bool, with_transaction_hash:bool,
cpi_context:CompressedCpiContext, proof:Option<CompressedProof>,
new_address_params:Vec<NewAddressParamsAssignedPacked>, and
account_infos:Vec<CompressedAccountInfo>; remove the incorrect
account_option_config:AccountOptions, relay_fee, read_only_accounts, and
read_only_addresses entries and ensure the struct name
InstructionDataInvokeCpiWithAccountInfo and referenced types
(CompressedCpiContext, CompressedProof, NewAddressParamsAssignedPacked,
CompressedAccountInfo) are used exactly as in the source so the docs match the
implementation.
In `@programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_READ_ONLY.md`:
- Around line 11-34: Update the InvokeCpiWithReadOnly documentation to add a
dedicated "CPI Invocation" section that specifies the target program ID
(explicit in instruction data), required PDA signer seeds and bump values, how
account mapping/passthrough should be handled for both writable and read-only
compressed accounts, and include a full runnable example showing: the
instruction construction (InvokeCpiWithReadOnly), how to populate the accounts
array (writable inputs nullified, outputs created, read-only accounts verified),
how to supply PDA seeds + bump to sign the CPI, and a minimal on-chain/off-chain
invocation snippet demonstrating execute mode for read-only addresses; ensure
the section references the existing terms InvokeCpiWithReadOnly, InvokeCpi,
"read-only addresses", and "account mode" so reviewers can locate the changes.
- Around line 41-61: Add proper blank lines before and after fenced code blocks
and label non-Rust blocks with a language identifier to satisfy markdownlint;
for the shown block containing the ZInstructionDataInvokeCpiWithReadOnly struct
ensure there is an empty line above the opening ```rust and below the closing
``` and similarly add language identifiers (e.g., text) for any non-Rust fenced
blocks referenced (also fix the other affected fenced blocks around lines
118-149). Locate the code fences around ZInstructionDataInvokeCpiWithReadOnly,
ZNewAddressParamsAssignedPacked, ZInAccount,
ZOutputCompressedAccountWithPackedContext, ZPackedReadOnlyAddress, and
ZPackedReadOnlyCompressedAccount and apply the blank-line and language-label
fixes consistently.
- Around line 39-179: The docs currently list is_compress, with_cpi_context, and
with_transaction_hash as public bool fields on
ZInstructionDataInvokeCpiWithReadOnly, and reference read-only verification as
steps 8/9; update the doc to state that the zero-copy meta struct
(ZInstructionDataInvokeCpiWithReadOnlyMeta /
ZInstructionDataInvokeCpiWithReadOnly) stores those flags as u8 and exposes them
via getter methods (e.g. is_compress(), with_cpi_context(),
with_transaction_hash()), and fix the processing step references to match the
implementation (read-only account verification at step 14 and proof verification
at step 16) or remove hard step numbers to avoid drift.
In `@programs/system/docs/invoke/INVOKE.md`:
- Around line 128-162: The markdown sections around the example code blocks lack
blank lines; update INVOKE.md so each fenced code block is preceded and followed
by an empty line for consistency (add blank lines around the blocks that contain
the instruction_data slice and zero_copy_at call, the
InvokeInstruction::from_account_infos call, the
input_compressed_accounts_signer_check call, and the process::<false,
InvokeInstruction, ZInstructionDataInvoke> call). Ensure the empty line is
inserted immediately before the opening ``` and immediately after the closing
``` for those code blocks so markdownlint rules are satisfied and readability is
consistent.
- Around line 63-67: Update the fenced code block that shows the data layout
(the three-line block starting with "[0..8]: Discriminator") to include a
language specifier for syntax highlighting (for example change the opening ```
to ```text) so the block is rendered consistently; ensure only the opening fence
is modified and the block content remains unchanged.
| - Verifies ZK proofs for compressed account state transitions | ||
| - Manages CPI context for multi-program transactions | ||
| - Coordinates with account-compression program via CPI | ||
| - Handles SOL compression/decompression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Fix markdown formatting for consistency.
Multiple markdown formatting issues flagged by linter:
- Missing blank lines around headings (lines 9, 15, 20, 144, 153, 174)
- Code blocks without language specifiers (lines 76, 99)
- Code blocks not surrounded by blank lines (lines 16, 76, 99, 154)
These issues affect readability and consistency with markdown best practices.
📝 Suggested formatting fixes
- Add blank lines before headings (lines 9, 15, 20, 144, 153, 174):
- - Handles SOL compression/decompression
+ - Handles SOL compression/decompression
+
## Used In- Add language specifiers to code blocks (lines 76, 99):
- ```
+ ```text
# Run all system program tests- Add blank lines around code blocks (lines 16, 76, 99, 154):
- **Compressed Token Program** - All compressed token operations (mint, transfer, burn) invoke this program via CPI
+
**Example transaction flow:**Also applies to: 15-16, 20-20, 76-76, 99-99, 144-144, 153-154, 174-174
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
In `@programs/system/CLAUDE.md` at line 9, The markdown in CLAUDE.md needs spacing
and fence fixes: add a blank line before the heading that currently reads
"Handles SOL compression/decompression" (and similarly add blank lines before
headings at the other flagged positions), ensure every fenced code block is
surrounded by a blank line (fix blocks at the positions flagged) and add
language specifiers (use "text" for the plain shell/example blocks) to the
fenced code blocks currently missing them; locate these edits by searching for
the heading text "Handles SOL compression/decompression" and the three fenced
code blocks shown in the diff and adjust spacing and fence headers accordingly.
| ## Used In | ||
|
|
||
| The Light System Program is invoked by: | ||
|
|
||
| - **Compressed Token Program** - All compressed token operations (mint, transfer, burn) invoke this program via CPI | ||
| - **Custom Anchor Programs** - Programs using Light SDK invoke this for compressed PDA operations via `InvokeCpi` or `InvokeCpiWithAccountInfo` | ||
| - **Direct Clients** - For simple compressed SOL transfers using the `Invoke` instruction | ||
| - **Multi-Program Transactions** - Any transaction requiring multiple programs to coordinate via shared CPI context | ||
|
|
||
| **Example transaction flow:** | ||
| 1. Program A calls `InvokeCpiWithAccountInfo` with `first_set_context=true` to write to CPI context | ||
| 2. Program B calls `InvokeCpiWithAccountInfo` with `set_context=true` to append additional data | ||
| 3. Program C calls `InvokeCpiWithAccountInfo` with `execute=true` to execute the combined state transition with a single ZK proof | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the structure of programs/system directory
find programs/system -type f -name "*.md" | head -20
echo "---"
# Check if docs directory exists
ls -la programs/system/ 2>/dev/null | grep -E "^d"Repository: Lightprotocol/light-protocol
Length of output: 897
🏁 Script executed:
#!/bin/bash
# Search for InvokeCpiWithAccountInfo in the codebase to find its actual implementation
rg "InvokeCpiWithAccountInfo" --type rust -A 5 -B 2 | head -100Repository: Lightprotocol/light-protocol
Length of output: 7792
🏁 Script executed:
#!/bin/bash
# Also search for the parameter names mentioned in the example
rg "first_set_context|set_context.*execute" --type rust -B 3 -A 3 | head -100Repository: Lightprotocol/light-protocol
Length of output: 9050
🏁 Script executed:
#!/bin/bash
# Read the INVOKE_CPI_WITH_ACCOUNT_INFO.md documentation
cat programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_ACCOUNT_INFO.mdRepository: Lightprotocol/light-protocol
Length of output: 7118
🏁 Script executed:
#!/bin/bash
# Read the CPI_CONTEXT.md documentation
cat programs/system/docs/CPI_CONTEXT.mdRepository: Lightprotocol/light-protocol
Length of output: 8482
🏁 Script executed:
#!/bin/bash
# Search for "execute" parameter in the context of CPI/InvokeCpiWithAccountInfo
rg "execute" --type rust programs/system/src -B 2 -A 2 | head -80Repository: Lightprotocol/light-protocol
Length of output: 1682
Correct the multi-program transaction flow example to accurately reflect the CompressedCpiContext state machine.
The example transaction flow describes a valid use case, and the referenced documentation files do exist. However, line 23 contains an inaccuracy: there is no execute=true parameter. According to the CPI_CONTEXT.md documentation, the final invocation should have both first_set_context and set_context set to false (or omitted, since false is the default), not execute=true. Update the example to show:
Program C calls `InvokeCpiWithAccountInfo` with `first_set_context=false` and `set_context=false` to execute the combined state transition with a single ZK proof
This accurately reflects the three-state machine: First Set (clear and write), Set Context (append), and Execute (both flags false).
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
15-15: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
16-16: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
20-20: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
In `@programs/system/CLAUDE.md` around lines 11 - 24, Update the example
transaction flow to match the CompressedCpiContext state machine: replace the
incorrect "execute=true" in the Program C step with a call to
InvokeCpiWithAccountInfo where first_set_context=false and set_context=false (or
omit both since false is default) to represent the Execute state; reference the
CompressedCpiContext semantics (see CPI_CONTEXT.md) and keep the other steps
(Program A: first_set_context=true, Program B: set_context=true) unchanged.
| ## Processing Pipeline | ||
|
|
||
| The 19-step processing flow (`src/processor/process.rs`) is the core of the program: | ||
|
|
||
| 1. **Allocate CPI Data** - Pre-allocate memory for account-compression CPI | ||
| 2. **Deserialize Accounts** - Parse and validate Merkle tree accounts | ||
| 3. **Process Addresses** - Derive new addresses, verify read-only addresses | ||
| 4. **Process Outputs** - Hash output accounts, validate indices | ||
| 5. **Process Inputs** - Hash input accounts, create transaction hash | ||
| 6. **Sum Check** - Verify lamport conservation (inputs + compress = outputs + decompress) | ||
| 7. **SOL Compress/Decompress** - Transfer SOL to/from Sol Pool PDA | ||
| 8. **Verify Read-Only** - Verify read-only accounts by index | ||
| 9. **Verify ZK Proof** - Validate zero-knowledge proof covering all inputs/outputs/addresses | ||
| 10. **Transfer Fees** - Pay network, address, and rollover fees | ||
| 11. **Copy CPI Context** - Copy outputs for indexing (when using CPI context) | ||
| 12. **CPI Account Compression** - Execute state transition via CPI to account-compression program | ||
|
|
||
| **See:** [docs/PROCESSING_PIPELINE.md](docs/PROCESSING_PIPELINE.md) for detailed step-by-step breakdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, locate the PROCESSING_PIPELINE.md file
find programs/system -name "PROCESSING_PIPELINE.md" -type fRepository: Lightprotocol/light-protocol
Length of output: 115
🏁 Script executed:
# Also check the CLAUDE.md file to see its full content
cat -n programs/system/CLAUDE.md | head -200Repository: Lightprotocol/light-protocol
Length of output: 10269
🏁 Script executed:
# Search for step numbers in the docs directory
find programs/system/docs -type f -name "*.md" | head -20Repository: Lightprotocol/light-protocol
Length of output: 571
🏁 Script executed:
# Check the detailed PROCESSING_PIPELINE.md to count the actual steps
cat -n programs/system/docs/PROCESSING_PIPELINE.mdRepository: Lightprotocol/light-protocol
Length of output: 17920
Update step count or clarify that this is a simplified overview.
Line 152 states "The 19-step processing flow" but lists only 12 steps (lines 154–166). The detailed PROCESSING_PIPELINE.md does contain 19 discrete steps, but this summary groups related steps together. Clarify whether this section should enumerate all 19 steps or note that this is a high-level summary.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
153-153: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
154-154: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In `@programs/system/CLAUDE.md` around lines 150 - 167, The header currently
misstates "The 19-step processing flow" while the list in the "Processing
Pipeline" section (referencing src/processor/process.rs) shows 12 grouped steps;
update the text to either list all 19 discrete steps from
docs/PROCESSING_PIPELINE.md or change the sentence to indicate this is a
high-level summary (e.g., "A high-level 12-step summary of the 19-step
processing flow") and add a clarifying pointer to docs/PROCESSING_PIPELINE.md;
make the change near the "Processing Pipeline" section heading to ensure
consistency between the step count and the enumerated items.
| ### Discriminator | ||
| ```rust | ||
| CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR: [u8; 8] = [34, 184, 183, 14, 100, 80, 183, 124] | ||
| ``` | ||
|
|
||
| ### Ownership | ||
| - **Owner:** Light System Program (`SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7`) | ||
|
|
||
| ### State Layout | ||
|
|
||
| **Source:** `programs/system/src/cpi_context/state.rs` | ||
|
|
||
| ```rust | ||
| pub struct ZCpiContextAccount2<'a> { | ||
| pub fee_payer: Ref<&'a mut [u8], Pubkey>, // 32 bytes - Transaction fee payer | ||
| pub associated_merkle_tree: Ref<&'a mut [u8], Pubkey>, // 32 bytes - Associated state Merkle tree | ||
| _associated_queue: Ref<&'a mut [u8], Pubkey>, // 32 bytes - Placeholder for future queue | ||
| _place_holder_bytes: Ref<&'a mut [u8], [u8; 32]>, // 32 bytes - Reserved | ||
| pub new_addresses: ZeroCopyVecU8<'a, CpiContextNewAddressParamsAssignedPacked>, // Variable | ||
| pub readonly_addresses: ZeroCopyVecU8<'a, ZPackedReadOnlyAddress>, // Variable | ||
| pub readonly_accounts: ZeroCopyVecU8<'a, ZPackedReadOnlyCompressedAccount>, // Variable | ||
| pub in_accounts: ZeroCopyVecU8<'a, CpiContextInAccount>, // Variable | ||
| pub out_accounts: ZeroCopyVecU8<'a, CpiContextOutAccount>, // Variable | ||
| total_output_data_len: Ref<&'a mut [u8], U16>, // 2 bytes - Total serialized output size | ||
| output_data_len: Ref<&'a mut [u8], U16>, // 2 bytes - Number of output data entries | ||
| pub output_data: Vec<ZeroCopySliceMut<'a, U16, u8>>, // Variable - Output account data | ||
| remaining_data: &'a mut [u8], // Remaining capacity | ||
| } | ||
| ``` | ||
|
|
||
| **Fixed Header Size:** 8 (discriminator) + 32 + 32 + 32 + 32 = 136 bytes | ||
|
|
||
| **Note:** The `Ref<&'a mut [u8], T>` wrapper is used for zero-copy access to fixed-size fields. The lifetime parameter `'a` ensures the account data remains borrowed for the duration of the operation. | ||
|
|
||
| ### Initialization Parameters | ||
|
|
||
| **Source:** `programs/system/src/cpi_context/state.rs` | ||
|
|
||
| ```rust | ||
| pub struct CpiContextAccountInitParams { | ||
| pub associated_merkle_tree: Pubkey, | ||
| pub associated_queue: Pubkey, // Currently placeholder (always Pubkey::default()) | ||
| pub new_addresses_len: u8, // Default: 10 - Pre-allocated capacity | ||
| pub readonly_addresses_len: u8, // Default: 10 - Pre-allocated capacity | ||
| pub readonly_accounts_len: u8, // Default: 10 - Pre-allocated capacity | ||
| pub in_accounts_len: u8, // Default: 20 - Pre-allocated capacity | ||
| pub out_accounts_len: u8, // Default: 30 - Pre-allocated capacity | ||
| } | ||
| ``` | ||
|
|
||
| **Capacity Planning:** | ||
| The length parameters specify pre-allocated capacity for each vector collection. This capacity determines the maximum number of items that can be stored without resizing. Choose values based on expected transaction complexity: | ||
| - Simple transactions: Use defaults (10/10/10/20/30) | ||
| - Complex multi-program transactions: Increase capacity as needed | ||
| - Each item has a fixed size (see Supporting Types section for exact sizes) | ||
|
|
||
| **Default Account Size:** `DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2` = 14020 bytes | ||
|
|
||
| **Source:** `program-libs/batched-merkle-tree/src/constants.rs` | ||
|
|
||
| **Size Calculation Example with Defaults:** | ||
| ``` | ||
| Fixed header: 136 bytes | ||
| Vector metadata (5 vectors × 2 bytes): 10 bytes | ||
| New addresses (10 × 70 bytes): 700 bytes | ||
| Readonly addresses (10 × 36 bytes): 360 bytes | ||
| Readonly accounts (10 × 48 bytes): 480 bytes | ||
| Input accounts (20 × 144 bytes): 2880 bytes | ||
| Output accounts (30 × 128 bytes): 3840 bytes | ||
| Output data metadata: 4 bytes | ||
| Remaining buffer for output data: ~5610 bytes | ||
| Total: 14020 bytes | ||
| ``` | ||
|
|
||
| **Note:** The remaining buffer is used for variable-length output data storage (compressed account data payloads). | ||
|
|
||
| ### Serialization | ||
|
|
||
| Uses zero-copy serialization via `light_zero_copy` crate for performance: | ||
| - **`Ref<&'a mut [u8], T>`** - Zero-copy wrapper for fixed-size fields (fee_payer, associated_merkle_tree, etc.) | ||
| - **`ZeroCopyVecU8<'a, T>`** - Variable-length vectors with u8 length prefix (2 bytes: 1 byte length + 1 byte capacity) | ||
| - **`ZeroCopySliceMut<'a, U16, u8>`** - Output data slices with u16 length prefix | ||
|
|
||
| **Memory Layout:** | ||
| ``` | ||
| [8 bytes discriminator] | ||
| [32 bytes fee_payer] | ||
| [32 bytes associated_merkle_tree] | ||
| [32 bytes _associated_queue] | ||
| [32 bytes _place_holder_bytes] | ||
| [2 bytes new_addresses metadata][variable new_addresses data] | ||
| [2 bytes readonly_addresses metadata][variable readonly_addresses data] | ||
| [2 bytes readonly_accounts metadata][variable readonly_accounts data] | ||
| [2 bytes in_accounts metadata][variable in_accounts data] | ||
| [2 bytes out_accounts metadata][variable out_accounts data] | ||
| [2 bytes total_output_data_len] | ||
| [2 bytes output_data_len] | ||
| [variable output_data with u16 length prefixes] | ||
| [remaining unallocated space] | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the struct definitions
find . -name "state.rs" -path "*/cpi_context/*" | head -5Repository: Lightprotocol/light-protocol
Length of output: 114
🏁 Script executed:
# Look for the struct definition and discriminator constant
rg -n "ZCpiContextAccount2|CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR" --type=rs -g "programs/system/**"Repository: Lightprotocol/light-protocol
Length of output: 98
🏁 Script executed:
# Find the default size constant
rg -n "DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2" --type=rsRepository: Lightprotocol/light-protocol
Length of output: 98
🏁 Script executed:
# Get the supporting types to verify their sizes
rg -n "CpiContextNewAddressParamsAssignedPacked|ZPackedReadOnlyAddress|ZPackedReadOnlyCompressedAccount|CpiContextInAccount|CpiContextOutAccount" --type=rs -g "programs/system/**"Repository: Lightprotocol/light-protocol
Length of output: 98
🏁 Script executed:
# Read the state.rs file to find struct and constants
wc -l ./programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 118
🏁 Script executed:
# Search without type flag
rg -n "ZCpiContextAccount2|CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR" programs/system/Repository: Lightprotocol/light-protocol
Length of output: 3820
🏁 Script executed:
# Find DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2
rg -n "DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2" .Repository: Lightprotocol/light-protocol
Length of output: 2597
🏁 Script executed:
# Find the supporting type definitions
rg -n "struct CpiContextNewAddressParamsAssignedPacked|struct ZPackedReadOnlyAddress|struct ZPackedReadOnlyCompressedAccount|struct CpiContextInAccount|struct CpiContextOutAccount" programs/system/Repository: Lightprotocol/light-protocol
Length of output: 929
🏁 Script executed:
# Read the struct definition from state.rs
sed -n '44,60p' programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 1098
🏁 Script executed:
# Read more of state.rs to see full struct
sed -n '44,150p' programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 5167
🏁 Script executed:
# Read the constants file
cat programs/system/src/constants.rs | head -30Repository: Lightprotocol/light-protocol
Length of output: 1558
🏁 Script executed:
# Read the default size constant
sed -n '30,40p' program-libs/batched-merkle-tree/src/constants.rsRepository: Lightprotocol/light-protocol
Length of output: 644
🏁 Script executed:
# Read the supporting type definitions
cat programs/system/src/cpi_context/account.rsRepository: Lightprotocol/light-protocol
Length of output: 4310
🏁 Script executed:
# Read the address types
cat programs/system/src/cpi_context/address.rsRepository: Lightprotocol/light-protocol
Length of output: 1402
🏁 Script executed:
# Find ZPackedMerkleContext definition
rg -n "struct ZPackedMerkleContext" programs/system/Repository: Lightprotocol/light-protocol
Length of output: 142
🏁 Script executed:
# Find ZPackedReadOnlyAddress definition in source
rg -n "struct ZPackedReadOnlyAddress" programs/system/src/Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
# Find ZPackedReadOnlyCompressedAccount definition in source
rg -n "struct ZPackedReadOnlyCompressedAccount" programs/system/src/Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
# Search more broadly for these types - they might be imported
rg -n "ZPackedMerkleContext|ZPackedReadOnlyAddress|ZPackedReadOnlyCompressedAccount" programs/system/src/cpi_context/ | head -20Repository: Lightprotocol/light-protocol
Length of output: 2058
🏁 Script executed:
# Check the imports at the top of state.rs
head -30 programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 1130
🏁 Script executed:
# Try to find the types in the workspace - they might be in another crate
fd -e rs | xargs grep -l "struct ZPackedMerkleContext" 2>/dev/null | head -5Repository: Lightprotocol/light-protocol
Length of output: 137
🏁 Script executed:
# Let me check what's available in the repo
ls -la program-libs/Repository: Lightprotocol/light-protocol
Length of output: 1515
🏁 Script executed:
# Read the zero_copy.rs file to find ZPackedMerkleContext, ZPackedReadOnlyAddress, ZPackedReadOnlyCompressedAccount
cat program-libs/compressed-account/src/instruction_data/zero_copy.rsRepository: Lightprotocol/light-protocol
Length of output: 50384
🏁 Script executed:
# Let me double-check the struct size calculations using Rust's size_of
python3 << 'EOF'
# Struct sizes based on field types from source
types = {
'ZPackedReadOnlyAddress': {
'fields': {'address': 32, 'address_merkle_tree_root_index': 2, 'address_merkle_tree_account_index': 1},
'doc_claims': 36
},
'ZPackedMerkleContext': {
'fields': {'merkle_tree_pubkey_index': 1, 'queue_pubkey_index': 1, 'leaf_index': 4, 'prove_by_index': 1},
'doc_claims': 'N/A (inferred from ReadOnlyCompressed)'
},
'ZPackedReadOnlyCompressedAccount': {
'fields': {'account_hash': 32, 'merkle_context': 7, 'root_index': 2},
'doc_claims': 48
},
'CpiContextOutAccount': {
'fields': {'owner': 32, 'has_data': 1, 'discriminator': 8, 'data_hash': 32, 'output_merkle_tree_index': 1, 'lamports': 8, 'with_address': 1, 'address': 32},
'doc_claims': 128
},
'CpiContextInAccount': {
'fields': {'owner': 32, 'has_data': 1, 'discriminator': 8, 'data_hash': 32, 'merkle_context': 7, 'root_index': 2, 'lamports': 8, 'with_address': 1, 'address': 32},
'doc_claims': 144
},
'CpiContextNewAddressParamsAssignedPacked': {
'fields': {'owner': 32, 'seed': 32, 'address_queue_account_index': 1, 'address_merkle_tree_account_index': 1, 'address_merkle_tree_root_index': 2, 'assigned_to_account': 1, 'assigned_account_index': 1},
'doc_claims': 70
}
}
total_size_from_correct_sizes = 8 + 32 + 32 + 32 + 32 # fixed header
total_size_from_correct_sizes += 5 * 2 # vector metadata
total_size_from_correct_sizes += 10 * 70 # new_addresses
total_size_from_correct_sizes += 10 * 35 # readonly_addresses (correct: 35 not 36)
total_size_from_correct_sizes += 10 * 41 # readonly_accounts (correct: 41 not 48)
total_size_from_correct_sizes += 20 * 123 # in_accounts (correct: 123 not 144)
total_size_from_correct_sizes += 30 * 115 # out_accounts (correct: 115 not 128)
total_size_from_correct_sizes += 4 # output_data metadata
print("Struct Size Analysis:")
print("=" * 70)
for name, info in types.items():
actual = sum(info['fields'].values())
claimed = info['doc_claims']
status = "✓" if actual == claimed else "✗ MISMATCH"
print(f"{name:45} Actual: {actual:3} Doc: {claimed:>3} {status}")
print("\n" + "=" * 70)
print(f"Total size with CORRECT struct sizes: {total_size_from_correct_sizes} bytes")
print(f"Documented default size: 14020 bytes")
print(f"Difference: {14020 - total_size_from_correct_sizes} bytes")
EOFRepository: Lightprotocol/light-protocol
Length of output: 158
Fix struct size calculations and totals in V2 account documentation.
The documented type sizes contain errors that compound in the total account size calculation. Verified against source:
| Type | Documented | Actual | Source |
|---|---|---|---|
CpiContextNewAddressParamsAssignedPacked |
70 | 70 | ✓ |
ZPackedReadOnlyAddress |
36 | 35 | program-libs/compressed-account/src/instruction_data/zero_copy.rs:29-33 |
ZPackedReadOnlyCompressedAccount |
48 | 41 | zero_copy.rs:584-590 (includes ZPackedMerkleContext: 7 bytes) |
CpiContextInAccount |
144 | 123 | programs/system/src/cpi_context/account.rs:37-52 |
CpiContextOutAccount |
128 | 115 | account.rs:19-35 |
The size calculation breakdown using these values yields ~7,520 bytes, not the documented 14,020. Either document the actual field breakdown with correct type sizes, or clarify that the 14020-byte constant includes substantial reserved buffer space beyond the documented structures. Reference the actual struct definitions in programs/system/src/cpi_context/{account.rs,address.rs} and program-libs/compressed-account/src/instruction_data/zero_copy.rs for authoritative sizing.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
15-15: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
16-16: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
20-20: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
76-76: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
76-76: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
99-99: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
99-99: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@programs/system/docs/ACCOUNTS.md` around lines 15 - 114, The documentation's
V2 account size math is incorrect: recompute and update the type sizes for
ZPackedReadOnlyAddress, ZPackedReadOnlyCompressedAccount, CpiContextInAccount,
and CpiContextOutAccount (use the actual sizes from zero_copy.rs and account.rs)
and then either (A) replace the per-type breakdown and total with the corrected
totals that sum to the actual layout, or (B) explicitly state that
DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 (the 14020 constant) includes an additional
reserved buffer beyond the serialized fields; update the Size Calculation
Example and the "Total" line accordingly and reference the actual symbols/types
CpiContextNewAddressParamsAssignedPacked, ZPackedReadOnlyAddress,
ZPackedReadOnlyCompressedAccount, CpiContextInAccount, CpiContextOutAccount and
the constant DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 so readers can verify against
program-libs/compressed-account/src/instruction_data/zero_copy.rs and
programs/system/src/cpi_context/{account.rs,address.rs}.
| | Field | Value | | ||
| |-------|-------| | ||
| | **Discriminator** | `[233, 112, 71, 66, 121, 33, 178, 188]` | | ||
| | **Enum** | `InstructionDiscriminator::InitializeCpiContextAccount` | | ||
| | **Path** | `programs/system/src/accounts/init_context_account.rs` | | ||
|
|
||
| ## Description | ||
|
|
||
| Initializes a new CPI context account (version 2) for use in multi-program compressed account transactions. The account is associated with a specific state Merkle tree and allocated with default capacity parameters. | ||
|
|
||
| ### Use Cases | ||
| - Setting up a CPI context account for a new Merkle tree | ||
| - Preparing for multi-program transactions that share a single ZK proof | ||
| - Creating dedicated context accounts for specific applications | ||
|
|
||
| ### State Changes | ||
| - Account data is initialized with version 2 discriminator | ||
| - Associated Merkle tree is set | ||
| - Default capacity parameters are applied | ||
|
|
||
| --- | ||
|
|
||
| ## Instruction Data | ||
|
|
||
| This instruction takes no additional data beyond the discriminator. | ||
|
|
||
| ```rust | ||
| // Instruction data layout: | ||
| // [0..8]: Discriminator | ||
| ``` | ||
|
|
||
| **Total size:** 8 bytes | ||
|
|
||
| --- | ||
|
|
||
| ## Accounts | ||
|
|
||
| | Index | Name | Signer | Writable | Description | | ||
| |-------|------|--------|----------|-------------| | ||
| | 0 | `fee_payer` | Yes | Yes | Pays for account creation | | ||
| | 1 | `cpi_context_account` | No | Yes | Account to initialize (must be pre-allocated) | | ||
| | 2 | `associated_merkle_tree` | No | No | State Merkle tree to associate with | | ||
|
|
||
| ### Account Validations | ||
|
|
||
| **fee_payer (index 0):** | ||
| - Must be a signer | ||
| - Must be writable | ||
|
|
||
| **cpi_context_account (index 1):** | ||
| - Must be pre-allocated with exactly 14020 bytes (`DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2`) | ||
| - Must be owned by Light System Program | ||
| - Discriminator must be zero (uninitialized) | ||
|
|
||
| **associated_merkle_tree (index 2):** | ||
| - Must be owned by Account Compression Program | ||
| - Discriminator must match state or batched Merkle tree | ||
|
|
||
| --- | ||
|
|
||
| ## Instruction Logic | ||
|
|
||
| ### Step 1: Validate Accounts | ||
| ```rust | ||
| let ctx = InitializeCpiContextAccount::from_account_infos(accounts)?; | ||
| ``` | ||
|
|
||
| ### Step 2: Initialize Account | ||
| ```rust | ||
| let params = CpiContextAccountInitParams::new(*ctx.associated_merkle_tree.key()); | ||
| cpi_context_account_new::<false>(ctx.cpi_context_account, params)?; | ||
| ``` | ||
|
|
||
| Initialization: | ||
| 1. Write version 2 discriminator: `[34, 184, 183, 14, 100, 80, 183, 124]` | ||
| 2. Set fee_payer to zero (will be set during operation) | ||
| 3. Set associated_merkle_tree | ||
| 4. Set up vector capacities with defaults: | ||
| - `new_addresses_len`: 10 | ||
| - `readonly_addresses_len`: 10 | ||
| - `readonly_accounts_len`: 10 | ||
| - `in_accounts_len`: 20 | ||
| - `out_accounts_len`: 30 | ||
|
|
||
| --- | ||
|
|
||
| ## Errors | ||
|
|
||
| | Code | Error | Cause | | ||
| |------|-------|-------| | ||
| | - | `NotEnoughAccountKeys` | Less than 3 accounts provided | | ||
| | - | `MissingRequiredSignature` | fee_payer is not a signer | | ||
| | - | `IllegalOwner` | associated_merkle_tree not owned by account-compression program OR cpi_context_account not owned by Light System Program | | ||
| | 6042 | `StateMerkleTreeAccountDiscriminatorMismatch` | associated_merkle_tree discriminator doesn't match state or batched Merkle tree | | ||
| | 6055 | `InvalidCpiContextOwner` | cpi_context_account not owned by Light System Program | | ||
| | 6056 | `InvalidCpiContextDiscriminator` | cpi_context_account discriminator is not zero (already initialized) | | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the init_context_account.rs file and related code
fd -type f -name "init_context_account.rs" programs/system/src/
# Check for constants and types
fd -type f -name "*.rs" programs/system/src/ | head -20Repository: Lightprotocol/light-protocol
Length of output: 200
🏁 Script executed:
#!/bin/bash
# Search for the init_context_account implementation details
rg -n "InitializeCpiContextAccount|init_context_account" programs/system/src/ -t rs -A 5 | head -100Repository: Lightprotocol/light-protocol
Length of output: 200
🏁 Script executed:
#!/bin/bash
# Find size and discriminator constants
rg -n "DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2|CPI_CONTEXT.*DISCRIMINATOR|14020" programs/system/src/ -t rsRepository: Lightprotocol/light-protocol
Length of output: 200
🏁 Script executed:
#!/bin/bash
# Find error codes and definitions
rg -n "6042|6055|6056|InvalidCpiContextOwner|InvalidCpiContextDiscriminator|StateMerkleTreeAccountDiscriminatorMismatch" programs/system/src/ -t rs -B 2 -A 2Repository: Lightprotocol/light-protocol
Length of output: 98
🏁 Script executed:
#!/bin/bash
# Look for the cpi_context_account_new function and initialization logic
rg -n "cpi_context_account_new|CpiContextAccountInitParams" programs/system/src/ -t rs -B 3 -A 10Repository: Lightprotocol/light-protocol
Length of output: 98
🏁 Script executed:
#!/bin/bash
# Find init_context_account.rs and examine directory structure
find programs/system/src -name "*init*" -type fRepository: Lightprotocol/light-protocol
Length of output: 124
🏁 Script executed:
#!/bin/bash
# Search without type flag to find relevant code
rg "InitializeCpiContextAccount|cpi_context_account_new" programs/system/src -A 5 | head -150Repository: Lightprotocol/light-protocol
Length of output: 4591
🏁 Script executed:
#!/bin/bash
# Find size constants
rg "DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2|14020|CPI_CONTEXT" programs/system/src -B 2 -A 2 | head -100Repository: Lightprotocol/light-protocol
Length of output: 5242
🏁 Script executed:
#!/bin/bash
# Find error code definitions
rg "6042|6055|6056" programs/system/src -B 3 -A 1Repository: Lightprotocol/light-protocol
Length of output: 1159
🏁 Script executed:
#!/bin/bash
# Look for discriminator definitions
rg "233.*112.*71.*66|34.*184.*183.*14" programs/system/srcRepository: Lightprotocol/light-protocol
Length of output: 330
🏁 Script executed:
#!/bin/bash
# Find the DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 value
rg "DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2" programs/ -B 2 -A 2Repository: Lightprotocol/light-protocol
Length of output: 4052
🏁 Script executed:
#!/bin/bash
# Check the full init_context_account.rs file to understand account validation
cat -n programs/system/src/accounts/init_context_account.rs | head -150Repository: Lightprotocol/light-protocol
Length of output: 4138
🏁 Script executed:
#!/bin/bash
# Look for CpiContextAccountInitParams to verify vector defaults
rg "CpiContextAccountInitParams|new_addresses_len|readonly_addresses_len" programs/ -B 3 -A 5 | head -150Repository: Lightprotocol/light-protocol
Length of output: 11009
🏁 Script executed:
#!/bin/bash
# Check account validation logic in cpi_context_account_new function
rg -A 40 "pub fn cpi_context_account_new" programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 1953
🏁 Script executed:
#!/bin/bash
# Look for account ownership and signer checks
rg "check_owner|is_signer|writable|IllegalOwner|NotEnoughAccountKeys" programs/system/src/accounts/init_context_account.rs -B 2 -A 2Repository: Lightprotocol/light-protocol
Length of output: 1067
🏁 Script executed:
#!/bin/bash
# Find the CpiContextAccountInitParams implementation and its defaults
rg -B 5 -A 15 "impl CpiContextAccountInitParams" programs/system/src/Repository: Lightprotocol/light-protocol
Length of output: 1500
🏁 Script executed:
#!/bin/bash
# Look for the new() method specifically
rg -A 10 "fn new\(" programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 483
🏁 Script executed:
#!/bin/bash
# Check if there are any account size validations
rg "account_info.data_len|data.len()" programs/system/src/accounts/init_context_account.rs -B 2 -A 2Repository: Lightprotocol/light-protocol
Length of output: 54
Update error handling section with actual error codes and variant names returned by account-checks functions.
The documented errors table conflates generic error concepts with actual implementation. Based on the code analysis:
- Associated merkle tree ownership validation returns
AccountOwnedByWrongProgram(20001) fromcheck_owner, not the genericIllegalOwnerlisted - Fee payer signer validation returns
InvalidSigner(20009) fromcheck_signer, notMissingRequiredSignature - CPI context account ownership validation correctly returns
InvalidCpiContextOwner(6055)
Updated Errors section
| Code | Error | Cause |
|---|---|---|
| - | NotEnoughAccountKeys |
Less than 3 accounts provided |
| 20009 | InvalidSigner |
fee_payer is not a signer |
| 20001 | AccountOwnedByWrongProgram |
associated_merkle_tree not owned by Account Compression Program |
| 6042 | StateMerkleTreeAccountDiscriminatorMismatch |
associated_merkle_tree discriminator doesn't match state or batched Merkle tree |
| 6055 | InvalidCpiContextOwner |
cpi_context_account not owned by Light System Program |
| 6056 | InvalidCpiContextDiscriminator |
cpi_context_account discriminator is not zero (already initialized) |
All other validation rules, discriminators, sizes, and initialization logic match the source code correctly.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
18-18: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
23-23: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
62-62: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
63-63: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
67-67: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
68-68: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
77-77: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
78-78: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
84-84: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
85-85: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In `@programs/system/docs/init/INIT_CPI_CONTEXT_ACCOUNT.md` around lines 5 - 101,
The Errors table mixes generic names with actual error variants returned by our
account checks; update the documented errors to use the real error codes and
variant names produced by check_owner and check_signer (e.g.,
AccountOwnedByWrongProgram 20001 for associated_merkle_tree ownership and
InvalidSigner 20009 for fee_payer signer), retain InvalidCpiContextOwner 6055
for cpi_context_account ownership, keep NotEnoughAccountKeys and the state-tree
discriminator error (StateMerkleTreeAccountDiscriminatorMismatch 6042), and
remove or replace any “IllegalOwner”/“MissingRequiredSignature” entries so the
table matches the actual return values from check_owner and check_signer in
InitializeCpiContextAccount/cpi_context_account_new.
| ## Description | ||
|
|
||
| Extended CPI invocation instruction that supports read-only compressed accounts and addresses. This enables programs to verify the existence of compressed state without modifying it, useful for authorization checks and multi-account validations. | ||
|
|
||
| ### Use Cases | ||
| - Verify compressed account exists before performing operations | ||
| - Check compressed PDA state for authorization | ||
| - Read-only access to compressed token balances | ||
| - Proof-of-ownership checks without state modification | ||
|
|
||
| ### Key Differences from InvokeCpi | ||
| | Feature | InvokeCpi | InvokeCpiWithReadOnly | | ||
| |---------|-----------|------------------------| | ||
| | **Read-only accounts** | Not supported | Supported | | ||
| | **Read-only addresses** | Not supported | Supported (execute mode only) | | ||
| | **Account mode** | Fixed (Anchor) | Configurable (Anchor/V2) | | ||
| | **Invoking program ID** | Implicit (from accounts) | Explicit (in instruction data) | | ||
|
|
||
| ### State Changes | ||
| - Input compressed accounts are nullified (writable inputs only) | ||
| - Output compressed accounts are created | ||
| - Read-only accounts are verified but not modified | ||
| - Read-only addresses are verified to exist | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add required CPI invocation details and full example.
This CPI doc doesn’t include the required target program, PDA signer seeds/bump, account mapping passthrough, or a full CPI example. Please add a dedicated CPI section covering those items and a runnable example snippet. Based on learnings, CPI docs must include these elements.
If you want, I can draft the missing CPI section and example.
Based on learnings, …
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
15-15: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
22-22: Tables should be surrounded by blank lines
(MD058, blanks-around-tables)
29-29: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
In `@programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_READ_ONLY.md` around lines 11
- 34, Update the InvokeCpiWithReadOnly documentation to add a dedicated "CPI
Invocation" section that specifies the target program ID (explicit in
instruction data), required PDA signer seeds and bump values, how account
mapping/passthrough should be handled for both writable and read-only compressed
accounts, and include a full runnable example showing: the instruction
construction (InvokeCpiWithReadOnly), how to populate the accounts array
(writable inputs nullified, outputs created, read-only accounts verified), how
to supply PDA seeds + bump to sign the CPI, and a minimal on-chain/off-chain
invocation snippet demonstrating execute mode for read-only addresses; ensure
the section references the existing terms InvokeCpiWithReadOnly, InvokeCpi,
"read-only addresses", and "account mode" so reviewers can locate the changes.
| **Source:** `program-libs/compressed-account/src/instruction_data/with_readonly.rs` | ||
|
|
||
| ```rust | ||
| pub struct ZInstructionDataInvokeCpiWithReadOnly<'a> { | ||
| // Metadata (fixed layout) | ||
| pub mode: u8, // AccountMode: 0 = Anchor, 1 = V2 | ||
| pub bump: u8, | ||
| pub invoking_program_id: Pubkey, | ||
| pub compress_or_decompress_lamports: U64, | ||
| pub is_compress: bool, | ||
| pub with_cpi_context: bool, | ||
| pub with_transaction_hash: bool, | ||
| pub cpi_context: ZCompressedCpiContext, | ||
|
|
||
| // Variable-length fields (zero-copy slices) | ||
| pub proof: Option<Ref<'a, [u8], CompressedProof>>, | ||
| pub new_address_params: ZeroCopySliceBorsh<'a, ZNewAddressParamsAssignedPacked>, | ||
| pub input_compressed_accounts: Vec<ZInAccount<'a>>, | ||
| pub output_compressed_accounts: Vec<ZOutputCompressedAccountWithPackedContext<'a>>, | ||
| pub read_only_addresses: ZeroCopySliceBorsh<'a, ZPackedReadOnlyAddress>, | ||
| pub read_only_accounts: ZeroCopySliceBorsh<'a, ZPackedReadOnlyCompressedAccount>, | ||
| } | ||
| ``` | ||
|
|
||
| ### Key Fields | ||
|
|
||
| **Mode Selection:** | ||
| - `mode`: Account mode (0 = Anchor, 1 = V2) | ||
| - `invoking_program_id`: Program ID making the CPI (embedded in instruction data) | ||
| - `bump`: PDA bump seed for signer verification | ||
|
|
||
| **Read-Only Features:** | ||
| - `read_only_accounts`: Compressed accounts to verify existence without modification | ||
| - `read_only_addresses`: Addresses to verify existence in address tree | ||
|
|
||
| --- | ||
|
|
||
| ## Accounts | ||
|
|
||
| Account layout depends on the `mode` field: | ||
|
|
||
| ### Anchor Mode (mode = 0) | ||
|
|
||
| Same 11 accounts as [INVOKE_CPI.md](INVOKE_CPI.md): | ||
|
|
||
| | Index | Name | Signer | Writable | Description | | ||
| |-------|------|--------|----------|-------------| | ||
| | 0-10 | (same as InvokeCpi) | - | - | See InvokeCpi documentation | | ||
| | 11+ | `remaining_accounts` | - | - | Merkle trees, queues | | ||
|
|
||
| ### V2 Mode (mode = 1) | ||
|
|
||
| Dynamic account list based on `account_option_config`: | ||
|
|
||
| | Index | Name | Signer | Writable | Description | | ||
| |-------|------|--------|----------|-------------| | ||
| | 0 | `fee_payer` | Yes | Yes | Pays transaction fees | | ||
| | 1 | `authority` | Yes | No | Authority (signer in V2 mode) | | ||
| | 2+ | Dynamic accounts | - | - | Determined by account_option_config | | ||
|
|
||
| --- | ||
|
|
||
| ## Instruction Logic | ||
|
|
||
| ### Processing Flow | ||
| 1. **CPI signer checks:** Verify invoking program authority | ||
| 2. **CPI context processing:** Handle first_set_context, set_context, or execute modes | ||
| 3. **Main processing pipeline:** Execute 19-step pipeline | ||
| - Step 8: **Read-only account verification** | ||
| - By index: Direct leaf lookup (if prove_by_index = true) | ||
| - By ZKP: Inclusion proof verification (if prove_by_index = false) | ||
| - Duplicate check: Ensure read-only accounts don't overlap with writable inputs | ||
| - Step 9: **ZK proof verification** (includes read-only proofs) | ||
| 4. **Read-only address verification** (execute mode only): | ||
| - Non-inclusion check: Verify address not in bloom filter queue | ||
| - Inclusion proof: Verify address exists in address Merkle tree | ||
|
|
||
| --- | ||
|
|
||
| ## Read-Only Account Processing | ||
|
|
||
| ### Read-Only Compressed Accounts | ||
|
|
||
| ```rust | ||
| pub struct ZPackedReadOnlyCompressedAccount { | ||
| pub account_hash: [u8; 32], | ||
| pub merkle_context: ZPackedMerkleContext, | ||
| pub root_index: U16, | ||
| } | ||
| ``` | ||
|
|
||
| - `account_hash`: Pre-computed hash of the compressed account | ||
| - `merkle_context`: Merkle tree location (tree index, queue index, leaf index) | ||
| - `root_index`: Index of the Merkle root to verify against | ||
|
|
||
| **Verification:** Account hash inclusion in Merkle tree (by index or ZKP). Account is NOT nullified. | ||
|
|
||
| ### Read-Only Addresses | ||
|
|
||
| ```rust | ||
| pub struct ZPackedReadOnlyAddress { | ||
| pub address: [u8; 32], | ||
| pub address_merkle_tree_account_index: u8, | ||
| pub address_merkle_tree_root_index: U16, | ||
| } | ||
| ``` | ||
|
|
||
| **Verification:** | ||
| 1. Bloom filter check: Verify address is NOT in queue (not pending insertion) | ||
| 2. Inclusion proof: Verify address exists in address Merkle tree | ||
|
|
||
| --- | ||
|
|
||
| ## Limitations | ||
|
|
||
| ### Read-Only Addresses with CPI Context | ||
|
|
||
| Read-only addresses cannot be used when writing to CPI context account (first_set_context or set_context modes). | ||
|
|
||
| ```rust | ||
| if let Some(readonly_addresses) = instruction_data.read_only_addresses() { | ||
| if !readonly_addresses.is_empty() { | ||
| return Err(SystemProgramError::Unimplemented)?; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **Workaround:** Perform read-only address verification in the final executing program. | ||
|
|
||
| --- | ||
|
|
||
| ## Errors | ||
|
|
||
| | Code | Error | Cause | | ||
| |------|-------|-------| | ||
| | 6034 | `ReadOnlyAddressAlreadyExists` | Read-only address found in bloom filter queue | | ||
| | 6035 | `ReadOnlyAccountDoesNotExist` | Read-only account hash not found in Merkle tree | | ||
| | 6053 | `DuplicateAccountInInputsAndReadOnly` | Same account in both writable inputs and read-only | | ||
| | 6063 | `Unimplemented` | Read-only addresses with CPI context write mode | | ||
|
|
||
| See [INSTRUCTIONS.md](../INSTRUCTIONS.md) for complete error list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify instruction data structs and read-only types
echo "=== Searching for struct definitions ==="
rg -n --type=rs 'ZInstructionDataInvokeCpiWithReadOnly|ZPackedReadOnlyCompressedAccount|ZPackedReadOnlyAddress' --max-count=20
echo ""
echo "=== Searching for error codes ==="
rg -n --type=rs 'ReadOnlyAddressAlreadyExists|ReadOnlyAccountDoesNotExist|DuplicateAccountInInputsAndReadOnly' --max-count=20Repository: Lightprotocol/light-protocol
Length of output: 200
🏁 Script executed:
#!/bin/bash
# Find the instruction processor and processing logic
echo "=== Searching for InvokeCpiWithReadOnly instruction handling ==="
fd -e rs . --search-path=programs/system/src | head -20
echo ""
echo "=== Search for invoke_cpi processing ==="
rg -n 'invoke_cpi_with_read_only|InvokeCpiWithReadOnly' --type=rs -C 2 | head -50Repository: Lightprotocol/light-protocol
Length of output: 200
🏁 Script executed:
#!/bin/bash
# Verify struct field definitions match documentation
echo "=== Finding compressed-account instruction data definitions ==="
fd -e rs . --search-path=program-libs/compressed-account/src/instruction_data
echo ""
echo "=== Search for the with_readonly.rs file ==="
find . -name 'with_readonly.rs' -o -name '*readonly*' | grep -E '\.(rs|md)$'Repository: Lightprotocol/light-protocol
Length of output: 1000
🏁 Script executed:
#!/bin/bash
# Read the with_readonly.rs file to verify struct definitions
wc -l program-libs/compressed-account/src/instruction_data/with_readonly.rsRepository: Lightprotocol/light-protocol
Length of output: 145
🏁 Script executed:
#!/bin/bash
# Read the with_readonly.rs content
head -200 program-libs/compressed-account/src/instruction_data/with_readonly.rsRepository: Lightprotocol/light-protocol
Length of output: 6048
🏁 Script executed:
#!/bin/bash
# Search for error codes in the system program
echo "=== Searching for error code definitions ==="
rg -n 'ReadOnlyAddressAlreadyExists|ReadOnlyAccountDoesNotExist|DuplicateAccountInInputsAndReadOnly|Unimplemented' --type=rs -B 2 -A 2Repository: Lightprotocol/light-protocol
Length of output: 160
🏁 Script executed:
#!/bin/bash
# Search for processing pipeline and read-only verification logic
echo "=== Searching for read_only processing and verification ==="
rg -n 'read_only|ReadOnly' program-libs/compressed-account/src/instruction_data/with_readonly.rs -A 3 | head -100Repository: Lightprotocol/light-protocol
Length of output: 3862
🏁 Script executed:
#!/bin/bash
# Read more of the with_readonly.rs to see struct definition details
sed -n '385,500p' program-libs/compressed-account/src/instruction_data/with_readonly.rsRepository: Lightprotocol/light-protocol
Length of output: 4141
🏁 Script executed:
#!/bin/bash
# Search for error code definitions in system program
echo "=== Searching for error definitions ==="
find programs/system/src -name '*.rs' -exec grep -l 'ReadOnlyAddressAlreadyExists\|ReadOnlyAccountDoesNotExist\|DuplicateAccountInInputsAndReadOnly' {} \;Repository: Lightprotocol/light-protocol
Length of output: 284
🏁 Script executed:
#!/bin/bash
# Search for error enum definitions more broadly
echo "=== Searching for SystemProgramError or similar ==="
rg -n 'enum.*Error|ReadOnlyAddress|ReadOnlyAccount' programs/system/src --type=rs -C 2 | head -80Repository: Lightprotocol/light-protocol
Length of output: 167
🏁 Script executed:
#!/bin/bash
# Look for the instruction processor to verify processing steps
find programs/system/src -name '*.rs' | xargs grep -l 'invoke_cpi.*read_only\|process.*invoke.*cpi' | head -5Repository: Lightprotocol/light-protocol
Length of output: 142
🏁 Script executed:
#!/bin/bash
# Read the errors file to verify error codes
echo "=== Reading errors.rs ==="
cat programs/system/src/errors.rsRepository: Lightprotocol/light-protocol
Length of output: 11488
🏁 Script executed:
#!/bin/bash
# Read the read_only_account processor
echo "=== Reading read_only_account.rs ==="
head -200 programs/system/src/processor/read_only_account.rsRepository: Lightprotocol/light-protocol
Length of output: 3863
🏁 Script executed:
#!/bin/bash
# Read the read_only_address processor
echo "=== Reading read_only_address.rs ==="
head -200 programs/system/src/processor/read_only_address.rsRepository: Lightprotocol/light-protocol
Length of output: 1381
🏁 Script executed:
#!/bin/bash
# Search for the instruction processor to check processing steps
echo "=== Searching in invoke_cpi processor ==="
head -300 programs/system/src/invoke_cpi/processor.rs | tail -200Repository: Lightprotocol/light-protocol
Length of output: 2337
🏁 Script executed:
#!/bin/bash
# Check for the 19-step processing pipeline and steps 8 and 9
echo "=== Searching for processing pipeline steps ==="
rg -n 'step.*8|step.*9|read_only.*verification|process.*pipeline' programs/system/src --type=rs -C 2 | head -100Repository: Lightprotocol/light-protocol
Length of output: 163
🏁 Script executed:
#!/bin/bash
# Search for the limitation about read-only addresses with CPI context
echo "=== Searching for read-only addresses limitation ==="
rg -n 'readonly_addresses.*first_set_context|readonly_addresses.*set_context|Unimplemented.*readonly' programs/system/src --type=rs -C 3Repository: Lightprotocol/light-protocol
Length of output: 168
🏁 Script executed:
#!/bin/bash
# Check the process.rs file to understand the processing steps
echo "=== Reading process.rs to find processing steps ==="
head -150 programs/system/src/processor/process.rsRepository: Lightprotocol/light-protocol
Length of output: 6285
🏁 Script executed:
#!/bin/bash
# Continue reading process.rs to find all processing steps
sed -n '80,250p' programs/system/src/processor/process.rsRepository: Lightprotocol/light-protocol
Length of output: 7189
🏁 Script executed:
#!/bin/bash
# Search more comprehensively for read_only and CPI context interaction
find programs/system/src -type f -name '*.rs' -exec grep -l 'read_only.*cpi_context\|cpi_context.*read_only' {} \;Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
#!/bin/bash
# Find where the limitation check is (Unimplemented error for read-only addresses with CPI context)
grep -n 'read_only_addresses\|readonly_addresses' programs/system/src/processor/process.rs -C 3 | head -80Repository: Lightprotocol/light-protocol
Length of output: 2525
🏁 Script executed:
#!/bin/bash
# Look for the limitation check about read-only addresses with CPI context
grep -rn 'readonly_addresses.*Unimplemented\|read_only_addresses.*first_set_context\|read_only_addresses.*set_context' programs/system/src/ 2>/dev/null || echo "Not found in programs/system"
# Also search in compressed-account lib
grep -rn 'readonly_addresses.*Unimplemented\|read_only_addresses.*first_set_context\|read_only_addresses.*set_context' program-libs/compressed-account/ 2>/dev/null || echo "Not found in program-libs/compressed-account"Repository: Lightprotocol/light-protocol
Length of output: 145
🏁 Script executed:
#!/bin/bash
# Look at the full process.rs to see steps 8 and 13
wc -l programs/system/src/processor/process.rs
sed -n '200,320p' programs/system/src/processor/process.rsRepository: Lightprotocol/light-protocol
Length of output: 5712
🏁 Script executed:
#!/bin/bash
# Search specifically for the Unimplemented error being returned for read-only addresses
grep -n 'Unimplemented' programs/system/src/processor/process.rs programs/system/src/invoke_cpi/processor.rs programs/system/src/processor/read_only*.rs 2>/dev/nullRepository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
#!/bin/bash
# Search more thoroughly for any validation of read-only addresses with CPI context
echo "=== Searching for CPI context validation with read-only ==="
grep -rn 'read_only\|cpi_context' program-libs/compressed-account/src/instruction_data/with_readonly.rs | head -50Repository: Lightprotocol/light-protocol
Length of output: 2931
🏁 Script executed:
#!/bin/bash
# Check if there's validation in the invoke_cpi module initialization or validation
grep -n 'first_set_context\|set_context' programs/system/src/invoke_cpi/processor.rs -C 2Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
#!/bin/bash
# Search in the entire codebase for the specific error check
grep -rn 'is_empty().*readonly\|readonly.*is_empty()' programs/system/src/processor/ | grep -i 'return.*unimplemented\|first_set_context\|set_context'Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
#!/bin/bash
# Look at the full invoke_cpi processor
cat programs/system/src/invoke_cpi/processor.rsRepository: Lightprotocol/light-protocol
Length of output: 2295
🏁 Script executed:
#!/bin/bash
# Look for where the read-only addresses with CPI context limitation might be checked
# Search in the entire system program for the pattern
grep -rn 'read_only_addresses\|readonly_addresses' programs/system/src --include='*.rs' | grep -i 'context\|unimplemented' | head -20Repository: Lightprotocol/light-protocol
Length of output: 2129
🏁 Script executed:
#!/bin/bash
# Check if there's any validation in WrappedInstructionData or account_option_config
grep -rn 'account_option_config\|WrappedInstructionData' programs/system/src --include='*.rs' -A 5 | grep -B 5 'read_only' | head -50Repository: Lightprotocol/light-protocol
Length of output: 54
🏁 Script executed:
#!/bin/bash
# Read the full process.rs file to get a complete picture of all steps
wc -l programs/system/src/processor/process.rs && echo "---" && tail -100 programs/system/src/processor/process.rsRepository: Lightprotocol/light-protocol
Length of output: 4486
🏁 Script executed:
#!/bin/bash
# Check the exact error return in state.rs
sed -n '187,195p' programs/system/src/cpi_context/state.rsRepository: Lightprotocol/light-protocol
Length of output: 534
🏁 Script executed:
#!/bin/bash
# Verify the exact struct field naming (is_compress vs is_compress: u8)
grep -n 'is_compress\|with_cpi_context\|with_transaction_hash' program-libs/compressed-account/src/instruction_data/with_readonly.rs | head -30Repository: Lightprotocol/light-protocol
Length of output: 1636
Fix processing step references and clarify struct field representation.
The struct field documentation and processing step numbering have discrepancies with the implementation. The actual ZInstructionDataInvokeCpiWithReadOnlyMeta stores is_compress, with_cpi_context, and with_transaction_hash as u8 fields with getter methods (not public bool fields as shown). More significantly, the processing steps referenced in "Step 8: Read-only account verification" and "Step 9: ZK proof verification" don't match the actual pipeline—read-only account verification occurs at step 14 and proof verification at step 16.
Update the documentation to:
- Clarify that
is_compress,with_cpi_context, andwith_transaction_hashare accessed via getter methods in the zero-copy struct - Correct the processing step numbers or remove specific step references to avoid drift
All error codes and the read-only addresses with CPI context limitation are correctly documented and implemented.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
58-58: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
111-111: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
112-112: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
117-117: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
118-118: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
122-122: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
123-123: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
149-149: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In `@programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_READ_ONLY.md` around lines 39
- 179, The docs currently list is_compress, with_cpi_context, and
with_transaction_hash as public bool fields on
ZInstructionDataInvokeCpiWithReadOnly, and reference read-only verification as
steps 8/9; update the doc to state that the zero-copy meta struct
(ZInstructionDataInvokeCpiWithReadOnlyMeta /
ZInstructionDataInvokeCpiWithReadOnly) stores those flags as u8 and exposes them
via getter methods (e.g. is_compress(), with_cpi_context(),
with_transaction_hash()), and fix the processing step references to match the
implementation (read-only account verification at step 14 and proof verification
at step 16) or remove hard step numbers to avoid drift.
| ```rust | ||
| pub struct ZInstructionDataInvokeCpiWithReadOnly<'a> { | ||
| // Metadata (fixed layout) | ||
| pub mode: u8, // AccountMode: 0 = Anchor, 1 = V2 | ||
| pub bump: u8, | ||
| pub invoking_program_id: Pubkey, | ||
| pub compress_or_decompress_lamports: U64, | ||
| pub is_compress: bool, | ||
| pub with_cpi_context: bool, | ||
| pub with_transaction_hash: bool, | ||
| pub cpi_context: ZCompressedCpiContext, | ||
|
|
||
| // Variable-length fields (zero-copy slices) | ||
| pub proof: Option<Ref<'a, [u8], CompressedProof>>, | ||
| pub new_address_params: ZeroCopySliceBorsh<'a, ZNewAddressParamsAssignedPacked>, | ||
| pub input_compressed_accounts: Vec<ZInAccount<'a>>, | ||
| pub output_compressed_accounts: Vec<ZOutputCompressedAccountWithPackedContext<'a>>, | ||
| pub read_only_addresses: ZeroCopySliceBorsh<'a, ZPackedReadOnlyAddress>, | ||
| pub read_only_accounts: ZeroCopySliceBorsh<'a, ZPackedReadOnlyCompressedAccount>, | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolve markdownlint issues for fenced blocks.
Static analysis flags missing blank lines and missing language identifiers for some fenced code blocks. Please add blank lines around headings/tables and label non-Rust blocks (e.g., text).
Also applies to: 118-149
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
58-58: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@programs/system/docs/invoke_cpi/INVOKE_CPI_WITH_READ_ONLY.md` around lines 41
- 61, Add proper blank lines before and after fenced code blocks and label
non-Rust blocks with a language identifier to satisfy markdownlint; for the
shown block containing the ZInstructionDataInvokeCpiWithReadOnly struct ensure
there is an empty line above the opening ```rust and below the closing ``` and
similarly add language identifiers (e.g., text) for any non-Rust fenced blocks
referenced (also fix the other affected fenced blocks around lines 118-149).
Locate the code fences around ZInstructionDataInvokeCpiWithReadOnly,
ZNewAddressParamsAssignedPacked, ZInAccount,
ZOutputCompressedAccountWithPackedContext, ZPackedReadOnlyAddress, and
ZPackedReadOnlyCompressedAccount and apply the blank-line and language-label
fixes consistently.
| ``` | ||
| [0..8]: Discriminator | ||
| [8..12]: Vec length prefix (4 bytes, always skip) | ||
| [12..]: Serialized instruction data | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Add language specifier to code block.
The data layout code block should specify a language for proper syntax highlighting.
📝 Proposed fix for code block
-```
+```text
[0..8]: Discriminator
[8..12]: Vec length prefix (4 bytes, always skip)
[12..]: Serialized instruction dataBased on learnings, documentation formatting should be consistent.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| [0..8]: Discriminator | |
| [8..12]: Vec length prefix (4 bytes, always skip) | |
| [12..]: Serialized instruction data | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
63-63: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@programs/system/docs/invoke/INVOKE.md` around lines 63 - 67, Update the
fenced code block that shows the data layout (the three-line block starting with
"[0..8]: Discriminator") to include a language specifier for syntax
highlighting (for example change the opening ``` to ```text) so the block is
rendered consistently; ensure only the opening fence is modified and the block
content remains unchanged.
| ### Step 1: Parse Instruction Data | ||
| ```rust | ||
| let instruction_data = &instruction_data[4..]; // Skip vec prefix | ||
| let (inputs, _) = ZInstructionDataInvoke::zero_copy_at(instruction_data)?; | ||
| ``` | ||
|
|
||
| ### Step 2: Parse and Validate Accounts | ||
| ```rust | ||
| let (ctx, remaining_accounts) = InvokeInstruction::from_account_infos(accounts)?; | ||
| ``` | ||
|
|
||
| ### Step 3: Verify Authority Signature | ||
| ```rust | ||
| input_compressed_accounts_signer_check( | ||
| &inputs.input_compressed_accounts_with_merkle_context, | ||
| ctx.authority.key(), | ||
| )?; | ||
| ``` | ||
|
|
||
| **For each input compressed account:** | ||
| - Account owner must equal authority pubkey | ||
| - Account must NOT have data (`data.is_none()`) | ||
|
|
||
| ### Step 4: Process State Transition | ||
| ```rust | ||
| process::<false, InvokeInstruction, ZInstructionDataInvoke>( | ||
| wrapped_inputs, | ||
| None, // No CPI context | ||
| &ctx, | ||
| 0, // Default relay fee | ||
| remaining_accounts, | ||
| )?; | ||
| ``` | ||
|
|
||
| Executes the full 19-step processing pipeline (see [PROCESSING_PIPELINE.md](../PROCESSING_PIPELINE.md)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Add blank lines around code blocks for consistency.
Several code blocks in the Instruction Logic section are missing blank lines before/after, which violates markdown best practices and the markdownlint rules flagged in static analysis.
📝 Proposed formatting fixes
Add blank lines:
- Before line 130 (code block)
- After line 132
- Before line 136 (code block)
- After line 138
- Before line 141 (code block)
- After line 145
- Before line 153 (code block)
- After line 160
This improves readability and consistency with markdown standards.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
128-128: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
129-129: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
134-134: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
135-135: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
139-139: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
140-140: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
151-151: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
152-152: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
In `@programs/system/docs/invoke/INVOKE.md` around lines 128 - 162, The markdown
sections around the example code blocks lack blank lines; update INVOKE.md so
each fenced code block is preceded and followed by an empty line for consistency
(add blank lines around the blocks that contain the instruction_data slice and
zero_copy_at call, the InvokeInstruction::from_account_infos call, the
input_compressed_accounts_signer_check call, and the process::<false,
InvokeInstruction, ZInstructionDataInvoke> call). Ensure the empty line is
inserted immediately before the opening ``` and immediately after the closing
``` for those code blocks so markdownlint rules are satisfied and readability is
consistent.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.