Files
psf-memo-db/util/profiles/backfill_seen.js
T

115 lines
3.0 KiB
JavaScript

/*
Utility: backfill profile `seen` timestamps from on-chain block header times.
Reads each profile's txid, looks up blockHeight in ptxs, fetches block header
time via BCH RPC, and writes seen = block.time * 1000 (Unix ms).
Run from the psf-memo-db repo root (memo-db must be stopped to avoid lock conflicts):
node util/profiles/backfill_seen.js
Environment (same as psf-memo-indexer):
RPC_IP, RPC_PORT, RPC_USER, RPC_PASS
*/
import 'dotenv/config'
import level from 'level'
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const rpcIp = process.env.RPC_IP || '172.17.0.1'
const rpcPort = process.env.RPC_PORT || '8332'
const rpcUser = process.env.RPC_USER || 'bitcoin'
const rpcPass = process.env.RPC_PASS || 'password'
const profilesDb = level(`${__dirname}/../../leveldb/current/profiles`, {
valueEncoding: 'json'
})
const ptxsDb = level(`${__dirname}/../../leveldb/current/ptxs`, {
valueEncoding: 'json'
})
const blockTimeCache = new Map()
async function rpcCall (method, params = []) {
const response = await fetch(`http://${rpcIp}:${rpcPort}/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${Buffer.from(`${rpcUser}:${rpcPass}`).toString('base64')}`
},
body: JSON.stringify({
jsonrpc: '1.0',
id: method,
method,
params
})
})
const body = await response.json()
if (body.error) {
throw new Error(body.error.message || `RPC ${method} failed`)
}
return body.result
}
async function getBlockTimeMs (blockHeight) {
if (blockTimeCache.has(blockHeight)) {
return blockTimeCache.get(blockHeight)
}
const hash = await rpcCall('getblockhash', [blockHeight])
const header = await rpcCall('getblockheader', [hash, true])
const seen = header.time * 1000
blockTimeCache.set(blockHeight, seen)
return seen
}
async function backfillProfiles () {
let updated = 0
let skipped = 0
let errors = 0
try {
for await (const [addr, profile] of profilesDb.iterator()) {
try {
if (!profile.txid) {
skipped++
continue
}
const ptx = await ptxsDb.get(profile.txid)
if (!ptx || ptx.blockHeight === undefined) {
console.warn(`No ptx blockHeight for ${addr} txid ${profile.txid}`)
skipped++
continue
}
const seen = await getBlockTimeMs(ptx.blockHeight)
if (profile.seen === seen) {
skipped++
continue
}
await profilesDb.put(addr, { ...profile, seen })
updated++
console.log(`${addr}: seen ${profile.seen} -> ${seen} (block ${ptx.blockHeight})`)
} catch (err) {
errors++
console.error(`Error updating ${addr}:`, err.message)
}
}
console.log(`\nBackfill complete. Updated: ${updated}, skipped: ${skipped}, errors: ${errors}`)
} finally {
await profilesDb.close()
await ptxsDb.close()
}
}
backfillProfiles().catch((err) => {
console.error('Backfill failed:', err.message)
process.exit(1)
})