Skip to content

Drop the on-chain participation records (2.0.0) - #165

Closed
0xdevcollins wants to merge 1 commit into
feat/escrow-open-poolfrom
feat/offchain-participation
Closed

0xdevcollins wants to merge 1 commit into
feat/escrow-open-poolfrom
feat/offchain-participation

Conversation

@0xdevcollins

@0xdevcollins 0xdevcollins commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Breaking. Removes apply_to_bounty, withdraw_application, submit, withdraw_submission and the applicant/submission read accessors. Eleven entry points, and nothing else moved: the built ABI diff against the deployed wasm is exactly that list.

Based on feat/escrow-open-pool, not testnet, because that is where 1.7.0 lives. Targeting testnet would drag every unrelated 1.7.0 commit into this diff.

Why

Nothing on chain ever read them. select_winners and claim_prize check the award record and the caller's signature, never the applicant index or a submission anchor, so the only consumer of an application was submit, and the only consumer of a submission was withdraw_submission. A closed loop, and a permissionless one: anyone could write an entry for any active event without going through Boundless, which is what made the record worthless as evidence.

Applying and submitting are product facts and now live in Postgres. See boundlessfi/boundless-nestjs#867.

Two properties improve on the way out

Also moot: #131 (resubmit after selection). #142 closes with the version bump, which aligns Cargo.toml (still 1.6.0) with the contractmeta.

Compatibility

Storage keys and error discriminants are retired in place, never renumbered, following the existing // 36-38 retired precedent. Rows left on live events stay identifiable until they archive; nothing reads or writes them.

INITIAL_VERSION, the contractmeta and Cargo.toml all go to 2.0.0. Removing entrypoints is breaking, and the upgrade is proposed with that version string.

Threat model

Updated on the base branch, so the model moves with the code rather than inside this diff (d1a8b2f, e64c7de):

  • Void in 2.0.0: Tamp.3 and Tamp.17 (overwriting a submission that no longer exists) and DoS.13 (the dead grant-submission branch, whose second candidate remediation is exactly this change). The rows keep their place and carry a Void (2.0.0) status, so the 72-threat count and every cross-reference still resolve.
  • Narrowed: Tamp.14 keeps its remediation, but burning a derived child marker now needs a privileged signature. DoS.7 loses one of its two coupled callers.
  • Open items drop from ten to eight. Flow steps 5 and 6 keep their numbers so every (Step n) reference still resolves. Appendix A strikes the removed entrypoints rather than deleting the rows, matching how the code retires storage keys.
  • The Scout report's historical note records that the findings touching apply(), remove_applicant(), applicant_at(), applicant_count() and applicants_snapshot() no longer have an on-chain counterpart. The assessments stay unedited as the record of the scan.

Checks

cargo test --workspace 198 + 66 + 3 green, cargo clippy --workspace --all-targets no warnings on touched code, cargo fmt --check clean, stellar contract build --locked succeeds. The compatibility crate still drives the old wasm's apply_to_bounty through its pinned client, which is correct: it tests upgrading from that version.

Deploying

This upgrades a contract holding live mainnet escrow. It needs the multisig session with simulate-before-sign per the runbook, and a decision on ordering against #123: once the upgrade timelock is back at 17,280 ledgers, every upgrade waits a day.

BREAKING: removes apply_to_bounty, withdraw_application, submit,
withdraw_submission and the applicant/submission read accessors.

Nothing on chain ever read them. select_winners and claim_prize check
the award record and the caller's signature, never the applicant index
or a submission anchor, so the only consumer of an application was
submit, and the only consumer of a submission was withdraw_submission.
A closed loop, and a permissionless one: anyone could write an entry
for any active event without going through Boundless, which is what
made the record worthless as evidence. Applying and submitting are
product facts and now live in Postgres.

Two properties improve on the way out. No permissionless entrypoint
calls the profile contract any more, so the derived child op_ids of
claim_prize and claim_milestone can no longer be burned by a stranger
(#129 keeps its fix, but loses its cheap
exploit path). And grant submissions stop being a dead branch: submit
demanded an application that apply_to_bounty refused to create for a
grant, so the call could only ever fail (#132).

Storage keys and error discriminants are retired in place rather than
deleted, so the remaining variants keep their positions and rows left
on live events stay identifiable until they archive. Version goes to
2.0.0 on both the contractmeta and Cargo (which also closes the #142
mismatch): removing entrypoints is breaking, and the upgrade is
proposed with that version string.

docs/threat-model.md, BACKLOG.md and the Scout report are not touched
here; they are checked out dirty in a stash and their STRIDE rows need
the same pass.
@almanax-ai

almanax-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Plan expired

Your subscription has expired. Please renew your subscription to continue using CI/CD integration and other features.

0xdevcollins added a commit that referenced this pull request Oct 3, 2026
Lands #120 (events 1.7.0 and profile 1.2.0, the code mainnet runs) and #165 (events 2.0.0, on-chain participation removed), plus the pre-audit hardening on top.

- Grant roster, forfeit_milestone, and no cancelling a grant while awards are owed.
- Refunds that cannot be delivered are held for claim_refund.
- The owner can reclaim management before the first award.
- A two-step crowdfunding release validator.
- Caps sized to mainnet limits.
- The 17,280-ledger upgrade timelock is restored.
- migrate refuses until conversion completes.
- Dead code, storage keys and errors removed.
- soroban-sdk 28 and CLI 28.1.0.
- Reproducible builds: scripts/build-release-linux.sh matches CI's hash from any machine.
- 386 tests pass, plus a replay of the real mainnet upgrade against captured mainnet storage.
- The final build passed 317 of 317 on-chain checks on testnet.
- Threat model 1.2: 76 threats, none open.

The full commit history is on the feat/grant-hardening branch.
@0xdevcollins

Copy link
Copy Markdown
Collaborator Author

Landed in #166, squash-merged into testnet as 8b4f054, together with #120, which this was stacked on, and the pre-audit hardening. Events 2.0.0, including this change, runs on testnet as b63afc1f… and passed 317 of 317 on-chain checks. It is not on mainnet yet; see docs/upgrade-runbook.md, section 8.

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