Files
xo-stack/packages/primitives/src/block-header.ts
T

183 lines
4.8 KiB
TypeScript

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());
}
}