Skip to content

feat: harden LLM resolver settlement, recovery, and key management - #31

Open
iamnycx wants to merge 4 commits into
jewl-labs:mainfrom
iamnycx:feat/resolver-safety
Open

feat: harden LLM resolver settlement, recovery, and key management#31
iamnycx wants to merge 4 commits into
jewl-labs:mainfrom
iamnycx:feat/resolver-safety

Conversation

@iamnycx

@iamnycx iamnycx commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR completes the on-chain safety and operational lifecycle required by the trusted Opal LLM resolver. It builds on #29, which introduced the dedicated resolver signer and submit_llm_resolution, and adds the settlement, recovery, rotation, specification, and fund-safety guarantees needed to operate that resolver beyond a one-off demo.

The resolver remains deliberately separate from governance: it may post a challengeable verdict, while only the protocol authority may rotate the resolver key or change governance-controlled state.

Protocol behavior

No-fault Unresolvable settlement

Unresolvable now has explicit economics across the LLM and vote paths:

  • every assertion and dispute principal is returned
  • no protocol fee is charged
  • neither side is treated as correct or slashed
  • the assertion is voided as Unresolvable

TooEarly is rejected by the shared outcome validation path. Statements whose truth does not yet exist resolve as Unresolvable, following ADR-0005.

Verifiable Resolution Specs

Every new assertion must provide a canonical Resolution Spec reference in this bounded form:

ar://<43-character-transaction-id>#sha256=<64-character-lowercase-hex-digest>

create_assertion validates the reference before accepting the assertion. This gives the off-chain resolver both a retrievable immutable document and the digest required to verify its exact bytes before resolution.

Pending-round recovery

ProtocolConfig now includes llm_resolution_timeout_seconds.

After requested_at + llm_resolution_timeout_seconds, anyone may call recover_pending_llm to move a still-pending round to a challengeable Unresolvable verdict. Recovery opens the normal LLM challenge window instead of immediately settling funds.

Resolver submission and timeout recovery share the same PendingLLM and unset-outcome guards, so only one path can win.

Resolver rotation

The new update_resolver instruction allows the governance authority to rotate a compromised, depleted, or retired hot resolver key without redeploying the program.

Rotation rejects:

  • unauthorized signers
  • the default public key
  • the governance authority itself
  • the currently configured resolver

After rotation, the old resolver is rejected immediately by submit_llm_resolution.

Initialization and fund safety

Protocol initialization is authenticated against the programs upgrade authority and matching ProgramData account. Configuration invariants reject unsafe economics, an empty resolver, and reuse of the governance authority as the hot resolver.

Settlement instructions validate the expected treasury and participant token accounts before transfers. Substituted payout accounts are rejected without moving funds, and the tests assert token conservation across decisive and no-fault outcomes.

Account-layout compatibility

This adds llm_resolution_timeout_seconds to ProtocolConfig, producing the 201-byte config layout expected by the companion resolver service.

Existing deployments using the older layout require a fresh initialization or an explicit migration. This must land before initializing the next devnet or production deployment.

Validation

bun run test:local:

  • 28 passed
  • 0 failed
  • 91 assertions

Coverage includes:

  • upgrade-authority and ProgramData authentication
  • configuration invariants
  • governance-only resolver rotation
  • immediate old-key rejection
  • strict Resolution Spec validation
  • True and False settlement
  • LLM and vote Unresolvable no-fault settlement
  • TooEarly rejection
  • timed-out PendingLLM recovery
  • challenge-window enforcement
  • mismatched protocol accounts
  • substituted payout token accounts
  • payout conservation across settlement paths

The companion resolver cross-repository suite also passes against this program artifact for direct verdict settlement, timeout recovery, permissionless finalization, and resolver rotation.

Related

The on-chain program should be merged and deployed before the companion resolver is deployed, because the resolver intentionally fails preflight against the older account layout.

Summary by CodeRabbit

  • New Features

    • Resolution Specs now use verifiable Arweave references with SHA-256 digests.
    • Timed-out LLM resolutions can be recovered as challengeable Unresolvable outcomes.
    • Authorized protocol administrators can rotate the resolver key.
    • LLM resolution timeouts are configurable.
  • Bug Fixes

    • Unresolvable outcomes now return all bonds, charge no fee, and slash no party.
    • Deprecated or invalid outcome values are rejected.
    • Strengthened protocol initialization and payout-account validation.
  • Documentation

    • Updated architecture, glossary, resolution, tokenomics, and README guidance to reflect current behavior.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@iamnycx is attempting to deploy a commit to the nitish-bot-8298's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR validates Arweave Resolution Spec references, restricts accepted outcomes, adds resolver rotation and timeout recovery, and implements explicit no-fault settlement for Unresolvable. Documentation and integration tests are updated for configuration, recovery, payouts, conservation, and validation.

