Skip to content

feat(gasless): Ed25519 signature-based gasless task assignment (#077) - #103

Merged
bakarezainab merged 1 commit into
LatterFixxx:mainfrom
Mrwicks00:feat/077-gasless-signature-task-assignment
Aug 27, 2026
Merged

bakarezainab merged 1 commit into
LatterFixxx:mainfrom
Mrwicks00:feat/077-gasless-signature-task-assignment

Conversation

@Mrwicks00

@Mrwicks00 Mrwicks00 commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

feat(gasless): Ed25519 signature-based gasless task assignment (#77)

Closes #77

Summary

Adds a gasless path for claiming an open task. A contributor signs a
structured authorization message off-chain with their Ed25519 key; a gas
relayer
submits it on-chain and pays the fees. The contract re-derives the
exact signed bytes from the call arguments, verifies the signature against the
contributor's registered key, and enforces a per-contributor nonce so a
captured message can never be replayed.

Today assign_task (src/lib.rs) requires assignee.require_auth() — the
contributor must hold XLM and submit the transaction themselves. This PR keeps
that path untouched and adds assign_task_gasless alongside it.

How it works

  1. Key registration (once). The contributor registers an Ed25519 public key,
    either themselves via register_signing_key or through an admin via
    admin_set_signing_key (so onboarding can itself be gasless).

  2. Off-chain signing. The contributor's client builds a GaslessAssignment:

    {
      "contract":          "C...",   // this contract (domain separation)
      "task_id":           42,
      "contributor":       "G...",   // address to be assigned
      "nonce":             0,        // == current on-chain nonce for contributor
      "expiration_ledger": 1234567   // request void once ledger passes this
    }

    It fetches the canonical bytes from gasless_assignment_payload (the SDK XDR
    encoding of the struct) and signs them, producing a 64-byte signature.

  3. Relayer submission. The relayer calls
    assign_task_gasless(relayer, request, signature). Only relayer authorizes
    the transaction.

  4. On-chain validation (every failure reverts): request.contract must be
    this deployment; the ledger must not have passed expiration_ledger;
    request.nonce must equal the contributor's current nonce (then incremented);
    the contributor must have a registered key; env.crypto().ed25519_verify must
    accept the signature over the re-encoded request; the task must exist and be
    Open.

Acceptance criteria

Criterion Where
Structured message validation GaslessAssignment contract type + full field validation in assign_task_gasless
Verify signer credentials (Ed25519) env.crypto().ed25519_verify against the contributor's registered key
Per-contributor nonce to prevent replay GaslessKey::Nonce(Address), checked and consumed per assignment
Off-chain signature parsing/validation gasless_assignment_payload returns the exact bytes to sign; canonical XDR round-trip
Relayer execution endpoint assign_task_gasless(relayer, request, signature) — relayer is sole authorizer/fee payer
Unit tests verifying gasless assignments src/gasless_test.rs — 11 tests

SECP256k1 is intentionally out of scope — Ed25519 is the Stellar-native key type.
It is a mechanical follow-up via env.crypto().secp256k1_recover behind a
key-type discriminator.

Changes

  • src/gasless.rs — new module. GaslessAssignment type, GaslessKey
    storage enum, register_signing_key / admin_set_signing_key / get_signing_key
    / get_assignment_nonce / assignment_payload / assign_task_gasless. Module
    header documents the full off-chain flow and JSON shape.
  • src/lib.rs — registers the module; extracts the assignment state
    transition from assign_task into a shared apply_task_assignment helper used
    by both the interactive and gasless paths; exposes the six new contract methods.
  • src/events.rs — emit_signing_key_registered, emit_gasless_assignment.
  • src/gasless_test.rs — 11 unit tests (below).
  • Cargo.toml — dev-dependency ed25519-dalek = "2" (already in the lock
    graph via soroban-sdk testutils) to produce real signatures in tests.
  • README.md — module table row.

Test plan

  • cargo test --lib gasless — 11 passed, 0 failed:
    • test_gasless_assignment_happy_path — task → InProgress, assignee == contributor,
      nonce 0→1, and env.auths() shows the relayer authorized while the contributor did not.
    • test_replay_rejected — resubmitting the same signed message panics.
    • test_wrong_signer_rejected — signature from an unregistered key panics.
    • test_tampered_field_rejected — relayer swaps contributor after signing → panics.
    • test_expired_request_rejected — expiration_ledger in the past panics.
    • test_unregistered_contributor_rejected — no key on file panics.
    • test_wrong_contract_rejected — request bound to another deployment panics.
    • test_non_open_task_rejected — second gasless claim on an assigned task panics.
    • test_admin_set_signing_key_enables_full_gasless_onboarding — admin registers key, gasless assign works.
    • test_admin_set_signing_key_rejects_non_admin — non-admin registration panics.
    • test_nonce_advances_across_sequential_assignments — nonce 0→1→2 over two tasks; stale nonce rejected.
  • cargo test --lib — 127 passed / 12 failed. The 12 failures are
    pre-existing on main (confirmed via git stash): 9 benchmark::*,
    swap_router_test::test_refund_on_missing_oracle_price,
    swap_router_test::test_refund_on_unresolved_route,
    test::test_create_and_complete_task_flow. This PR adds 11 passing tests and
    introduces no regressions.
  • cargo build --target wasm32-unknown-unknown --release — clean.
  • cargo clippy --lib --tests — no new warnings in the added files.

Summary by CodeRabbit

  • New Features

    • Added gasless task assignment using Ed25519 signatures and relayer-submitted transactions.
    • Added signing-key registration, administrator onboarding, payload generation, and assignment nonce tracking.
    • Added expiration checks, contract binding, replay protection, and assignment events.
    • Preserved the existing authenticated task-assignment flow.
  • Documentation

    • Documented the new gasless assignment module and implementation details.

…rFixxx#77)

Contributors can claim an open task without holding XLM: they sign a
structured GaslessAssignment message off-chain with their Ed25519 key and a
gas relayer submits it via assign_task_gasless. The contract re-derives the
canonical XDR payload, verifies the signature against the contributor's
registered key, and enforces a per-contributor nonce to block replay.

- src/gasless.rs: GaslessAssignment type, GaslessKey storage, key
  registration (self-service + admin), nonce tracking, relayer endpoint
  with domain separation and expiration checks
- src/lib.rs: extract shared apply_task_assignment helper used by both
  assign_task and the gasless path; expose six new contract methods
- src/events.rs: signing-key + gasless-assignment events
- src/gasless_test.rs: 11 unit tests
- Cargo.toml: ed25519-dalek dev-dependency for test signing
@coderabbitai

coderabbitai Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f6fd618-cdbf-4dde-b832-51cd4305647f

📥 Commits

Reviewing files that changed from the base of the PR and between 2a31ac4 and 0498755.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml
  • README.md
  • pr-077.md
  • src/events.rs
  • src/gasless.rs
  • src/gasless_test.rs
  • src/lib.rs
  • tasks/todo.md

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Adds Ed25519 signature-based gasless task assignment. Contributors or admins register signing keys. Relayers submit validated assignments with contract-bound payloads, expirations, and contributor nonces. Shared assignment logic updates task state and emits events. Tests cover success, rejection, onboarding, and replay protection.

Changes

Gasless assignment flow

Layer / File(s) Summary
Assignment contract and key registration
src/gasless.rs, src/lib.rs
Defines the GaslessAssignment payload and storage keys. Adds contributor and admin signing-key registration, key and nonce queries, canonical payload generation, and public contract endpoints.
Shared task assignment transition
src/lib.rs
Moves open-task validation, assignment updates, persistence, status changes, timestamps, and assignment events into apply_task_assignment, which supports both assignment paths.
Relayer validation and gasless execution
src/gasless.rs, src/events.rs
Validates relayer authorization, pause state, contract binding, expiration, nonce, registered key, and Ed25519 signature. Consumes valid nonces, assigns tasks, and emits registration and gasless-assignment events.
Gasless flow validation and documentation
src/gasless_test.rs, Cargo.toml, README.md, pr-077.md, tasks/todo.md
Adds fixtures and tests for successful assignments, rejection cases, admin onboarding, and nonce progression. Documents the module and implementation record, and adds the test dependency.

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

Merge Risk: ⚪ Minimal · up to 04987

The PR adds gasless task assignment with signature and nonce validation while preserving the existing assignment path. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Contributor
  participant Relayer
  participant SmartContract
  participant ContractStorage
  Contributor->>SmartContract: Request canonical assignment payload
  SmartContract->>ContractStorage: Read signing key and nonce
  SmartContract-->>Contributor: Return payload bytes
  Contributor->>Relayer: Send signed assignment
  Relayer->>SmartContract: Submit assign_task_gasless
  SmartContract->>ContractStorage: Validate and consume nonce
  SmartContract->>SmartContract: Apply task assignment
  SmartContract-->>Relayer: Emit assignment event
Loading

Suggested reviewers: bakarezainab, securify001

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.27% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 4 files. (4 skipped: … 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 clearly and concisely identifies the main change: Ed25519 signature-based gasless task assignment.
Linked Issues check ✅ Passed The PR satisfies the linked issue objectives. It adds structured authorization payload handling, Ed25519 signature parsing and verification, per-contributor nonce replay protection, relayer execution …
Out of Scope Changes check ✅ Passed The changes remain within scope. The implementation, shared assignment logic, events, tests, dependency, README updates, and implementation record all support the gasless task-assignment objectives.
Full details: Linked Issues check

Explanation

The PR satisfies the linked issue objectives. It adds structured authorization payload handling, Ed25519 signature parsing and verification, per-contributor nonce replay protection, relayer execution endpoints, and comprehensive unit tests.

Full details: Docstring Coverage

Explanation

Docstring coverage is 70.27% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 4 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@bakarezainab
bakarezainab merged commit e3d774a into LatterFixxx:main Aug 27, 2026
2 checks passed
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.

#077: Cryptographic Signature-Based Gasless Task Assignment

2 participants