Canonical indexes and recoverable market observation

This commit is contained in:
2026-10-20 14:35:45 -04:00
parent fc65e497a8
commit e48081f041
11 changed files with 583 additions and 54 deletions
+17 -13
View File
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
import { mkdir, open, unlink } from 'node:fs/promises';
import { z } from 'zod';
import { NETWORK, CONSENSUS } from '../../shared/testnet.ts';
import { testnetOwnerOf, transactionCardIds } from '../../shared/testnet.ts';
import { transactionCardIds } from '../../shared/testnet.ts';
import { CHAIN_ID, blockSchema, tradeSchema, transferSchema, transactionSchema } from './consensus.ts';
import { RandomX } from './randomx.ts';
import { Ledger } from './ledger.ts';
@@ -56,11 +56,10 @@ let custody: CustodyMarket | undefined;
try {
if (custodyConfig === 'sandbox') custody = await CustodyMarket.open(dataDir, { feeBps: paymentConfig.feeBps,
card: (hash, owner, sequence) => {
const s = ledger.snapshot(), b = s.blocks.find(block => block.tokenId === hash);
if (!b || testnetOwnerOf(s, hash) !== owner || s.blocks.length + 1 - b.height < CONSENSUS.coinbaseMaturity ||
s.transfers.filter(tx => transactionCardIds(tx).includes(hash)).length !== sequence ||
s.pendingTransfers.some(tx => transactionCardIds(tx).includes(hash))) return undefined;
return { title: b.article.title };
const nft = ledger.nft(hash);
if (!nft || nft.owner !== owner || nft.confirmations < CONSENSUS.coinbaseMaturity || nft.sequence !== sequence ||
ledger.pendingTransactions().some(tx => transactionCardIds(tx).includes(hash))) return undefined;
return { title: nft.block.article.title };
} });
} catch(e) {market.close();peers.close();ledger.close();engine.close();await lock.close();await unlink(lockPath);throw e;}
const app = express();
@@ -138,6 +137,9 @@ app.get('/api/decks/:address', rateLimit({windowMs:60000,limit:60,standardHeader
res.status(400).json({error:'Invalid deck page'});return;
}
res.setHeader('Cache-Control','no-store');
if(req.query.tip!==undefined && req.query.tip!==ledger.summary().tip){
res.status(409).json({error:'Canonical tip changed; restart deck pagination.'});return;
}
res.json(ledger.deck(req.params.address,offset,limit));
});
// Optional live data is intentionally not used by genesis, block validation or NFT ownership.
@@ -156,13 +158,15 @@ app.get('/p2p/blocks',(req,res)=>{
});
app.get('/p2p/mempool',(_req,res)=>res.json({transactions:ledger.pendingTransactions()}));
app.get('/api/nfts/:id', (req,res)=>{
const s=ledger.snapshot();const block=s.blocks.find(b=>b.tokenId===req.params.id);
if(!block){res.status(404).json({error:'NFT not found on the canonical chain'});return;}
const owner=testnetOwnerOf(s,block.tokenId)??block.owner;
const sequence=s.transfers.filter(tx=>transactionCardIds(tx).includes(block.tokenId)).length;
res.json({network:NETWORK,chainId:CHAIN_ID,tokenId:block.tokenId,owner,sequence,blockHash:block.hash,
confirmations:s.blocks.length-block.height+1,article:block.article,statsSnapshot:block.statsSnapshot,
stateRoot:s.blocks.at(-1)?.stateRoot});
if(typeof req.params.id!=='string'||!/^[a-f0-9]{64}$/.test(req.params.id)){
res.status(400).json({error:'Invalid NFT identifier'});return;
}
const nft=ledger.nft(req.params.id);
if(!nft){res.status(404).json({error:'NFT not found on the canonical chain'});return;}
const block=nft.block;
res.json({network:NETWORK,chainId:CHAIN_ID,tokenId:block.tokenId,owner:nft.owner,sequence:nft.sequence,blockHash:block.hash,
confirmations:nft.confirmations,article:block.article,statsSnapshot:block.statsSnapshot,
stateRoot:ledger.block(ledger.summary().tip)?.stateRoot});
});
const templateLimit=rateLimit({windowMs:60000,limit:20,standardHeaders:'draft-8',legacyHeaders:false});
app.post('/api/mining/template', templateLimit, async (req,res)=>{