bech32 1.3.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +2 -3
- data/README.md +75 -15
- data/exe/bech32 +24 -5
- data/lib/bech32/nostr/entity.rb +163 -0
- data/lib/bech32/nostr/nip19.rb +44 -0
- data/lib/bech32/nostr.rb +8 -0
- data/lib/bech32/version.rb +1 -1
- data/lib/bech32.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5c153cf694b5bcfc8e09c9aff4439771137dbb558ef082e05f6330763c3eda4
|
4
|
+
data.tar.gz: a7397025e69d847ab649a9f276bf0582d11c1f70ab447b60fd1073fdec6c8c63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9619a2d66ee0f18b6ef974365cba1a2475506a25a369ff6a426b034ed1eae01b1a6566c572481019ec84e2fcf34f0dae143a8d6a8edbb9d27e0bf343f1d4180f
|
7
|
+
data.tar.gz: 22550c98747a96c87eb9007ad8e6f99730f7e205574afddd97b296c5338dfc91bd50618ab012853bde479ceea86bc93ff8bfb056555401d2bf5a7de86d433373
|
data/.github/workflows/main.yml
CHANGED
@@ -19,15 +19,14 @@ jobs:
|
|
19
19
|
runs-on: ubuntu-latest
|
20
20
|
strategy:
|
21
21
|
matrix:
|
22
|
-
ruby-version: ['
|
22
|
+
ruby-version: ['3.0', '3.1', '3.2']
|
23
23
|
|
24
24
|
steps:
|
25
25
|
- uses: actions/checkout@v2
|
26
26
|
- name: Set up Ruby
|
27
27
|
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
28
28
|
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
-
|
30
|
-
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
29
|
+
uses: ruby/setup-ruby@v1
|
31
30
|
with:
|
32
31
|
ruby-version: ${{ matrix.ruby-version }}
|
33
32
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Bech32 [![Build Status](https://
|
1
|
+
# Bech32 [![Build Status](https://github.com/azuchi/bech32rb/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/azuchi/bech32rb/actions/workflows/main.yml) [![Gem Version](https://badge.fury.io/rb/bech32.svg)](https://badge.fury.io/rb/bech32) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) <img src="http://segwit.co/static/public/images/logo.png" width="100">
|
2
2
|
|
3
3
|
The implementation of the Bech32/Bech32m encoder and decoder for Ruby.
|
4
4
|
|
@@ -50,17 +50,6 @@ hrp, data, spec = Bech32.decode('BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4')
|
|
50
50
|
# spec is whether Bech32::Encoding::BECH32 or Bech32::Encoding::BECH32M
|
51
51
|
```
|
52
52
|
|
53
|
-
Decode Bech32-encoded Segwit address into `Bech32::SegwitAddr` instance.
|
54
|
-
|
55
|
-
```ruby
|
56
|
-
addr = 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'
|
57
|
-
segwit_addr = Bech32::SegwitAddr.new(addr)
|
58
|
-
|
59
|
-
# generate script pubkey
|
60
|
-
segwit_addr.to_script_pubkey
|
61
|
-
=> 0014751e76e8199196d454941c45d1b3a323f1433bd6
|
62
|
-
```
|
63
|
-
|
64
53
|
#### Advanced
|
65
54
|
|
66
55
|
The maximum number of characters of Bech32 defined in [BIP-173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) is limited to 90 characters.
|
@@ -84,7 +73,20 @@ hrp = 'bc'
|
|
84
73
|
data = [0, 14, 20, 15, 7, 13, 26, 0, 25, 18, 6, 11, 13, 8, 21, 4, 20, 3, 17, 2, 29, 3, 12, 29, 3, 4, 15, 24, 20, 6, 14, 30, 22]
|
85
74
|
|
86
75
|
bech = Bech32.encode(hrp, data, Bech32::Encoding::BECH32)
|
87
|
-
=> bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
|
76
|
+
=> 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
|
77
|
+
```
|
78
|
+
|
79
|
+
### Segwit
|
80
|
+
|
81
|
+
Decode Bech32-encoded Segwit address into `Bech32::SegwitAddr` instance.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
addr = 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4'
|
85
|
+
segwit_addr = Bech32::SegwitAddr.new(addr)
|
86
|
+
|
87
|
+
# generate script pubkey
|
88
|
+
segwit_addr.to_script_pubkey
|
89
|
+
=> '0014751e76e8199196d454941c45d1b3a323f1433bd6'
|
88
90
|
```
|
89
91
|
|
90
92
|
Encode Segwit script into Bech32 Segwit address.
|
@@ -95,7 +97,43 @@ segwit_addr.script_pubkey = '0014751e76e8199196d454941c45d1b3a323f1433bd6'
|
|
95
97
|
|
96
98
|
# generate addr
|
97
99
|
segwit_addr.addr
|
98
|
-
=> bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
|
100
|
+
=> 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
|
101
|
+
```
|
102
|
+
|
103
|
+
### Nostr
|
104
|
+
|
105
|
+
Supports encoding/decoding of Nostr's [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) entities.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# Decode bare entity
|
109
|
+
bech32 = 'npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg'
|
110
|
+
entity = Bech32::Nostr::NIP19.parse(bech32)
|
111
|
+
entity.hrp
|
112
|
+
=> 'npub'
|
113
|
+
entity.data
|
114
|
+
=> '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'
|
115
|
+
|
116
|
+
# Decode tlv entity
|
117
|
+
bech32 = 'nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p'
|
118
|
+
entity = Bech32::Nostr::NIP19.parse(bech32)
|
119
|
+
entity.hrp
|
120
|
+
=> 'nprofile'
|
121
|
+
entity.entries[0].value
|
122
|
+
=> '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'
|
123
|
+
entity.entries[1].value
|
124
|
+
=> 'wss://r.x.com'
|
125
|
+
|
126
|
+
# Encode bare entity
|
127
|
+
entity = Bech32::Nostr::BareEntity.new('npub', '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e')
|
128
|
+
entity.encode
|
129
|
+
=> 'npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg'
|
130
|
+
|
131
|
+
# Encode tlv entity
|
132
|
+
entry_relay = Bech32::Nostr::TLVEntry.new(Bech32::Nostr::TLVEntity::TYPE_RELAY, 'wss://relay.nostr.example')
|
133
|
+
entry_author = Bech32::Nostr::TLVEntry.new(Bech32::Nostr::TLVEntity::TYPE_AUTHOR, '97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322')
|
134
|
+
entity = Bech32::Nostr::TLVEntity.new(Bech32::Nostr::NIP19::HRP_EVENT, [entry_relay, entry_author])
|
135
|
+
entity.encode
|
136
|
+
=> 'nevent1qyvhwumn8ghj7un9d3shjtnwdaehgu3wv4uxzmtsd3jsygyhcu9ygdn2v56uz3dnx0uh865xmlwz675emfsccsxxguz6mx8rygstv78u'
|
99
137
|
```
|
100
138
|
|
101
139
|
### CLI
|
@@ -115,8 +153,30 @@ The `decode` command takes bech32/bech32m string as arguments and outputs `HRP`,
|
|
115
153
|
|
116
154
|
$ bech32 decode genesis1qyfe883hey6jrgj2xvk5g3dfmfqfzm7a4wez4pd2krf7ltsxffd6u6nrvjvv0uzda6whtuhzscj72zuj6udkmknakfn0anc07gaya2303dnngu3sxqctqvg5v7nw0fvkwqce7auqkxv3sj5j7e8d6vu4zy5qlssvffxqccxqcfgwc98zenr3qrzk5msfgkvjnj0ejpl8zunedeqhvajry08ueg99ynwgd2sdntrq22gensffcj03r3yjv4cv4j20ee8vujxve3fssj2demyvknzwe3glqt6t954vknpdvurthpc9fpex7t97nvqryeqpjrpscap6q0zvfpgv3nxzpy6wmx24k9xcu35vuatm5zvn7t084ec7tlw2gh0av9xwumwkv02hats66l5hkwd55rwkm2q8fsd4zvqwpvqqqqtdc6rp
|
117
155
|
HRP: genesis
|
118
|
-
DATA:
|
156
|
+
DATA: [0, 4, 9, 25, 7, 7, 17, 23, 25, 4, 26, 18, 3, 8, 18, 10, 6, 12, 22, 20, 8, 17, 13, 9, 27, 9, 0, 9, 2, 27, 30, 29, 21, 14, 25, 2, 21, 1, 13, 10, 22, 3, 9, 30, 31, 11, 16, 6, 9, 9, 13, 26, 28, 26, 19, 3, 12, 18, 12, 12, 15, 28, 2, 13, 29, 26, 14, 23, 11, 28, 23, 2, 16, 24, 18, 30, 10, 2, 28, 18, 26, 28, 13, 22, 27, 22, 19, 29, 22, 9, 19, 15, 29, 19, 24, 15, 30, 8, 29, 4, 29, 10, 17, 15, 17, 13, 19, 19, 8, 28, 17, 16, 6, 0, 24, 11, 0, 12, 8, 20, 12, 30, 19, 14, 15, 9, 12, 22, 14, 0, 24, 25, 30, 29, 28, 0, 22, 6, 12, 17, 16, 18, 20, 18, 30, 25, 7, 13, 26, 12, 28, 21, 2, 4, 20, 0, 31, 16, 16, 12, 9, 9, 6, 0, 24, 24, 6, 0, 24, 9, 8, 14, 24, 5, 7, 2, 25, 19, 3, 17, 0, 3, 2, 22, 20, 27, 16, 9, 8, 22, 12, 18, 19, 18, 15, 25, 18, 1, 31, 7, 2, 28, 19, 25, 13, 25, 0, 23, 12, 29, 18, 3, 4, 15, 7, 28, 25, 8, 5, 5, 4, 19, 14, 8, 13, 10, 16, 13, 19, 11, 3, 0, 10, 10, 8, 25, 19, 16, 9, 9, 24, 18, 15, 17, 3, 17, 4, 18, 12, 21, 24, 12, 21, 18, 10, 15, 25, 25, 7, 12, 28, 18, 6, 12, 25, 17, 9, 16, 16, 18, 10, 13, 25, 27, 4, 12, 22, 19, 2, 14, 25, 17, 8, 31, 0, 11, 26, 11, 5, 20, 21, 12, 22, 19, 1, 13, 12, 28, 3, 11, 23, 1, 24, 5, 9, 1, 25, 6, 30, 11, 5, 30, 19, 12, 0, 3, 4, 25, 0, 1, 18, 3, 1, 16, 24, 29, 1, 26, 0, 15, 2, 12, 9, 1, 8, 12, 17, 19, 6, 2, 1, 4, 26, 14, 27, 6, 10, 21, 22, 5, 6, 24, 28, 17, 20, 12, 28, 29, 11, 27, 20, 2, 12, 19, 30, 11, 15, 7, 21, 25, 24, 30, 11, 31, 14, 10, 8, 23, 15, 29, 12, 5, 6, 14, 28, 27, 14, 22, 12, 15, 10, 23, 29, 11, 16, 26, 26, 31, 20, 23, 22, 14, 13, 20, 20, 3, 14, 22, 27, 10, 0, 7, 9, 16, 13, 21, 2, 12, 0, 14, 1, 12, 0, 0, 0, 0]
|
157
|
+
TYPE: bech32
|
158
|
+
|
159
|
+
Note: `DATA` is not bit-converted.
|
160
|
+
|
161
|
+
If bech32 string has segwit hrp, it will also output witness version and witness program:
|
162
|
+
|
163
|
+
$ bech32 decode bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y
|
164
|
+
HRP: bc
|
165
|
+
DATA: [1, 14, 20, 15, 7, 13, 26, 0, 25, 18, 6, 11, 13, 8, 21, 4, 20, 3, 17, 2, 29, 3, 12, 29, 3, 4, 15, 24, 20, 6, 14, 30, 22, 14, 20, 15, 7, 13, 26, 0, 25, 18, 6, 11, 13, 8, 21, 4, 20, 3, 17, 2, 29, 3, 12, 29, 3, 4, 15, 24, 20, 6, 14, 30, 22]
|
166
|
+
TYPE: bech32m
|
167
|
+
WITNESS VERSION: 1
|
168
|
+
WITNESS PROGRAM: 751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6
|
169
|
+
|
170
|
+
If bech32 string has NIP-19 hrp, it will also output NIP-19 entry:
|
171
|
+
|
172
|
+
$ bech32 decode nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p
|
173
|
+
HRP: nprofile
|
174
|
+
DATA: [0, 0, 16, 3, 23, 28, 6, 6, 7, 31, 5, 25, 6, 17, 17, 20, 0, 30, 23, 25, 15, 9, 15, 5, 29, 25, 18, 15, 21, 2, 1, 29, 2, 1, 31, 15, 19, 25, 10, 24, 8, 28, 22, 4, 29, 14, 13, 10, 21, 27, 29, 4, 11, 7, 8, 1, 1, 21, 27, 23, 6, 28, 25, 26, 5, 28, 23, 23, 4, 11, 19, 24, 5, 25, 17, 22, 30, 27, 8, 1, 2, 21, 27, 23, 6, 28, 25, 26, 5, 28, 23, 22, 8, 26, 19, 2, 12, 5, 25, 18, 28, 28, 27, 1, 12, 17, 21, 22, 4, 11, 19, 3, 13, 29, 22, 16]
|
119
175
|
TYPE: bech32
|
176
|
+
NIP19 Entities:
|
177
|
+
special: 3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
|
178
|
+
relay: wss://r.x.com
|
179
|
+
relay: wss://djbas.sadkb.com
|
120
180
|
|
121
181
|
## License
|
122
182
|
|
data/exe/bech32
CHANGED
@@ -18,13 +18,32 @@ class CLI < Thor
|
|
18
18
|
|
19
19
|
desc 'decode "bech32string"', 'Decoding bech32/bech32m string.'
|
20
20
|
def decode(bech32_str)
|
21
|
-
|
22
|
-
if
|
21
|
+
hrp, data, spec = Bech32.decode(bech32_str, bech32_str.length)
|
22
|
+
if hrp
|
23
23
|
puts <<~EOS
|
24
|
-
HRP: #{
|
25
|
-
DATA: #{
|
26
|
-
TYPE: #{
|
24
|
+
HRP: #{hrp}
|
25
|
+
DATA: #{data}
|
26
|
+
TYPE: #{spec == Bech32::Encoding::BECH32 ? 'bech32' : 'bech32m'}
|
27
27
|
EOS
|
28
|
+
if [Bech32::SegwitAddr::HRP_MAINNET, Bech32::SegwitAddr::HRP_TESTNET, Bech32::SegwitAddr::HRP_REGTEST].include?(hrp)
|
29
|
+
addr = Bech32::SegwitAddr.new(bech32_str)
|
30
|
+
puts <<~EOS
|
31
|
+
WITNESS VERSION: #{addr.ver}
|
32
|
+
WITNESS PROGRAM: #{addr.prog.pack('C*').unpack1('H*')}
|
33
|
+
EOS
|
34
|
+
elsif Bech32::Nostr::NIP19::ALL_PREFIXES.include?(hrp)
|
35
|
+
entity = Bech32::Nostr::NIP19.decode(bech32_str)
|
36
|
+
if entity.is_a?(Bech32::Nostr::BareEntity)
|
37
|
+
puts <<~EOS
|
38
|
+
NIP19 Entity: #{entity.data}
|
39
|
+
EOS
|
40
|
+
else
|
41
|
+
puts 'NIP19 Entities:'
|
42
|
+
entity.entries.each do |entry|
|
43
|
+
puts " #{entry.to_s}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
28
47
|
else
|
29
48
|
puts 'Can not decode.'
|
30
49
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module Bech32
|
2
|
+
module Nostr
|
3
|
+
class BareEntity
|
4
|
+
attr_reader :hrp
|
5
|
+
attr_reader :data
|
6
|
+
|
7
|
+
# Initialize bare entity.
|
8
|
+
# @param [String] hrp human-readable part.
|
9
|
+
# @param [String] data Entity data(hex string).
|
10
|
+
def initialize(hrp, data)
|
11
|
+
raise ArgumentError, "HRP #{hrp} is unsupported." unless NIP19::BARE_PREFIXES.include?(hrp)
|
12
|
+
raise ArgumentError, "Data whose HRP is #{hrp} must be 32 bytes." unless [data].pack('H*').bytesize == 32
|
13
|
+
@hrp = hrp
|
14
|
+
@data = data
|
15
|
+
end
|
16
|
+
|
17
|
+
# Encode bare entity to bech32 string.
|
18
|
+
# @return [String] bech32 string.
|
19
|
+
def encode
|
20
|
+
Bech32.encode(hrp, Bech32.convert_bits([data].pack('H*').unpack('C*'), 8, 5), Bech32::Encoding::BECH32)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class TLVEntry
|
25
|
+
|
26
|
+
attr_reader :type
|
27
|
+
attr_reader :label
|
28
|
+
attr_reader :value
|
29
|
+
|
30
|
+
def initialize(type, value, label = nil)
|
31
|
+
raise ArgumentError, "Type #{type} unsupported." unless TLVEntity::TYPES.include?(type)
|
32
|
+
|
33
|
+
@type = type
|
34
|
+
@value = value
|
35
|
+
@label = label
|
36
|
+
end
|
37
|
+
|
38
|
+
# Convert to binary data.
|
39
|
+
# @return [String] binary data.
|
40
|
+
def to_payload
|
41
|
+
data = if value.is_a?(Integer)
|
42
|
+
[value].pack('N')
|
43
|
+
else
|
44
|
+
hex_string?(value) ? [value].pack('H*') : value
|
45
|
+
end
|
46
|
+
len = data.bytesize
|
47
|
+
[type, len].pack('CC') + data
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
type_label = case type
|
52
|
+
when TLVEntity::TYPE_SPECIAL
|
53
|
+
'special'
|
54
|
+
when TLVEntity::TYPE_RELAY
|
55
|
+
'relay'
|
56
|
+
when TLVEntity::TYPE_AUTHOR
|
57
|
+
'author'
|
58
|
+
when TLVEntity::TYPE_KIND
|
59
|
+
'kind'
|
60
|
+
else
|
61
|
+
'unknown'
|
62
|
+
end
|
63
|
+
"#{type_label}: #{value}"
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Check whether +str+ is hex string or not.
|
69
|
+
# @param [String] str string.
|
70
|
+
# @return [Boolean]
|
71
|
+
def hex_string?(str)
|
72
|
+
return false if str.bytes.any? { |b| b > 127 }
|
73
|
+
return false if str.length % 2 != 0
|
74
|
+
hex_chars = str.chars.to_a
|
75
|
+
hex_chars.all? { |c| c =~ /[0-9a-fA-F]/ }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class TLVEntity
|
80
|
+
|
81
|
+
TYPE_SPECIAL = 0
|
82
|
+
TYPE_RELAY = 1
|
83
|
+
TYPE_AUTHOR = 2
|
84
|
+
TYPE_KIND = 3
|
85
|
+
|
86
|
+
TYPES = [TYPE_SPECIAL, TYPE_RELAY, TYPE_AUTHOR, TYPE_KIND]
|
87
|
+
|
88
|
+
attr_reader :hrp
|
89
|
+
attr_reader :entries
|
90
|
+
|
91
|
+
# Initialize TLV entity.
|
92
|
+
# @param [String] hrp human-readable part.
|
93
|
+
# @param [Array<TLVEntry>] entries TLV entries.
|
94
|
+
# @return [TLVEntity]
|
95
|
+
def initialize(hrp, entries)
|
96
|
+
raise ArgumentError, "HRP #{hrp} is unsupported." unless NIP19::TLV_PREFIXES.include?(hrp)
|
97
|
+
entries.each do |e|
|
98
|
+
raise ArgumentError, "Entries must be TLVEntry. #{e.class} given." unless e.is_a?(TLVEntry)
|
99
|
+
end
|
100
|
+
|
101
|
+
@hrp = hrp
|
102
|
+
@entries = entries
|
103
|
+
end
|
104
|
+
|
105
|
+
# Parse TLV entity from data.
|
106
|
+
# @param [String] hrp human-readable part.
|
107
|
+
# @param [String] data Entity data(binary format).
|
108
|
+
# @return [TLVEntity]
|
109
|
+
def self.parse(hrp, data)
|
110
|
+
buf = ::StringIO.new(data)
|
111
|
+
entries = []
|
112
|
+
until buf.eof?
|
113
|
+
type, len = buf.read(2).unpack('CC')
|
114
|
+
case type
|
115
|
+
when TYPE_SPECIAL # special
|
116
|
+
case hrp
|
117
|
+
when NIP19::HRP_PROFILE
|
118
|
+
entries << TLVEntry.new(type, buf.read(len).unpack1('H*'), 'pubkey')
|
119
|
+
when NIP19::HRP_RELAY
|
120
|
+
entries << TLVEntry.new(type, buf.read(len), 'relay')
|
121
|
+
when NIP19::HRP_EVENT
|
122
|
+
entries << TLVEntry.new(type, buf.read(len).unpack1('H*'), 'id')
|
123
|
+
when NIP19::HRP_EVENT_COORDINATE
|
124
|
+
entries << TLVEntry.new(type, buf.read(len), 'identifier')
|
125
|
+
end
|
126
|
+
when TYPE_RELAY # relay
|
127
|
+
case hrp
|
128
|
+
when NIP19::HRP_PROFILE, NIP19::HRP_EVENT, NIP19::HRP_EVENT_COORDINATE
|
129
|
+
entries << TLVEntry.new(type, buf.read(len), 'relay')
|
130
|
+
else
|
131
|
+
raise ArgumentError, "Type: #{type} does not supported for HRP: #{hrp}"
|
132
|
+
end
|
133
|
+
when TYPE_AUTHOR # author
|
134
|
+
case hrp
|
135
|
+
when NIP19::HRP_EVENT, NIP19::HRP_EVENT_COORDINATE
|
136
|
+
entries << TLVEntry.new(type, buf.read(len).unpack1('H*'), 'author')
|
137
|
+
else
|
138
|
+
raise ArgumentError, "Type: #{type} does not supported for HRP: #{hrp}"
|
139
|
+
end
|
140
|
+
when TYPE_KIND # kind
|
141
|
+
case hrp
|
142
|
+
when NIP19::HRP_EVENT, NIP19::HRP_EVENT_COORDINATE
|
143
|
+
entries << TLVEntry.new(type, buf.read(len).unpack1('H*').to_i(16), 'kind')
|
144
|
+
else
|
145
|
+
raise ArgumentError, "Type: #{type} does not supported for HRP: #{hrp}"
|
146
|
+
end
|
147
|
+
else
|
148
|
+
raise ArgumentError, "Unknown TLV type: #{type}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
TLVEntity.new(hrp, entries)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Encode tlv entity to bech32 string.
|
156
|
+
# @return [String] bech32 string.
|
157
|
+
def encode
|
158
|
+
data = entries.map(&:to_payload).join
|
159
|
+
Bech32.encode(hrp, Bech32.convert_bits(data.unpack('C*'), 8, 5), Bech32::Encoding::BECH32)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module Bech32
|
4
|
+
module Nostr
|
5
|
+
module NIP19
|
6
|
+
|
7
|
+
HRP_PUBKEY = 'npub'
|
8
|
+
HRP_PRIVATE_KEY = 'nsec'
|
9
|
+
HRP_NOTE_ID = 'note'
|
10
|
+
HRP_PROFILE = 'nprofile'
|
11
|
+
HRP_EVENT = 'nevent'
|
12
|
+
HRP_RELAY = 'nrelay'
|
13
|
+
HRP_EVENT_COORDINATE = 'naddr'
|
14
|
+
|
15
|
+
BARE_PREFIXES = [HRP_PUBKEY, HRP_PRIVATE_KEY, HRP_NOTE_ID]
|
16
|
+
TLV_PREFIXES = [HRP_PROFILE, HRP_EVENT, HRP_RELAY, HRP_EVENT_COORDINATE]
|
17
|
+
ALL_PREFIXES = BARE_PREFIXES + TLV_PREFIXES
|
18
|
+
|
19
|
+
module_function
|
20
|
+
|
21
|
+
# Decode nip19 string.
|
22
|
+
# @param [String] string Bech32 string.
|
23
|
+
# @return [BareEntity]
|
24
|
+
# @return [TLVEntity]
|
25
|
+
def decode(string)
|
26
|
+
hrp, data, spec = Bech32.decode(string, string.length)
|
27
|
+
|
28
|
+
raise ArgumentError, 'Invalid nip19 string.' if hrp.nil?
|
29
|
+
raise ArgumentError, 'Invalid bech32 spec.' unless spec == Bech32::Encoding::BECH32
|
30
|
+
|
31
|
+
entity = Bech32.convert_bits(data, 5, 8, false).pack('C*')
|
32
|
+
raise ArgumentError, "Data whose HRP is #{hrp} must be 32 bytes." if BARE_PREFIXES.include?(hrp) && entity.bytesize != 32
|
33
|
+
if BARE_PREFIXES.include?(hrp)
|
34
|
+
BareEntity.new(hrp, entity.unpack1('H*'))
|
35
|
+
elsif TLV_PREFIXES.include?(hrp)
|
36
|
+
TLVEntity.parse(hrp, entity)
|
37
|
+
else
|
38
|
+
raise ArgumentError, "HRP #{hrp} is unsupported."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/bech32/nostr.rb
ADDED
data/lib/bech32/version.rb
CHANGED
data/lib/bech32.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bech32
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shigeyuki Azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -75,6 +75,9 @@ files:
|
|
75
75
|
- bin/setup
|
76
76
|
- exe/bech32
|
77
77
|
- lib/bech32.rb
|
78
|
+
- lib/bech32/nostr.rb
|
79
|
+
- lib/bech32/nostr/entity.rb
|
80
|
+
- lib/bech32/nostr/nip19.rb
|
78
81
|
- lib/bech32/segwit_addr.rb
|
79
82
|
- lib/bech32/version.rb
|
80
83
|
homepage: https://github.com/azuchi/bech32rb
|