TypeScript/JavaScript implementation of the KHeavyHash algorithm compatible with the Go reference implementation at github.com/bcutil/kheavyhash.
⚠️ Important Notice: This implementation is not optimized for mining purposes and is not intended for mining. It is designed for reference, testing, and educational purposes. For production mining operations, please use optimized implementations.
- ✅ Full TypeScript support with type definitions
- ✅ JavaScript/ES modules compatible
- ✅ Deterministic output matching Go reference implementation
- ✅ Comprehensive test suite following FIRST principles
- ✅ Docker development environment (VS Code/Cursor dev container support)
- ✅ CI/CD ready
- ✅ Zero dependencies (pure TypeScript implementation)
- Node.js: >= 18.0.0
- TypeScript: >= 4.5.0
- JavaScript: ES2022+ (modern browsers and Node.js)
- Module System: ESM (ES Modules) with CommonJS fallback support
pnpm add @blockutil/kheavyhashor
npm install @blockutil/kheavyhashor
yarn add @blockutil/kheavyhashimport { kheavyhash, bytesToHex, hexToBytes } from '@blockutil/kheavyhash';
// Construct 80-byte input
const prePowHash = hexToBytes('0ad86e9bef09726cdc75913e44ec96521391c7ceb2aae3c633f46a94bf4d2546');
// Set timestamp as little-endian 64-bit integer
const timestamp = new Uint8Array(8);
const view = new DataView(timestamp.buffer);
view.setBigUint64(0, BigInt(0x19568d36b3e), true); // true = little-endian
// Padding must be all zeros (32 bytes)
const padding = new Uint8Array(32);
// Set nonce as little-endian 64-bit integer
const nonce = new Uint8Array(8);
view.setBigUint64(0, BigInt(0x571506849306fd39), true); // true = little-endian
// Combine into 80-byte input
const input = new Uint8Array(80);
input.set(prePowHash, 0); // Bytes 0-31
input.set(timestamp, 32); // Bytes 32-39
input.set(padding, 40); // Bytes 40-71 (zeros)
input.set(nonce, 72); // Bytes 72-79
// Hash
const hash = kheavyhash(input);
console.log(bytesToHex(hash)); // 32-byte hash as hex stringimport { kheavyhash, bytesToHex } from '@blockutil/kheavyhash';
// All 80 bytes as hex string (160 hex characters)
const hexInput =
'0ad86e9bef09726cdc75913e44ec96521391c7ceb2aae3c633f46a94bf4d2546' + // prePowHash (32 bytes)
'3ed68b9501950000' + // timestamp (8 bytes, little-endian)
'0'.repeat(64) + // padding (32 bytes, must be zeros)
'39fd0630849571'; // nonce (8 bytes, little-endian)
const hash = kheavyhash(hexInput);
console.log(bytesToHex(hash));import { kheavyhash, bytesToHex, hexToBytes } from '@blockutil/kheavyhash';
// Initial prePowHash
let prePowHash = hexToBytes('0ad86e9bef09726cdc75913e44ec96521391c7ceb2aae3c633f46a94bf4d2546');
const timestamp = 0x19568d36b3e; // Fixed timestamp
let nonce = 0;
// Mine for multiple iterations
for (let i = 0; i < 1000; i++) {
// Construct input
const input = new Uint8Array(80);
input.set(prePowHash, 0);
// Set timestamp (little-endian)
const timestampBytes = new Uint8Array(8);
const view = new DataView(timestampBytes.buffer);
view.setBigUint64(0, BigInt(timestamp), true);
input.set(timestampBytes, 32);
// Padding (already zeros, no need to set)
// Set nonce (little-endian)
const nonceBytes = new Uint8Array(8);
view.setBigUint64(0, BigInt(nonce), true);
input.set(nonceBytes, 72);
// Hash
const hash = kheavyhash(input);
// Use output as next prePowHash
prePowHash = hash;
// Increment nonce for next iteration
nonce++;
if (i % 100 === 0) {
console.log(`Iteration ${i}: ${bytesToHex(hash)}`);
}
}import { validateInput, kheavyhash } from '@blockutil/kheavyhash';
function safeHash(input: unknown): Uint8Array | null {
if (!validateInput(input)) {
console.error('Invalid input: must be 80-byte Uint8Array or 160-char hex string');
return null;
}
try {
return kheavyhash(input);
} catch (error) {
console.error('Hash failed:', error);
return null;
}
}
// Usage
const hash1 = safeHash(new Uint8Array(80)); // ✅ Valid
const hash2 = safeHash('0'.repeat(160)); // ✅ Valid
const hash3 = safeHash(new Uint8Array(79)); // ❌ Invalid (wrong length)KHeavyHash requires exactly 80 bytes with the following structure:
| Bytes | Field | Size | Description | Endianness |
|---|---|---|---|---|
| 0-31 | prePowHash |
32 bytes | Previous proof-of-work hash | N/A |
| 32-39 | timestamp |
8 bytes | Block timestamp | Little-endian |
| 40-71 | padding |
32 bytes | Must be all zeros | N/A |
| 72-79 | nonce |
8 bytes | Mining nonce | Little-endian |
Important Notes:
- The
timestampandnoncemust be encoded as little-endian 64-bit integers - The
paddingfield (bytes 40-71) must be all zeros - Total input must be exactly 80 bytes (160 hex characters if using hex string)
For complete API documentation, see API.md.
Main exports:
kheavyhash(input: Uint8Array | string): Uint8Array- Main hash functionvalidateInput(input: unknown): boolean- Input validation type guardhexToBytes(hex: string): Uint8Array- Convert hex string to bytesbytesToHex(bytes: Uint8Array, prefix?: boolean): string- Convert bytes to hex string
All functions are fully documented with JSDoc comments. Use your IDE's IntelliSense for inline documentation.
- Node.js >= 18.0.0
- pnpm >= 8.0.0 (or npm/yarn)
# Install dependencies
pnpm install
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Build
pnpm build
# Lint
pnpm lint
# Format code
pnpm format
# Type check
pnpm type-checkThis project includes a Docker development environment for consistent builds:
# Build and start container
docker-compose up -d
# Run commands in container
docker-compose exec kheavyhash pnpm test
docker-compose exec kheavyhash pnpm build
# Stop container
docker-compose downThe project includes a .devcontainer configuration for VS Code and Cursor:
- Open the project in VS Code/Cursor
- When prompted, click "Reopen in Container"
- The development environment will be set up automatically
This project follows FIRST principles for unit testing:
- Fast: Tests run quickly with no external dependencies
- Independent: Each test is isolated and independent
- Repeatable: Tests produce deterministic results
- Self-Validating: Clear pass/fail outcomes
- Timely: Tests written alongside implementation
# Run all tests
pnpm test
# Run tests with coverage
pnpm test:coverage
# Run tests in watch mode
pnpm test:watchTest vectors from the Go reference implementation are available in test/kheavyhash-vectors.test.ts. These validate the implementation against known outputs from github.com/bcutil/kheavyhash.
Test 1000 iterations by feeding output back as input:
# TypeScript-only test
pnpm test:iterations
# Compare with Go implementation (requires Go)
pnpm test:iterations:goCompare the TypeScript implementation with the Go reference implementation side-by-side:
# Test if Go module is accessible
bash scripts/test-go-module.sh
# Using bash script (requires Go and built TypeScript)
bash scripts/compare-with-go.sh
# Using TypeScript script (requires Go, tsx, and built TypeScript)
pnpm test:comparePrerequisites for comparison script:
- Go must be installed
- Go module
github.com/bcutil/kheavyhashmust be available - TypeScript implementation must be built (
pnpm build)
This project follows Semantic Versioning (SemVer 2.0.0).
MAJOR.MINOR.PATCH
- MAJOR version: Incompatible API changes
- MINOR version: Backward-compatible new features
- PATCH version: Backward-compatible bug fixes
- Current:
0.1.0(pre-release) - Stability: Pre-1.0.0 versions may have breaking changes
See CHANGELOG.md (if available) or git history for version history.
This project uses Conventional Commits for commit messages.
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Allowed types:
feat: New feature (→ MINOR bump)fix: Bug fix (→ PATCH bump)docs: Documentation changesstyle: Formatting, no code logic changesrefactor: Code changes that neither fix bugs nor add featurestest: Add/modify tests onlychore: Maintenance tasksperf: Performance improvementsbuild: Build system changesci: CI configuration changesrevert: Reverts a previous commit
Breaking changes:
- Must include
!after the type/scope, OR - Must include a
BREAKING CHANGE:footer - Breaking changes require a MAJOR version bump
MIT - See LICENSE file for details.
This package includes code adapted from external sources. See THIRD_PARTY_LICENSES.md for details.