Skip to content

Context words read their true value per entrypoint or revert (M02) - #2921

Closed
thedavidmeister wants to merge 5 commits into
mainfrom
fix-m02-context-word-entrypoints
Closed

thedavidmeister wants to merge 5 commits into
mainfrom
fix-m02-context-word-entrypoints

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Context words compiled by the sub parser are plain context<col row> reads, and every raindex post-task entrypoint reused the same calling-context column for unrelated data. So a word read in an entrypoint it was not written for silently returned another entrypoint's value instead of failing:

Property this PR enforces, for the raindex entrypoints: every context word reads its true value in its entrypoint, or reverts loudly (array OOB panic 0x32). Never a silent wrong value.

Fix

Per-entrypoint-family context columns (CALLING_CONTEXT_COLUMNS = 6). Each raindex entrypoint fills only its own columns; every other column is empty, so a wrong-entrypoint word panics out of bounds.

col contents filled by
0 base [sender, raindex] all raindex entrypoints
1 order [hash, owner, counterparty, clearer] calculate-io, handle-io (all rows); add/remove order tasks ([hash, owner] only)
2-4 calculations, vault inputs, vault outputs calculate-io, handle-io (unchanged)
5 deposit [token, vaultId, before, after, decimals, depositor] deposit task
6 withdraw [token, vaultId, before, after, target, decimals, withdrawer] withdraw task
7 signers all (when signed context present)
8+ signed context all
  • order-clearer moves to (1,3), depositor to (5,5), withdrawer to (6,6); nothing reads base (0,0) any more.
  • order-counterparty in add/remove still reverts: there is no counterparty, and a zero sentinel would be a silent wrong value.
  • Signers / signed-context columns derive from CONTEXT_COLUMNS (CONTEXT_SIGNED_CONTEXT_SIGNERS_COLUMN = CONTEXT_COLUMNS, start = signers + 1).
  • signers subparser word uses the raw operand as row without & 0xFF masking (unlike signed-context) #2653: signers stay unmasked (signer<256> fails parse with ContextGridOverflow); the redundant & 0xFF on the signed-context row is removed (operand bytes are already single bytes); column-byte extraction mask kept.
  • The sub parser is the raindex sub parser: the post-arb task context is unchanged from main apart from the shared signers/signed-context renumbering.

Why this design

Rejected alternatives:

  • Fill missing rows with zeros/sentinels: silent wrong values, the exact defect.
  • Consumer-side operand scan for disallowed words: alias-blind (raw context<a b> defeats it).
  • Constants markers + deploy-time validation: extra gas and a new mechanism.
  • Parse-time schema per entrypoint: upstream change, and the sub parser cannot see which entrypoint the bytecode targets.
  • Interpreter operand masking: upstream.
  • Same-column row ranges per family: needs silent filler rows.

Evidence

  • RaindexV6.contextWordEntrypoints.t.sol: every word x {deposit4, withdraw4, addOrder4, removeOrder3, entask2, calculate-io, handle-io}: true value in its family, stdError.indexOOBError elsewhere; order hash pinned in calculate and handle via store writes.
  • RaindexV6SubParser.signers.t.sol: signer<256> reverts ContextGridOverflow(7, 256).
  • Routing / wiring tests updated to the new layout; word-parser table asserted one slot per word, all pairwise distinct (no aliasing).

Full forge test on e464be1 (merge of main @ f971796): 736 passed / 3 failed (739). The 3 failures are env/network (testIsStartBlockFlare, testSuitesLiveOnEverySupportedNetwork, testSupportedNetworkChainIdsAreBound). Regeneration with rainix sol-shell @ 2aea60e5 (build-meta.sh, Build.sol until stable, forge build, script/build.sh, forge fmt) is committed.

