183 lines
4.8 KiB
TypeScript
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());
|
|
}
|
|
}
|