class ProofOfWork

Constants

VERSION

Public Class Methods

generate(identifier, options = {}) click to toggle source
# File lib/proof_of_work.rb, line 7
def generate(identifier, options = {})
  options[:now]           ||= Time.now
  options[:bits]          ||= 20
  options[:extension]     ||= ""
  options[:salt_chars]    ||= 8
  options[:stamp_seconds] ||= false

  timestamp = if !!options[:stamp_seconds]
                options[:now].strftime("%y%m%d%H%M%S")
              else
                options[:now].strftime("%y%m%d")
              end

  challenge = "%s:" * 6 % [
    VERSION,
    options[:bits],
    timestamp,
    identifier,
    options[:extension],
    salt(options[:salt_chars])
  ]

  challenge + hashcash(challenge, options[:bits])
end
valid?(stamp, options = {}) { |extension| ... } click to toggle source
# File lib/proof_of_work.rb, line 32
def valid?(stamp, options = {})
  _, bits_claim, date, identifier, extension, rand, counter = stamp.split(":")

  return false if options[:identifier] && options[:identifier] != identifier
  return false if options[:bits] && options[:bits].to_i > bits_claim.to_i

  if options[:expiration_date]
    return false if date < options[:expiration_date].strftime("%y%m%d%H%M%S")
  end

  hex_digits = (bits_claim.to_i/4.0).floor.to_i

  sha1 = OpenSSL::Digest::SHA1.new
  sha1 << stamp

  is_valid = sha1.hexdigest.start_with?("0" * hex_digits)
  yield(extension) if block_given?

  is_valid
end

Private Class Methods

hashcash(challenge, bits) click to toggle source
# File lib/proof_of_work.rb, line 60
def hashcash(challenge, bits)
  hex_digits = (bits/4.0).ceil
  zeros = "0" * hex_digits
  counter = 0

  loop do
    sha1 = OpenSSL::Digest::SHA1.new
    sha1 << challenge
    sha1 << counter.to_s(16)

    digest = sha1.hexdigest
    section = digest[0...hex_digits]

    return counter.to_s(16) if section == zeros

    counter += 1
  end
end
salt(length) click to toggle source
# File lib/proof_of_work.rb, line 55
def salt(length)
  letters = [*("a".."z"), *("A".."Z"), "+", "/", "="]
  length.times.map{ letters.sample }.join
end