nostr_ruby 0.2.0 → 0.3.0
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/Gemfile.lock +10 -8
- data/README.md +286 -161
- data/lib/bech32.rb +84 -0
- data/lib/client.rb +184 -0
- data/lib/context.rb +48 -0
- data/lib/event.rb +188 -0
- data/lib/event_wizard.rb +31 -0
- data/lib/filter.rb +57 -0
- data/lib/key.rb +15 -0
- data/lib/kind.rb +10 -0
- data/lib/message_handler.rb +107 -0
- data/lib/nostr_ruby.rb +13 -273
- data/lib/signer.rb +89 -0
- data/lib/version.rb +3 -0
- data/nostr_ruby-0.2.0.gem +0 -0
- data/nostr_ruby.gemspec +4 -4
- metadata +19 -9
- data/lib/custom_addr.rb +0 -59
- data/lib/nostr_ruby/version.rb +0 -3
data/lib/nostr_ruby.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'version'
|
2
2
|
require_relative 'crypto_tools'
|
3
|
+
|
3
4
|
require 'ecdsa'
|
4
5
|
require 'schnorr'
|
5
6
|
require 'json'
|
@@ -8,277 +9,16 @@ require 'bech32'
|
|
8
9
|
require 'unicode/emoji'
|
9
10
|
require 'websocket-client-simple'
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
key[:private_key]
|
23
|
-
end
|
24
|
-
|
25
|
-
hex_public_key = if key[:public_key]&.include?('npub')
|
26
|
-
Nostr.to_hex(key[:public_key])
|
27
|
-
else
|
28
|
-
key[:public_key]
|
29
|
-
end
|
30
|
-
|
31
|
-
if hex_private_key
|
32
|
-
@private_key = hex_private_key
|
33
|
-
group = ECDSA::Group::Secp256k1
|
34
|
-
@public_key = group.generator.multiply_by_scalar(private_key.to_i(16)).x.to_s(16).rjust(64, '0')
|
35
|
-
elsif hex_public_key
|
36
|
-
@public_key = hex_public_key
|
37
|
-
else
|
38
|
-
raise 'Missing private or public key'
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def keys
|
43
|
-
keys = { public_key: @public_key }
|
44
|
-
keys[:private_key] = @private_key if @private_key
|
45
|
-
keys
|
46
|
-
end
|
47
|
-
|
48
|
-
def bech32_keys
|
49
|
-
bech32_keys = { public_key: Nostr.to_bech32(@public_key, 'npub') }
|
50
|
-
bech32_keys[:private_key] = Nostr.to_bech32(@private_key, 'nsec') if @private_key
|
51
|
-
bech32_keys
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.to_hex(bech32_key)
|
55
|
-
public_addr = CustomAddr.new(bech32_key)
|
56
|
-
public_addr.to_scriptpubkey
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.to_bech32(hex_key, hrp)
|
60
|
-
custom_addr = CustomAddr.new
|
61
|
-
custom_addr.scriptpubkey = hex_key
|
62
|
-
custom_addr.hrp = hrp
|
63
|
-
custom_addr.addr
|
64
|
-
end
|
65
|
-
|
66
|
-
def sign_event(event)
|
67
|
-
raise 'Invalid pubkey' unless event[:pubkey].is_a?(String) && event[:pubkey].size == 64
|
68
|
-
raise 'Invalid created_at' unless event[:created_at].is_a?(Integer)
|
69
|
-
raise 'Invalid kind' unless (0..29_999).include?(event[:kind])
|
70
|
-
raise 'Invalid tags' unless event[:tags].is_a?(Array)
|
71
|
-
raise 'Invalid content' unless event[:content].is_a?(String)
|
72
|
-
|
73
|
-
serialized_event = [
|
74
|
-
0,
|
75
|
-
event[:pubkey],
|
76
|
-
event[:created_at],
|
77
|
-
event[:kind],
|
78
|
-
event[:tags],
|
79
|
-
event[:content]
|
80
|
-
]
|
81
|
-
|
82
|
-
serialized_event_sha256 = nil
|
83
|
-
if @pow_difficulty_target
|
84
|
-
nonce = 1
|
85
|
-
loop do
|
86
|
-
nonce_tag = ['nonce', nonce.to_s, @pow_difficulty_target.to_s]
|
87
|
-
nonced_serialized_event = serialized_event.clone
|
88
|
-
nonced_serialized_event[4] = nonced_serialized_event[4] + [nonce_tag]
|
89
|
-
serialized_event_sha256 = Digest::SHA256.hexdigest(JSON.dump(nonced_serialized_event))
|
90
|
-
if match_pow_difficulty?(serialized_event_sha256)
|
91
|
-
event[:tags] << nonce_tag
|
92
|
-
break
|
93
|
-
end
|
94
|
-
nonce += 1
|
95
|
-
end
|
96
|
-
else
|
97
|
-
serialized_event_sha256 = Digest::SHA256.hexdigest(JSON.dump(serialized_event))
|
98
|
-
end
|
99
|
-
|
100
|
-
private_key = Array(@private_key).pack('H*')
|
101
|
-
message = Array(serialized_event_sha256).pack('H*')
|
102
|
-
event_signature = Schnorr.sign(message, private_key).encode.unpack('H*')[0]
|
103
|
-
|
104
|
-
event['id'] = serialized_event_sha256
|
105
|
-
event['sig'] = event_signature
|
106
|
-
event
|
107
|
-
end
|
108
|
-
|
109
|
-
def build_event(payload)
|
110
|
-
if @nip26_delegation_tag
|
111
|
-
payload[:tags] = [] unless payload[:tags]
|
112
|
-
payload[:tags] << @nip26_delegation_tag
|
113
|
-
end
|
114
|
-
event = sign_event(payload)
|
115
|
-
['EVENT', event]
|
116
|
-
end
|
117
|
-
|
118
|
-
def build_metadata_event(name, about, picture, nip05)
|
119
|
-
data = {}
|
120
|
-
data[:name] = name if name
|
121
|
-
data[:about] = about if about
|
122
|
-
data[:picture] = picture if picture
|
123
|
-
data[:nip05] = nip05 if nip05
|
124
|
-
event = {
|
125
|
-
"pubkey": @public_key,
|
126
|
-
"created_at": Time.now.utc.to_i,
|
127
|
-
"kind": 0,
|
128
|
-
"tags": [],
|
129
|
-
"content": data.to_json
|
130
|
-
}
|
131
|
-
|
132
|
-
build_event(event)
|
133
|
-
end
|
134
|
-
|
135
|
-
def build_note_event(text, channel_key = nil)
|
136
|
-
event = {
|
137
|
-
"pubkey": @public_key,
|
138
|
-
"created_at": Time.now.utc.to_i,
|
139
|
-
"kind": channel_key ? 42 : 1,
|
140
|
-
"tags": channel_key ? [['e', channel_key]] : [],
|
141
|
-
"content": text
|
142
|
-
}
|
143
|
-
|
144
|
-
build_event(event)
|
145
|
-
end
|
146
|
-
|
147
|
-
def build_recommended_relay_event(relay)
|
148
|
-
raise 'Invalid relay' unless relay.start_with?('wss://') || relay.start_with?('ws://')
|
149
|
-
|
150
|
-
event = {
|
151
|
-
"pubkey": @public_key,
|
152
|
-
"created_at": Time.now.utc.to_i,
|
153
|
-
"kind": 2,
|
154
|
-
"tags": [],
|
155
|
-
"content": relay
|
156
|
-
}
|
157
|
-
|
158
|
-
build_event(event)
|
159
|
-
end
|
160
|
-
|
161
|
-
def build_contact_list_event(contacts)
|
162
|
-
event = {
|
163
|
-
"pubkey": @public_key,
|
164
|
-
"created_at": Time.now.utc.to_i,
|
165
|
-
"kind": 3,
|
166
|
-
"tags": contacts.map { |c| ['p'] + c },
|
167
|
-
"content": ''
|
168
|
-
}
|
169
|
-
|
170
|
-
build_event(event)
|
171
|
-
end
|
172
|
-
|
173
|
-
def build_dm_event(text, recipient_public_key)
|
174
|
-
encrypted_text = CryptoTools.aes_256_cbc_encrypt(@private_key, recipient_public_key, text)
|
175
|
-
|
176
|
-
event = {
|
177
|
-
"pubkey": @public_key,
|
178
|
-
"created_at": Time.now.utc.to_i,
|
179
|
-
"kind": 4,
|
180
|
-
"tags": [['p', recipient_public_key]],
|
181
|
-
"content": encrypted_text
|
182
|
-
}
|
183
|
-
|
184
|
-
build_event(event)
|
185
|
-
end
|
186
|
-
|
187
|
-
def build_deletion_event(events, reason = '')
|
188
|
-
event = {
|
189
|
-
"pubkey": @public_key,
|
190
|
-
"created_at": Time.now.utc.to_i,
|
191
|
-
"kind": 5,
|
192
|
-
"tags": events.map{ |e| ['e', e] },
|
193
|
-
"content": reason
|
194
|
-
}
|
195
|
-
|
196
|
-
build_event(event)
|
197
|
-
end
|
198
|
-
|
199
|
-
def build_reaction_event(reaction, event, author)
|
200
|
-
raise 'Invalid reaction' unless ['+', '-'].include?(reaction) || reaction.match?(Unicode::Emoji::REGEX)
|
201
|
-
raise 'Invalid author' unless event.is_a?(String) && event.size == 64
|
202
|
-
raise 'Invalid event' unless author.is_a?(String) && author.size == 64
|
203
|
-
|
204
|
-
event = {
|
205
|
-
"pubkey": @public_key,
|
206
|
-
"created_at": Time.now.utc.to_i,
|
207
|
-
"kind": 7,
|
208
|
-
"tags": [['e', event], ['p', author]],
|
209
|
-
"content": reaction
|
210
|
-
}
|
211
|
-
|
212
|
-
build_event(event)
|
213
|
-
end
|
214
|
-
|
215
|
-
def decrypt_dm(event)
|
216
|
-
data = event[1]
|
217
|
-
sender_public_key = data[:pubkey] != @public_key ? data[:pubkey] : data[:tags][0][1]
|
218
|
-
encrypted = data[:content].split('?iv=')[0]
|
219
|
-
iv = data[:content].split('?iv=')[1]
|
220
|
-
CryptoTools.aes_256_cbc_decrypt(@private_key, sender_public_key, encrypted, iv)
|
221
|
-
end
|
222
|
-
|
223
|
-
def get_delegation_tag(delegatee_pubkey, conditions)
|
224
|
-
delegation_message_sha256 = Digest::SHA256.hexdigest("nostr:delegation:#{delegatee_pubkey}:#{conditions}")
|
225
|
-
signature = Schnorr.sign(Array(delegation_message_sha256).pack('H*'), Array(@private_key).pack('H*')).encode.unpack('H*')[0]
|
226
|
-
[
|
227
|
-
"delegation",
|
228
|
-
@public_key,
|
229
|
-
conditions,
|
230
|
-
signature
|
231
|
-
]
|
232
|
-
end
|
233
|
-
|
234
|
-
def set_delegation(tag)
|
235
|
-
@nip26_delegation_tag = tag
|
236
|
-
end
|
237
|
-
|
238
|
-
def reset_delegation
|
239
|
-
@nip26_delegation_tag = nil
|
240
|
-
end
|
241
|
-
|
242
|
-
def self.verify_delegation_signature(delegatee_pubkey, tag)
|
243
|
-
delegation_message_sha256 = Digest::SHA256.hexdigest("nostr:delegation:#{delegatee_pubkey}:#{tag[2]}")
|
244
|
-
Schnorr.valid_sig?(Array(delegation_message_sha256).pack('H*'), Array(tag[1]).pack('H*'), Array(tag[3]).pack('H*'))
|
245
|
-
end
|
246
|
-
|
247
|
-
def build_req_event(filters)
|
248
|
-
['REQ', SecureRandom.random_number.to_s, filters]
|
249
|
-
end
|
250
|
-
|
251
|
-
def build_close_event(subscription_id)
|
252
|
-
['CLOSE', subscription_id]
|
253
|
-
end
|
254
|
-
|
255
|
-
def build_notice_event(message)
|
256
|
-
['NOTICE', message]
|
257
|
-
end
|
258
|
-
|
259
|
-
def match_pow_difficulty?(event_id)
|
260
|
-
@pow_difficulty_target.nil? || @pow_difficulty_target == [event_id].pack("H*").unpack("B*")[0].index('1')
|
261
|
-
end
|
262
|
-
|
263
|
-
def set_pow_difficulty_target(n)
|
264
|
-
@pow_difficulty_target = n
|
265
|
-
end
|
266
|
-
|
267
|
-
def test_post_event(event, relay)
|
268
|
-
response = nil
|
269
|
-
ws = WebSocket::Client::Simple.connect relay
|
270
|
-
ws.on :message do |msg|
|
271
|
-
puts msg
|
272
|
-
response = JSON.parse(msg.data)
|
273
|
-
ws.close
|
274
|
-
end
|
275
|
-
ws.on :open do
|
276
|
-
ws.send event.to_json
|
277
|
-
end
|
278
|
-
while response.nil? do
|
279
|
-
sleep 0.1
|
280
|
-
end
|
281
|
-
response[0] == 'OK'
|
282
|
-
end
|
12
|
+
require_relative 'bech32'
|
13
|
+
require_relative 'context'
|
14
|
+
require_relative 'kind'
|
15
|
+
require_relative 'key'
|
16
|
+
require_relative 'event'
|
17
|
+
require_relative 'filter'
|
18
|
+
require_relative 'signer'
|
19
|
+
require_relative 'client'
|
20
|
+
require_relative 'message_handler'
|
21
|
+
|
22
|
+
module Nostr
|
283
23
|
|
284
24
|
end
|
data/lib/signer.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Nostr
|
2
|
+
class Signer
|
3
|
+
|
4
|
+
attr_reader :private_key
|
5
|
+
attr_reader :public_key
|
6
|
+
|
7
|
+
def initialize(private_key:)
|
8
|
+
@private_key = private_key
|
9
|
+
unless @public_key
|
10
|
+
@public_key = Nostr::Key::get_public_key(@private_key)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def nsec
|
15
|
+
Nostr::Bech32.encode_nsec(@private_key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def npub
|
19
|
+
Nostr::Bech32.encode_npub(@public_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign(event)
|
23
|
+
|
24
|
+
raise ArgumentError, "Event is not signable" unless event.signable?
|
25
|
+
|
26
|
+
event.pubkey = @public_key if event.pubkey.nil? || event.pubkey.empty?
|
27
|
+
|
28
|
+
raise ArgumentError, "Pubkey doesn't match the private key" unless event.pubkey == @public_key
|
29
|
+
|
30
|
+
if event.kind == Nostr::Kind::DIRECT_MESSAGE
|
31
|
+
dm_recipient = event.tags.select{|t| t[0] == "p"}.first[1]
|
32
|
+
event.content = CryptoTools.aes_256_cbc_encrypt(@private_key, dm_recipient, event.content)
|
33
|
+
end
|
34
|
+
|
35
|
+
if event.delegation
|
36
|
+
event.tags << event.delegation
|
37
|
+
end
|
38
|
+
|
39
|
+
event_sha256_digest = nil
|
40
|
+
if event.pow
|
41
|
+
nonce = 1
|
42
|
+
loop do
|
43
|
+
nonce_tag = ['nonce', nonce.to_s, event.pow.to_s]
|
44
|
+
nonced_serialized_event = event.serialize.clone
|
45
|
+
nonced_serialized_event[4] = nonced_serialized_event[4] + [nonce_tag]
|
46
|
+
event_sha256_digest = Digest::SHA256.hexdigest(JSON.dump(nonced_serialized_event))
|
47
|
+
if Nostr::Event.match_pow_difficulty?(event_sha256_digest, event.pow)
|
48
|
+
event.tags << nonce_tag
|
49
|
+
break
|
50
|
+
end
|
51
|
+
nonce += 1
|
52
|
+
end
|
53
|
+
else
|
54
|
+
event_sha256_digest = Digest::SHA256.hexdigest(JSON.dump(event.serialize))
|
55
|
+
end
|
56
|
+
|
57
|
+
event.id = event_sha256_digest
|
58
|
+
binary_private_key = Array(@private_key).pack('H*')
|
59
|
+
binary_message = Array(event.id).pack('H*')
|
60
|
+
event.sig = Schnorr.sign(binary_message, binary_private_key).encode.unpack('H*')[0]
|
61
|
+
event
|
62
|
+
end
|
63
|
+
|
64
|
+
def decrypt(event)
|
65
|
+
case event.kind
|
66
|
+
when Nostr::Kind::DIRECT_MESSAGE
|
67
|
+
data = event.content.split('?iv=')[0]
|
68
|
+
iv = event.content.split('?iv=')[1]
|
69
|
+
dm_recipient = event.tags.select{|t| t[0] == "p"}.first[1]
|
70
|
+
event.content = CryptoTools.aes_256_cbc_decrypt(@private_key, dm_recipient, data, iv)
|
71
|
+
event
|
72
|
+
else
|
73
|
+
raise "Unable to decrypt a kind #{event.kind} event"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def generate_delegation_tag(to:, conditions:)
|
78
|
+
delegation_message_sha256 = Digest::SHA256.hexdigest("nostr:delegation:#{to}:#{conditions}")
|
79
|
+
signature = Schnorr.sign(Array(delegation_message_sha256).pack('H*'), Array(@private_key).pack('H*')).encode.unpack('H*')[0]
|
80
|
+
[
|
81
|
+
"delegation",
|
82
|
+
@public_key,
|
83
|
+
conditions,
|
84
|
+
signature
|
85
|
+
]
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/lib/version.rb
ADDED
Binary file
|
data/nostr_ruby.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
$:.unshift File.expand_path('../lib', __FILE__)
|
2
|
-
require '
|
2
|
+
require 'version'
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'nostr_ruby'
|
6
|
-
s.version =
|
6
|
+
s.version = Nostr::VERSION
|
7
7
|
s.summary = 'A Ruby library to interact with the Nostr protocol'
|
8
8
|
s.description = 'NostrRuby is a Ruby library to interact with the Nostr protocol. At this stage the focus is the creation of public events and private encrypted messages.'
|
9
9
|
s.authors = ['Daniele Tonon']
|
@@ -14,9 +14,9 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.require_paths = ['lib']
|
15
15
|
|
16
16
|
s.add_dependency 'base64', '~> 0.1.1'
|
17
|
-
s.add_dependency 'bech32', '~> 1.
|
17
|
+
s.add_dependency 'bech32', '~> 1.4.0'
|
18
18
|
s.add_dependency 'bip-schnorr', '~> 0.4.0'
|
19
19
|
s.add_dependency 'json', '~> 2.6.2'
|
20
20
|
s.add_dependency 'unicode-emoji', '~> 3.3.1'
|
21
|
-
s.add_dependency 'websocket
|
21
|
+
s.add_dependency 'faye-websocket', '~> 0.11'
|
22
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nostr_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniele Tonon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.
|
33
|
+
version: 1.4.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 1.
|
40
|
+
version: 1.4.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bip-schnorr
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,19 +81,19 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 3.3.1
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name: websocket
|
84
|
+
name: faye-websocket
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: '0.11'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: '0.11'
|
97
97
|
description: NostrRuby is a Ruby library to interact with the Nostr protocol. At this
|
98
98
|
stage the focus is the creation of public events and private encrypted messages.
|
99
99
|
email:
|
@@ -105,10 +105,20 @@ files:
|
|
105
105
|
- Gemfile.lock
|
106
106
|
- LICENSE.md
|
107
107
|
- README.md
|
108
|
+
- lib/bech32.rb
|
109
|
+
- lib/client.rb
|
110
|
+
- lib/context.rb
|
108
111
|
- lib/crypto_tools.rb
|
109
|
-
- lib/
|
112
|
+
- lib/event.rb
|
113
|
+
- lib/event_wizard.rb
|
114
|
+
- lib/filter.rb
|
115
|
+
- lib/key.rb
|
116
|
+
- lib/kind.rb
|
117
|
+
- lib/message_handler.rb
|
110
118
|
- lib/nostr_ruby.rb
|
111
|
-
- lib/
|
119
|
+
- lib/signer.rb
|
120
|
+
- lib/version.rb
|
121
|
+
- nostr_ruby-0.2.0.gem
|
112
122
|
- nostr_ruby.gemspec
|
113
123
|
homepage: https://github.com/dtonon/nostr-ruby
|
114
124
|
licenses:
|
data/lib/custom_addr.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
class CustomAddr
|
2
|
-
|
3
|
-
attr_accessor :hrp # human-readable part
|
4
|
-
attr_accessor :prog # witness program
|
5
|
-
|
6
|
-
def initialize(addr = nil)
|
7
|
-
@hrp, @prog = parse_addr(addr) if addr
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_scriptpubkey
|
11
|
-
prog.map{|p|[p].pack("C")}.join.unpack('H*').first
|
12
|
-
end
|
13
|
-
|
14
|
-
def scriptpubkey=(script)
|
15
|
-
values = [script].pack('H*').unpack("C*")
|
16
|
-
@prog = values
|
17
|
-
end
|
18
|
-
|
19
|
-
def addr
|
20
|
-
spec = Bech32::Encoding::BECH32
|
21
|
-
Bech32.encode(hrp, convert_bits(prog, 8, 5), spec)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def parse_addr(addr)
|
27
|
-
hrp, data, spec = Bech32.decode(addr)
|
28
|
-
raise 'Invalid address.' if hrp.nil? || data[0].nil?
|
29
|
-
# raise 'Invalid witness version' if ver > 16
|
30
|
-
prog = convert_bits(data, 5, 8, false)
|
31
|
-
# raise 'Invalid witness program' if prog.nil? || prog.length < 2 || prog.length > 40
|
32
|
-
# raise 'Invalid witness program with version 0' if ver == 0 && (prog.length != 20 && prog.length != 32)
|
33
|
-
[hrp, prog]
|
34
|
-
end
|
35
|
-
|
36
|
-
def convert_bits(data, from, to, padding=true)
|
37
|
-
acc = 0
|
38
|
-
bits = 0
|
39
|
-
ret = []
|
40
|
-
maxv = (1 << to) - 1
|
41
|
-
max_acc = (1 << (from + to - 1)) - 1
|
42
|
-
data.each do |v|
|
43
|
-
return nil if v < 0 || (v >> from) != 0
|
44
|
-
acc = ((acc << from) | v) & max_acc
|
45
|
-
bits += from
|
46
|
-
while bits >= to
|
47
|
-
bits -= to
|
48
|
-
ret << ((acc >> bits) & maxv)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
if padding
|
52
|
-
ret << ((acc << (to - bits)) & maxv) unless bits == 0
|
53
|
-
elsif bits >= from || ((acc << (to - bits)) & maxv) != 0
|
54
|
-
return nil
|
55
|
-
end
|
56
|
-
ret
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
data/lib/nostr_ruby/version.rb
DELETED