nostrb 0.1.0.1 → 0.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f6159cbd2c246ae573103bd68e56e7cd97b0d3d81d36d8e0816abc5bd23c3c1
4
- data.tar.gz: 8bcf8fe77b3baa61882ea849df549b2800ebbf5e788d55c2c1c00efddc99e1f8
3
+ metadata.gz: a89ed8e93f661c8de971b8cba920363431d8a6a21ba22487877b09c041b25123
4
+ data.tar.gz: 4d2f947fc9244d1db2c003b27c004f7a666fa67e1f3e36283fecfb396228647c
5
5
  SHA512:
6
- metadata.gz: 369121a3fa1b9ead9bc245a3bd5f18ce6a6e2cc3571e1c91019afb4752845de7edbef086b755ad3b90d4b1a072997b2cce8047e19c4db293cc14328332febb56
7
- data.tar.gz: 8b19fa2c2b738e53711fdb4bbdac6a196ce8499b8a212b742be734318a526bd495bed6ddfd97f771c005323ee2583f6207feea8f79850b95e7c3aced24ac3cec
6
+ metadata.gz: 5d0a5f3137c9350da36a431790b3dc18dc3b0d38a32d605ba6bc3ee5c1c9b13950026e0aff037f486f55f6b5d0943323bbd4342de0e558289c7c785f7809c548
7
+ data.tar.gz: 6f835fda78aca5c38d43e05a73af41cc88a13a5ec7a880406bc7b35c9c984cddd0b5ebdce3a56b30faff9a276971584c750c419f010f0f5e52532465c63a2e53
data/Rakefile CHANGED
@@ -5,11 +5,16 @@ Rake::TestTask.new :test do |t|
5
5
  t.warning = true
6
6
  end
7
7
 
8
+ Rake::TestTask.new :test_less do |t|
9
+ t.pattern = "test/{[!r][!e][!l][!a][!y]}*.rb"
10
+ t.warning = true
11
+ end
12
+
8
13
  task :relay do |t|
9
- sh "ruby -I lib examples/relay.rb"
14
+ sh "bundle exec falcon serve --bind wss://localhost:7070"
10
15
  end
11
16
 
12
- task default: [:test, :relay]
17
+ task default: [:test]
13
18
 
14
19
  begin
15
20
  require 'buildar'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0.1
1
+ 0.2.0.1
@@ -0,0 +1,75 @@
1
+ require 'async'
2
+ require 'async/http/endpoint'
3
+ require 'async/websocket/client'
4
+
5
+ require 'nostrb/source'
6
+
7
+ module Nostrb
8
+ class Client
9
+ attr_reader :url, :endpoint, :sid
10
+
11
+ def initialize(relay_url, sid: nil)
12
+ @url = relay_url
13
+ @endpoint = Async::HTTP::Endpoint.parse(@url)
14
+ @sid = sid.nil? ? Source.random_sid : sid
15
+ end
16
+
17
+ def log msg
18
+ warn msg
19
+ end
20
+
21
+ # open, send req, get response, return response
22
+ def single(req)
23
+ Sync do
24
+ Async::WebSocket::Client.connect(@endpoint) do |conn|
25
+ conn.write(Nostrb.json(req))
26
+ conn.flush
27
+ resp = conn.read
28
+ conn.shutdown
29
+ Nostrb.parse(resp.buffer)
30
+ end
31
+ end
32
+ end
33
+
34
+ def publish(signed_event)
35
+ case single(Nostrb::Source.publish(signed_event))
36
+ in ['OK', String => event_id, ok, String => msg]
37
+ log "id mismatch: #{event_id}" unless event_id == signed_event.id
38
+ log msg unless ok
39
+ ok
40
+ in ['NOTICE', String => msg]
41
+ log msg
42
+ end
43
+ end
44
+
45
+ def subscribe(*filters, &blk)
46
+ Sync do
47
+ Async::WebSocket::Client.connect(@endpoint) do |conn|
48
+ conn.write(Nostrb.json(Nostrb::Source.subscribe(@sid, *filters)))
49
+ conn.flush
50
+ eose = false
51
+ while !eose and resp = conn.read
52
+ case Nostrb.parse(resp.buffer)
53
+ in ['EVENT', String => sid, Hash => event]
54
+ log "sid mismatch: #{sid}" unless sid == @sid
55
+ yield event
56
+ in ['EOSE', String => sid]
57
+ log "sid mismatch: #{sid}" unless sid == @sid
58
+ eose = true
59
+ end
60
+ end
61
+ conn.shutdown
62
+ end
63
+ end
64
+ end
65
+
66
+ def close
67
+ case single(Nostrb::Source.close(@sid))
68
+ in ['CLOSED', String => sid, String => msg]
69
+ log "sid mismatch: #{sid}" unless sid == @sid
70
+ log msg unless msg.empty?
71
+ true
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/nostrb/event.rb CHANGED
@@ -19,25 +19,38 @@ module Nostrb
19
19
  tag_values('d', tags).first
20
20
  end
21
21
 
22
+ def self.freeze_tags(tags)
23
+ tags.each { |a|
24
+ a.each { |s| s.freeze }
25
+ a.freeze
26
+ }
27
+ tags.freeze
28
+ end
29
+
22
30
  attr_reader :content, :kind, :tags, :pk
23
31
 
24
32
  def initialize(content = '', kind: 1, tags: [], pk:)
25
- @content = Nostrb.txt!(content)
33
+ @content = Nostrb.txt!(content) # frozen
26
34
  @kind = Nostrb.kind!(kind)
27
35
  @tags = Nostrb.tags!(tags)
28
- @pk = Nostrb.key!(pk)
36
+ @pk = Nostrb.key!(pk) # frozen
29
37
  end
30
38
 
31
39
  alias_method :to_s, :content
32
40
 
33
41
  def serialize(created_at)
34
- [0, self.pubkey, Nostrb.int!(created_at), @kind, @tags, @content]
42
+ [0, self.pubkey, Nostrb.int!(created_at), @kind, @tags, @content].freeze
43
+ end
44
+
45
+ def freeze
46
+ Event.freeze_tags(@tags)
47
+ self
35
48
  end
36
49
 
37
50
  def to_a = serialize(Time.now.to_i)
38
51
  def pubkey = SchnorrSig.bin2hex(@pk)
39
52
  def digest(created_at) = Event.digest(serialize(created_at))
40
- def sign(sk) = SignedEvent.new(self, sk)
53
+ def sign(sk) = SignedEvent.new(self.freeze, sk)
41
54
 
42
55
  #
43
56
  # Tags
@@ -82,10 +95,11 @@ module Nostrb
82
95
  Nostrb.pubkey!(parsed.fetch("pubkey"))
83
96
  Nostrb.kind!(parsed.fetch("kind"))
84
97
  Nostrb.tags!(parsed.fetch("tags"))
98
+ Event.freeze_tags(parsed['tags'])
85
99
  Nostrb.int!(parsed.fetch("created_at"))
86
100
  Nostrb.id!(parsed.fetch("id"))
87
101
  Nostrb.sig!(parsed.fetch("sig"))
88
- parsed
102
+ parsed.freeze
89
103
  end
90
104
 
91
105
  def self.digest(valid) = Nostrb.digest(Nostrb.json(serialize(valid)))
@@ -96,7 +110,7 @@ module Nostrb
96
110
  valid["created_at"],
97
111
  valid["kind"],
98
112
  valid["tags"],
99
- valid["content"], ]
113
+ valid["content"], ].freeze
100
114
  end
101
115
 
102
116
  # Validate the id (optional) and signature
@@ -127,8 +141,8 @@ module Nostrb
127
141
  def initialize(event, sk)
128
142
  @event = Nostrb.check!(event, Event)
129
143
  @created_at = Time.now.to_i
130
- @digest = @event.digest(@created_at)
131
- @signature = SchnorrSig.sign(Nostrb.key!(sk), @digest)
144
+ @digest = @event.digest(@created_at).freeze
145
+ @signature = SchnorrSig.sign(Nostrb.key!(sk), @digest).freeze
132
146
  end
133
147
 
134
148
  def content = @event.content
@@ -138,8 +152,8 @@ module Nostrb
138
152
  def to_s = @event.to_s
139
153
  def serialize = @event.serialize(@created_at)
140
154
 
141
- def id = SchnorrSig.bin2hex(@digest)
142
- def sig = SchnorrSig.bin2hex(@signature)
155
+ def id = SchnorrSig.bin2hex(@digest).freeze
156
+ def sig = SchnorrSig.bin2hex(@signature).freeze
143
157
 
144
158
  def to_h
145
159
  Hash[ "content" => @event.content,
@@ -148,7 +162,7 @@ module Nostrb
148
162
  "pubkey" => @event.pubkey,
149
163
  "created_at" => @created_at,
150
164
  "id" => self.id,
151
- "sig" => self.sig ]
165
+ "sig" => self.sig ].freeze
152
166
  end
153
167
  end
154
168
  end
data/lib/nostrb/filter.rb CHANGED
@@ -71,6 +71,10 @@ module Nostrb
71
71
  @limit = nil
72
72
  end
73
73
 
74
+ def to_s
75
+ self.to_h.to_s
76
+ end
77
+
74
78
  def add_ids(*event_ids)
75
79
  @ids += event_ids.each { |id| Nostrb.id!(id) }
76
80
  end
@@ -138,7 +142,7 @@ module Nostrb
138
142
  h["since"] = @since unless @since.nil?
139
143
  h["until"] = @until unless @until.nil?
140
144
  h["limit"] = @limit unless @limit.nil?
