/**
* Proof-of-Work for Hyper-Registry namespace claims
*
* STATUS: Implementation sketch - not yet functional
*/
import crypto from 'hypercore-crypto'
/**
* Get required difficulty for a name
* Shorter names require more work (Sybil resistance)
*/
export function getDifficulty(name) {
const len = name.length
if (len <= 2) return 26 // ~67M hashes
if (len <= 4) return 22 // ~4M hashes
if (len <= 8) return 18 // ~262K hashes
if (len <= 16) return 14 // ~16K hashes
return 10 // ~1K hashes
}
/**
* Mine a proof-of-work for a namespace claim
*
* @param {string} name - Namespace name
* @param {string} pubkey - Root public key (hex)
* @param {number} difficulty - Required leading zero bits
* @returns {Promise<Object>} - { nonce, difficulty, hash, attempts, timeMs }
*/
export async function mineProof(name, pubkey, difficulty) {
const startTime = Date.now()
let nonce = 0
let hash
while (true) {
hash = hashProof(name, pubkey, nonce)
if (countLeadingZeroBits(hash) >= difficulty) {
return {
nonce,
difficulty,
hash: hash.toString('hex'),
attempts: nonce + 1,
timeMs: Date.now() - startTime
}
}
nonce++
// Yield to event loop periodically
if (nonce % 10000 === 0) {
await new Promise(resolve => setImmediate(resolve))
}
// Progress logging
if (nonce % 500000 === 0) {
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1)
const rate = (nonce / 1000 / parseFloat(elapsed)).toFixed(0)
console.log(` ...${(nonce / 1000000).toFixed(1)}M hashes (${elapsed}s, ${rate}k/s)`)
}
}
}
/**
* Validate a proof-of-work
*
* @param {string} name - Namespace name
* @param {string} pubkey - Root public key (hex)
* @param {Object} proof - { nonce, difficulty, hash }
* @param {number} requiredDifficulty - Minimum required difficulty
* @returns {boolean}
*/
export function validateProof(name, pubkey, proof, requiredDifficulty) {
// Check difficulty meets requirement
if (proof.difficulty < requiredDifficulty) {
return false
}
// Recompute hash
const hash = hashProof(name, pubkey, proof.nonce)
// Verify hash matches
if (hash.toString('hex') !== proof.hash) {
return false
}
// Verify leading zeros
if (countLeadingZeroBits(hash) < requiredDifficulty) {
return false
}
return true
}
/**
* Hash a proof attempt
*/
function hashProof(name, pubkey, nonce) {
const data = Buffer.from(`hyper-registry:${name}:${pubkey}:${nonce}`)
return crypto.hash(data)
}
/**
* Count leading zero bits in a buffer
*/
function countLeadingZeroBits(buffer) {
let zeros = 0
for (const byte of buffer) {
if (byte === 0) {
zeros += 8
} else {
// Count leading zeros in this byte
for (let i = 7; i >= 0; i--) {
if ((byte & (1 << i)) === 0) {
zeros++
} else {
return zeros
}
}
}
}
return zeros
}
/**
* Estimate time to mine at given difficulty
* Assumes ~500k hashes/second on typical hardware
*/
export function estimateMiningTime(difficulty) {
const expectedHashes = Math.pow(2, difficulty)
const hashesPerSecond = 500000
const seconds = expectedHashes / hashesPerSecond
if (seconds < 60) {
return `~${seconds.toFixed(0)} seconds`
} else if (seconds < 3600) {
return `~${(seconds / 60).toFixed(0)} minutes`
} else {
return `~${(seconds / 3600).toFixed(1)} hours`
}
}