import { Transaction } from "./transaction"; import { parseTxBodies } from "./txBody"; import { cborBackend } from "cbor-rpc"; import { blake } from "../lib/blake"; // or "libcardano" — adjust import import { RawTransaction } from "./cddlTypes"; import { parseTxWitness } from "./txWitnessSet"; import { parseAuxData } from "./auxiliary-data"; // ──────────────────────────────────────────────── // Unified Block & Header types // ──────────────────────────────────────────────── export class BlockHeader { version: number; slotNo!: number; blockNo!: number; headerHash!: Buffer; prevHeaderHash!: Buffer; minting?: { // issuerVkey: Buffer; minerPubKey: Buffer; vrfVkey: Buffer; vrfResult: [Buffer, Buffer]; operationalCert: [Buffer, number, number, Buffer]; protocolVersion: [number, number]; kesSignature: Buffer; }; /** Byron blocks are not fully supported */ byron?: { isEBB?: boolean; byronGenesisKey?: Buffer; byronDifficulty?: any; byronSignature?: Buffer; }; public constructor(version:number,headerData: any, serializedHader?: Buffer) { this.version = version; if(serializedHader instanceof BlockHeader){ Object.assign(this, serializedHader); return; } if (version === 0) { this.parseByronHeader(headerData) } else { if(serializedHader) { this.headerHash = blake.hash32(serializedHader); } else { this.headerHash = blake.hash32(cborBackend.encode(headerData)); } this.parseShelleyHeader(headerData); } } private parseByronHeader(byronBlockHeader: any) { let slotNo: [number, number]; // Byron block header is encoded as CBOR inside header[1].value // blockhead = [ "protocolMagic" : u32 // , "prevBlock" : blockid // , "bodyProof" : blockproof // , "consensusData" : blockcons // , "extraData" : blockheadex // ] // blockcons = [slotid, pubkey (headerGenesisKey), difficulty, blocksig] const blockcons = byronBlockHeader[3]; let headerHashData: any; let isEBB = false; if (byronBlockHeader[0] === 1) { // normal block // [slotNo, pubkey, difficulty, blocksig] = header[3] slotNo = byronBlockHeader[3][0]; headerHashData = [1, byronBlockHeader]; } else { // epoch boundary block (EBB) // [ epochid, difficulty ] = header[3] // For EBBs, use epoch * 21600 as slot (mainnet epoch length) const epochId = byronBlockHeader[3][0]; slotNo = [epochId, 0]; headerHashData = [0, byronBlockHeader]; isEBB = true; } this.headerHash = blake.hash32(cborBackend.encode(headerHashData)) this.slotNo = slotNo[1] this.blockNo = slotNo[1] this.prevHeaderHash = byronBlockHeader[1] this.byron = { isEBB } } private parseShelleyHeader(decodedHeader: any) { // decodedHeader shape: [[blockNo, slotNo, prevHeaderHash, ...], ...] // signature = decodedHeader[1] const headerData = decodedHeader[0]; this.blockNo = headerData[0] as number; this.slotNo = headerData[1] as number; this.prevHeaderHash = headerData[2]; this.minting = { minerPubKey: headerData[3], vrfVkey: headerData[4], vrfResult: headerData[5], // [6] = block body size operationalCert: headerData[8], protocolVersion: headerData[9], kesSignature: decodedHeader[1], // is thi sheaer 1? } } public static fromCborObject(version:number,headerData: any,serializedHeader?:Buffer): BlockHeader { return new BlockHeader(version,headerData,serializedHeader); } public static fromBytes(version:number,headerBytes: Buffer): BlockHeader { const decoded = cborBackend.decode(headerBytes); return BlockHeader.fromCborObject(version,decoded,headerBytes); } } // @ts-ignore export class Block extends BlockHeader { transactions: Transaction[]; private constructor(block_object: any, serialized_header?: Buffer) { super(block_object[0][0], block_object[1][0], serialized_header); this.transactions = parseBlockTransactions(block_object[0][0], block_object[1]); } public static override fromCborObject(block: any, serialized_header?: Buffer): Block { return new Block(block, serialized_header); } public static override fromBytes(blockBytes: Buffer): Block { return Block.fromCborObject(cborBackend.decode(blockBytes)); } public static fromHeaderAndBytes(header: BlockHeader, blockBytes: Buffer): Block { const decoded = cborBackend.decode(blockBytes); const b = new Block(decoded, header as any); return Object.assign(b, header); } public static fromHeaderAndCborObject(header: BlockHeader, block: any): Block { const b = new Block(block, header as any); return Object.assign(b, header); } } export function parseBlockTransactions(block_version:number,block_object:any): Transaction[] { if (block_version <= 1) { console.debug("Byron block ignored", block_version); return []; // we can't parse byron era block } else { } // block_content[0] is the header const bodies: Map[] = block_object[1]; const transactionWitnessSet: Map[] = block_object[2]; const auxiliaryDataSet: Map = block_object[3]; const invalidTxs: Set = new Set(block_object[4]); const txBodies = parseTxBodies(bodies); const txs: Transaction[] = txBodies.map((body, index) => { return { body: body, valid: !invalidTxs.has(index), hash: blake.hash32(cborBackend.encode(bodies[index])), } as Transaction; }); transactionWitnessSet.forEach((witnessSet, index) => { txs[index].witnessSet = parseTxWitness(witnessSet); }); auxiliaryDataSet.forEach((auxData, index) => { txs[index].auxiliaryData = parseAuxData(auxData); }); return txs; }