@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.
- package/cli.js +109 -0
- 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')
|