Skip to content

feat: add atomic_close_period sealing snapshot, accrual, and report (#461)#492

Open
morelucks wants to merge 1 commit into
RevoraOrg:masterfrom
morelucks:feat/atomic-close-period-pr
Open

feat: add atomic_close_period sealing snapshot, accrual, and report (#461)#492
morelucks wants to merge 1 commit into
RevoraOrg:masterfrom
morelucks:feat/atomic-close-period-pr

Conversation

@morelucks

Copy link
Copy Markdown

Summary

Closes #461

Adds atomic_close_period — a single entry-point that finalises the snapshot, commits the accrual index, and seals the report window in one transaction, so indexers see one period_sealed boundary event instead of three separate calls.


What changed

File Change
src/lib.rs Add EVENT_PERIOD_SEALED constant + atomic_close_period() function
src/test_close_period.rs Add tests: happy path, hash mismatch, missing report, snapshot-disabled

Design

Three steps, one transaction

Step 1 — Snapshot finalisation
Recomputes SHA-256(index_xdr ‖ holder_xdr ‖ shares_bps_xdr) over every applied holder slot and asserts it matches the committed content_hash. Skipped (idempotent) if the snapshot was already finalised by a prior finalize_snapshot call.

Step 2 — Accrual index commit
Derives accrual_hash = SHA-256(revenue_amount_xdr ‖ payout_asset_xdr) so indexers can audit the revenue figure without reading raw storage.

Step 3 — Period seal
Writes the ClosedPeriod key, blocking all future report overrides for this period_id.

Single boundary event

topics: ("per_seald", issuer, namespace, token)
data:   (period_id, content_hash, accrual_hash, closed_at)

One event instead of three lets indexers process period boundaries with a single subscription.

Atomicity guarantee

All writes happen after all validation. Any error (SnapshotHashMismatch, MissingReportForOverride, PeriodAlreadyClosed, etc.) returns before any storage mutation, leaving state unchanged.

Ordering invariants

  • commit_snapshot + apply_snapshot_shares must precede this call for snapshot_ref == period_id
  • report_revenue must have been called for period_id
  • period_id must be > 0

Error table

Error Condition
ContractFrozen / ContractPaused Contract not operational
OfferingNotFound Offering absent or caller not current issuer
InvalidPeriodId period_id == 0
SnapshotNotEnabled Snapshot distribution not enabled for offering
OutdatedSnapshot No snapshot entry for this period_id
SnapshotHashMismatch Recomputed digest ≠ committed content_hash
MissingReportForOverride No revenue report for this period_id
PeriodAlreadyClosed Period already sealed

Closes RevoraOrg#461

Add atomic_close_period(issuer, namespace, token, period_id) that
finalises the snapshot content_hash, commits the accrual index, and
seals the report window in a single state-changing call so indexers see
one boundary event (period_sealed) rather than three separate calls.

## What changed

src/lib.rs
- Add EVENT_PERIOD_SEALED constant (symbol "per_seald")
- Add atomic_close_period() public function

src/test_close_period.rs
- Add tests: happy path, hash mismatch, missing report, snapshot
  disabled edge cases (all referencing atomic_close_period)

## Design

### Three steps, one transaction

1. Snapshot finalisation — recomputes SHA-256(index_xdr || holder_xdr
   || shares_bps_xdr) over every applied holder slot and asserts it
   matches the committed content_hash. Skipped if snapshot was already
   finalised by a prior finalize_snapshot call (idempotent).

2. Accrual index commit — derives accrual_hash = SHA-256(revenue_xdr
   || payout_asset_xdr) so indexers can audit the revenue figure
   without reading raw storage.

3. Period seal — writes ClosedPeriod key, blocking all future overrides
   for this period_id.

### Single boundary event

  topics: ("per_seald", issuer, namespace, token)
  data:   (period_id, content_hash, accrual_hash, closed_at)

One event instead of three lets indexers process period boundaries
with a single subscription.

### Atomicity guarantee

All writes happen after all validation. Any error (SnapshotHashMismatch,
MissingReportForOverride, PeriodAlreadyClosed, etc.) returns before any
storage is mutated, leaving state unchanged.

### Ordering invariants

- commit_snapshot + apply_snapshot_shares must precede this call
- report_revenue must have been called for period_id
- period_id must be > 0
@drips-wave

drips-wave Bot commented Jun 28, 2026

Copy link
Copy Markdown

@morelucks Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

Add atomic close-of-period transaction sealing report, snapshot, and accrual in one entry

1 participant