Changes

Resolution lifecycle

Layer / File(s) Summary
Resolution Spec and outcome validation
programs/opal/src/constants.rs, programs/opal/src/utils.rs, programs/opal/src/instructions/create_assertion.rs, docs/architecture.md, docs/glossary.md, docs/resolution.md, docs/tokenomics.md, README.md, tests/opal.test.ts
Adds bounded ar://<tx-id>#sha256=<digest> validation, rejects malformed references, and limits outcomes to True, False, and Unresolvable; documentation and tests reflect deprecated TooEarly behavior.
Resolver configuration and timeout recovery
programs/opal/src/state/protocol_config.rs, programs/opal/src/instructions/initialize_protocol_config.rs, programs/opal/src/instructions/recover_pending_llm.rs, programs/opal/src/instructions/update_resolver.rs, programs/opal/src/lib.rs, programs/opal/src/instructions/mod.rs, programs/opal/src/instructions/submit_llm_resolution.rs, docs/architecture.md, docs/resolution.md, README.md, tests/opal.test.ts
Adds resolver timeout configuration, upgrade-authority checks, governance-only resolver rotation, and permissionless timeout recovery into a challengeable Unresolvable state.
Explicit no-fault settlement economics
programs/opal/src/instructions/finalize_llm_resolution.rs, programs/opal/src/instructions/finalize_vote_resolution_placeholder.rs, docs/architecture.md, docs/resolution.md, docs/tokenomics.md, README.md, tests/opal.test.ts
Refunds all principal bonds with zero fee for Unresolvable, preserves decisive payout branches, and tests conservation, vault draining, no-fault outcomes, and payout-account validation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Resolver
  participant SubmitLlmResolution
  participant ProtocolConfig
  participant LlmResolutionRound
  Resolver->>SubmitLlmResolution: submit True, False, or Unresolvable
  SubmitLlmResolution->>ProtocolConfig: verify resolver key
  SubmitLlmResolution->>LlmResolutionRound: store validated outcome
Loading

Possibly related PRs

Suggested reviewers: nitish-bot, mahelikaa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: resolver settlement hardening, timeout recovery, and resolver key rotation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@programs/opal/src/instructions/recover_pending_llm.rs`:
- Around line 67-71: Update submit_llm_resolution to calculate the round’s
recovery deadline from requested_at and config.llm_resolution_timeout_seconds,
then reject submissions when now is at or beyond that deadline using the
appropriate OpalError. Reuse the same checked deadline logic and boundary
semantics as recover_pending_llm, while preserving submission behavior for
rounds still within the timeout.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a422a664-7ff5-44e9-b73e-e6241f4603a8

📥 Commits

Reviewing files that changed from the base of the PR and between ba89557 and e2cee92.

📒 Files selected for processing (19)
  • README.md
  • docs/architecture.md
  • docs/glossary.md
  • docs/resolution.md
  • docs/tokenomics.md
  • programs/opal/src/constants.rs
  • programs/opal/src/errors.rs
  • programs/opal/src/instructions/create_assertion.rs
  • programs/opal/src/instructions/finalize_llm_resolution.rs
  • programs/opal/src/instructions/finalize_vote_resolution_placeholder.rs
  • programs/opal/src/instructions/initialize_protocol_config.rs
  • programs/opal/src/instructions/mod.rs
  • programs/opal/src/instructions/recover_pending_llm.rs
  • programs/opal/src/instructions/submit_llm_resolution.rs
  • programs/opal/src/instructions/update_resolver.rs
  • programs/opal/src/lib.rs
  • programs/opal/src/state/protocol_config.rs
  • programs/opal/src/utils.rs
  • tests/opal.test.ts

Comment on lines +67 to +71
let recovery_deadline = checked_add_i64(
llm_round.requested_at,
config.llm_resolution_timeout_seconds,
)?;
require!(now >= recovery_deadline, OpalError::DeadlineNotReached);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Enforce the timeout in resolver submission too.

recover_pending_llm only makes recovery available after this deadline. The supplied submit_llm_resolution handler still accepts any PENDING_LLM round without checking requested_at, so a resolver can submit a decisive result after expiry before a recovery transaction lands. Reject late submissions using this same deadline.

🤖 Prompt for 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.

In `@programs/opal/src/instructions/recover_pending_llm.rs` around lines 67 - 71,
Update submit_llm_resolution to calculate the round’s recovery deadline from
requested_at and config.llm_resolution_timeout_seconds, then reject submissions
when now is at or beyond that deadline using the appropriate OpalError. Reuse
the same checked deadline logic and boundary semantics as recover_pending_llm,
while preserving submission behavior for rounds still within the timeout.

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.

1 participant