@bsv/overlay 2.2.0 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE.txt +57 -27
  2. package/README.md +102 -266
  3. package/dist/cjs/mod.d.ts +15 -0
  4. package/dist/cjs/mod.d.ts.map +1 -0
  5. package/dist/cjs/mod.js +4 -1
  6. package/dist/cjs/mod.js.map +1 -1
  7. package/dist/cjs/package.json +46 -18
  8. package/dist/cjs/src/Advertisement.d.ts +9 -0
  9. package/dist/cjs/src/Advertisement.d.ts.map +1 -0
  10. package/dist/cjs/src/Advertiser.d.ts +36 -0
  11. package/dist/cjs/src/Advertiser.d.ts.map +1 -0
  12. package/dist/cjs/src/BASM.d.ts +110 -0
  13. package/dist/cjs/src/BASM.d.ts.map +1 -0
  14. package/dist/cjs/src/BASM.js.map +1 -1
  15. package/dist/cjs/src/BASMRemote.d.ts +14 -0
  16. package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
  17. package/dist/cjs/src/BASMRemote.js.map +1 -1
  18. package/dist/cjs/src/Engine.d.ts +377 -0
  19. package/dist/cjs/src/Engine.d.ts.map +1 -0
  20. package/dist/cjs/src/Engine.js +430 -436
  21. package/dist/cjs/src/Engine.js.map +1 -1
  22. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
  23. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
  24. package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
  25. package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
  26. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
  27. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
  28. package/dist/cjs/src/GASP/OverlayGASPStorage.js +34 -31
  29. package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
  30. package/dist/cjs/src/LookupFormula.d.ts +29 -0
  31. package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
  32. package/dist/cjs/src/LookupService.d.ts +85 -0
  33. package/dist/cjs/src/LookupService.d.ts.map +1 -0
  34. package/dist/cjs/src/Output.d.ts +32 -0
  35. package/dist/cjs/src/Output.d.ts.map +1 -0
  36. package/dist/cjs/src/SafeLog.d.ts +14 -0
  37. package/dist/cjs/src/SafeLog.d.ts.map +1 -0
  38. package/dist/cjs/src/SafeLog.js +44 -0
  39. package/dist/cjs/src/SafeLog.js.map +1 -0
  40. package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
  41. package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
  42. package/dist/cjs/src/TopicManager.d.ts +35 -0
  43. package/dist/cjs/src/TopicManager.d.ts.map +1 -0
  44. package/dist/cjs/src/storage/Storage.d.ts +237 -0
  45. package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
  46. package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
  47. package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
  48. package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
  49. package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
  50. package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
  51. package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
  52. package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
  53. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
  54. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
  55. package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
  56. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
  57. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
  58. package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
  59. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
  60. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
  61. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
  62. package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  63. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
  64. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
  65. package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
  66. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
  67. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
  68. package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
  69. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
  70. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
  71. package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
  72. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
  73. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
  74. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
  75. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
  76. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
  77. package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
  78. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
  79. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
  80. package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
  81. package/dist/esm/mod.js +2 -1
  82. package/dist/esm/mod.js.map +1 -1
  83. package/dist/esm/src/BASMRemote.js.map +1 -1
  84. package/dist/esm/src/Engine.js +408 -414
  85. package/dist/esm/src/Engine.js.map +1 -1
  86. package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
  87. package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
  88. package/dist/esm/src/GASP/OverlayGASPStorage.js +23 -19
  89. package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
  90. package/dist/esm/src/SafeLog.js +40 -0
  91. package/dist/esm/src/SafeLog.js.map +1 -0
  92. package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
  93. package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js +36 -12
  94. package/dist/esm/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
  95. package/dist/types/mod.d.ts +5 -4
  96. package/dist/types/mod.d.ts.map +1 -1
  97. package/dist/types/src/BASMRemote.d.ts.map +1 -1
  98. package/dist/types/src/Engine.d.ts +15 -0
  99. package/dist/types/src/Engine.d.ts.map +1 -1
  100. package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
  101. package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  102. package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
  103. package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  104. package/dist/types/src/SafeLog.d.ts +14 -0
  105. package/dist/types/src/SafeLog.d.ts.map +1 -0
  106. package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
  107. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  108. package/dist/types/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -1
  109. package/package.json +87 -30
  110. package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
  111. package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
  112. package/dist/types/tsconfig.types.tsbuildinfo +0 -1
  113. package/docs/API.md +0 -944
  114. package/docs/BRC-136-BASM.md +0 -327
  115. package/docs/README.md +0 -16
  116. package/docs/Synchronization.md +0 -212
  117. package/docs/concepts/00-overview.md +0 -85
  118. package/docs/concepts/01-best-practices.md +0 -202
  119. package/docs/concepts/02-query-performance.md +0 -345
  120. package/docs/concepts/03-database-monitoring.md +0 -211
  121. package/docs/concepts/04-pagination-example.md +0 -186
  122. package/docs/concepts/05-recommendations-summary.md +0 -158
  123. package/docs/concepts/README.md +0 -20
  124. package/docs/examples/README.md +0 -15
  125. package/docs/examples/gs-wip.md +0 -111
  126. package/docs/internal/README.md +0 -11
  127. package/mod.ts +0 -40
  128. package/src/Advertisement.ts +0 -8
  129. package/src/Advertiser.ts +0 -41
  130. package/src/BASM.ts +0 -208
  131. package/src/BASMRemote.ts +0 -54
  132. package/src/Engine.ts +0 -2199
  133. package/src/GASP/OverlayGASPRemote.ts +0 -108
  134. package/src/GASP/OverlayGASPStorage.ts +0 -383
  135. package/src/LookupFormula.ts +0 -31
  136. package/src/LookupService.ts +0 -136
  137. package/src/Output.ts +0 -31
  138. package/src/TopicManager.ts +0 -40
  139. package/src/__tests/BASM.test.ts +0 -44
  140. package/src/__tests/BASMChain.test.ts +0 -290
  141. package/src/__tests/Engine.test.ts +0 -1345
  142. package/src/__tests/OverlayGASPRemote.test.ts +0 -135
  143. package/src/__tests/OverlayGASPStorage.test.ts +0 -171
  144. package/src/storage/Storage.ts +0 -260
  145. package/src/storage/knex/KnexStorage.ts +0 -651
  146. package/src/storage/knex/all-migrations.ts +0 -32
  147. package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
  148. package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
  149. package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -40
  150. package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
  151. package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
  152. package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
  153. package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
  154. package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
  155. package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +0 -123
