Class: Zold::Score

Inherits:
Object
  • Object
show all
Defined in:
lib/zold/score.rb

Overview

Score

Defined Under Namespace

Classes: CantParse

Constant Summary collapse

STRENGTH =

Default strength for the entire system, in production mode. The larger the number, the more difficult it is to find the next score for a node. If the number if too small, the values of the score will be big and the amount of data to be transferred from node to node will increase. The number is set empirically.

8
BEST_BEFORE =

The maximum amount of hours a score can stay “fresh.” After that it will be considered expired.

24
ZERO =

The default no-value score.

Score.new(
  time: Time.now, host: 'localhost',
  invoice: 'NOPREFIX@ffffffffffffffff'
)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(time: Time.now, host:, port: 4096, invoice:, suffixes: [], strength: Score::STRENGTH, created: Time.now) ⇒ Score

Makes a new object of the class.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/zold/score.rb', line 61

def initialize(time: Time.now, host:, port: 4096, invoice:, suffixes: [],
  strength: Score::STRENGTH, created: Time.now)
  raise 'Time can\'t be nil' if time.nil?
  unless time.is_a?(Time)
    raise "Time must be Time, while #{time.class.name} is provided"
  end
  @time = time
  raise 'Host can\'t be nil' if host.nil?
  unless /^[0-9a-z\.\-]+$/.match?(host)
    raise "Host \"#{host}\" is in a wrong format"
  end
  @host = host
  raise 'Port can\'t be nil' if port.nil?
  unless port.is_a?(Integer)
    raise "Port must be Integer, while #{port.class.name} is provided"
  end
  if port > 65_535
    raise "Port must be less than 65535, while #{port} is provided"
  end
  unless port.positive?
    raise "Port must be positive integer, while #{port} is provided"
  end
  @port = port
  raise 'Invoice can\'t be nil' if invoice.nil?
  unless /^[a-zA-Z0-9]{8,32}@[a-f0-9]{16}$/.match?(invoice)
    raise "Invoice \"#{invoice}\" is wrong"
  end
  @invoice = invoice
  raise 'Suffixes can\'t be nil' if suffixes.nil?
  raise 'Suffixes are not an array' unless suffixes.is_a?(Array)
  @suffixes = suffixes
  raise 'Strength can\'t be nil' if strength.nil?
  unless strength.positive?
    raise "Strength must be positive integer, while #{strength} is provided"
  end
  @strength = strength
  raise 'Created can\'t be nil' if created.nil?
  unless created.is_a?(Time)
    raise "Created must be Time, while #{created.class.name} is provided"
  end
  @created = created
end

Instance Attribute Details

#createdObject (readonly)

Returns the value of attribute created.



58
59
60
# File 'lib/zold/score.rb', line 58

def created
  @created
end

#hostObject (readonly)

Returns the value of attribute host.



58
59
60
# File 'lib/zold/score.rb', line 58

def host
  @host
end

#invoiceObject (readonly)

Returns the value of attribute invoice.



58
59
60
# File 'lib/zold/score.rb', line 58

def invoice
  @invoice
end

#portObject (readonly)

Returns the value of attribute port.



58
59
60
# File 'lib/zold/score.rb', line 58

def port
  @port
end

#strengthObject (readonly)

Returns the value of attribute strength.



58
59
60
# File 'lib/zold/score.rb', line 58

def strength
  @strength
end

#suffixesObject (readonly)

Returns the value of attribute suffixes.



58
59
60
# File 'lib/zold/score.rb', line 58

def suffixes
  @suffixes
end

#timeObject (readonly)

Returns the value of attribute time.



58
59
60
# File 'lib/zold/score.rb', line 58

def time
  @time
end

Class Method Details

.parse(text) ⇒ Object

Parses it back from the text generated by to_s.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/zold/score.rb', line 165

def self.parse(text)
  raise 'Can\'t parse nil' if text.nil?
  parts = text.split(' ', 7)
  raise 'Invalid score, not enough parts' if parts.length < 6
  Score.new(
    time: Time.at(parts[1].hex),
    host: parts[2],
    port: parts[3].hex,
    invoice: "#{parts[4]}@#{parts[5]}",
    suffixes: parts[6] ? parts[6].split(' ') : [],
    strength: parts[0].to_i
  )
rescue StandardError => e
  raise CantParse, "#{e.message} in \"#{text}\""
end

.parse_json(json) ⇒ Object

Parses it back from the JSON.



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/zold/score.rb', line 111

def self.parse_json(json)
  raise CantParse, 'JSON can\'t be nil' if json.nil?
  Score.new(
    time: Time.parse(json['time']),
    host: json['host'],
    port: json['port'],
    invoice: json['invoice'],
    suffixes: json['suffixes'],
    strength: json['strength']
  )
rescue StandardError => e
  raise CantParse, "#{e.message} in \"#{json}\""
end

Instance Method Details

#<(other) ⇒ Object

Compare with another Score, by value.



132
133
134
135
# File 'lib/zold/score.rb', line 132

