Skip to content

Audit TTL coverage — 27 extend_ttl sites against 37 persistent and 13 instance storage uses #260

Description

@Jagadeeshftw

Priority: High  ·  Area: Soroban contract / state archival  ·  Est. effort: 8–12 h

📌 Problem

Soroban archives ledger entries whose TTL lapses. An archived persistent entry is unreadable until explicitly restored; an archived instance entry takes the contract instance with it.

This contract does manage TTL — grep -rn 'extend_ttl\|extend_instance' src/ returns 27 sites, which is a genuinely better starting point than many Soroban codebases. But the storage surface is larger:

  • env.storage().persistent()37 uses
  • env.storage().instance()13 uses

The gap between 27 and 50 is what this issue asks you to characterise. It is not automatically a defect: a single extend_ttl may correctly cover several reads of the same key, and some entries may be intentionally short-lived. The deliverable is establishing which of the 50 sites are covered, which are deliberately not, and which are genuine gaps.

The failure mode if there are gaps: an anchor, liquidity entry or settlement record created and left for longer than the default TTL — an ordinary lifecycle for a settlement awaiting execution — can be archived out from under the contract. The next operation then fails against a record that logically still exists.

🎯 Design decision required

State and defend:

  1. Coverage model. After the audit, decide how coverage should be guaranteed going forward: typed accessors in storage.rs that bump internally, entrypoint-boundary bumps, or an explicit keeper entrypoint. Argue which, and how it prevents new code from silently bypassing it.
  2. Instance TTL. Establish whether instance storage is extended and where. Instance archival is the most severe failure mode, so it warrants an explicit answer even if the audit shows it is already handled.
  3. Rent and abuse. Who pays for extensions, and can an attacker drive the contract into paying to keep adversarial entries alive? State the answer for your chosen model.

🧩 Requirements and context

  • The audit table is the primary deliverable: every persistent and instance storage site, whether its key's TTL is extended, and where.
  • Thresholds must have a single source of truth. If constants already exist in src/storage.rs, use them; do not introduce a second set.
  • Soroban test utilities can advance ledger sequence, so TTL behaviour is testable in-crate. "Untestable" is not an accepted reason to skip tests.
  • Report the wasm byte delta; the contract's size is currently unmeasured (tracked separately).
  • Do not change storage keys or the data model.

🛠️ Suggested execution

  1. Produce the 50-site audit table; post it in the PR before writing code.
  2. For any gap, write a failing test — create the record, advance the ledger past the threshold, show the read fails.
  3. Implement the coverage model.
  4. Show the tests passing.
  5. Add an instance-storage survival test.

✅ Acceptance criteria

  • The PR contains the full audit of all 37 persistent and 13 instance sites with before/after coverage.
  • Sites intentionally left un-extended are identified with reasons.
  • For each genuine gap, a test advances the ledger past the threshold and proves the record survives.
  • Instance-storage TTL behaviour is explicitly established and tested.
  • No duplicate TTL constants exist.
  • The rent/abuse question is answered for the chosen model.
  • cargo test passes; wasm byte delta reported.

🚫 Out of scope

  • Restoring already-archived entries (RestoreFootprint) — this is prevention only.
  • Changing storage keys or the data model.
  • The wasm size guard — separate issue.

🧪 Verification

cargo test
cargo build --target wasm32-unknown-unknown --release
grep -rn "storage()\.persistent()\|storage()\.instance()" src/ | wc -l
cargo fmt --all -- --check

📤 What your PR must include

  1. The 50-site audit table with coverage before and after.
  2. Your coverage model and how it prevents future bypass.
  3. The rent/abuse analysis.
  4. Failing-then-passing test output for each gap.
  5. Closes #<n>.

🔒 Security notes

This is a funds-and-records availability issue. If an entry backing a settlement or a liquidity position archives while the position is still live, the operation that depends on it fails against a record that logically exists, and recovery needs an explicit restore that nothing in the contract or its documentation currently describes. The longer the gap between creating a record and acting on it, the more likely this becomes — and settlements awaiting execution are exactly that case.

📋 Guidelines

  • Minimum 95% test coverage on changed lines
  • Clear documentation
  • Timeframe: 96 hours from assignment
  • One logical change per commit; no merge commits

💬 Join our community

Working on this, or want to sanity-check your approach before you start? Come and ask — the maintainers are there and happy to help.

Telegram: https://t.me/Grainlify

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSGrantFox open-source programMaybe RewardedGrantFox: potentially rewarded contributionThird CampaignGrantFox third campaign issuebugSomething isn't workingpriority:highHigh difficulty / architectural or cross-cutting

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions