Class: HashCash::Stamp
- Inherits:
-
Object
- Object
- HashCash::Stamp
- Defined in:
- lib/hashcash.rb
Overview
The HashCash::Stamp class can be used to create and verify proof of work, so called hash cash, as defined on hashcash.org.
Basically, it creates a ‘stamp’, which when hashed with SHA-1 has a certain amount of 0 bytes at the top.
To create a new stamp, call the constructor with the :resource parameter to specify a resource for which this stamp will be valid (e.g. an email address).
To verify a stamp, call it with a string representation of the stamp as the :stamp parameter and call verify with a resource on it.
Constant Summary collapse
- STAMP_VERSION =
1
Instance Attribute Summary collapse
-
#bits ⇒ Object
readonly
Returns the value of attribute bits.
-
#date ⇒ Object
readonly
Returns the value of attribute date.
-
#resource ⇒ Object
readonly
Returns the value of attribute resource.
-
#stamp_string ⇒ Object
readonly
Returns the value of attribute stamp_string.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
-
#initialize(args) ⇒ Stamp
constructor
To construct a new HashCash::Stamp object, pass the :resource parameter to it, e.g.
-
#to_s ⇒ Object
A string representation of the stamp.
-
#verify(resources, bits = 20) ⇒ Object
Verify a stamp for a given resource or resources and a number of bits.
Constructor Details
#initialize(args) ⇒ Stamp
To construct a new HashCash::Stamp object, pass the :resource parameter to it, e.g.
s = HashCash::Stamp.new(:resource => ‘[email protected]’)
This creates a 20 bit hash cash stamp, which can be retrieved using the stamp_string() attribute reader method.
Optionally, the parameters :bits and :date can be passed to the method to change the number of bits the stamp is worth and the issuance date (which is checked on the server for an expiry with a default deviance of 2 days, pass a Time object).
Alternatively, a stamp can be passed to the constructor by passing it as a string to the :stamp parameter, e.g.
s = HashCash::Stamp.new(:stamp => ‘1:20:060408:[email protected]::1QTjaYd7niiQA/sc:ePa’)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/hashcash.rb', line 41 def initialize(args) if ! args || (! args[:stamp] && ! args[:resource]) then raise ArgumentError, 'either stamp or stamp parameters needed' end # existing stamp in string format if args[:stamp] then @stamp_string = args[:stamp] (@version, @bits, @date, @resource, ext, @rand, @counter) \ = args[:stamp].split(':') @bits = @bits.to_i if @version.to_i != STAMP_VERSION then raise ArgumentError, "incorrect stamp version #{@version}" end @date = parse_date(@date) # new stamp to be created elsif args[:resource] then @resource = args[:resource] # optional parameters: bits and date @bits = args[:bits] || 20 @bits = @bits.to_i if args[:date] && ! args[:date].class == Time then raise ArgumentError, 'date needs to be a Time object' end @date = args[:date] || Time.now # create first part of stamp string random_string = Base64.encode64(OpenSSL::Random.random_bytes(12)).chomp first_part = "#{STAMP_VERSION}:#{@bits}:" + \ "#{date_to_str(@date)}:#{@resource}" + \ "::#{random_string}:" ctr = 0 @stamp_string = nil while ! @stamp_string do test_stamp = first_part + ctr.to_s(36) if Digest::SHA1.digest(test_stamp).unpack('B*')[0][0,@bits].to_i == 0 @stamp_string = test_stamp end ctr += 1 end end end |
Instance Attribute Details
#bits ⇒ Object (readonly)
Returns the value of attribute bits.
20 21 22 |
# File 'lib/hashcash.rb', line 20 def bits @bits end |
#date ⇒ Object (readonly)
Returns the value of attribute date.
20 21 22 |
# File 'lib/hashcash.rb', line 20 def date @date end |
#resource ⇒ Object (readonly)
Returns the value of attribute resource.
20 21 22 |
# File 'lib/hashcash.rb', line 20 def resource @resource end |
#stamp_string ⇒ Object (readonly)
Returns the value of attribute stamp_string.
20 21 22 |
# File 'lib/hashcash.rb', line 20 def stamp_string @stamp_string end |
#version ⇒ Object (readonly)
Returns the value of attribute version.
20 21 22 |
# File 'lib/hashcash.rb', line 20 def version @version end |
Instance Method Details
#to_s ⇒ Object
A string representation of the stamp
115 116 117 |
# File 'lib/hashcash.rb', line 115 def to_s @stamp_string end |
#verify(resources, bits = 20) ⇒ Object
Verify a stamp for a given resource or resources and a number of bits. The resources parameter can either be a string for a single resource or an array of strings for more than one possible resource (for example if you have different email addresses and want the stamp to verify against one of them).
The method checks the resource, the time of issuance and the number of 0 bits when the stamp is SHA1-hashed. It returns true if all checks are successful and raises an exception otherwise.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/hashcash.rb', line 92 def verify(resources, bits = 20) # check for correct resource if resources.class != String && resources.class != Array then raise ArgumentError, "resource must be either String or Array" end if resources.class == String then resources = [ resources ] end if ! resources.include? @resource then raise "Stamp is not valid for the given resource(s)." end # check if difference is greater than 2 days if (Time.now - @date).to_i.abs > 2*24*60*60 then raise "Stamp is expired/not yet valid" end # check 0 bits in stamp if (Digest::SHA1.hexdigest(@stamp_string).hex >> (160-bits) != 0) then raise "Invalid stamp, not enough 0 bits" end true end |