Skip to content

Roll back burns and buyers with the index, and pay rewards only on settled mints - #51

Merged
droplister merged 1 commit into
mainfrom
fix/rollback-completeness
Sep 30, 2026
Merged

droplister merged 1 commit into
mainfrom
fix/rollback-completeness

Conversation

@droplister

Copy link
Copy Markdown
Member

Follow-up to #47. Rule: what comes from the chain is rolled back; what records something that happened off it is not.

rollbackIndexTo now also:

  • deletes token_burns after the block, restates launches.burned_quantity for the launches those burns touched (same sum as reconcileBurnedSupply, only the surviving burns), and restates burn_totals (its count is kept by an insert-only trigger);
  • deletes behavior_buyers rows whose every buy was after the block (the asset refold adds back the ones still true); behavior_totals.buyers is then recomputed by the rollup that already runs after a rollback;
  • marks the community rollup stale (community_rollup_stale in chain_state, read by hasCommunityRollup) so the next tick rebuilds it from the remaining mints instead of waiting up to six hours. Using a marker instead of deleting the row means the public endpoint never shows zeros in between.

Burn cursors. The burn scan reads from two cursors of its own: Core tx_index for receives and event_index for destructions. A re-parse hands those numbers out again from the fork, so the cursors can't be rewound to a block number. The rollback sets telegram_burn_rescan_from_block to block + 1 instead, and a later rollback can only move it lower. On its next run the scan reads both feeds newest-first until it reaches a row below that block, then sets both cursors to the node's newest values, which can be lower than the old ones. advanceBurnCursor does that write without the usual monotonic guard and clears the marker in the same batch, and only if the marker still names the block it re-read from. Burns that come back are not announced twice, because announcements are deduplicated by key in announced.

Left alone on purpose: announced, announcement_work and the reward tables. There is now a comment in rollbackIndexTo saying why.

Reward safety. scripts/reward-batch.mjs had no confirmation buffer: it took the first N conforming mints, including mints in the tip block. Its query now lives in scripts/lib/reward-eligibility.mjs and only counts mints at block_index <= tip - 5 (six confirmations, where tip is Counterparty's parsed height or --height). If mint #N is not that deep yet the script refuses to run. The manifest records settled_through_block, and docs/reward-distributions.md step 1 says the same. reward_accounts is a display rollup that is already rebuilt after a rollback, so it is unchanged.

Tests (tests/api-indexer-safety.test.ts, real migrations on node:sqlite):

  • the buyer rollback;
  • the burn rollback: supply, count, the rescan marker only moving lower, announced untouched, the community rollup going stale;
  • the rescan itself: re-reads by block, records and reconciles, drops both cursors below the old ones, clears the marker, then goes back to monotonic cursors and a two-probe quiet tick;
  • a marker that a second rollback moved survives;
  • the six-confirmation eligibility boundary.

Gates run locally: npm run check (111 files, 1425 tests, both typechecks, lint, numeric, i18n), next build, wrangler deploy --dry-run.

…ttled mints

A Core re-parse or reorg now also takes back the chain-derived rows the
first rollback missed: token burns after the fork (with the launches'
burned supply and the burn count restated), buyers whose every buy was
after it, and the community rollup, which is marked stale so the next
tick rebuilds it instead of waiting hours.

The burn scan's cursors are Core's tx_index and event_index, which a
re-parse hands out again from the fork, so they cannot be rewound to a
block. The rollback leaves a marker instead; the next scan re-reads by
block from there, resets both cursors to what the node now has, and
clears the marker with them. Announcement keys keep it from reposting.

Telegram announcements and reward records stay, since they record
things that happened off the chain. reward-batch.mjs now counts only
mints six confirmations deep (tip - 5 or older), so a reorg cannot
change who a frozen batch paid.
@droplister
droplister merged commit 0db3a34 into main Sep 30, 2026
1 check passed
@droplister
droplister deleted the fix/rollback-completeness branch September 30, 2026 17:50
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