OverviewFilesCommitsIssuesDocsReleasesPeers

p2pbuilders / src / backend / pow.js

83 lines · javascript ref:
'use strict'

const sodium = require('sodium-universal')
const b4a = require('b4a')
const { encodePreimage, NONCE_BYTES } = require('./ops')

const HASH_BYTES = 32

const DEFAULT_BITS = {
  comment: 16,
  post: 18,
  board_create: 22
}

function blake2b (input, out) {
  out = out || b4a.alloc(HASH_BYTES)
  sodium.crypto_generichash(out, input)
  return out
}

function leadingZeroBits (buf) {
  let bits = 0
  for (let i = 0; i < buf.length; i++) {
    const b = buf[i]
    if (b === 0) { bits += 8; continue }
    // count leading zeros in this byte
    for (let m = 0x80; m; m >>>= 1) {
      if (b & m) return bits
      bits++
    }
    return bits
  }
  return bits
}

function digestPreimage (op) {
  return blake2b(encodePreimage(op))
}

function hashWithNonce (digest, nonce, out) {
  const buf = b4a.alloc(digest.length + nonce.length)
  buf.set(digest, 0)
  buf.set(nonce, digest.length)
  return blake2b(buf, out)
}

function writeNonce (nonce, counter) {
  // 8-byte big-endian counter, room to grow
  const view = new DataView(nonce.buffer, nonce.byteOffset, nonce.length)
  view.setBigUint64(0, BigInt(counter), false)
}

function mint (op, bits, { maxAttempts = 1e9 } = {}) {
  const digest = digestPreimage(op)
  const nonce = b4a.alloc(NONCE_BYTES)
  const hashOut = b4a.alloc(HASH_BYTES)
  for (let counter = 0; counter < maxAttempts; counter++) {
    writeNonce(nonce, counter)
    hashWithNonce(digest, nonce, hashOut)
    if (leadingZeroBits(hashOut) >= bits) {
      return { bits, nonce: b4a.from(nonce) }
    }
  }
  throw new Error(`pow mint exceeded ${maxAttempts} attempts`)
}

function verify (op, bits) {
  if (!op.pow) return false
  if (op.pow.bits < bits) return false
  const digest = digestPreimage(op)
  const hash = hashWithNonce(digest, op.pow.nonce)
  return leadingZeroBits(hash) >= op.pow.bits
}

module.exports = {
  DEFAULT_BITS,
  HASH_BYTES,
  leadingZeroBits,
  digestPreimage,
  mint,
  verify
}