import { binToHex, hexToBin, isHex, hash256 } from '@bitauth/libauth'; export interface BlockHeaderRaw { version: number; previousBlockHash: Uint8Array; merkleRoot: Uint8Array; timestamp: number; bits: number; nonce: number; } /** * Represents a Bitcoin Cash block header. * This class handles parsing and serialization of BCH block headers between different formats * (raw object, bytes, hex) and provides access to individual header fields. */ export class BlockHeader { /** * Creates a new BlockHeader instance. * @param version - The block version * @param previousBlockHash - Hash of the previous block (32 bytes) * @param merkleRoot - Merkle root of the block's transactions (32 bytes) * @param timestamp - Block timestamp in seconds since Unix epoch * @param bits - Compact representation of the target threshold * @param nonce - Value modified during mining to make the block hash valid */ constructor( private readonly version: number, private readonly previousBlockHash: Uint8Array, private readonly merkleRoot: Uint8Array, private readonly timestamp: number, private readonly bits: number, private readonly nonce: number, ) {} /** * Creates a BlockHeader instance from a byte array. * @param bytes - 80-byte array containing the serialized block header * @returns A new BlockHeader instance * @throws Error if the byte array length is not 80 */ static fromBytes(bytes: Uint8Array): BlockHeader { if (bytes.length !== 80) { throw new Error('Invalid block header length'); } const view = new DataView(bytes.buffer); return new BlockHeader( view.getInt32(0, true), bytes.slice(4, 36), bytes.slice(36, 68), view.getUint32(68, true), view.getUint32(72, true), view.getUint32(76, true), ); } /** * Creates a BlockHeader instance from a hex string. * @param hex - 160-character hex string representing the block header * @returns A new BlockHeader instance * @throws Error if the hex string length is not 160 */ static fromHex(hex: string): BlockHeader { // Ensure that the given string is hex. if (!isHex(hex)) { throw new Error('The given string is not valid hexadecimal.'); } return BlockHeader.fromBytes(hexToBin(hex)); } /** * Creates a BlockHeader instance from a raw object representation. * @param obj - Object containing the block header fields * @returns A new BlockHeader instance */ static fromRaw(obj: BlockHeaderRaw): BlockHeader { return new BlockHeader( obj.version, obj.previousBlockHash, obj.merkleRoot, obj.timestamp, obj.bits, obj.nonce, ); } /** * Serializes the block header to a byte array. * @returns 80-byte array containing the serialized block header */ toBytes(): Uint8Array { const buffer = new ArrayBuffer(80); const view = new DataView(buffer); const bytes = new Uint8Array(buffer); view.setInt32(0, this.version, true); bytes.set(this.previousBlockHash, 4); bytes.set(this.merkleRoot, 36); view.setUint32(68, this.timestamp, true); view.setUint32(72, this.bits, true); view.setUint32(76, this.nonce, true); return bytes; } /** * Converts the block header to a raw object representation. * @returns Object containing the block header fields */ toRaw(): BlockHeaderRaw { return { version: this.version, previousBlockHash: this.previousBlockHash, merkleRoot: this.merkleRoot, timestamp: this.timestamp, bits: this.bits, nonce: this.nonce, }; } /** * Converts the block header to a hex string. * @returns 160-character hex string representing the block header */ toHex(): string { return binToHex(this.toBytes()); } // Getters getVersion(): number { return this.version; } getPreviousBlockHash(): Uint8Array { return this.previousBlockHash; } getMerkleRoot(): Uint8Array { return this.merkleRoot; } getTimestamp(): number { return this.timestamp; } getBits(): number { return this.bits; } getNonce(): number { return this.nonce; } /** * Gets the previous block hash as a hex string. * @returns 64-character hex string */ getPreviousBlockHashHex(): string { return binToHex(this.previousBlockHash); } /** * Gets the merkle root as a hex string. * @returns 64-character hex string */ getMerkleRootHex(): string { return binToHex(this.merkleRoot); } /** * Calculates the hash of this block header. * @returns 32-byte array containing the block hash */ getHash(): Uint8Array { return hash256(this.toBytes()); } /** * Gets the block hash as a hex string. * @returns 64-character hex string */ getHashHex(): string { return binToHex(this.getHash()); } }