141
- h
145
+ h.freeze
142
146
  end
143
147
  end
144
148
  end
data/lib/nostrb/json.rb CHANGED
@@ -14,6 +14,6 @@ module Nostrb
14
14
  space_before: '',
15
15
  }
16
16
 
17
- def self.parse(json) = JSON.parse(json, **JSON_OPTIONS)
18
- def self.json(object) = JSON.generate(object, **JSON_OPTIONS)
17
+ def self.parse(json) = JSON.parse(json, **JSON_OPTIONS).freeze
18
+ def self.json(object) = JSON.generate(object, **JSON_OPTIONS).freeze
19
19
  end
data/lib/nostrb/oj.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'oj'
2
2
 
3
3
  module Nostrb
4
- def self.parse(json) = Oj.load(json, mode: :strict)
5
- def self.json(object) = Oj.dump(object, mode: :strict)
4
+ def self.parse(json) = Oj.load(json, mode: :strict).freeze
5
+ def self.json(object) = Oj.dump(object, mode: :strict).freeze
6
6
  end
data/lib/nostrb/relay.rb CHANGED
@@ -14,7 +14,7 @@ require 'set' # jruby wants this
14
14
  # for replaceable events with same timestamp, lowest id wins
15
15
 
16
16
  module Nostrb
17
- class Server
17
+ class Relay
18
18
  def self.event(sid, event) = ["EVENT", Nostrb.sid!(sid), event.to_h]
19
19
  def self.ok(eid, msg = "", ok: true)
20
20
  ["OK", Nostrb.id!(eid), !!ok, ok ? Nostrb.txt!(msg) : Nostrb.help!(msg)]
@@ -58,10 +58,10 @@ module Nostrb
58
58
  when 'CLOSE'
59
59
  [handle_close(Nostrb.sid!(a[1]))]
60
60
  else
61
- [Server.notice("unexpected: #{a[0].inspect}")]
61
+ [Relay.notice("unexpected: #{a[0].inspect}")]
62
62
  end
63
63
  rescue StandardError => e
64
- [Server.error(e)]
64
+ [Relay.error(e)]
65
65
  end
66
66
  end
67
67
 
@@ -70,7 +70,7 @@ module Nostrb
70
70
  begin
71
71
  hsh = SignedEvent.validate!(hsh)
72
72
  rescue Nostrb::Error, KeyError, RuntimeError => e
73
- return Server.error(e)
73
+ return Relay.error(e)
74
74
  end
75
75
 
76
76
  eid = hsh.fetch('id')
@@ -94,11 +94,11 @@ module Nostrb
94
94
  raise(SignedEvent::Error, "kind: #{hsh['kind']}")
95
95
  end
96
96
 
97
- Server.ok(eid)
97
+ Relay.ok(eid)
98
98
  rescue SignedEvent::Error => e
99
- Server.ok(eid, Server.message(e), ok: false)
99
+ Relay.ok(eid, Relay.message(e), ok: false)
100
100
  rescue Nostrb::Error, KeyError, RuntimeError => e
101
- Server.error(e)
101
+ Relay.error(e)
102
102
  end
103
103
  end
104
104
 
@@ -124,19 +124,19 @@ module Nostrb
124
124
 
125
125
  filters.each { |f|
126
126
  @reader.process_events(f).each { |h|
127
- responses << Server.event(sid, h) if f.match? h
127
+ responses << Relay.event(sid, h) if f.match? h
128
128
  }
129
129
  @reader.process_r_events(f).each { |h|
130
- responses << Server.event(sid, h) if f.match? h
130
+ responses << Relay.event(sid, h) if f.match? h
131
131
  }
132
132
  }
133
133
  responses = responses.to_a
134
- responses << Server.eose(sid)
134
+ responses << Relay.eose(sid)
135
135
  end
136
136
 
137
137
  # single response
138
138
  def handle_close(sid)
139
- Server.closed(sid, "reason: CLOSE requested")
139
+ Relay.closed(sid, "reason: CLOSE requested")
140
140
  end
141
141
  end
142
142
  end
data/lib/nostrb/sequel.rb CHANGED
@@ -4,7 +4,7 @@ require 'nostrb/sqlite'
4
4
  module Nostrb
5
5
  module Sequel
6
6
  class Storage < SQLite::Storage
7
- FILENAME = 'sequel.db'
7
+ FILENAME = 'sequel.tmp.db'
8
8
  TABLES = [:events, :tags, :r_events, :r_tags]
9
9
 
10
10
  def self.schema_line(col, cfg)
@@ -77,7 +77,7 @@ module Nostrb
77
77
  end
78
78
 
79
79
  def create_tables
80
- @db.create_table :events do
80
+ @db.create_table :events, strict: true do
81
81
  text :content, null: false
82
82
  int :kind, null: false
83
83
  text :tags, null: false