@@ -1,327 +0,0 @@
1
- # BRC-136 BASM Synchronization & Admin/Janitor Removal
2
-
3
- This document explains how the BRC-136 Block-Anchored synchronization layer
4
- ([spec](https://bsv.brc.dev/overlays/0136)) is implemented in this engine, and —
5
- critically — **how it interacts with admin and janitor removal of outputs from a
6
- topic**. The two systems are deliberately decoupled. Read the final section
7
- ("How BASM and removal interact") before operating a node that runs the janitor
8
- or removes tokens via the admin API.
9
-
10
- ## What BRC-136 gives you
11
-
12
- GASP ([Synchronization.md](./Synchronization.md)) replicates transaction *data*
13
- between peers. BRC-136 sits alongside it and answers a different question:
14
-
15
- > Do two overlays **agree about which transactions were admitted to a topic** at
16
- > every block height, from the topic's genesis to the chain tip?
17
-
18
- It does this with a per-topic **Topic Anchor Chain (TAC)** — a cumulative hash
19
- that commits to every block's admitted-transaction set. If two nodes report the
20
- same `TAC(topic, H)`, they provably agree on every admitted transaction for that
21
- topic from genesis through height `H`. A single round trip detects agreement; a
22
- binary search over heights localizes any divergence in `O(log H)` rounds.
23
-
24
- ### Core data structures
25
-
26
- | Concept | Type | Source of truth |
27
- |---|---|---|
28
- | Admitted set at a block | `AdmittedTxRef[]` | `applied_transactions` table |
29
- | Block anchor | `TopicBlockAnchor` | `topic_block_anchors` table |
30
- | Cumulative chain hash | `tac` field on the anchor | derived |
31
-
32
- - **`basmRoot`** — Merkle root over the admitted txids at one block height, in
33
- canonical block order. Empty set → 32 zero bytes; single txid → the txid
34
- itself (unhashed); multiple → `SHA256d` binary tree with Bitcoin odd-leaf
35
- duplication. See `computeBasmRoot` in `src/BASM.ts`.
36
- - **`tac`** — `SHA256d(prevTac ‖ blockHash ‖ basmRoot)`, all inputs in internal
37
- byte order. See `computeTac` in `src/BASM.ts`.
38
-
39
- ### Gap-free chains
40
-
41
- The TAC must advance on **every** block — including blocks with no admitted
42
- transactions — or peers could not distinguish "no transactions here" from "I
43
- haven't synced this height." The engine keeps chains contiguous:
44
-
45
- - `advanceTopicAnchorChains(toHeight)` extends each topic forward with empty
46
- anchors (zero root, zero count) up to `toHeight`.
47
- - `rebuildTopicAnchorChain(...)` / `recomputeTopicBlockAnchor(...)` rebuild a
48
- contiguous slice after an admission (including out-of-order proofs), filling
49
- any missing heights with empty anchors rather than leaving a hole.
50
-
51
- A chain only *starts* at the topic's first admitted height (its genesis).
52
- Covered by `src/__tests/BASMChain.test.ts`.
53
-
54
- ## The admitted set is a historical fact, not current UTXO state
55
-
56
- This is the key design decision, and the source of the assurance this layer
57
- provides.
58
-
59
- The admitted set for `(topic, height)` is read by
60
- `Storage.findAdmittedTransactionsForBlock`, which queries:
61
-
62
- ```
63
- applied_transactions WHERE topic = ? AND blockHeight = ? AND proven = true AND blockIndex IS NOT NULL
64
- ```
65
-
66
- It is **append-only for proven transactions**. A row lands here when a
67
- transaction is admitted to the topic and has a merkle proof
68
- (`buildAppliedTransactionRecord` sets `proven: true` only when proof metadata is
69
- present). From that point the row — and therefore the `basmRoot` and `tac` that
70
- commit to it — is an immutable record of "this overlay received and admitted
71
- this txid at this block height."
72
-
73
- It is **not** derived from current unspent outputs. Spending an output, pruning
74
- its history, or removing it from lookup does not change the admitted set.
75
-
76
- ## The lookup set is separate, mutable, and policy-driven
77
-
78
- What an overlay *serves* in lookup responses is a different set, in different
79
- storage:
80
-
81
- - The engine `outputs` table, filtered to `spent: false`.
82
- - The lookup service's own index (e.g. the Mongo `shipRecords` / `slapRecords`
83
- collections for the discovery services).
84
-
85
- This set is mutable by design. Outputs get marked spent, pruned from history,
86
- removed by an admin, or deleted by the janitor.
87
-
88
- ## Removal paths — exactly what each one touches
89
-
90
- | Path | Code | Touches lookup set | Touches admitted set (`applied_transactions`) |
91
- |---|---|---|---|
92
- | Spend / history prune | `Engine.deleteUTXODeep` → `deleteOutput` | yes | **no** |
93
- | Admin remove-token | `/admin/remove-token` → `evictFromServices` → `LookupService.outputEvicted` | yes (lookup index) | **no** |
94
- | Janitor (dead SHIP/SLAP host) | `JanitorService.handleUnhealthyOutput` → `deleteOne` + ban | yes (lookup index) | **no** |
95
- | Ban enforcement | `BanAwareLookupWrapper.outputAdmittedByTopic` | blocks re-entry into lookup index on GASP re-sync | **no** |
96
- | Unproven eviction | `Engine.evictUnprovenTransactions` → `deleteAppliedTransaction` | yes | yes — **but only `proven: false` rows** |
97
-
98
- Notes:
99
-
100
- - **`deleteUTXODeep` never deletes the applied-transaction record.** It calls
101
- `deleteOutput` only. The admission proof survives a normal spend or prune.
102
- - **Admin and janitor removal operate on the lookup layer only.** Admin
103
- `evictFromServices` calls `LookupService.outputEvicted`; the janitor deletes
104
- from its own Mongo collection and writes to the ban list. Neither calls
105
- `deleteAppliedTransaction`, `deleteOutput` on proven engine state, or anything
106
- that mutates `topic_block_anchors`.
107
- - **The ban list is a lookup-layer filter.** `BanAwareLookupWrapper` blocks
108
- banned outpoints/domains at `outputAdmittedByTopic`, preventing a removed token
109
- from re-entering the *lookup index* when GASP re-syncs it from a peer. It does
110
- not — and must not — block the BRC-136 admitted record.
111
- - **`evictUnprovenTransactions` is the only path that deletes an
112
- applied-transaction row**, and it filters to `proven: false`. Such rows never
113
- appear in any anchor (the anchor query requires `proven: true`), so evicting
114
- them cannot change any `basmRoot` or `tac`.
115
-
116
- ## How BASM and removal interact
117
-
118
- This is the question operators most need answered.
119
-
120
- ### You can always prove you received and processed a token
121
-
122
- Even after the janitor deletes a dead host's advertisement, or an admin removes a
123
- malicious output via `/admin/remove-token` (with or without a ban), the overlay
124
- **retains full proof that it received and admitted that token**:
125
-
126
- - the `applied_transactions` row (`proven: true`, with `blockHeight`,
127
- `blockIndex`, `blockHash`, `merkleRoot`),
128
- - the `topic_block_anchors` row whose `basmRoot` commits to that txid, and
129
- - the ability to serve a compound merkle path for it via the BRC-136 endpoints.
130
-
131
- Removal is a **lookup-serving policy decision**. It changes what the node returns
132
- to lookup queries. It does not, and cannot, rewrite the historical admitted set
133
- or the TAC.
134
-
135
- ### TAC agreement means agreement on *admission*, not on *lookup results*
136
-
137
- This is the most important operational caveat:
138
-
139
- > **A matching TAC proves two overlays agree on which transactions were admitted.
140
- > It does NOT prove they will return identical lookup results.**
141
-
142
- Bans and removals are **per-node policy and are not synchronized between peers**.
143
- Two honest overlays running the same topic manager can therefore:
144
-
145
- - report the **same** `TAC(topic, H)` (identical admitted sets), while
146
- - returning **different** lookup results — because one node banned a malicious
147
- output that the other still serves.
148
-
149
- This is correct and intended. BRC-136 anchors *receipt and admission*, which is
150
- an objective, deterministic function of chain state and the topic manager. It
151
- intentionally does not anchor each node's local moderation policy.
152
-
153
- ### Consequence: divergence diagnosis
154
-
155
- - **TACs match** → the two nodes admitted exactly the same transactions. Any
156
- difference in lookup output is explained by local removal/ban policy, not by a
157
- sync problem.
158
- - **TACs diverge** → the nodes genuinely disagree about admission at some height
159
- (e.g. different topic-manager versions, a missed transaction, or a reorg
160
- handled differently). This is a real sync issue; the binary-search localizes
161
- the height.
162
-
163
- Removing a token from lookup will **not** cause TAC divergence. If you ever see
164
- TAC divergence after only running the janitor/admin removal, that is a bug —
165
- something is incorrectly deleting proven `applied_transactions` rows or mutating
166
- anchors.
167
-
168
- ### Caveat for the GASP validation guidance
169
-
170
- [Synchronization.md](./Synchronization.md) suggests validating sync by comparing
171
- per-topic SQL row counts across nodes. Once the janitor or admin removal is
172
- active, **lookup-set counts can legitimately differ between nodes even when their
173
- TACs match**. To validate BRC-136 agreement, compare `TAC(topic, tip)` between
174
- peers — not lookup row counts.
175
-
176
- ## Reorg handling
177
-
178
- A blockchain reorganization can orphan a block whose transactions were already
179
- admitted and anchored. Since anchors and the admitted set are keyed by
180
- `(topic, blockHeight, blockHash)`, an orphaned block would otherwise leave stale
181
- `applied_transactions` rows and an anchor whose `basmRoot`/`tac` commit to a
182
- block hash that is no longer canonical — permanently diverging the TAC from peers
183
- that observed the reorg. The engine reconciles this automatically.
184
-
185
- ### Chaintracks is the reorg authority
186
-
187
- Reorg detection is **not** reinvented here. The production chain tracker should
188
- be a go-chaintracks compatible service. Arcade exposes go-chaintracks under
189
- `/chaintracks/v2`, and a standalone go-chaintracks deployment may expose the same
190
- API at `/v2`:
191
-
192
- - **Arcade-mounted Chaintracks:** `GET /chaintracks/v2/reorg/stream`
193
- - **Standalone go-chaintracks:** `GET /v2/reorg/stream`
194
-
195
- The stream emits `data: <JSON>\n\n` frames (no `event:`/`id:` lines;
196
- `: keepalive` comments between events). Each frame is a `ReorgEvent`:
197
-
198
- ```jsonc
199
- {
200
- "orphanedHashes": ["<blockHash>", ...], // blocks removed from the active chain
201
- "commonAncestor": { "height": 100, "hash": "..." },
202
- "newTip": { "height": 103, "hash": "..." },
203
- "depth": 3
204
- }
205
- ```
206
-
207
- - The same go-chaintracks-compatible service answers the merkle-root checks
208
- (`isValidRootForHeight`) the engine already trusts for SPV verification.
209
-
210
- `packages/overlays/overlay-express/src/ReorgStream.ts` consumes this stream and
211
- maps each event to the engine: `orphanedHashes` → blocks to reconcile,
212
- `commonAncestor.height + 1` → rebuild floor, `newTip.height` → rebuild ceiling.
213
- Block hashes are lower-cased to match stored display-hex hashes (go-sdk
214
- `chainhash.Hash` marshals as reversed/display hex).
215
-
216
- > **No replay on this stream.** It carries no event ids, so a reorg that fires
217
- > while the SSE client is disconnected is lost. Every (re)connect therefore runs a
218
- > catch-up revalidation sweep (below), and the periodic block poll runs the same
219
- > sweep as a standing fallback for chain trackers that expose no reorg stream.
220
-
221
- ### What the engine does on a reorg — `Engine.handleReorg`
222
-
223
- 1. **Demote orphaned admissions.** For each orphaned block hash, every **proven**
224
- `applied_transactions` row anchored to it is demoted to unproven
225
- (`demoteAppliedTransactionToUnproven`): block height/hash/index/merkleRoot are
226
- cleared and `proven` is set to `false`, **but `firstSeenHeight` is kept**. The
227
- transaction immediately leaves the admitted set.
228
- 2. **Rebuild affected anchor chains.** Every topic whose chain intersects the
229
- reorged height range is rebuilt over the **canonical** block hashes (the rebuild
230
- forces header re-resolution rather than reusing the stale stored hash). Topics
231
- with no demoted transaction are rebuilt too, because the canonical block hash
232
- for those heights changed and the `tac` must re-chain over it.
233
-
234
- `handleReorg` is **idempotent**: a clean window demotes nothing and reproduces an
235
- identical TAC, so it is safe to run on every reorg event, SSE reconnect, and poll.
236
-
237
- ### Fallback / catch-up — `Engine.revalidateRecentAnchors(depth = 3)`
238
-
239
- Scans proven `applied_transactions` in `[tip - depth + 1, tip]`. Any row whose
240
- proof root no longer validates (`isValidRootForHeight`) or whose block hash
241
- diverges from the canonical header is treated as orphaned and fed into
242
- `handleReorg`. This is the reorg path for chain trackers without a reorg stream,
243
- and the catch-up step on every SSE reconnect. Default depth is 3 blocks
244
- (`reorgScanDepth`, configurable via `configureReorgStream`).
245
-
246
- ### Interaction with admin/janitor removal
247
-
248
- Reorg recovery and lookup-layer removal are independent and do not interfere:
249
-
250
- - Removal/bans touch only the lookup layer; they never demote `applied_transactions`.
251
- - Reorg demotion touches only the admitted set (proven → unproven) via the chain
252
- tracker; it never consults the ban list or lookup index.
253
-
254
- A demoted transaction follows the engine's unproven lifecycle. The preferred
255
- maintenance path is refresh-before-evict:
256
-
257
- 1. If the transaction is re-mined and a provider calls `/arc-ingest` with a
258
- proof, `handleNewMerkleProof` re-proves it at its new height and the anchor
259
- includes it again.
260
- 2. If no callback arrives, `refreshUnprovenTransactionProofs` asks configured
261
- proof providers such as Arcade for a fresh proof.
262
- 3. `maintainUnprovenTransactions` refreshes proofs first, then calls
263
- `evictUnprovenTransactions` for rows that are still unproven past the
264
- configured threshold.
265
-
266
- The "we received it" record survives until a proof, a terminal provider
267
- invalidation, or age-based eviction resolves it.
268
-
269
- ### Provider invalidation and double spends
270
-
271
- Provider callbacks can also report terminal rejection. When `/arc-ingest`
272
- classifies a callback as double spend or another terminal invalid outcome, the
273
- Express layer evicts the applied transaction immediately through
274
- `Engine.evictAppliedTransaction`. This removes the transaction from the admitted
275
- set and notifies lookup services through their `outputEvicted` path. It is more
276
- important to stop serving rejected data than to wait for the normal unproven
277
- eviction threshold.
278
-
279
- ### Configuration
280
-
281
- ```ts
282
- server.configureChaintracks('https://arcade.example', {
283
- apiPrefix: '/chaintracks/v2',
284
- reorgStream: true,
285
- scanDepth: 3
286
- })
287
-
288
- server.configureEnableBASMSync(true)
289
- server.configureUnprovenMaintenance({
290
- thresholdBlocks: 144,
291
- intervalMs: 60 * 60 * 1000
292
- })
293
- ```
294
-
295
- - `apiPrefix` — `/chaintracks/v2` for Arcade-mounted Chaintracks; `/v2` for many
296
- standalone go-chaintracks deployments.
297
- - `reorgStream` — when enabled, the SSE adapter reconciles reorgs in real time.
298
- - `scanDepth` — sweep depth from tip (default `3`).
299
- - `thresholdBlocks` — how old an unproven row must be before maintenance tries
300
- proof refresh and eviction.
301
- - The block poll (`basmBlockPollIntervalMs`) runs the sweep as a fallback even
302
- when no stream is configured.
303
-
304
- ## Verification checklist for operators
305
-
306
- 1. After removing a token, confirm its `applied_transactions` row still exists
307
- (`proven: true`) and the `topic_block_anchors` row for its height is unchanged.
308
- 2. Confirm `TAC(topic, tip)` is unchanged by the removal.
309
- 3. Confirm the token no longer appears in lookup responses (and, if banned, does
310
- not reappear after a GASP re-sync).
311
-
312
- If all three hold, removal is behaving correctly: the node no longer serves the
313
- token, yet can still prove it was received and admitted.
314
-
315
- ### Reorg verification
316
-
317
- 4. After a reorg orphans an admitted transaction's block, confirm its
318
- `applied_transactions` row is demoted (`proven: false`, block fields cleared,
319
- `firstSeenHeight` retained) and the affected `topic_block_anchors` rows now
320
- carry the canonical block hashes with a recomputed `tac`.
321
- 5. Confirm `TAC(topic, tip)` reconverges with peers that observed the same reorg.
322
- 6. If the orphaned transaction is re-mined, confirm `/arc-ingest` or
323
- `refreshUnprovenTransactionProofs` re-proves it and the anchor re-includes it.
324
- Otherwise confirm `maintainUnprovenTransactions` eventually evicts it as
325
- unproven.
326
- 7. If a provider reports a double spend, confirm the applied transaction is
327
- evicted immediately and lookup services no longer return it.
package/docs/README.md DELETED
@@ -1,16 +0,0 @@
1
- # Overlay Services Engine Documentation
2
-
3
- The documentation is split into various pages, each covering a set of related functionality. The pages are as follows:
4
-
5
- ## Main Documentation
6
-
7
- - [API](./API.md) — How to interact with the Overlay Services Engine
8
- - [Synchronization](./Synchronization.md) — GASP peer synchronization
9
- - [BRC-136 BASM](./BRC-136-BASM.md) — Block-anchored sync, and how it interacts with admin/janitor removal
10
- - [Concepts](./concepts/README.md) — High-level concepts
11
- - [Examples](./examples/README.md) — Guides, tutorials and examples of using the Engine
12
- - [Internal](./internal/README.md) — References for working with internal system components
13
-
14
- ---
15
-
16
- [🏠 Home](./README.md) | [📚 API](./API.md) | [💡 Concepts](./concepts/README.md) | [📖 Examples](./examples/README.md) | [⚙️ Internal](./internal/README.md)
@@ -1,212 +0,0 @@
1
- ## About
2
-
3
- **GASP (Graph Aware Sync Protocol)** is the synchronization protocol overlay nodes use to replicate overlay-relevant transaction data between peers in a way that is **verifiable**, **complete**, and **bandwidth-efficient**. Instead of just sending raw TXIDs or copying a flat UTXO list, GASP reconciles **transaction graphs** by exchanging transactions/outputs with proof material and then **recursively requesting any missing input transactions** needed to validate what was received.
4
-
5
- At a high level, GASP is built around:
6
-
7
- - **Legitimacy:** Nodes only finalize data they can validate by anchoring it back to the blockchain (e.g., using merkle proofs / SPV-style verification).
8
- - **Completeness:** If a transaction depends on other transactions, the protocol recursively fetches all required inputs so the end result isn’t partial or broken.
9
- - **Efficiency:** Nodes only sync what they don’t have, reducing duplicates and bandwidth.
10
- - **Redundancy / availability:** Multiple nodes can converge on the same overlay view over time, improving uptime and reducing single-host dependency.
11
-
12
- ## Importance
13
-
14
- Overlays are useful because they let you track only the topic you care about. But once an overlay is distributed (multiple nodes serving the same topic), nodes need a path to **catch up** and **stay consistent** over time.
15
-
16
- GASP matters because it gives you:
17
-
18
- - **Redundancy and uptime:** If one overlay host goes offline, other synced hosts can still serve the same topic, reducing single points of failure.
19
- - **Fast bootstrap:** A fresh node can synchronize overlay state from peers instead of re-ingesting all previous transactions from scratch.
20
- - **Lower-trust sync (for correctness):** Peers exchange transactions with proofs and recursively prove inputs, so received data can be validated instead of trusted blindly. *(This doesn’t replace your auth policy; it reduces trust needed for correctness.)*
21
- - **Scaling the ecosystem:** As more apps and topics exist, GASP enables horizontal growth (more hosts for the same topic) instead of centralizing into one massive indexer.
22
-
23
- ## How GASP Works
24
-
25
- One party initiates sync by summarizing what it currently has, then the peer responds with what’s missing, and both sides iterate until they converge.
26
-
27
- A typical flow looks like:
28
-
29
- 1) **Summarize local state**
30
- The initiator summarizes known spendable outpoints (commonly via a Bloom filter over TXID+VOUT).
31
-
32
- 2) **Responder sends “missing” inventory**
33
- The responder identifies items the initiator likely doesn’t have and returns inventory entries containing the output/transaction plus proof material (and optional metadata).
34
-
35
- 3) **Recursive completion**
36
- If the initiator is missing any input transactions required to validate what it received, it requests them. This repeats recursively until the needed graph is complete.
37
-
38
- 4) **Verification + finalize**
39
- Anything that can’t be validated/anchored is ignored rather than partially imported.
40
-
41
- ## Usage
42
-
43
- There are two main ways to activate and run GASP sync:
44
-
45
- - **With CARS (cloud / managed):** Use CARS menus to enable sync options and deploy.
46
- - **Without CARS (direct / self-managed):** Run your own Overlay Express server and trigger sync using the admin endpoints.
47
-
48
- > **Important:** For GASP sync to work, *both* parties must have GASP enabled and must be configured to sync the same topic(s).
49
-
50
- ---
51
-
52
- ### With CARS
53
-
54
- > If your project has a package.json script called `cars`, you can use `npm run cars`. Otherwise you typically run `cars` directly.
55
-
56
- ### Using CARS (high level)
57
-
58
- - `npm run cars` *(or `cars`)*
59
- - Manage Projects
60
- - Edit Advanced Engine Config
61
- - Choose correct CARS config
62
- - Toggle `gaspSync`
63
- - Edit `syncConfiguration`
64
- - Add your topic manager name(s) (e.g. `tm_example`)
65
- - Back → Done
66
- - Back to main menu
67
- - Build Artifact → Auto-create new release and upload latest artifact now
68
-
69
- ### Side notes
70
-
71
- - Simply toggling `gaspSync` is not enough if your node is not “interested” in syncing your topic.
72
- You must also ensure your topic manager name is included in `syncConfiguration` so the engine knows which topic(s) to sync.
73
-
74
- ---
75
-
76
- ### Without CARS
77
-
78
- Without CARS, you run your own Overlay Express server and configure the overlay engine so it:
79
- 1) **discovers peers / publishes ads** (via an advertiser), and
80
- 2) knows **which topic managers to sync** (via `syncConfiguration`), and
81
- 3) has **GASP enabled**.
82
-
83
- ## Minimal engine config you must have
84
-
85
- The most important lines are:
86
-
87
- ```ts
88
- server.configureEngineParams({
89
- advertiser: wa,
90
- syncConfiguration: {
91
- 'tm_plite': 'SHIP',
92
- 'tm_blockbeta': 'SHIP',
93
- },
94
- logTime: false,
95
- logPrefix: '[OVERLAY] ',
96
- throwOnBroadcastFailure: false,
97
- suppressDefaultSyncAdvertisements: true,
98
- })
99
- // ^ tells the engine which topic(s) you want to sync, and the discovery mechanism to use (e.g. SHIP)
100
-
101
- server.configureEnableGASPSync(true)
102
- // ^ enables GASP sync in Overlay Express
103
- ```
104
-
105
- ## **Overlay Express Setup Example**
106
-
107
- ```ts
108
- import { WalletAdvertiser } from '@bsv/overlay-discovery-services'
109
- import OverlayExpress from '@bsv/overlay-express'
110
- import { config } from 'dotenv'
111
- import packageJson from '../package.json'
112
- import PollrTopicManager from './services/pollroverlay/PollrTopicManager'
113
- import PollrLookupServiceFactory from './services/pollroverlay/PollrLookupServiceFactory'
114
- import ForumTopicManager from './services/blockitoverlay/ForumTopicManager'
115
- import ForumLookupService from './services/blockitoverlay/ForumLookupServiceFactory'
116
-
117
- config()
118
-
119
- const main = async () => {
120
- const server = new OverlayExpress(
121
- process.env.NODE_NAME!,
122
- process.env.SERVER_PRIVATE_KEY!,
123
- process.env.HOSTING_URL!,
124
- process.env.ADMIN_TOKEN! // your chosen admin token to use the admin API
125
- )
126
-
127
- const wa = new WalletAdvertiser(
128
- process.env.NETWORK! as 'main' | 'test',
129
- process.env.SERVER_PRIVATE_KEY!,
130
- process.env.WALLET_STORAGE_URL!,
131
- process.env.HOSTING_URL!
132
- )
133
-
134
- await wa.init()
135
-
136
- server.configureEngineParams({
137
- advertiser: wa,
138
- syncConfiguration: {
139
- 'tm_plite': 'SHIP',
140
- 'tm_blockbeta': 'SHIP',
141
- },
142
- logTime: false,
143
- logPrefix: '[OVERLAY] ',
144
- throwOnBroadcastFailure: false,
145
- suppressDefaultSyncAdvertisements: true,
146
- })
147
-
148
- server.configureArcApiKey(process.env.ARC_API_KEY!)
149
- server.configurePort(8080)
150
-
151
- await server.configureKnex(process.env.KNEX_URL!)
152
- await server.configureMongo(process.env.MONGO_URL!)
153
-
154
- server.configureTopicManager('tm_plite', new PollrTopicManager())
155
- server.configureLookupServiceWithMongo('ls_plite', PollrLookupServiceFactory)
156
-
157
- server.configureTopicManager('tm_blockbeta', new ForumTopicManager())
158
- server.configureLookupServiceWithMongo('ls_blockbeta', ForumLookupService)
159
-
160
- server.configureEnableGASPSync(true)
161
-
162
- await server.configureEngine()
163
-
164
- server.app.get('/version', (req, res) => res.json(packageJson))
165
-
166
- await server.start()
167
- }
168
-
169
- main()
170
- ```
171
-
172
- # **Validation**
173
-
174
- A practical way to validate sync is to compare per-topic records in your SQL database before and after sync.
175
-
176
- Example (adapt the table/query to your schema):
177
- ### **Before sync**
178
-
179
- ```
180
- +-------------+----+
181
- | topic | n |
182
- +-------------+----+
183
- | tm_plite | 2 |
184
- | tm_blockbeta| 1 |
185
- | tm_ship | 2 |
186
- | tm_slap | 2 |
187
- +-------------+----+
188
- ```
189
-
190
- ### **After sync**
191
-
192
- ```
193
- +-------------+------+
194
- | topic | n |
195
- +-------------+------+
196
- | tm_plite | 10 |
197
- | tm_blockbeta| 17 |
198
- | tm_ship | 2177 |
199
- | tm_slap | 2 |
200
- +-------------+------+
201
- ```
202
-
203
- **What you’re looking for:** the topic(s) you care about (e.g. tm_plite, tm_blockbeta) should move toward the same counts/data across nodes after sync, and your lookup results for identical queries should converge as well.
204
-
205
- > **Caveat (BRC-136 / janitor / admin removal):** Row-count comparison is only a
206
- > valid convergence check on nodes that do **not** remove outputs locally. Once
207
- > the janitor or `/admin/remove-token` is active, lookup-set counts can
208
- > legitimately differ between peers even when they fully agree on what was
209
- > admitted, because bans/removals are per-node policy and are not synchronized.
210
- > To verify that two nodes agree on which transactions were admitted to a topic,
211
- > compare their per-topic Topic Anchor Chain (`TAC`) at the tip rather than
212
- > lookup row counts. See [BRC-136 BASM](./BRC-136-BASM.md).
@@ -1,85 +0,0 @@
1
- # Overlay Use and Optimization in the BSV Ecosystem
2
-
3
- [🏠 Home](../README.md) | [📚 API](../API.md) | [💡 Concepts](./README.md) | [📖 Examples](../examples/README.md) | [⚙️ Internal](../internal/README.md)
4
-
5
- **Navigation:** [Overview](./00-overview.md) | [Best Practices](./01-best-practices.md) | [Query Performance](./02-query-performance.md) | [Database Monitoring](./03-database-monitoring.md) | [Pagination Example](./04-pagination-example.md) | [Recommendations Summary](./05-recommendations-summary.md)
6
-
7
- ---
8
-
9
- ## Introduction
10
-
11
- Overlays are distributed application layers built on top of Bitcoin SV (BSV). They allow applications to publish structured data and then query it efficiently through **Lookup Services** and **Topic Managers**. This pattern enables developers to build scalable, high‑performance, privacy‑aware applications without needing to index or scan the entire blockchain themselves.
12
-
13
- Applications such as **Convo Messenger**, **Tempo**, and **MetaMarket** use overlays to organize messages, content references, metadata, and other application‑specific data in a predictable and queryable way.
14
-
15
- This documentation is written for **developers who want to build overlays for their own applications**. It explains how overlays work, what must be stored, how to structure queries, and how to keep overlay implementations efficient and maintainable.
16
-
17
- ---
18
-
19
- ## Purpose of This Documentation
20
-
21
- Developers implementing overlays commonly need guidance on:
22
-
23
- * What data a Lookup Service should store
24
- * How to design efficient queries for retrieving overlay data
25
- * How to index and structure Lookup Service databases
26
- * How to keep overlays healthy and performant as they scale
27
-
28
- This documentation focuses on **practical guidance** that helps developers design overlays correctly and avoid common pitfalls. Examples from real applications—such as Convo Messenger—are used when relevant.
29
-
30
- This documentation does **not** cover internal BSVA system design, Overlay Express internals, or future platform development proposals.
31
-
32
- ---
33
-
34
- ## What This Series Covers
35
-
36
- Each document in this series focuses on a key aspect of overlay implementation.
37
-
38
- ### 1. Best Practices for Overlay Design
39
-
40
- * What Lookup Services are required to store
41
- * What additional metadata overlays often include
42
- * What kinds of data should *not* be stored (e.g., large payloads)
43
- * How to use UHRP references for external content
44
-
45
- ### 2. Query Optimization and Performance
46
-
47
- * How to design efficient queries
48
- * How to choose indexes for common access patterns
49
- * How to avoid unbounded or slow query patterns
50
-
51
- ### 3. Monitoring and Debugging Overlays
52
-
53
- * How to monitor your own overlay deployment
54
- * How to inspect stored data safely
55
- * How to track ingestion issues from your Topic Manager
56
-
57
- ### 4. Pagination and Practical Examples
58
-
59
- * Why pagination is critical for performance
60
- * How to implement pagination in Lookup Services and clients
61
- * Examples drawn from live overlay behavior (e.g., Convo Messenger)
62
-
63
- ### 5. Summary of Recommendations
64
-
65
- * A concise reference of best practices
66
- * Quick reminders for developers building overlays
67
-
68
- ---
69
-
70
- ## Deliverables
71
-
72
- This documentation includes:
73
-
74
- * **Markdown files** that explain how to build, index, and monitor overlay implementations
75
- * **Examples** demonstrating common patterns such as pagination and indexed lookup queries
76
- * **Guidelines** that help ensure overlays remain scalable and consistent across different applications
77
-
78
- ---
79
-
80
- ## Next Steps
81
-
82
- 1. Read the general best practices for overlay storage (`01-best-practices.md`).
83
- 2. Review query performance and indexing fundamentals (`02-query-performance.md`).
84
- 3. Explore examples using Convo Messenger (`04-pagination-example.md`).
85
- 4. Refer to the summary document for quick reminders or cross-references.