Skip to content

feat: pinocchio ephemeral permission#81

Open
Dodecahedr0x wants to merge 44 commits into
mainfrom
dode/ephemeral-permission
Open

feat: pinocchio ephemeral permission#81
Dodecahedr0x wants to merge 44 commits into
mainfrom
dode/ephemeral-permission

Conversation

@Dodecahedr0x
Copy link
Copy Markdown
Contributor

@Dodecahedr0x Dodecahedr0x commented May 6, 2026

Closes #80

Summary by CodeRabbit

  • New Features

    • Added Pinocchio ephemeral permission counter program with delegation, permission account management, and commitment workflows for ephemeral rollup interactions
  • Tests

    • Added end-to-end integration tests demonstrating counter initialization, delegation, and execution across base and ephemeral layers
  • Documentation

    • Added comprehensive README with setup instructions, build/test commands, and instruction specifications
  • Chores

    • Refactored legacy counter example
    • Added project configuration and MIT license

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Warning

Review limit reached

@Dodecahedr0x, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 4 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 423f52af-2d51-452f-a6ff-771428d2de9d

📥 Commits

Reviewing files that changed from the base of the PR and between 5d06557 and 3f20e16.

📒 Files selected for processing (1)
  • 00-LEGACY_EXAMPLES/anchor-counter/app/src/App.tsx

Walkthrough

This PR introduces a complete Pinocchio-based Solana counter program with ephemeral-permission integration, including no_std Rust implementation (entrypoint, processor, state), comprehensive TypeScript tests, and project configuration. It also updates the legacy Anchor counter example with a new IDL and UI refactoring.

Changes

Pinocchio Ephemeral Permission Counter

Layer / File(s) Summary
Project configuration & metadata
pinocchio-ephemeral-permission-counter/.env.example, .gitignore, .yarnrc.yml, Cargo.toml, package.json, tsconfig.json, LICENSE
Environment variables, build tooling (Yarn/Cargo), TypeScript config, and package metadata establish the project foundation.
Project documentation
pinocchio-ephemeral-permission-counter/README.md
Full documentation covering software requirements, setup/build/test flow, program model (PDA derivation, account layout), and instruction format with discriminators and examples.
Crate root & module wiring
pinocchio-ephemeral-permission-counter/src/lib.rs
No_std crate attributes, module declarations for entrypoint/processor/state, program ID declaration, and public re-export of process_instruction.
Instruction entrypoint & dispatch
pinocchio-ephemeral-permission-counter/src/entrypoint.rs
Unsafe C ABI entrypoint, 8-byte InstructionDiscriminator enum with conversion, inner_process_instruction routing logic, payload helpers (read_u64), and conditional logging when the logging feature is enabled.
Processor logic & CPI handlers
pinocchio-ephemeral-permission-counter/src/processor.rs
Eight processor functions orchestrating counter lifecycle (initialize/increase/delegate/commit/undelegate), ephemeral-permission management (create/update/close), delegation callback, and counter state verification via load_and_verify_counter.
On-chain state & PDA utilities
pinocchio-ephemeral-permission-counter/src/state.rs
Counter struct with C-compatible layout and padding, SIZE constant, unsafe load/load_mut with alignment checks, and PDA derivation helpers (find_pda, derive_pda).
Integration tests (Vitest)
pinocchio-ephemeral-permission-counter/tests/pinocchio-ephemeral-permission-counter.test.ts
End-to-end test suite covering base-layer initialization/increment, delegation to ephemeral rollup, on-rollup operations (increment, permission lifecycle), permission close, and commit/undelegate with signature validation.

Legacy Anchor Counter Example Updates

Layer / File(s) Summary
Private counter IDL definition
00-LEGACY_EXAMPLES/anchor-counter/app/src/idl/private_counter.json
New Anchor IDL defining eight instructions (commit, delegate, increment, etc.), Counter/Member types, and discriminators for private counter integration.
React UI component updates
00-LEGACY_EXAMPLES/anchor-counter/app/src/App.tsx
Refactored imports and formatting (multi-line, double-quoted strings); adds privateCounterIdl import (unused) and duplicate publicCounterIdl; preserves base/ephemeral transaction and UI rendering logic.
Legacy example package.json
00-LEGACY_EXAMPLES/anchor-counter/app/package.json
Minor end-of-file whitespace adjustment; no functional changes to dependencies or scripts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • Issue #80 (feat: example of pinocchio ephemeral permissions): This PR directly implements the feature, introducing a complete Pinocchio-based ephemeral-permission counter example with end-to-end tests and documentation.