QA

  • Discriminating tests: RaindexV6ContextWordEntrypointsTest (testDepositContextWords, testWithdrawContextWords, testAddOrderContextWords, testRemoveOrderContextWords, testEntaskContextWords, testCalculateOrderContextWords, testHandleIOContextWords), testSubParserContextSignerUnhappyIndexOverflow. Each pins every word x entrypoint: true value in its family, stdError.indexOOBError elsewhere. Base cannot pass them: there deposit/withdraw/order words share calling-context column 1 (wrong-entrypoint reads return another family's value instead of reverting) and the clearer/depositor/withdrawer words do not exist.
  • Mutations applied: 22/22 killed (full suite per mutant, regenerated each time, on e464be1; the 3 env tests excluded from killers). LibRaindex: calling-context column offset -> many incl. testAddOrderContextWords; CALLING_CONTEXT_COLUMNS+1 -> testRouteSignersRowFromOperand, testSubParserContextSignerUnhappyIndexOverflow; signers column-1 -> testDepositTaskSignerWordsReadRealSigners; signed-context start+1 -> testDepositTaskSignerWordsReadRealSigners. RaindexV6: deposit/withdraw column swap and depositor/withdrawer value -> testDepositContextWords/testWithdrawContextWords; add/remove post column and owner -> testAddOrderContextWords; clearer=counterparty -> testContextEmptyStack. LibRaindexSubParser: clearer row, depositor/withdrawer/deposit-token/withdraw-decimals columns -> testRouteOrderClearer, testRouteDepositor, testRouteWithdrawer, testDepositContextWords, testWithdrawContextWords; restored signed-context row & 0xFF -> testSignedContextWideRowReverts; meta at wrong column -> matrix tests. RaindexV6SubParser: parser/handler table column swaps and base/depositor parser swaps -> matrix tests, testWordParsersPairwiseDistinct. Script and per-mutant logs: /home/gildlab/artifacts/raindex-2921-arb-revert-2026-10-26-mutation/.
  • Oracle: values the test itself passes to each entrypoint (msg.sender via prank, token, vault id, amounts, order owner/counterparty), independent of the sub parser and of LibRaindex.
  • Category check: Audit (Protofire M02): order-counterparty() exposed in add/remove post-actions but its context row is never populated (reverts) #2619 (order-counterparty in add/remove) and Post-action context: subparser words silently alias other fields or revert OOB (generalizes Protofire M02 #2619) #2647 (every post-action word silently aliases or reverts OOB) ask for the property across all words; covered for every word x calculate-io, handle-io, deposit4, withdraw4, addOrder4, removeOrder3, entask2: true value in its family or OOB revert (order-counterparty in add/remove reverts loudly by design, no zero sentinel). signers subparser word uses the raw operand as row without & 0xFF masking (unlike signed-context) #2653: signers stay unmasked (signer<256> reverts ContextGridOverflow); the redundant & 0xFF on the signed-context row is removed so both reject wide rows the same way.

Migration

  • Re-parse expressions with the new sub parser.
  • Raw signer reads context<5 i> / context<6+i j> -> context<7 i> / context<8+i j>.
  • Raw raindex post-task reads of deposit/withdraw data context<1 n> -> context<5 n> / context<6 n>.
  • Old bytecode on new RaindexV6 mostly fails loudly, EXCEPT old signer/signed-context reads compiled to col 5/6+ read the deposit/withdraw columns silently inside deposit/withdraw tasks: old and new bytecode must not be mixed.
  • Order-eval columns 0-4 keep their indices; the ContextV2 event order column gains a 4th row (clearer).

Supersedes #2779 and #2778.

Closes #2619
Closes #2647
Closes #2653

🤖 Generated with Claude Code

thedavidmeister and others added 2 commits October 25, 2026 20:32
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Oct 25, 2026
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Walkthrough

The calling-context layout now separates order, deposit, and withdrawal data into dedicated columns. Raindex entrypoints populate those columns, and sub-parser routing and tests use the updated context positions.

Changes: Calling-context layout and parser routing

Layer / File(s) Summary
Calling-context schema
src/lib/LibRaindex.sol, test/util/fixture/LibRaindexSubParserContextFixture.sol
The calling-context width and row definitions change. The fixture places the clearer hash in its calling-context row.
Entrypoint context population
src/concrete/raindex/RaindexV6.sol, test/concrete/raindex/RaindexV6.contextWordEntrypoints.t.sol, test/util/lib/LibTestContextWords.sol
Deposit and withdrawal entrypoints pass dedicated context columns. Order-related entrypoints pass their calling-context values through the new column helper. Tests check context-word availability and evaluated order hashes.
Sub-parser routing and metadata
src/concrete/parser/RaindexV6SubParser.sol, src/lib/LibRaindexSubParser.sol, crates/test_fixtures/abis/*
Base, order-clearer, deposit, and withdrawal words use updated context mappings. Signed-context row selection uses the operand shifted right by eight bits. Compiled bytecode fixtures are replaced.
Parser routing and bounds tests
test/concrete/parser/RaindexV6SubParser.*.t.sol
Tests cover updated routing coordinates, operand-handler slots, parser-table sizing, and overflow behavior for wide signer and signed-context rows.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant RaindexV6
  participant LibRaindex
  participant LibRaindexSubParser
  RaindexV6->>LibRaindex: callingContextColumn(column, values)
  LibRaindex-->>RaindexV6: Context columns with unfilled columns empty
  RaindexV6->>LibRaindexSubParser: Context values for word routing
Loading

Possibly related issues

  • rainlanguage/raindex#2908: The PR updates signer and signed-context column positions and tests routing and out-of-bounds behavior.

Possibly related PRs

Merge Risk: Minimal · up to e464b

No demonstrated regression needs to be fixed before merge. The inaccurate parser comments can be corrected separately.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: context words now return the true value for each entrypoint or revert when the value is unavailable. The changeset matches this. It moves context columns and rows per …
Linked Issues check Passed The changes meet the coding requirements for #2619, #2647, and #2653. For #2619 and #2647, RaindexV6 uses separate context columns, and the added entrypoint matrix tests verify true values in the va…
Out of Scope Changes check Passed The changed source files implement the linked issue objectives. The parser mapping changes, context-layout changes, fixtures, ABI regeneration, and routing, wiring, and matrix tests support the new co…
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Full details: Title check

Explanation

The title names the main change: context words now return the true value for each entrypoint or revert when the value is unavailable. The changeset matches this. It moves context columns and rows per entrypoint family, adds column-level revert tests, and removes the redundant signed-context row mask. The title also cites the M02 finding label, which adds no meaning for a reader scanning history but does not make the title misleading.

Full details: Linked Issues check

Explanation

The changes meet the coding requirements for #2619, #2647, and #2653. For #2619 and #2647, RaindexV6 uses separate context columns, and the added entrypoint matrix tests verify true values in the valid family and stdError.indexOOBError for unavailable words. Add/remove order contexts do not populate a counterparty row, so order-counterparty() reverts instead of returning a sentinel. For #2653, signer indices remain unmasked, and signed-context row selection no longer applies &amp; 0xFF; the parser test expects a wide row to revert with ContextGridOverflow. The subparser routing and wiring tests cover the updated column and row mappings.

Full details: Out of Scope Changes check

Explanation

The changed source files implement the linked issue objectives. The parser mapping changes, context-layout changes, fixtures, ABI regeneration, and routing, wiring, and matrix tests support the new context behavior. The post-arb layout revision also supports the stated no-aliasing property. No unrelated functional change is established by the reviewed summary.

Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (14 skipped: 14 unsupported.)

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@thedavidmeister
thedavidmeister changed the base branch from fix-2908-signer-context-columns to main October 25, 2026 21:45
thedavidmeister and others added 3 commits October 25, 2026 21:49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dex sub parser is for the dex. The post-arb task keeps main's context:
balances in column 1, full base column. CALLING_CONTEXT_COLUMNS drops to 6;
signers and signed context derive from CONTEXT_COLUMNS (7, 8+).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔇 Additional comments: 15
src/lib/LibRaindex.sol: 1

22-31: LGTM!

Also applies to: 33-61, 99-107, 110-116

test/util/fixture/LibRaindexSubParserContextFixture.sol: 1

15-15: LGTM!

Also applies to: 44-48, 52-52, 61-61

src/concrete/raindex/RaindexV6.sol: 1

55-56: LGTM!

Also applies to: 179-187, 218-233, 316-319, 725-728

test/concrete/raindex/RaindexV6.contextWordEntrypoints.t.sol: 1

1-307: LGTM!

test/util/lib/LibTestContextWords.sol: 1

1-41: LGTM!

crates/test_fixtures/abis/RaindexV6.json: 1

2365-2365: LGTM!

crates/test_fixtures/abis/RaindexV6SubParser.json: 1

245-245: LGTM!

src/concrete/parser/RaindexV6SubParser.sol: 1

17-18: LGTM!

Also applies to: 35-35, 51-53, 112-113, 121-122, 171-171, 183-183, 205-206, 217-218, 280-280, 287-287, 297-297, 305-305

src/lib/LibRaindexSubParser.sol: 2

13-17: LGTM!

Also applies to: 91-92, 117-145, 326-326, 336-336, 346-346, 356-356, 366-366, 376-376, 386-386, 396-396, 406-406, 416-416, 426-427, 438-438, 451-456, 476-482, 628-628, 671-671


319-319: Trivial · Maintainability & Code Quality · Low value

⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.

Update the stale NatSpec on the deposit and withdraw sub-parser functions.

The @dev comments on subParserDepositToken through subParserWithdrawTokenDecimals (Lines 319 to 427) still say each word maps to the "calling context column". This change moved those words to CONTEXT_DEPOSIT_COLUMN and CONTEXT_WITHDRAW_COLUMN. The new subParserDepositor and subParserWithdrawer comments are correct. Change the old comments to "deposit column" and "withdraw column".

test/concrete/parser/RaindexV6SubParser.operandHandlersWiring.t.sol: 1

14-18: LGTM!

Also applies to: 28-28, 37-48, 70-71, 75-75, 81-82, 95-96, 99-99

test/concrete/parser/RaindexV6SubParser.routing.t.sol: 1

63-63: LGTM!

Also applies to: 77-80, 136-140, 143-143, 147-147, 151-151, 155-155, 158-162, 165-165, 169-169, 173-173, 177-177, 181-181, 186-186, 189-191, 195-195, 197-205

test/concrete/parser/RaindexV6SubParser.routingTokenDecimalsAndMasks.t.sol: 1

12-14: LGTM!

Also applies to: 26-26, 35-35, 39-39, 47-47, 51-55, 59-60

test/concrete/parser/RaindexV6SubParser.signers.t.sol: 1

7-7: LGTM!

Also applies to: 76-86

test/concrete/parser/RaindexV6SubParser.wordParsersWiring.t.sol: 1

7-14: LGTM!

Also applies to: 17-19, 21-24, 27-28, 31-32, 35-41


Review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between f971796 and e464be1.

Files ignored due to path filters: 6
  • src/generated/RaindexV6SubParserPointers.sol is excluded by !**/generated/**
  • src/generated/candidate/GenericPoolRaindexV6ArbOrderTaker.sol is excluded by !**/generated/**
  • src/generated/candidate/GenericPoolRaindexV6FlashBorrower.sol is excluded by !**/generated/**
  • src/generated/candidate/RaindexV6.sol is excluded by !**/generated/**
  • src/generated/candidate/RaindexV6SubParser.sol is excluded by !**/generated/**
  • src/generated/candidate/RouteProcessorRaindexV6ArbOrderTaker.sol is excluded by !**/generated/**
Files selected for processing: 16
  • crates/test_fixtures/abis/RaindexV6.json
  • crates/test_fixtures/abis/RaindexV6SubParser.json
  • meta/RaindexV6SubParser.rain.meta
  • meta/RaindexV6SubParserAuthoringMeta.rain.meta
  • src/concrete/parser/RaindexV6SubParser.sol
  • src/concrete/raindex/RaindexV6.sol
  • src/lib/LibRaindex.sol
  • src/lib/LibRaindexSubParser.sol
  • test/concrete/parser/RaindexV6SubParser.operandHandlersWiring.t.sol
  • test/concrete/parser/RaindexV6SubParser.routing.t.sol
  • test/concrete/parser/RaindexV6SubParser.routingTokenDecimalsAndMasks.t.sol
  • test/concrete/parser/RaindexV6SubParser.signers.t.sol
  • test/concrete/parser/RaindexV6SubParser.wordParsersWiring.t.sol
  • test/concrete/raindex/RaindexV6.contextWordEntrypoints.t.sol
  • test/util/fixture/LibRaindexSubParserContextFixture.sol
  • test/util/lib/LibTestContextWords.sol

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Superseded by the per-column stack, all merged: #2926 (deposit), #2927 (withdraw), #2928 (order column, clearer only in clear, sender kept), #2930 (signed-context row reverts past 255).

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