Class: Drand::Chain

Inherits:
Object
  • Object
show all
Defined in:
lib/drand/chain.rb

Constant Summary collapse

DEFAULT_ENDPOINTS =
["https://api.drand.sh"].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(chain_hash:, genesis_time:, period:, base_url: nil, endpoints: nil, name: "custom", scheme: nil, public_key: nil) ⇒ Chain

Returns a new instance of Chain.

Raises:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/drand/chain.rb', line 17

def initialize(chain_hash:, genesis_time:, period:, base_url: nil, endpoints: nil,
               name: "custom", scheme: nil, public_key: nil)
  raise ArgumentError, "chain_hash required" if chain_hash.nil? || chain_hash.empty?
  raise ArgumentError, "genesis_time must be an Integer" unless genesis_time.is_a?(Integer)
  raise ArgumentError, "period must be a positive Integer" unless period.is_a?(Integer) && period.positive?

  @chain_hash = chain_hash
  @genesis_unix = genesis_time
  @period = period
  @name = name
  @scheme = scheme
  @public_key = public_key
  @endpoints = resolve_endpoints(base_url, endpoints)
  @http = HttpClient.new(endpoints: @endpoints, chain_hash: chain_hash)
end

Instance Attribute Details

#chain_hashObject (readonly)

Returns the value of attribute chain_hash.



15
16
17
# File 'lib/drand/chain.rb', line 15

def chain_hash
  @chain_hash
end

#endpointsObject (readonly)

Returns the value of attribute endpoints.



15
16
17
# File 'lib/drand/chain.rb', line 15

def endpoints
  @endpoints
end

#nameObject (readonly)

Returns the value of attribute name.



15
16
17
# File 'lib/drand/chain.rb', line 15

def name
  @name
end

#periodObject (readonly)

Returns the value of attribute period.



15
16
17
# File 'lib/drand/chain.rb', line 15

def period
  @period
end

#public_keyObject (readonly)

Returns the value of attribute public_key.



15
16
17
# File 'lib/drand/chain.rb', line 15

def public_key
  @public_key
end

#schemeObject (readonly)

Returns the value of attribute scheme.



15
16
17
# File 'lib/drand/chain.rb', line 15

def scheme
  @scheme
end

Instance Method Details

#base_urlObject

Kept for backward compatibility; returns the first endpoint.



38
39
40
# File 'lib/drand/chain.rb', line 38

def base_url
  @endpoints.first
end

#current_roundObject



59
60
61
# File 'lib/drand/chain.rb', line 59

def current_round
  round_at(Time.now.utc)
end

#draw(range, round: current_round, verify: verifiable?) ) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/drand/chain.rb', line 73

def draw(range, round: current_round, verify: verifiable?)
  lo, hi = normalize_range(range)
  data = self.round(round, verify: verify)
  {
    value: sample_in(data[:randomness], lo, hi),
    range: { min: lo, max: hi },
    round: data[:round],
    chain: @name,
    chain_hash: @chain_hash,
    randomness: data[:randomness],
    signature: data[:signature],
    verified: data[:verified],
    served_by: data[:served_by]
  }
end

#genesis_timeObject



42
43
44
# File 'lib/drand/chain.rb', line 42

def genesis_time
  Time.at(@genesis_unix).utc
end

#round(number, verify: verifiable?) ) ⇒ Object

Raises:



63
64
65
66
67
68
69
70
71
# File 'lib/drand/chain.rb', line 63

def round(number, verify: verifiable?)
  raise ArgumentError, "round must be an Integer" unless number.is_a?(Integer)
  raise RoundError, "round must be >= 1" if number < 1
  raise RoundError, "round #{number} is in the future" if number > current_round

  data = @http.fetch_round(number)
  data[:verified] = verify ? verify!(data) : false
  data
end

#round_at(time) ⇒ Object

Raises:



46
47
48
49
50
51
# File 'lib/drand/chain.rb', line 46

def round_at(time)
  t = to_utc(time)
  elapsed = t.to_r - @genesis_unix
  raise RoundError, "time is before chain genesis" if elapsed.negative?
  (elapsed / @period).floor + 1
end

#time_of(round) ⇒ Object

Raises:



53
54
55
56
57
# File 'lib/drand/chain.rb', line 53

def time_of(round)
  raise ArgumentError, "round must be an Integer" unless round.is_a?(Integer)
  raise RoundError, "round must be >= 1" if round < 1
  Time.at(@genesis_unix + (round - 1) * @period).utc
end

#verifiable?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/drand/chain.rb', line 33

def verifiable?
  !@scheme.nil? && !@public_key.nil?
end

#verify(round_data) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/drand/chain.rb', line 89

def verify(round_data)
  unless verifiable?
    raise VerificationError, "chain has no public_key/scheme; cannot verify"
  end
  Verifier.verify(
    scheme: @scheme,
    public_key: @public_key,
    round: round_data.fetch(:round),
    signature: round_data.fetch(:signature),
    previous_signature: round_data[:previous_signature]
  )
end