Skip to content

fix(events): cap per-event hackathon submissions to bound storage growth - #86

Merged
0xdevcollins merged 3 commits into
boundlessfi:testnetfrom
armandocodecr:fix/hackathon-submission-storage-cap
Jul 23, 2026
Merged

fix(events): cap per-event hackathon submissions to bound storage growth#86
0xdevcollins merged 3 commits into
boundlessfi:testnetfrom
armandocodecr:fix/hackathon-submission-storage-cap

Conversation

@armandocodecr

@armandocodecr armandocodecr commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Hackathon events have needs_application = false, so any address could call submit() and create a persistent EventSubmission entry with an arbitrarily long content_uri, with no cap on the number of distinct submissions. This let an attacker spam submit() from many fresh addresses to grow the contract's persistent state and rent burden without bound. This adds a per-event submission counter with a cap, and a length bound on content_uri.

Closes the storage bloat issue surfaced by the Almanax scan of e1f1793 (severity medium, boundless-events, event_ops.rs submit).

Closes #71

Changes

  • New DataKey::EventSubmissionCount(u64), appended at the end of the enum to preserve existing key discriminants.
  • New storage::submission_count and storage::append_submission, mirroring the existing append_contributor/append_applicant pattern: reserve the slot against the cap before writing, and treat an existing submission as a no-op (re-submission updates in place and must not recount).
  • storage::remove_submission now decrements the counter, so withdrawing a submission frees its slot for a future submitter instead of counting against the cap forever.
  • New MAX_SUBMISSIONS_PER_EVENT (5,000, matching the existing applicant/contributor caps) and MAX_CONTENT_URI_LEN (256) constants in event_ops.rs.
  • submit() now rejects an oversized content_uri and reserves a submission slot before writing the entry.
  • New error variant Error::TooManySubmissions (44). For the content_uri length check, I reused Error::TitleTooLong rather than adding a second new variant, since the contracterror enum was already at 49 of the 50-variant cap (one new variant was all I could add). This project already has documented precedent for exactly this tradeoff, see BACKLOG.md's L7 entry, which reused Error::InvalidPillar for an unrelated check for the same reason.

Testing

Added to contracts/events/src/tests/hackathon_pillar.rs:

  • submit_beyond_cap_reverts: fast-forwards the per-event counter directly to the cap (rather than performing 5,000 real submissions) and confirms the next submit() reverts with Error::TooManySubmissions.
  • submit_oversized_content_uri_reverts: a content_uri one byte over MAX_CONTENT_URI_LEN reverts.
  • resubmit_by_existing_applicant_does_not_increment_submission_count: submitting twice from the same address keeps the counter at 1.
  • withdraw_submission_frees_the_slot_for_future_submitters: withdrawing brings the counter back to 0.

