class ActiveHashcash::Stamp
This is the model to store hashcash stamps. Unless you need something really specific, you should not need interact directly with that class.
Public Class Methods
# File app/models/active_hashcash/stamp.rb, line 18 def self.filter_by(params) scope = all scope = scope.request_path_starts_with(params[:request_path_starts_with]) if params[:request_path_starts_with].present? scope = scope.ip_address_starts_with(params[:ip_address_starts_with]) if params[:ip_address_starts_with].present? scope = scope.created_from(params[:created_from]) if params[:created_from].present? scope = scope.created_to(params[:created_to]) if params[:created_to].present? scope = scope.bits_from(params[:bits_from]) if params[:bits_from].present? scope = scope.bits_to(params[:bits_to]) if params[:bits_to].present? scope end
# File app/models/active_hashcash/stamp.rb, line 66 def self.mint(resource, attributes = {}) new({ version: 1, bits: ActiveHashcash.bits, date: Date.today.strftime(ActiveHashcash.date_format), resource: resource, ext: "sha256", rand: SecureRandom.alphanumeric(16), counter: 0, }.merge(attributes)).work end
Parse and instantiate a stamp from a string which respects the hashcash format:
ver:bits:date:resource:[ext]:rand:counter
# File app/models/active_hashcash/stamp.rb, line 60 def self.parse(string) args = string.to_s.split(":") return if args.size != 7 new(version: args[0], bits: args[1], date: Date.strptime(args[2], ActiveHashcash.date_format), resource: args[3], ext: args[4], rand: args[5], counter: args[6]) end
Verify and save the hashcash stamp. Saving in the database prevent from double spending the same stamp.
# File app/models/active_hashcash/stamp.rb, line 49 def self.spend(string, resource, bits, date, options = {}) return false unless stamp = parse(string) stamp.attributes = options stamp.verify(resource, bits, date) && stamp.save rescue ActiveRecord::RecordNotUnique false end
Returns stamp counts per period for disjoint windows. periods must be sorted shortest to longest (as ensured by ActiveHashcash.throttle_rules=).
# File app/models/active_hashcash/stamp.rb, line 31 def self.sum_by_periods(periods) return [] if periods.blank? cutoffs = periods.map(&:ago) columns = cutoffs.each_with_index.map do |cutoff, index| if index.zero? sanitize_sql(["sum(CASE WHEN created_at >= ? THEN 1 ELSE 0 END)", cutoff]) else previous_cutoff = cutoffs[index - 1] sanitize_sql(["sum(CASE WHEN created_at < ? AND created_at >= ? THEN 1 ELSE 0 END)", previous_cutoff, cutoff]) end end.join(", ") Array.wrap(where(created_at: cutoffs.min..).pluck(Arel.sql(columns)).first) end
Public Instance Methods
# File app/models/active_hashcash/stamp.rb, line 83 def authentic? if ext == "sha256" Digest::SHA256.hexdigest(to_s).hex >> (256 - bits) == 0 else Digest::SHA1.hexdigest(to_s).hex >> (160 - bits) == 0 end end
# File app/models/active_hashcash/stamp.rb, line 95 def to_s [version.to_i, bits, date.strftime("%y%m%d"), resource, ext, rand, counter].join(":") end
# File app/models/active_hashcash/stamp.rb, line 91 def verify(resource, bits, date) self.resource == resource && self.bits >= bits && self.date >= date && !self.date.future? && authentic? end
# File app/models/active_hashcash/stamp.rb, line 78 def work counter.next! until authentic? self end