Class: Nostr::Event

Inherits:
Object
  • Object
show all
Includes:
CryptoTools
Defined in:
lib/event.rb

Defined Under Namespace

Classes: ValidationError

Constant Summary collapse

ATTRIBUTES =
[:kind, :pubkey, :created_at, :tags, :content, :id, :sig, :pow, :delegation, :recipient]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CryptoTools

aes_256_cbc_decrypt, aes_256_cbc_encrypt, calculate_shared_key

Constructor Details

#initialize(kind:, pubkey: nil, created_at: nil, tags: [], content: nil, id: nil, sig: nil, pow: nil, delegation: nil, subscription_id: nil) ⇒ Event

Returns a new instance of Event.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/event.rb', line 16

def initialize(
  kind:,
  pubkey: nil,
  created_at: nil,
  tags: [],
  content: nil,
  id: nil,
  sig: nil,
  pow: nil,
  delegation: nil,
  subscription_id: nil
)
  @pubkey = pubkey
  @created_at = created_at ? created_at : Time.now.utc.to_i
  @kind = kind
  @tags = tags
  @content = content
  @id = id
  @sig = sig

  @pow = pow
  @delegation = delegation

end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



12
13
14
# File 'lib/event.rb', line 12

def errors
  @errors
end

Class Method Details

.from_message(message) ⇒ Object

Raises:

  • (ArgumentError)


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/event.rb', line 161

def self.from_message(message)
  subscription_id = message[1]
  event_data = message[2]

  event = new(
    subscription_id: subscription_id,
    kind: event_data["kind"],
    pubkey: event_data["pubkey"],
    created_at: event_data["created_at"],
    tags: event_data["tags"],
    content: event_data["content"],
    id: event_data["id"],
    sig: event_data["sig"],
    pow: event_data["nonce"]&.last&.to_i
  )
  raise ArgumentError, "Event is not valid" unless event.valid?
  return event
end

.match_pow_difficulty?(event_id, pow) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/event.rb', line 80

def self.match_pow_difficulty?(event_id, pow)
  pow.nil? || pow == [event_id].pack("H*").unpack("B*")[0].index('1')
end

Instance Method Details

#content=(content) ⇒ Object



54
55
56
57
58
# File 'lib/event.rb', line 54

def content=(content)
  return if @content == content
  @content = content
  reset!
end

#has_tag?(tag) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/event.rb', line 60

def has_tag?(tag)
  @tags.each_slice(2).any? { |e| e.first == tag }
end

#match_pow_difficulty?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/event.rb', line 76

def match_pow_difficulty?
  self.match_pow_difficulty?(@id, pow)
end

#serializeObject



84
85
86
87
88
89
90
91
92
93
# File 'lib/event.rb', line 84

def serialize
  [
    0,
    @pubkey,
    @created_at,
    @kind,
    @tags,
    @content
  ]
end

#signable?Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/event.rb', line 95

def signable?
  @errors = []

  # Check mandatory fields
  @errors << "Kind is missing" if @kind.nil?
  @errors << "Created at is missing" if @created_at.nil?

  # Type validations
  @errors << "Pubkey must be a string" if @pubkey && !@pubkey.is_a?(String)
  @errors << "Kind must be an integer" unless @kind.is_a?(Integer)
  if @created_at
    # Check if it's a valid Unix timestamp or can be converted to one
    begin
      timestamp = if @created_at.is_a?(Time)
        @created_at.to_i
      elsif @created_at.is_a?(Integer)
        @created_at
      elsif @created_at.respond_to?(:to_time)
        @created_at.to_time.to_i
      else
        raise ArgumentError
      end

      # Validate timestamp range
      @errors << "Created at is not a valid timestamp" unless
        timestamp.is_a?(Integer) &&
        timestamp >= 0
    rescue
      @errors << "Created at must be a valid datetime or Unix timestamp"
    end
  end
  @errors << "Tags must be an array" unless @tags.is_a?(Array)

  @errors << "Content must be a string" if @content && !@content.is_a?(String)
  @errors << "ID must be a string" if @id && !@id.is_a?(String)
  @errors << "Signature must be a string" if @sig && !@sig.is_a?(String)
  @errors << "POW must be an integer" if @pow && !@pow.is_a?(Integer)
  @errors << "Delegation must be an array" if @delegation && !@delegation.is_a?(Array)

  if @errors.any?
    raise ValidationError, @errors.join(", ")
  end

  true
end

#to_jsonObject



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/event.rb', line 64

def to_json
  {
    'kind': @kind,
    'pubkey': @pubkey,
    'created_at': @created_at,
    'tags': @tags,
    'content': @content,
    'id': @id,
    'sig': @sig,
  }
end

#typeObject



50
51
52
# File 'lib/event.rb', line 50

def type
  "EVENT"
end

#valid?Boolean

Returns:

  • (Boolean)


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/event.rb', line 141

def valid?
  begin
    signable?
  rescue ValidationError => e
    return false
  end

  # Additional checks for a valid signed event
  @errors = []
  @errors << "ID is missing" if @id.nil?
  @errors << "Signature is missing" if @sig.nil?
  @errors << "Pubkey is missing" if @pubkey.nil?

  if @errors.any?
    raise ValidationError, @errors.join(", ")
  end

  true
end