nwc-ruby 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -3
- data/README.md +6 -6
- data/lib/nwc_ruby/client.rb +5 -2
- data/lib/nwc_ruby/test_runner.rb +12 -5
- data/lib/nwc_ruby/transport/relay_connection.rb +36 -34
- data/lib/nwc_ruby/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5d73701179764ad26d1ad95d212e1abd5856788a951591e3f8489b16e66d0f0
|
|
4
|
+
data.tar.gz: 97047cb484ab707715cd0ce4590d356a9b0e23a307729c808f0098f043c94dba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7bfd6655a81a49a9c04e534a872be4be1438af498b77f452a14706feb8535b9421745cc4f15c488339532019e1f94fb0b56d14c55a565b440f6232fea067b74
|
|
7
|
+
data.tar.gz: 8af52b0b23683c1c62e0d64f4c02597c82c9aa64ae277da7d7099208453ba596db58b3762abdb728a08c2d063013befc39f8c699c39ce4e66aa921c77e90a839
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] — 2026-04-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed all RuboCop offenses (CI now passes clean).
|
|
15
|
+
- Extracted `start_heartbeat` / `start_poll` from `run_one_connection` to reduce
|
|
16
|
+
method length and perceived complexity.
|
|
17
|
+
- Corrected README and CHANGELOG: transport uses 15 s ping keepalive (not 30 s
|
|
18
|
+
ping + 45 s pong deadline — the pong deadline was removed in 0.1.1 due to
|
|
19
|
+
async-websocket 0.30 API changes).
|
|
20
|
+
- Fixed string quoting, hash alignment, guard clause, and line length offenses
|
|
21
|
+
across Rakefile, `bin/nwc_test`, `client.rb`, `test_runner.rb`, and
|
|
22
|
+
`relay_connection.rb`.
|
|
23
|
+
- Excluded `lib/nwc-ruby.rb` from `Naming/FileName` (compatibility shim).
|
|
24
|
+
|
|
10
25
|
## [0.2.0] — 2026-04-20
|
|
11
26
|
|
|
12
27
|
### Changed
|
|
@@ -52,9 +67,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
52
67
|
`multi_pay_keysend`, `make_invoice`, `lookup_invoice`, `list_transactions`,
|
|
53
68
|
`get_balance`, `get_info`, `sign_message`.
|
|
54
69
|
- Notification listener (kinds 23196 and 23197) with dedupe by `payment_hash`.
|
|
55
|
-
- Reliable long-running `Transport::RelayConnection`: RFC 6455 ping (
|
|
56
|
-
|
|
57
|
-
SIGTERM/SIGINT handling.
|
|
70
|
+
- Reliable long-running `Transport::RelayConnection`: RFC 6455 ping (15 s),
|
|
71
|
+
forced recycle (5 min), capped exponential backoff, SIGTERM/SIGINT handling.
|
|
58
72
|
- `NwcRuby.test` and `NwcRuby.test_notifications` diagnostic methods, backed
|
|
59
73
|
by `NwcRuby::TestRunner`. Announces read-only vs read+write, exercises every
|
|
60
74
|
advertised method, pays a Lightning address if the code is read+write.
|
data/README.md
CHANGED
|
@@ -34,8 +34,8 @@ heartbeats, zombie-TCP detection, reconnects, and backoff. You call methods.
|
|
|
34
34
|
- **Both encryption schemes** — NIP-44 v2 when the wallet advertises it
|
|
35
35
|
(validated against [paulmillr's test vectors][vectors]), NIP-04 fallback for
|
|
36
36
|
wallets that haven't migrated.
|
|
37
|
-
- **Bulletproof long-running transport** —
|
|
38
|
-
|
|
37
|
+
- **Bulletproof long-running transport** — 15 s ping keepalive, 5-min forced
|
|
38
|
+
recycle, capped exponential backoff, clean SIGTERM handling. Built on
|
|
39
39
|
[async-websocket][aw] (no dead EventMachine dependency).
|
|
40
40
|
- **Two diagnostic methods** — `NwcRuby.test` (info, read tests, write test
|
|
41
41
|
if applicable) and `NwcRuby.test_notifications` (listen forever in a separate
|
|
@@ -211,10 +211,10 @@ end
|
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
Under the hood, this subscribes to both kind 23196 (NIP-04) and kind 23197
|
|
214
|
-
(NIP-44 v2), dedupes by `payment_hash`, sends a WebSocket ping every
|
|
215
|
-
|
|
216
|
-
force-recycles the connection every 5 minutes
|
|
217
|
-
|
|
214
|
+
(NIP-44 v2), dedupes by `payment_hash`, sends a WebSocket ping every 15 seconds
|
|
215
|
+
to keep middleboxes from idle-closing the socket, reconnects with capped
|
|
216
|
+
exponential backoff on failure, and force-recycles the connection every 5 minutes
|
|
217
|
+
as a belt-and-suspenders check against silently dead TCP streams.
|
|
218
218
|
|
|
219
219
|
#### Resuming after a restart
|
|
220
220
|
|
data/lib/nwc_ruby/client.rb
CHANGED
|
@@ -143,7 +143,7 @@ module NwcRuby
|
|
|
143
143
|
# @param kinds [Array<Integer>] notification kinds to listen for. Defaults
|
|
144
144
|
# to both NIP-04 (23196) and NIP-44 v2 (23197). The listener dedupes by
|
|
145
145
|
# `payment_hash`, so receiving both is safe.
|
|
146
|
-
def subscribe_to_notifications(since: Time.now.to_i,
|
|
146
|
+
def subscribe_to_notifications(since: Time.now.to_i, # rubocop:disable Metrics/MethodLength
|
|
147
147
|
kinds: [NIP47::Methods::KIND_NOTIFICATION_NIP04,
|
|
148
148
|
NIP47::Methods::KIND_NOTIFICATION_NIP44],
|
|
149
149
|
sub_id: "nwc-#{SecureRandom.hex(4)}",
|
|
@@ -168,7 +168,10 @@ module NwcRuby
|
|
|
168
168
|
@notification_connection = conn
|
|
169
169
|
|
|
170
170
|
conn.on_open do |c|
|
|
171
|
-
@logger.info(
|
|
171
|
+
@logger.info(
|
|
172
|
+
"[nwc] subscribing sub_id=#{sub_id} client_pubkey=#{@connection_string.client_pubkey} " \
|
|
173
|
+
"wallet_pubkey=#{@connection_string.wallet_pubkey} kinds=#{kinds.inspect} since=#{since}"
|
|
174
|
+
)
|
|
172
175
|
c.send_req(sub_id: sub_id, filters: filters)
|
|
173
176
|
end
|
|
174
177
|
|
data/lib/nwc_ruby/test_runner.rb
CHANGED
|
@@ -56,8 +56,8 @@ module NwcRuby
|
|
|
56
56
|
|
|
57
57
|
if @pay_to_lightning_address
|
|
58
58
|
if @info.read_only?
|
|
59
|
-
warn!(
|
|
60
|
-
warn!(
|
|
59
|
+
warn!('pay_to_lightning_address was provided but this is a READ-ONLY code — it does not support pay_invoice.')
|
|
60
|
+
warn!(' → Generate a read+write NWC code if you need to test outbound payments.')
|
|
61
61
|
@out.puts
|
|
62
62
|
else
|
|
63
63
|
run_write_tests
|
|
@@ -205,7 +205,10 @@ module NwcRuby
|
|
|
205
205
|
try('lookup_invoice (payment_hash from previous step)', NIP47::Methods::LOOKUP_INVOICE) do
|
|
206
206
|
result = @client.lookup_invoice(payment_hash: @test_payment_hash)
|
|
207
207
|
@out.puts " #{DIM}state=#{result['state']} amount=#{result['amount']} msats#{CLR}"
|
|
208
|
-
|
|
208
|
+
unless result['state'] == 'pending'
|
|
209
|
+
warn!("lookup_invoice: `state` is #{result['state'].inspect}, " \
|
|
210
|
+
"expected 'pending' for a fresh invoice (optional per NIP-47)")
|
|
211
|
+
end
|
|
209
212
|
end
|
|
210
213
|
end
|
|
211
214
|
|
|
@@ -252,8 +255,12 @@ module NwcRuby
|
|
|
252
255
|
fail!('make_invoice: `payment_hash` is not a 64-char hex')
|
|
253
256
|
end
|
|
254
257
|
fail!('make_invoice: `amount` should echo 1000') unless result['amount'] == 1_000
|
|
255
|
-
|
|
256
|
-
|
|
258
|
+
unless result['type'] == 'incoming'
|
|
259
|
+
warn!("make_invoice: `type` is #{result['type'].inspect}, expected 'incoming' (optional per NIP-47)")
|
|
260
|
+
end
|
|
261
|
+
return if result['state'] == 'pending'
|
|
262
|
+
|
|
263
|
+
warn!("make_invoice: `state` is #{result['state'].inspect}, expected 'pending' (optional per NIP-47)")
|
|
257
264
|
end
|
|
258
265
|
|
|
259
266
|
# --- Lightning address resolver ----------------------------------------
|
|
@@ -135,39 +135,8 @@ module NwcRuby
|
|
|
135
135
|
|
|
136
136
|
Async::WebSocket::Client.connect(endpoint) do |conn|
|
|
137
137
|
@conn = conn
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
loop do
|
|
141
|
-
conn.send_ping
|
|
142
|
-
conn.flush
|
|
143
|
-
@logger.debug("[nwc] ping sent")
|
|
144
|
-
|
|
145
|
-
sleep @ping_interval
|
|
146
|
-
break if @stop
|
|
147
|
-
|
|
148
|
-
if Async::Clock.now - opened_at > @recycle_interval
|
|
149
|
-
raise TransportError, "recycle (#{@recycle_interval}s)"
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Some relays (e.g. strfry) store ephemeral events temporarily but
|
|
155
|
-
# do not push them to active subscriptions. Periodic re-subscribe
|
|
156
|
-
# forces the relay to return any newly-stored events.
|
|
157
|
-
poll = if @poll_interval && @poll_cb
|
|
158
|
-
top.async do
|
|
159
|
-
loop do
|
|
160
|
-
sleep @poll_interval
|
|
161
|
-
break if @stop
|
|
162
|
-
|
|
163
|
-
begin
|
|
164
|
-
@poll_cb.call(self)
|
|
165
|
-
rescue StandardError => e
|
|
166
|
-
@logger.warn("[nwc] poll error: #{e.message}")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
138
|
+
heartbeat = start_heartbeat(top, conn, opened_at)
|
|
139
|
+
poll = start_poll(top)
|
|
171
140
|
|
|
172
141
|
@open_cb&.call(self)
|
|
173
142
|
read_loop(conn)
|
|
@@ -183,6 +152,38 @@ module NwcRuby
|
|
|
183
152
|
end
|
|
184
153
|
end
|
|
185
154
|
|
|
155
|
+
def start_heartbeat(top, conn, opened_at)
|
|
156
|
+
top.async do
|
|
157
|
+
loop do
|
|
158
|
+
conn.send_ping
|
|
159
|
+
conn.flush
|
|
160
|
+
@logger.debug('[nwc] ping sent')
|
|
161
|
+
|
|
162
|
+
sleep @ping_interval
|
|
163
|
+
break if @stop
|
|
164
|
+
|
|
165
|
+
raise TransportError, "recycle (#{@recycle_interval}s)" if Async::Clock.now - opened_at > @recycle_interval
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def start_poll(top)
|
|
171
|
+
return unless @poll_interval && @poll_cb
|
|
172
|
+
|
|
173
|
+
top.async do
|
|
174
|
+
loop do
|
|
175
|
+
sleep @poll_interval
|
|
176
|
+
break if @stop
|
|
177
|
+
|
|
178
|
+
begin
|
|
179
|
+
@poll_cb.call(self)
|
|
180
|
+
rescue StandardError => e
|
|
181
|
+
@logger.warn("[nwc] poll error: #{e.message}")
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
186
187
|
def read_loop(conn)
|
|
187
188
|
while (message = conn.read)
|
|
188
189
|
break if @stop
|
|
@@ -224,7 +225,8 @@ module NwcRuby
|
|
|
224
225
|
# Close the socket to unblock the read loop.
|
|
225
226
|
begin
|
|
226
227
|
@conn&.close
|
|
227
|
-
rescue StandardError
|
|
228
|
+
rescue StandardError
|
|
229
|
+
nil
|
|
228
230
|
end
|
|
229
231
|
end
|
|
230
232
|
end
|
data/lib/nwc_ruby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nwc-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- MegalithicBTC
|
|
@@ -222,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
222
222
|
- !ruby/object:Gem::Version
|
|
223
223
|
version: '0'
|
|
224
224
|
requirements: []
|
|
225
|
-
rubygems_version: 3.6.
|
|
225
|
+
rubygems_version: 3.6.8
|
|
226
226
|
specification_version: 4
|
|
227
227
|
summary: Ruby client for Nostr Wallet Connect (NIP-47) with safe long-running relay
|
|
228
228
|
connections.
|