Ran locally:

  • cargo test --release (matches this repo's CI exactly): 267 passed, 0 failed (201 in boundless-events, 66 in boundless-profile).
  • cargo fmt --all -- --check: clean.
  • make build in contracts/events (stellar contract build): compiles, boundless_events.wasm is 60,491 bytes, under the CI's 64 KB ceiling with about 5 KB of headroom left.

I did not run cargo clippy as a gate since it isn't part of this repo's CI (verify-build.yml only builds, checks WASM size, and runs cargo test --release), and CONTRIBUTING.md's documented local check is just cargo test.

Summary by CodeRabbit

  • New Features
    • Added per-event submission cap and maximum allowed content_uri length.
    • Duplicate submissions by the same applicant now update the existing entry without consuming additional capacity.
  • Bug Fixes
    • Submissions beyond the cap now fail with a clear “too many” error.
    • Oversized content_uri values now fail with a “title too long” error.
    • Withdrawing (and removing nonexistent entries) no longer breaks capacity tracking; freed slots become available again.
  • Tests
    • Extended coverage for submission counter behavior and error codes.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e2ee0153-a479-4c94-b058-b677895b0467

📥 Commits

Reviewing files that changed from the base of the PR and between 12ace32 and 6c0258c.

📒 Files selected for processing (5)
  • contracts/events/src/errors.rs
  • contracts/events/src/event_ops.rs
  • contracts/events/src/storage.rs
  • contracts/events/src/tests/hackathon_pillar.rs
  • contracts/events/src/types.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • contracts/events/src/types.rs
  • contracts/events/src/storage.rs
  • contracts/events/src/event_ops.rs
  • contracts/events/src/tests/hackathon_pillar.rs

📝 Walkthrough

Walkthrough

Hackathon submissions now enforce a 5,000-submission per-event cap and a 256-character content URI limit. Persistent submission counts support idempotent resubmissions and slot reclamation after withdrawal, with tests covering the new behaviors.

Changes

Submission limit enforcement

Layer / File(s) Summary
Submission count storage contracts
contracts/events/src/types.rs, contracts/events/src/errors.rs, contracts/events/src/storage.rs
Adds the persistent event submission count key, capped/idempotent reservation, count reads, TTL refreshes, and count cleanup on withdrawal.
Submit validation and reservation
contracts/events/src/event_ops.rs
Adds submission and URI limits, rejects oversized content URIs, and reserves submission capacity before writing.
Submission behavior tests
contracts/events/src/tests/hackathon_pillar.rs
Tests cap enforcement, URI length validation, withdrawal slot release, and count-preserving resubmissions.

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

Sequence Diagram(s)

sequenceDiagram
  participant Applicant
  participant EventOps
  participant Storage
  Applicant->>EventOps: submit content_uri
  EventOps->>EventOps: validate URI length
  EventOps->>Storage: append_submission(event_id, applicant, cap)
  Storage-->>EventOps: reserve slot or return cap error
  EventOps->>Storage: persist submission
Loading

Possibly related PRs

Suggested reviewers: 0xdevcollins

Poem

I’m a rabbit guarding the gate,
Counting five thousand—then I wait.
Long links tumble into the bin,
Returning hares may hop back in.
Withdrawn slots bloom anew—
Bounded storage, carrots too!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.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 clearly summarizes the main fix: capping hackathon submissions to bound storage growth.
Linked Issues check ✅ Passed It adds per-event submission caps, URI length checks, idempotent resubmissions, and tests matching #71.
Out of Scope Changes check ✅ Passed All changes support submission capping, storage tracking, validation, or tests for #71.
✨ 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: 2

🤖 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 `@contracts/events/src/event_ops.rs`:
- Around line 25-26: Replace the global MAX_SUBMISSIONS_PER_EVENT and
MAX_CONTENT_URI_LEN constants with fields on EventRecord or its event variant
payload, and update all submission-count and content-URI validation to read
those per-event values. Ensure event creation/configuration initializes the
fields so each event can set its own limits, preserving the existing defaults
where required.

In `@contracts/events/src/storage.rs`:
- Around line 413-422: Update remove_submission to first check
get_submission(env, id, applicant) and return immediately when no submission
exists; only then remove the EventSubmission key and decrement or remove the
per-event count, preserving the existing count-update behavior for present
submissions.
🪄 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: CHILL

Plan: Pro

Run ID: 05063aad-1b20-43dc-bcfc-3327a6347985

📥 Commits

Reviewing files that changed from the base of the PR and between 188bc71 and 12ace32.

📒 Files selected for processing (5)
  • contracts/events/src/errors.rs
  • contracts/events/src/event_ops.rs
  • contracts/events/src/storage.rs
  • contracts/events/src/tests/hackathon_pillar.rs
  • contracts/events/src/types.rs

Comment thread contracts/events/src/event_ops.rs
Comment thread contracts/events/src/storage.rs
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 19, 2026
@0xdevcollins

Copy link
Copy Markdown
Collaborator

@armandocodecr Please fix conflict so i can merge

@almanax-ai

almanax-ai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Quota reached

Your plan allows 300 CI/CD file units per month. You've used 289 and this scan would add 28 more (total: 317).

@armandocodecr

Copy link
Copy Markdown
Contributor Author

@0xdevcollins Done

@0xdevcollins
0xdevcollins merged commit 845ed74 into boundlessfi:testnet Jul 23, 2026
4 checks passed
0xdevcollins added a commit that referenced this pull request Jul 23, 2026
…ractmeta (#98)

Version stamps had drifted and were internally inconsistent: events
contractmeta said 1.2.0 while INITIAL_VERSION said 1.3.0, and neither
reflected the public-surface / storage changes merged since (#86 submission
cap, #88 manager two-step, #96 OpSeen namespacing). Profile was still 1.1.0
despite #95 namespacing its OpSeen.

Bump both contracts coherently — INITIAL_VERSION, contractmeta, and the
Cargo package version all set to:
  events  1.3.0 -> 1.4.0   (submission cap, manager two-step, OpSeen ns)
  profile 1.1.0 -> 1.2.0   (namespaced OpSeen)

version()-asserting admin tests updated to match. 225 events + 66 profile
tests green; make build OK (events 56,091 B, profile 15,893 B, both under
the 64 KB ceiling); fmt clean.
0xdevcollins added a commit that referenced this pull request Jul 23, 2026
The submission deadline was enforced by the contract (reject apply/submit/
withdraw_submission after it, require+future-check at create), but it gates
nothing the contract is responsible for:
- no money path reads it (since #61 the payout/refund liveness is anchored
  to select-time via PRIZE_CLAIM_WINDOW_SECS, not the deadline);
- winners are the manager's discretion — select_winners never consults
  submissions, so a late submission row is inert;
- permissionless-submit storage abuse is bounded by the #86 count cap, not
  the deadline.

Meanwhile the deadline is immutable on-chain (no set/extend entrypoint), so
the contract could not support the extensions organizers do routinely. A
submission window — with its extensions, grace periods, and cutoffs — is a
backend/product concern; the chain keeps only custody, the count cap, and
discretionary winner selection.

Remove the enforcement:
- delete the deadline checks in submit / withdraw_submission / bounty apply
  and the create-time DeadlineMustBeFuture check;
- drop the required-deadline check from hackathon/crowdfunding validate_create.

Keep EventRecord.deadline as advisory metadata (still stored, emitted at
create, backend-owned) — no storage-layout change, no ABI break. The
now-dead DeadlineRequired/DeadlinePassed/DeadlineMustBeFuture variants are
retired, freeing 3 slots against the 50-case error-enum cap.

Removed the 5 tests that asserted the deleted behavior. 220 events + 66
profile green; make build OK (events 55,742 B); fmt clean.
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.

Hackathon submit() has no submission cap or content_uri length limit

2 participants