@@ -89,8 +89,7 @@ module Nostrb
89
89
  text :sig, null: false
90
90
  end
91
91
 
92
- @db.create_table :tags do
93
- # text :event_id, null: false # fk
92
+ @db.create_table :tags, strict: true do
94
93
  foreign_key :event_id, :events,
95
94
  key: :id,
96
95
  type: :text,
@@ -103,7 +102,7 @@ module Nostrb
103
102
  text :json, null: false
104
103
  end
105
104
 
106
- @db.create_table :r_events do
105
+ @db.create_table :r_events, strict: true do
107
106
  text :content, null: false
108
107
  int :kind, null: false
109
108
  text :tags, null: false
@@ -120,7 +119,7 @@ module Nostrb
120
119
  }
121
120
  end
122
121
 
123
- @db.create_table :r_tags do
122
+ @db.create_table :r_tags, strict: true do
124
123
  foreign_key :r_event_id, :r_events,
125
124
  key: :id,
126
125
  type: :text,
@@ -145,7 +144,7 @@ module Nostrb
145
144
  'pubkey' => event_row.fetch(:pubkey),
146
145
  'created_at' => event_row.fetch(:created_at),
147
146
  'id' => event_row.fetch(:id),
148
- 'sig' => event_row.fetch(:sig), ]
147
+ 'sig' => event_row.fetch(:sig), ].freeze
149
148
  end
150
149
 
151
150
  def self.event_clauses(filter)
data/lib/nostrb/source.rb CHANGED
@@ -11,30 +11,30 @@ module Nostrb
11
11
  #######################
12
12
  # Client Requests
13
13
 
14
- def self.publish(signed) = ["EVENT", signed.to_h]
14
+ def self.publish(signed) = ["EVENT", signed.to_h].freeze
15
15
 
16
16
  def self.subscribe(sid, *filters)
17
17
  ["REQ", Nostrb.sid!(sid), *filters.map { |f|
18
18
  Nostrb.check!(f, Filter).to_h
19
- }]
19
+ }].freeze
20
20
  end
21
21
 
22
- def self.close(sid) = ["CLOSE", Nostrb.sid!(sid)]
22
+ def self.close(sid) = ["CLOSE", Nostrb.sid!(sid)].freeze
23
23
 
24
24
  #######################
25
25
  # Utils / Init
26
26
 
27
27
  def self.random_sid
28
- SchnorrSig.bin2hex Random.bytes(32)
28
+ SchnorrSig.bin2hex(Random.bytes(32)).freeze
29
29
  end
30
30
 
31
31
  attr_reader :pk
32
32
 
33
33
  def initialize(pk)
34
- @pk = Nostrb.key!(pk)
34
+ @pk = Nostrb.key!(pk).freeze
35
35
  end
36
36
 
37
- def pubkey = SchnorrSig.bin2hex(@pk)
37
+ def pubkey = SchnorrSig.bin2hex(@pk).freeze
38
38
 
39
39
  ############################
40
40
  # Event Creation
data/lib/nostrb/sqlite.rb CHANGED
@@ -112,7 +112,7 @@ module Nostrb
112
112
  MB = KB * 1024
113
113
  GB = MB * 1024
114
114
 
115
- FILENAME = 'tmp.db'
115
+ FILENAME = 'sqlite.tmp.db'
116
116
  CONFIG = {
117
117
  default_transaction_mode: :immediate,
118
118
  }
@@ -241,7 +241,7 @@ CREATE TABLE events (content TEXT NOT NULL,
241
241
  pubkey TEXT NOT NULL,
242
242
  created_at INT NOT NULL,
243
243
  id TEXT PRIMARY KEY NOT NULL,
244
- sig TEXT NOT NULL)
244
+ sig TEXT NOT NULL) STRICT
245
245
  SQL
246
246
 
247
247
  @db.execute <<SQL
@@ -250,7 +250,7 @@ CREATE TABLE tags (event_id TEXT NOT NULL REFERENCES events (id)
250
250
  created_at INT NOT NULL,
251
251
  tag TEXT NOT NULL,
252
252
  value TEXT NOT NULL,
253
- json TEXT NOT NULL)
253
+ json TEXT NOT NULL) STRICT
254
254
  SQL
255
255
 
256
256
  @db.execute <<SQL
@@ -261,7 +261,7 @@ CREATE TABLE r_events (content TEXT NOT NULL,
261
261
  pubkey TEXT NOT NULL,
262
262
  created_at INT NOT NULL,
263
263
  id TEXT PRIMARY KEY NOT NULL,
264
- sig TEXT NOT NULL)
264
+ sig TEXT NOT NULL) STRICT
265
265
  SQL
266
266
 
267
267
  @db.execute <<SQL