def <(other)
  raise 'Can\'t compare with nil' if other.nil?
  value < other.value
end

#<=>(other) ⇒ Object

Compare with another Score, by value.



144
145
146
147
# File 'lib/zold/score.rb', line 144

def <=>(other)
  raise 'Can\'t compare with nil' if other.nil?
  value <=> other.value
end

#==(other) ⇒ Object

Compare with another Score, by text.



126
127
128
129
# File 'lib/zold/score.rb', line 126

def ==(other)
  raise 'Can\'t compare with nil' if other.nil?
  to_s == other.to_s
end

#>(other) ⇒ Object

Compare with another Score, by value.



138
139
140
141
# File 'lib/zold/score.rb', line 138

def >(other)
  raise 'Can\'t compare with nil' if other.nil?
  value > other.value
end

#ageObject

The age of the score in seconds.



244
245
246
# File 'lib/zold/score.rb', line 244

def age
  Time.now - @time
end

#expired?(hours = BEST_BEFORE) ⇒ Boolean

Returns TRUE if the age of the score is over 24 hours.

Returns:

  • (Boolean)


249
250
251
252
# File 'lib/zold/score.rb', line 249

def expired?(hours = BEST_BEFORE)
  raise 'Hours can\'t be nil' if hours.nil?
  age > hours * 60 * 60
end

#hashObject

Returns its crypto hash. Read the White Paper for more information.



182
183
184
185
186
187
# File 'lib/zold/score.rb', line 182

def hash
  raise 'Score has zero value, there is no hash' if @suffixes.empty?
  @suffixes.reduce(prefix) do |pfx, suffix|
    OpenSSL::Digest::SHA256.new("#{pfx} #{suffix}").hexdigest
  end
end

#nextObject

Calculates and returns the next score after the current one. This operation may take some time, from a few milliseconds to hours, depending on the CPU power and the strength of the current score.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/zold/score.rb', line 228

def next
  raise 'This score is not valid' unless valid?
  if expired?
    return Score.new(
      time: Time.now, host: @host, port: @port, invoice: @invoice,
      suffixes: [], strength: @strength
    )
  end
  suffix = ScoreSuffix.new(suffixes.empty? ? prefix : hash, @strength)
  Score.new(
    time: @time, host: @host, port: @port, invoice: @invoice,
    suffixes: @suffixes + [suffix.value], strength: @strength
  )
end

#prefixObject

The prefix for the hash calculating algorithm. See the White Paper for more details.



256
257
258
# File 'lib/zold/score.rb', line 256

def prefix
  "#{@time.utc.iso8601} #{@host} #{@port} #{@invoice}"
end

#reduced(max = 4) ⇒ Object

Returns a new score, which is a copy of the current one, but the amount of hash suffixes is reduced to the max provided.



215
216
217
218
219
220
221
222
223
# File 'lib/zold/score.rb', line 215

def reduced(max = 4)
  raise 'Max can\'t be nil' if max.nil?
  raise "Max can't be negative: #{max}" if max.negative?
  Score.new(
    time: @time, host: @host, port: @port, invoice: @invoice,
    suffixes: @suffixes[0..[max, suffixes.count].min - 1],
    strength: @strength
  )
end

#to_hObject

Converts the score to a hash, which can be used for JSON presentation of the score.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/zold/score.rb', line 196

def to_h
  {
    value: value,
    host: @host,
    port: @port,
    invoice: @invoice,
    time: @time.utc.iso8601,
    suffixes: @suffixes,
    strength: @strength,
    hash: value.zero? ? nil : hash,
    expired: expired?,
    valid: valid?,
    age: (age / 60).round,
    created: @created.utc.iso8601
  }
end

#to_mnemoObject

A simple mnemo of the score.



190
191
192
# File 'lib/zold/score.rb', line 190

def to_mnemo
  "#{value}:#{@time.strftime('%H%M')}"
end

#to_sObject

Converts it to a string. You can parse it back using parse().



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/zold/score.rb', line 151

def to_s
  pfx, bnf = @invoice.split('@')
  [
    @strength,
    @time.to_i.to_s(16),
    @host,
    @port.to_s(16),
    pfx,
    bnf,
    @suffixes.join(' ')
  ].join(' ')
end

#valid?Boolean

Returns TRUE if the score is valid: all its suffixes correctly consistute the hash, according to the algorithm explained in the White Paper.

Returns:

  • (Boolean)


262
263
264
# File 'lib/zold/score.rb', line 262

def valid?
  (@suffixes.empty? || hash.end_with?('0' * @strength)) && @time < Time.now
end

#valueObject

Returns the value of the score, from zero and up. The value is basically the amount of hash suffixes inside the score.



268
269
270
# File 'lib/zold/score.rb', line 268

def value
  @suffixes.length
end

#zero?Boolean

Returns TRUE if the value of the score is zero.

Returns:

  • (Boolean)


273
274
275
# File 'lib/zold/score.rb', line 273

def zero?
  @suffixes.empty?
end