62 lines
3.4 KiB
TypeScript
62 lines
3.4 KiB
TypeScript
import type { Article } from './protocol.ts';
|
|
import type { MintStatsSnapshot } from './enrichment.ts';
|
|
|
|
export const NETWORK = 'wikideck-randomx-testnet-5' as const;
|
|
export const CONSENSUS = {
|
|
version: 5, targetSeconds: 15 * 60, retargetInterval: 10, medianWindow: 11,
|
|
commitments: 'sha256-domain-separated-counted-merkle-v1', randomx: '1.2.1', powEndian: 'big',
|
|
maxFutureSeconds: 120, epochBlocks: 2048, epochDelay: 64,
|
|
maxTransactions: 64, coinbaseMaturity: 2, maxBlockBytes: 64 * 1024,
|
|
articleSource: 'wikipedia-random-with-image-and-stats-v1',
|
|
} as const;
|
|
export type NftTransfer = {
|
|
network: typeof NETWORK; chainId: string; cardHash: string; from: string; to: string;
|
|
sequence: number; publicKey: string; signature: string;
|
|
};
|
|
export type BlockHeader = {
|
|
version: 5; network: typeof NETWORK; chainId: string; height: number;
|
|
previousHash: string; createdAt: number; article: Article; statsSnapshot: MintStatsSnapshot;
|
|
owner: string; publicKey: string; nonce: number; target: string; key: string;
|
|
transactionsRoot: string; stateRoot: string;
|
|
};
|
|
export type TestnetBlock = BlockHeader & {
|
|
hash: string; tokenId: string; powHash: string; signature: string; transactions: NftTransfer[];
|
|
};
|
|
export type MiningTemplate = {
|
|
header: BlockHeader; transactions: NftTransfer[]; blob: string; nonceOffset: number;
|
|
};
|
|
export type NftState = Record<string, { owner: string; sequence: number; mintedHeight: number; pageId: number }>;
|
|
/** Persistent overlay: a block stores only NFT entries it changed, sharing its parent's state. */
|
|
export type NftStateLayer = { kind: 'layer'; parent: NftStateView; changes: NftState };
|
|
export type NftStateView = NftState | NftStateLayer;
|
|
export type TestnetSnapshot = {
|
|
network: typeof NETWORK; chainId: string; genesis: string; tip: string; cumulativeWork: string;
|
|
blocks: TestnetBlock[]; transfers: NftTransfer[]; pendingTransfers: NftTransfer[];
|
|
peers: number; algorithm: 'RandomX'; maturity: number;
|
|
};
|
|
export function nftTransferMessage(t: Omit<NftTransfer, 'publicKey' | 'signature'>): string {
|
|
return JSON.stringify([NETWORK, 'nft-transfer', t.chainId, t.cardHash, t.from, t.to, t.sequence]);
|
|
}
|
|
export function articleTuple(a: Article): unknown[] {
|
|
return [a.pageId, a.revisionId, a.title, a.extract, a.image ?? '', a.url, a.source, a.category];
|
|
}
|
|
export function statsSnapshotTuple(s: MintStatsSnapshot): unknown[] {
|
|
return [s.capturedAt, s.revisionId, s.pageBytes, s.languageCount, s.languagesTruncated,
|
|
[s.pageviews.status, s.pageviews.months.map(item => [item.month, item.views]), s.pageviews.latestMonth,
|
|
s.pageviews.total, s.pageviews.dailyAverage, s.pageviews.trendPercent],
|
|
[s.stats.attention, s.stats.depth, s.stats.reach]];
|
|
}
|
|
export function headerMessage(h: BlockHeader): string {
|
|
return JSON.stringify([NETWORK, h.version, h.chainId, h.height, h.previousHash, h.createdAt,
|
|
articleTuple(h.article), statsSnapshotTuple(h.statsSnapshot), h.owner, h.publicKey,
|
|
h.target, h.key, h.transactionsRoot, h.stateRoot]);
|
|
}
|
|
export function blockClaimMessage(hash: string): string {
|
|
return JSON.stringify([NETWORK, 'block-claim', hash]);
|
|
}
|
|
export function testnetOwnerOf(state: Pick<TestnetSnapshot, 'blocks' | 'transfers'>, hash: string): string | undefined {
|
|
let owner = state.blocks.find(b => b.tokenId === hash)?.owner;
|
|
for (const t of state.transfers) if (t.cardHash === hash) owner = t.to;
|
|
return owner;
|
|
}
|