@@ -270,7 +270,7 @@ CREATE TABLE r_tags (r_event_id TEXT NOT NULL REFERENCES r_events (id)
270
270
  created_at INT NOT NULL,
271
271
  tag TEXT NOT NULL,
272
272
  value TEXT NOT NULL,
273
- json TEXT NOT NULL)
273
+ json TEXT NOT NULL) STRICT
274
274
  SQL
275
275
  end
276
276
 
@@ -394,6 +394,10 @@ SQL
394
394
  end
395
395
 
396
396
  class Writer < Storage
397
+ def self.serialize_tags(valid)
398
+ valid.merge("tags" => Nostrb.json(valid["tags"]))
399
+ end
400
+
397
401
  # a valid hash, as returned from SignedEvent.validate!
398
402
  def add_event(valid)
399
403
  @add_event ||= @db.prepare("INSERT INTO events
@@ -402,12 +406,10 @@ SQL
402
406
  @add_tag ||= @db.prepare("INSERT INTO tags
403
407
  VALUES (:event_id, :created_at,
404
408
  :tag, :value, :json)")
405
- tags = valid["tags"]
406
- valid["tags"] = Nostrb.json(tags)
407
- @add_event.execute(valid) # insert event
408
- tags.each { |a| # insert tags
409
- @add_tag.execute(event_id: valid.fetch('id'),
410
- created_at: valid.fetch('created_at'),
409
+ @add_event.execute(Writer.serialize_tags(valid)) # insert event
410
+ valid["tags"].each { |a| # insert tags
411
+ @add_tag.execute(event_id: valid['id'],
412
+ created_at: valid['created_at'],
411
413
  tag: a[0],
412
414
  value: a[1],
413
415
  json: Nostrb.json(a))
@@ -423,14 +425,13 @@ SQL
423
425
  @add_rtag ||= @db.prepare("INSERT INTO r_tags
424
426
  VALUES (:r_event_id, :created_at,
425
427
  :tag, :value, :json)")
426
- tags = valid.fetch('tags')
427
- d_tags = tags.select { |a| a[0] == 'd' }
428
- valid['d_tag'] = d_tags.empty? ? nil : d_tags[0][1]
429
- valid['tags'] = Nostrb.json(tags)
430
- @add_r_event.execute(valid) # upsert event
431
- tags.each { |a| # insert tags
432
- @add_rtag.execute(r_event_id: valid.fetch('id'),
433
- created_at: valid.fetch('created_at'),
428
+ tags = valid['tags']
429
+ record = Writer.serialize_tags(valid)
430
+ record['d_tag'] = Event.d_tag(tags)
431
+ @add_r_event.execute(record) # upsert event
432
+ tags.each { |a| # insert tags
433
+ @add_rtag.execute(r_event_id: valid['id'],
434
+ created_at: valid['created_at'],
434
435
  tag: a[0],
435
436
  value: a[1],
436
437
  json: Nostrb.json(a))
data/lib/nostrb.rb CHANGED
@@ -7,14 +7,12 @@ rescue LoadError
7
7
  end
8
8
 
9
9
  module Nostrb
10
- GEMS = %w[rbsecp256k1 oj sqlite3 sequel]
11
-
12
10
  class Error < RuntimeError; end
13
11
  class SizeError < Error; end
14
12
  class FormatError < Error; end
15
13
 
16
14
  # return 32 bytes binary
17
- def self.digest(str) = Digest::SHA256.digest(str)
15
+ def self.digest(str) = Digest::SHA256.digest(str).freeze
18
16
 
19
17
  def self.check!(val, cls)
20
18
  val.is_a?(cls) ? val : raise(TypeError, "#{cls} expected: #{val.inspect}")
@@ -45,7 +43,7 @@ module Nostrb
45
43
  end
46
44
  raise(SizeError, str.length) if !length.nil? and str.length != length
47
45
  raise(SizeError, str.length) if !max.nil? and str.length > max
48
- str
46
+ str.freeze
49
47
  end
50
48
 
51
49
  def self.bin!(str, length: nil, max: nil)
@@ -58,6 +56,7 @@ module Nostrb
58
56
  str!(str, binary: false, length: length, max: max)
59
57
  end
60
58
  def self.pubkey!(str) = txt!(str, length: 64)
59
+
61
60
  def self.id!(str) = txt!(str, length: 64)
62
61
  def self.sid!(str) = txt!(str, max: 64)
63
62
  def self.sig!(str) = txt!(str, length: 128)
@@ -73,39 +72,44 @@ module Nostrb
73
72
  ary!(ary, max: 9999).each { |a| ary!(a, max: 99).each { |s| txt!(s) } }
74
73
  end
75
74
 
76
- def self.rbsecp256k1?
77
- begin
78
- require 'rbsecp256k1'; Secp256k1
79
- rescue LoadError, NameError
80
- false
75
+ # optional dependencies
76
+ module Optional
77
+ GEMS = %w[rbsecp256k1 oj sqlite3 sequel]
78
+
79
+ def self.rbsecp256k1?
80
+ begin
81
+ require 'rbsecp256k1'; ::Secp256k1
82
+ rescue LoadError, NameError
83
+ false
84
+ end
81
85
  end
82
- end
83
86
 
84
- def self.oj?
85
- begin
86
- require 'oj'; Oj
87
- rescue LoadError, NameError
88
- false
87
+ def self.oj?
88
+ begin
89
+ require 'oj'; ::Oj
90
+ rescue LoadError, NameError
91
+ false
92
+ end
89
93
  end
90
- end
91
94
 
92
- def self.sqlite3?
93
- begin
94
- require 'sqlite3'; SQLite3
95
- rescue LoadError, NameError
96
- false
95
+ def self.sqlite3?
96
+ begin
97
+ require 'sqlite3'; ::SQLite3
98
+ rescue LoadError, NameError
99
+ false
100
+ end
97
101
  end
98
- end
99
102
 
100
- def self.sequel?
101
- begin
102
- require 'sequel'; Sequel
103
- rescue LoadError, NameError
104
- false
103
+ def self.sequel?
104
+ begin
105
+ require 'sequel'; ::Sequel
106
+ rescue LoadError, NameError
107
+ false
108
+ end
105
109
  end
106
- end
107
110
 
108
- def self.gem_check
109
- GEMS.map { |gem| [gem, self.send("#{gem}?")] }.to_h
111
+ def self.gem_check
112
+ GEMS.map { |gem| [gem, self.send("#{gem}?")] }.to_h
113
+ end
110
114
  end
111
115
  end
data/test/relay.rb CHANGED
@@ -12,7 +12,7 @@ DB_FILE = ENV['INPUT_DB_FILE'] || 'testing.db'
12
12
  # use SQLite backing
13
13
  SQLite::Setup.new(DB_FILE).setup
14
14
 
15
- describe Server do
15
+ describe Relay do
16
16
  def valid_response!(resp)
17
17
  types = ["EVENT", "OK", "EOSE", "CLOSED", "NOTICE"]
18
18
  expect(resp).must_be_kind_of Array
@@ -26,7 +26,7 @@ describe Server do
26
26
  describe "class functions" do
27
27
  it "has an EVENT response, given subscriber_id and requested event" do
28
28
  sid = '1234'
29
- resp = Server.event(sid, Test::SIGNED)
29
+ resp = Relay.event(sid, Test::SIGNED)
30
30
  valid_response!(resp)
31
31
  expect(resp[0]).must_equal "EVENT"
32
32
  expect(resp[1]).must_equal sid
@@ -36,7 +36,7 @@ describe Server do
36
36
 
37
37
  it "has an OK response, given an event_id" do
38
38
  # positive ok
39
- resp = Server.ok(Test::SIGNED.id)
39
+ resp = Relay.ok(Test::SIGNED.id)
40
40
  valid_response!(resp)
41
41
  expect(resp[0]).must_equal "OK"
42
42
  expect(resp[1]).must_equal Test::SIGNED.id
@@ -44,7 +44,7 @@ describe Server do
44
44
  expect(resp[3]).must_be_kind_of String # empty by default
45
45
 
46
46
  # negative ok
47
- resp = Server.ok(Test::SIGNED.id, "error: testing", ok: false)
47
+ resp = Relay.ok(Test::SIGNED.id, "error: testing", ok: false)
48
48
  valid_response!(resp)
49
49
  expect(resp[0]).must_equal "OK"
50
50
  expect(resp[1]).must_equal Test::SIGNED.id
@@ -54,14 +54,14 @@ describe Server do
54
54
 
55
55
  # ok:false requires nonempty message
56
56
  expect {
57
- Server.ok(Test::SIGNED.id, "", ok: false)
57
+ Relay.ok(Test::SIGNED.id, "", ok: false)
58
58
  }.must_raise FormatError
59
- expect { Server.ok(Test::SIGNED.id, ok: false) }.must_raise FormatError
59
+ expect { Relay.ok(Test::SIGNED.id, ok: false) }.must_raise FormatError
60
60
  end
61
61
 
62
62
  it "has an EOSE response to conclude a series of EVENT responses" do
63
63
  sid = '1234'
64
- resp = Server.eose(sid)
64
+ resp = Relay.eose(sid)
65
65
  valid_response!(resp)
66
66
  expect(resp[0]).must_equal "EOSE"
67
67
  expect(resp[1]).must_equal sid
@@ -70,7 +70,7 @@ describe Server do
70
70
  it "has a CLOSED response to shut down a subscriber" do
71
71
  sid = '1234'
72
72
  msg = "closed: bye"
73
- resp = Server.closed(sid, msg)
73
+ resp = Relay.closed(sid, msg)
74
74
  valid_response!(resp)
75
75
  expect(resp[0]).must_equal "CLOSED"
76
76
  expect(resp[1]).must_equal sid
@@ -80,7 +80,7 @@ describe Server do
80
80
  it "has a NOTICE response to provide any message to the user" do
81
81
  msg = "all i ever really wanna do is get nice, " +
82
82
  "get loose and goof this little slice of life"
83
- resp = Server.notice(msg)
83
+ resp = Relay.notice(msg)
84
84
  valid_response!(resp)
85
85
  expect(resp[0]).must_equal "NOTICE"
86
86
  expect(resp[1]).must_equal msg
@@ -89,16 +89,16 @@ describe Server do
89
89
  it "formats Exceptions to a common string representation" do
90
90
  r = RuntimeError.new("stuff")
91
91
  expect(r).must_be_kind_of Exception
92
- expect(Server.message(r)).must_equal "RuntimeError: stuff"
92
+ expect(Relay.message(r)).must_equal "RuntimeError: stuff"
93
93
 
94
94
  e = Nostrb::Error.new("things")
95
95
  expect(e).must_be_kind_of Exception
96
- expect(Server.message(e)).must_equal "Error: things"
96
+ expect(Relay.message(e)).must_equal "Error: things"
97
97
  end
98
98
 
99
99
  it "uses NOTICE to return errors" do
100
100
  e = RuntimeError.new "stuff"
101
- resp = Server.error(e)
101
+ resp = Relay.error(e)
102
102
  valid_response!(resp)
103
103
  expect(resp[0]).must_equal "NOTICE"
104
104
  expect(resp[1]).must_equal "RuntimeError: stuff"
@@ -106,14 +106,14 @@ describe Server do
106
106
  end
107
107
 
108
108
  it "has no initialization parameters" do
109
- s = Server.new(DB_FILE)
110
- expect(s).must_be_kind_of Server
109
+ s = Relay.new(DB_FILE)
110
+ expect(s).must_be_kind_of Relay
111
111
  end
112
112
 
113
113
  # respond OK: true
114
114
  it "has a single response to EVENT requests" do
115
115
  json = Nostrb.json(Source.publish(Test::SIGNED))
116
- responses = Server.new(DB_FILE).ingest(json)
116
+ responses = Relay.new(DB_FILE).ingest(json)
117
117
  expect(responses).must_be_kind_of Array
118
118
  expect(responses.length).must_equal 1
119
119
 
@@ -126,7 +126,7 @@ describe Server do
126
126
 
127
127
  # store and retrieve with a subscription filter
128
128
  it "stores inbound events" do
129
- s = Server.new(DB_FILE)
129
+ s = Relay.new(DB_FILE)
130
130
  sk, pk = SchnorrSig.keypair
131
131
  e = Event.new('sqlite', pk: pk).sign(sk)
132
132
  resp = s.ingest Nostrb.json(Source.publish(e))
@@ -152,7 +152,7 @@ describe Server do
152
152
  end
153
153
 
154
154
  it "has multiple responses to REQ requets" do
155
- s = Server.new(DB_FILE)
155
+ s = Relay.new(DB_FILE)
156
156
  sk, pk = SchnorrSig.keypair
157
157
  e = Event.new('first', pk: pk).sign(sk)
158
158
  resp = s.ingest Nostrb.json(Source.publish(e))
@@ -204,7 +204,7 @@ describe Server do
204
204
  end
205
205
 
206
206
  it "has a single response to CLOSE requests" do
207
- s = Server.new(DB_FILE)
207
+ s = Relay.new(DB_FILE)
208
208
  sid = Test::EVENT.pubkey
209
209
  responses = s.ingest(Nostrb.json(Source.close(sid)))
210
210
 
@@ -221,9 +221,9 @@ describe Server do
221
221
  describe "error handling" do
222
222
  # invalid request type
223
223
  it "handles unknown unknown request types with an error notice" do
224
- a = Source.publish Test::SIGNED
224
+ a = Source.publish(Test::SIGNED).dup
225
225
  a[0] = 'NONSENSE'
226
- responses = Server.new(DB_FILE).ingest(Nostrb.json(a))
226
+ responses = Relay.new(DB_FILE).ingest(Nostrb.json(a))
227
227
  expect(responses).must_be_kind_of Array
228
228
  expect(responses.length).must_equal 1
229
229
 
@@ -236,10 +236,10 @@ describe Server do
236
236
 
237
237
  # replace leading open brace with space
238
238
  it "handles JSON parse errors with an error notice" do
239
- j = Nostrb.json(Nostrb::Source.publish(Test::SIGNED))
239
+ j = Nostrb.json(Nostrb::Source.publish(Test::SIGNED)).dup
240
240
  expect(j[9]).must_equal '{'
241
241
  j[9] = ' '
242
- resp = Server.new(DB_FILE).ingest(j)
242
+ resp = Relay.new(DB_FILE).ingest(j)
243
243
  expect(resp).must_be_kind_of Array
244
244
  expect(resp.length).must_equal 1
245
245
 
@@ -251,11 +251,12 @@ describe Server do
251
251
 
252
252
  # add "stuff":"things"
253
253
  it "handles unexpected fields with an error notice" do
254
- a = Nostrb::Source.publish(Test::SIGNED)
254
+ a = Nostrb::Source.publish(Test::SIGNED).dup
255
255
  expect(a[1]).must_be_kind_of Hash
256
+ a[1] = a[1].dup
256
257
  a[1]["stuff"] = "things"
257
258
 
258
- resp = Server.new(DB_FILE).ingest(Nostrb.json(a))
259
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(a))
259
260
  expect(resp).must_be_kind_of Array
260
261
  expect(resp.length).must_equal 1
261
262
 
@@ -269,9 +270,11 @@ describe Server do
269
270
  it "handles missing fields with an error notice" do
270
271
  a = Nostrb::Source.publish(Test::SIGNED)
271
272
  expect(a[1]).must_be_kind_of Hash
273
+ a = a.dup
274
+ a[1] = a[1].dup
272
275
  a[1].delete("tags")
273
276
 
274
- resp = Server.new(DB_FILE).ingest(Nostrb.json(a))
277
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(a))
275
278
  expect(resp).must_be_kind_of Array
276
279
  expect(resp.length).must_equal 1
277
280
 
@@ -283,11 +286,12 @@ describe Server do
283
286
 
284
287
  # cut "id" in half
285
288
  it "handles field format errors with an error notice" do
286
- a = Nostrb::Source.publish(Test::SIGNED)
289
+ a = Nostrb::Source.publish(Test::SIGNED).dup
287
290
  expect(a[1]).must_be_kind_of Hash
291
+ a[1] = a[1].dup
288
292
  a[1]["id"] = a[1]["id"].slice(0, 32)
289
293
 
290
- resp = Server.new(DB_FILE).ingest(Nostrb.json(a))
294
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(a))
291
295
  expect(resp).must_be_kind_of Array
292
296
  expect(resp.length).must_equal 1
293
297
 
@@ -301,9 +305,11 @@ describe Server do
301
305
  it "handles invalid signature with OK:false" do
302
306
  a = Nostrb::Source.publish(Test::SIGNED)
303
307
  expect(a[1]).must_be_kind_of Hash
308
+ a = a.dup
309
+ a[1] = a[1].dup
304
310
  a[1]["sig"] = SchnorrSig.bin2hex(Random.bytes(64))
305
311
 
306
- resp = Server.new(DB_FILE).ingest(Nostrb.json(a))
312
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(a))
307
313
  expect(resp).must_be_kind_of Array
308
314
  expect(resp.length).must_equal 1
309
315
 
@@ -318,16 +324,17 @@ describe Server do
318
324
 
319
325
  # "id" and "sig" spoofed from another event
320
326
  it "handles spoofed id with OK:false" do
321
- orig = Source.publish(Test.new_event('orig'))
322
- spoof = Source.publish(Test::SIGNED)
327
+ orig = Source.publish(Test.new_event('orig')).dup
328
+ spoof = Source.publish(Test::SIGNED).dup
323
329
 
330
+ orig[1] = orig[1].dup
324
331
  orig[1]["id"] = spoof[1]["id"]
325
332
  orig[1]["sig"] = spoof[1]["sig"]
326
333
 
327
334
  # now sig and id agree with each other, but not orig's content/metadata
328
335
  # the signature should verify, but the id should not
329
336
 
330
- resp = Server.new(DB_FILE).ingest(Nostrb.json(orig))
337
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(orig))
331
338
  expect(resp).must_be_kind_of Array
332
339
  expect(resp.length).must_equal 1
333
340
 
@@ -342,10 +349,11 @@ describe Server do
342
349
 
343
350
  # random "id"
344
351
  it "handles invalid id with OK:false" do
345
- a = Source.publish(Test::SIGNED)
352
+ a = Source.publish(Test::SIGNED).dup
353
+ a[1] = a[1].dup
346
354
  a[1]["id"] = SchnorrSig.bin2hex(Random.bytes(32))
347
355
 
348
- resp = Server.new(DB_FILE).ingest(Nostrb.json(a))
356
+ resp = Relay.new(DB_FILE).ingest(Nostrb.json(a))
349
357
  expect(resp).must_be_kind_of Array
350
358
  expect(resp.length).must_equal 1
351
359
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nostrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.1
4
+ version: 0.2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -33,6 +33,7 @@ files:
33
33
  - Rakefile
34
34
  - VERSION
35
35
  - lib/nostrb.rb
36
+ - lib/nostrb/client.rb
36
37
  - lib/nostrb/event.rb
37
38
  - lib/nostrb/filter.rb
38
39
  - lib/nostrb/json.rb