@relay-federation/bridge 0.3.10 → 0.3.11

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 (2) hide show
  1. package/cli.js +109 -0
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -11,6 +11,112 @@ import { StatusServer } from './lib/status-server.js'
11
11
 
12
12
  const command = process.argv[2]
13
13
 
14
+ /**
15
+ * Scan historical beacon registrations on startup.
16
+ * Fetches beacon address history from WhatsOnChain and parses each tx
17
+ * to populate the registeredPubkeys Set with all valid registrations.
18
+ *
19
+ * @param {Set<string>} registeredPubkeys - Set to populate with registered pubkeys
20
+ * @param {string} selfPubkey - Our own pubkey (skip self-registration)
21
+ */
22
+ async function backfillBeaconRegistry (registeredPubkeys, selfPubkey) {
23
+ const { BEACON_ADDRESS, PROTOCOL_PREFIX } = await import('@relay-federation/common/protocol')
24
+ const { extractOpReturnData, decodePayload } = await import('@relay-federation/registry/lib/cbor.js')
25
+ const { Transaction } = await import('@bsv/sdk')
26
+
27
+ console.log(` Beacon backfill: scanning ${BEACON_ADDRESS}...`)
28
+
29
+ try {
30
+ // Fetch confirmed history (registrations should be confirmed)
31
+ const historyResp = await fetch(
32
+ `https://api.whatsonchain.com/v1/bsv/main/address/${BEACON_ADDRESS}/confirmed/history`,
33
+ { signal: AbortSignal.timeout(30000) }
34
+ )
35
+ if (!historyResp.ok) {
36
+ console.log(` Beacon backfill: WoC returned ${historyResp.status}, skipping`)
37
+ return
38
+ }
39
+
40
+ const data = await historyResp.json()
41
+ const history = Array.isArray(data) ? data : (data.result || [])
42
+ if (history.length === 0) {
43
+ console.log(` Beacon backfill: no history found`)
44
+ return
45
+ }
46
+
47
+ console.log(` Beacon backfill: found ${history.length} transactions`)
48
+
49
+ // Track registrations and deregistrations to handle order correctly
50
+ // Process oldest first (history is typically newest-first from WoC)
51
+ const sortedHistory = [...history].sort((a, b) => (a.height || 0) - (b.height || 0))
52
+
53
+ let registered = 0
54
+ let deregistered = 0
55
+ let skipped = 0
56
+
57
+ for (const item of sortedHistory) {
58
+ const txid = item.tx_hash
59
+ if (!txid) continue
60
+
61
+ try {
62
+ // Rate limit to avoid WoC throttling
63
+ await new Promise(r => setTimeout(r, 100))
64
+
65
+ const txResp = await fetch(
66
+ `https://api.whatsonchain.com/v1/bsv/main/tx/${txid}/hex`,
67
+ { signal: AbortSignal.timeout(10000) }
68
+ )
69
+ if (!txResp.ok) {
70
+ skipped++
71
+ continue
72
+ }
73
+
74
+ const rawHex = await txResp.text()
75
+ const tx = Transaction.fromHex(rawHex)
76
+
77
+ // Find OP_RETURN output
78
+ const opReturnOutput = tx.outputs.find(out =>
79
+ out.satoshis === 0 && out.lockingScript.toHex().startsWith('006a')
80
+ )
81
+ if (!opReturnOutput) {
82
+ skipped++
83
+ continue
84
+ }
85
+
86
+ const { prefix, cborBytes } = extractOpReturnData(opReturnOutput.lockingScript)
87
+ if (prefix !== PROTOCOL_PREFIX) {
88
+ skipped++
89
+ continue
90
+ }
91
+
92
+ const entry = decodePayload(cborBytes)
93
+ if (!entry || !entry.pubkey) {
94
+ skipped++
95
+ continue
96
+ }
97
+
98
+ const pubHex = Buffer.from(entry.pubkey).toString('hex')
99
+ if (pubHex === selfPubkey) continue // skip self
100
+
101
+ if (entry.action === 'register') {
102
+ registeredPubkeys.add(pubHex)
103
+ registered++
104
+ } else if (entry.action === 'deregister') {
105
+ registeredPubkeys.delete(pubHex)
106
+ deregistered++
107
+ }
108
+ } catch {
109
+ skipped++
110
+ }
111
+ }
112
+
113
+ console.log(` Beacon backfill: +${registered} registered, -${deregistered} deregistered, ${skipped} skipped`)
114
+ console.log(` Registry: ${registeredPubkeys.size} trusted pubkeys after backfill`)
115
+ } catch (err) {
116
+ console.log(` Beacon backfill failed: ${err.message}`)
117
+ }
118
+ }
119
+
14
120
  switch (command) {
15
121
  case 'init':
16
122
  await cmdInit()
@@ -434,6 +540,9 @@ async function cmdStart () {
434
540
  }
435
541
  console.log(` Registry: ${registeredPubkeys.size} trusted pubkeys (self + seeds)`)
436
542
 
543
+ // ── 4a-2. Beacon backfill — scan historical registrations on startup ──
544
+ await backfillBeaconRegistry(registeredPubkeys, config.pubkeyHex)
545
+
437
546
  // ── 4b. Beacon address watcher — detect on-chain registrations ──
438
547
  const { extractOpReturnData, decodePayload, PROTOCOL_PREFIX } = await import('@relay-federation/registry/lib/cbor.js')
439
548
  const { Transaction: BsvTx } = await import('@bsv/sdk')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relay-federation/bridge",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "Bridge server — WebSocket peering, header sync, tx relay, CLI",
5
5
  "type": "module",
6
6
  "bin": {