Suggested reviewers

  • GabrielePicco
  • jonasXchen
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (1 warning, 2 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The linked issue #80 description is empty, providing no explicit coding requirements to validate against the implemented changes. Review the linked issue #80 directly or request additional context on the requirements for the ephemeral permission feature implementation.
Out of Scope Changes check ❓ Inconclusive Without explicit scope definition from linked issue #80, it is unclear whether changes to legacy examples (anchor-counter) are in-scope or represent feature creep. Clarify whether the anchor-counter updates are intentional as part of the ephemeral permission feature or if they should be excluded from this PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: pinocchio ephemeral permission' clearly and specifically describes the main addition—a new Pinocchio example demonstrating ephemeral permission functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dode/ephemeral-permission

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pinocchio-ephemeral-permission-counter/Cargo.toml`:
- Around line 17-18: Replace the local path dependency for the crate named
ephemeral-rollups-pinocchio in Cargo.toml with the commented-out git dependency
that pins the specific rev; update the existing line
`ephemeral-rollups-pinocchio = { path =
"../../ephemeral-rollups-sdk/rust/pinocchio" }` to use the git source and rev
from the commented line (the previously commented `ephemeral-rollups-pinocchio =
{ git = "https://github.com/magicblock-labs/ephemeral-rollups-sdk.git", rev =
"a8ecea450d3bbe5bac119a3f4c55cd9f96b4837b" }`) so the example builds for anyone
cloning this repo standalone.

In `@pinocchio-ephemeral-permission-counter/package.json`:
- Line 21: The package's license metadata is inconsistent: the package.json
"license" field currently reads "Apache-2.0" while the repository LICENSE file
is MIT; update one to match the other—either change the package.json "license"
value to "MIT" to match the LICENSE file or replace/update the LICENSE file to
Apache-2.0—ensure the "license" field in package.json and the repository LICENSE
file are identical so tooling and consumers see the same license.
- Line 33: Update the Vitest dependency in package.json from "vitest": "^0.34.0"
to a secure compatible range (recommend "vitest": "^3.0.0" for minimal breaking
changes or "vitest": "^4.0.0" if you will handle migration), then run npm/yarn
install and execute the test suite to catch any breaking changes; if
tests/config fail, follow Vitest migration guide to adjust
config/coverage/mocks/browser settings and ensure package.json scripts
referencing vitest still work.

In `@pinocchio-ephemeral-permission-counter/README.md`:
- Around line 57-59: Update the README Account Structure to reflect the real
on-chain size: change the Counter entry from "8 bytes (u64 count value)" to "48
bytes (struct size)" and mention the field breakdown to match
src/state.rs::Counter (id: Address 32 bytes, count: u64 8 bytes, bump: u8 1 byte
+ 7 bytes padding) and the SIZE = 48 constant; also note that tests read the
count at data.subarray(32, 40) to avoid confusion about layout and rent
footprint.
- Around line 36-55: The README's instruction list is incorrect and must be
updated to match the actual dispatcher in entrypoint.rs: replace entries 4–6
(Commit/IncrementAndCommit/IncrementAndUndelegate) with CreatePermission,
UpdatePermission, and ClosePermission to reflect the discriminators used in the
dispatcher; correct payload descriptions for InitializeCounter (takes a 32-byte
id / Address, not a bump), IncreaseCounter (takes only increase_by: u64), and
Delegate (no payload); remove any references to non-existent handlers and add a
note documenting the special undelegation-callback discriminator bytes
[196,28,41,206,48,37,51,167]; use the actual symbol names from the code
(InitializeCounter, IncreaseCounter, Delegate, CreatePermission,
UpdatePermission, ClosePermission) so the README mirrors entrypoint.rs exactly.

In `@pinocchio-ephemeral-permission-counter/src/entrypoint.rs`:
- Around line 113-120: The InitializeCounter branch currently slices
payload[..32] which panics on short input; update the
InstructionDiscriminator::InitializeCounter handling to use
payload.get(..32).ok_or(ProgramError::InvalidInstructionData) (or equivalent)
and then convert that 32-byte slice into an array before calling
Address::new_from_array, finally passing the resulting id into
process_initialize_counter so a short payload returns InvalidInstructionData
instead of panicking.

In `@pinocchio-ephemeral-permission-counter/src/processor.rs`:
- Around line 211-212: Replace redundant explicit field initializers using
field-init shorthand: in the struct initializers referencing permission_program,
magic_program, and permission (and the other occurrences noted around the blocks
initializing the same fields at the other locations), remove the repeated
`field: field` and use the shorthand `permission_program`, `magic_program`, and
`permission` instead; update the initializers inside the constructor/struct
literal where these identifiers appear (including the other occurrences at the
two additional locations mentioned) so they use the concise field-init form to
satisfy clippy::redundant_field_names.
- Around line 110-115: Extract the repeated "load + derive_pda + address-match"
logic into a helper fn named load_and_verify_counter that takes &AccountView,
checks owned_by(&crate::ID), borrows data, loads Counter::load, derives the PDA
via Counter::derive_pda(&counter.id, &[counter.bump]) and verifies the derived
PDA equals counter_info.address(), then returns Ok((counter.bump, counter.id))
or appropriate ProgramError (IllegalOwner / InvalidSeeds / propagate borrow
errors); replace the five-line blocks in process_increase_counter,
process_delegate, process_create_permission, process_update_permission,
process_close_permission and the scoped use in process_commit_and_undelegate to
call load_and_verify_counter and use its returned (bump, id) instead of
duplicating the logic.
- Around line 87-96: The log eagerly computes counter_data.count + increase_by
which can overflow before the checked_add; change the sequence in the function
where counter_data.count is updated so you first compute new_count using
counter_data.count.checked_add(increase_by).ok_or(ProgramError::ArithmeticOverflow)
(or otherwise compute into an Option/Result), then call log! with the
precomputed new_count, and finally assign counter_data.count = new_count; this
ensures no pre-logging overflow and that the logged "to" value matches the value
actually written.
- Around line 181-227: process_create_permission currently trusts authority_info
from the accounts slice without verifying the signer/owner; require
authority_info.is_signer() before proceeding and, for stronger protection,
compare authority_info.address() against a stored initializer/owner field on the
Counter (load via Counter::load and add owner to Counter struct) and reject if
mismatched; apply the same signer check for payer_info (payer_info.is_signer())
and mirror these checks in process_update_permission and
process_close_permission so the CPI cannot be front-run by an unrelated signer.
- Around line 74-85: The account ownership of counter/state must be verified
before deserializing; for each affected function (process_increase_counter,
process_delegate, process_commit_and_undelegate, process_create_permission,
process_update_permission, process_close_permission) add an explicit check using
counter_info.owned_by(&crate::ID) (matching the pattern in
process_initialize_counter) and return ProgramError::IllegalOwner (or
appropriate ProgramError) if false, before calling Counter::load or
Counter::load_mut (or any deserialization) and before deriving/verifying the PDA
via Counter::derive_pda; ensure you reference the same AccountView binding
(counter_info) used in the diff and perform the ownership check immediately
after extracting the account and before any try_borrow_mut/Counter::load*_
calls.
- Around line 321-327: process_undelegation_callback currently trusts a
hardcoded discriminator and calls undelegate without verifying the call came
from the delegation program; update the handler to require and validate the
Instructions sysvar account (add it to the accounts slice) and inspect the
previous instruction to ensure its program_id equals the delegation program's
Pubkey and its data starts with the expected discriminator before calling
undelegate (alternatively require a specific signer account and verify
payer_info.is_signer). Refer to process_undelegation_callback and undelegate
when adding the check so the entrypoint explicitly enforces the CPI origin.
- Around line 50-60: The inline magic number calculation for ephemeral_rent
should be replaced by named constants to document the account layout: define
constants (e.g., PERMISSION_HEADER_BYTES = 35 and SLOTS_PER_MEMBER = 2) and
compute ephemeral_rent using those with MAX_MEMBER_SIZE (cast to u64) and the
32-byte slot size, then use the new ephemeral_rent in the CreateAccount call
that uses payer_info, counter_info, lamports, and Counter::SIZE; update or
remove the TODO to reflect the refactor so future changes to header size or
slots-per-member won’t silently under-fund rent.

In `@pinocchio-ephemeral-permission-counter/src/state.rs`:
- Around line 15-37: The current load/load_mut docs and safety are misleading
and risk silent bugs: update both methods to require data.len() == Self::SIZE
(reject > as well as <), change the safety comment to state exactly which
invariants the caller must uphold (the byte slice is the account data owned by
this program and is not currently borrowed elsewhere), and make the Counter type
zero-init friendly by deriving or implementing an appropriate
zero-initialization marker (e.g., bytemuck::Zeroable/Pod or equivalent) so
zeroed account memory is valid; reference the Counter type and the load/load_mut
functions when making these changes.
- Line 13: Replace the hard-coded byte sum for the Counter struct's SIZE
constant with a compiler-calculated value using mem::size_of so it stays correct
if fields change: update the declaration of SIZE (the const in state.rs for
Counter) to use core::mem::size_of::<Counter>() (or size_of::<Self>() if inside
an impl) and ensure mem::size_of is in scope (use core::mem or std::mem as
appropriate).
- Line 20: The code uses usize::is_multiple_of (unstable on Rust <1.87) causing
MSRV mismatch; either update the project's MSRV to 1.87+ or replace the uses in
state.rs (functions load and load_mut) with an equivalent modulo-based alignment
check: compute core::mem::align_of::<Self>() and test (ptr as usize) % align ==
0 (negate as appropriate) instead of is_multiple_of, and update the error/branch
logic accordingly so both load and load_mut perform the same portable alignment
validation.

In
`@pinocchio-ephemeral-permission-counter/tests/pinocchio-secret-counter.test.ts`:
- Line 339: Reads using connectionEphemeralRollup.getAccountInfo(counterPda) are
missing an explicit commitment and can return stale data versus the writes made
with sendAndConfirmTransaction(..., { commitment: "confirmed" }); update each
read of getAccountInfo (e.g., the calls in the test that reference counterPda)
to pass { commitment: "confirmed" } so the read commitment matches the write
commitment and prevents flaky CI assertions on subarray(32, 40).
- Line 36: The test's suite name and filename drift from the crate name; update
either the test filename `pinocchio-secret-counter.test.ts` or the `describe`
label (currently "pinocchio-ephemeral-secret-counter") so they match the crate
`pinocchio-ephemeral-permission-counter`; specifically change the
`describe("pinocchio-ephemeral-secret-counter", ...)` label to
`describe("pinocchio-ephemeral-permission-counter", ...)` (or rename the file to
`pinocchio-ephemeral-permission-counter.test.ts`) so test output and searches
consistently reference the crate name.
- Around line 31-34: The test currently hardcodes PROGRAM_ID (and VAULT) which
can drift from the on-chain crate; update the test to load the program id from a
single source of truth instead of the literal
"AAWCg4eJHpdmUtM8Wz6Thm8FDi6C3vnMksf1pt2vfxhf": modify the code that defines
PROGRAM_ID (and optionally VAULT) to first check an environment variable (e.g.
process.env.PROGRAM_ID) and fall back to reading the generated artifact (e.g.
parse idl.json or the program keypair output from cargo build-sbf) to obtain the
program public key, ensuring tests always use the same ID declared by the
on-chain crate.
- Line 480: The test uses optional chaining on result.value
(expect(result.value?.err).toBeNull()), which hides potential bugs because
Connection.confirmTransaction always returns
RpcResponseAndContext<SignatureResult> so value is never undefined; update the
assertion to directly check result.value.err
(expect(result.value.err).toBeNull()) to remove the unnecessary ?. and surface
failures correctly, referencing the result returned by
Connection.confirmTransaction and the RpcResponseAndContext<SignatureResult>
shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3dc09330-c2f7-4fdc-b92e-c6b99385841f

📥 Commits

Reviewing files that changed from the base of the PR and between 3d91320 and 683ee73.

⛔ Files ignored due to path filters (2)
  • pinocchio-ephemeral-permission-counter/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (14)
  • pinocchio-ephemeral-permission-counter/.env.example
  • pinocchio-ephemeral-permission-counter/.gitignore
  • pinocchio-ephemeral-permission-counter/.yarnrc.yml
  • pinocchio-ephemeral-permission-counter/Cargo.toml
  • pinocchio-ephemeral-permission-counter/LICENSE
  • pinocchio-ephemeral-permission-counter/README.md
  • pinocchio-ephemeral-permission-counter/package.json
  • pinocchio-ephemeral-permission-counter/src/entrypoint.rs
  • pinocchio-ephemeral-permission-counter/src/lib.rs
  • pinocchio-ephemeral-permission-counter/src/processor.rs
  • pinocchio-ephemeral-permission-counter/src/state.rs
  • pinocchio-ephemeral-permission-counter/tests/pinocchio-secret-counter.test.ts
  • pinocchio-ephemeral-permission-counter/tsconfig.json
  • private-payments
💤 Files with no reviewable changes (1)
  • private-payments

Comment thread pinocchio-ephemeral-permission-counter/Cargo.toml Outdated
Comment thread pinocchio-ephemeral-permission-counter/package.json Outdated
Comment thread pinocchio-ephemeral-permission-counter/package.json Outdated
Comment thread pinocchio-ephemeral-permission-counter/README.md Outdated
Comment thread pinocchio-ephemeral-permission-counter/README.md Outdated
Comment thread pinocchio-ephemeral-permission-counter/src/state.rs
Comment thread pinocchio-ephemeral-permission-counter/tests/pinocchio-secret-counter.test.ts Outdated
Comment thread pinocchio-ephemeral-permission-counter/tests/pinocchio-secret-counter.test.ts Outdated
Comment thread pinocchio-ephemeral-permission-counter/tests/pinocchio-secret-counter.test.ts Outdated
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
counter-session-keys Error Error May 29, 2026 9:16am
er-rolldice Ready Ready Preview, Comment May 29, 2026 9:16am
magicblock-counter-example Error Error May 29, 2026 9:16am
magicblock-engine-examples Error Error May 29, 2026 9:16am
magicblock-rewards-dashboard Ready Ready Preview, Comment May 29, 2026 9:16am
spl-tokens Ready Ready Preview, Comment May 29, 2026 9:16am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: example of pinocchio ephemeral permissions

4 participants