From 7912ec363b07e1887edffded73038905a77ea3e5 Mon Sep 17 00:00:00 2001 From: Sami Date: Mon, 15 Jun 2026 00:48:17 -0700 Subject: [PATCH] explorer-api: populate per-block Addresses from verbose getblock tx outputs --- server.js | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index ccd1093..dbf857a 100644 --- a/server.js +++ b/server.js @@ -64,6 +64,42 @@ async function rpc(method, params = []) { } } +// --- Block address enrichment ------------------------------------------------- +// Attach the distinct addresses appearing in a block's transaction outputs. +// Triangles has a transparent ledger (addressindex/txindex enabled), so these +// are public. Privacy in Triangles is network-level (Tor) + encrypted SMSG +// messaging, not shielded transactions, so surfacing per-block addresses is fine. +async function attachAddresses(block) { + if (!block || !Array.isArray(block.tx)) return block; + const addrSet = new Set(); + for (const entry of block.tx.slice(0, 50)) { + // getblock(verbose) gives full tx objects here; fall back to a lookup if + // we only got a txid string. + let tx = entry; + if (typeof entry === 'string') { + try { tx = await rpc('getrawtransaction', [entry, 1]); } + catch (e) { continue; } + } + for (const vout of ((tx && tx.vout) || [])) { + const addrs = vout.scriptPubKey && vout.scriptPubKey.addresses; + if (Array.isArray(addrs)) addrs.forEach((a) => addrSet.add(a)); + } + } + block.addresses = Array.from(addrSet); + return block; +} + +// getblock + address enrichment, cached by hash (blocks are immutable). +async function getEnrichedBlock(hash) { + const key = 'eblock:' + hash; + const cached = getCached(key); + if (cached) return cached; + const block = await rpc('getblock', [hash, true]); + await attachAddresses(block); + setCache(key, block); + return block; +} + // REST endpoint: /rest/chaininfo (with cache for expensive gettxoutsetinfo) app.get('/rest/chaininfo', async (req, res) => { try { @@ -101,7 +137,7 @@ app.get('/rest/chaininfo', async (req, res) => { // REST endpoint: /rest/block/:hash app.get('/rest/block/:hash', async (req, res) => { try { - const block = await rpc('getblock', [req.params.hash, true]); // true = verbose with tx data + const block = await getEnrichedBlock(req.params.hash); res.json(block); } catch (error) { res.status(404).json({ error: 'Block not found' }); @@ -112,7 +148,7 @@ app.get('/rest/block/:hash', async (req, res) => { app.get('/rest/blockbyheight/:height', async (req, res) => { try { const hash = await rpc('getblockhash', [parseInt(req.params.height)]); - const block = await rpc('getblock', [hash, true]); // true = verbose + const block = await getEnrichedBlock(hash); res.json(block); } catch (error) { res.status(404).json({ error: 'Block not found' });