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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9dfad105c52c27a05e99b0576a46de13aac736914b8172b59766e393531857e
4
- data.tar.gz: bdadf23d8b3ea58fbbc6c303ca657e6d151d4096cbb6dfc260d105c2d5b5869e
3
+ metadata.gz: c5c153cf694b5bcfc8e09c9aff4439771137dbb558ef082e05f6330763c3eda4
4
+ data.tar.gz: a7397025e69d847ab649a9f276bf0582d11c1f70ab447b60fd1073fdec6c8c63
5
5
  SHA512:
6
- metadata.gz: 17c8acca310a4ebc24ba5e4b4f58132d79262ca73d6ebbe6b50952b2f0703c70b915b28500d866af86d3b9f715afdc614ddc2af9feb07d6de5fb16fff68dc16e
7
- data.tar.gz: ae1ad3dd861bed9849878241ae9edac81d26980a4cf8a6e911ecb36b7bdc547756404347232aff5608d4b9f24d2b0f6ecfc0e6476c6f37855bfbc2ff0410a8e9
6
+ metadata.gz: 9619a2d66ee0f18b6ef974365cba1a2475506a25a369ff6a426b034ed1eae01b1a6566c572481019ec84e2fcf34f0dae143a8d6a8edbb9d27e0bf343f1d4180f
7
+ data.tar.gz: 22550c98747a96c87eb9007ad8e6f99730f7e205574afddd97b296c5338dfc91bd50618ab012853bde479ceea86bc93ff8bfb056555401d2bf5a7de86d433373
@@ -19,15 +19,14 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.6', '2.7', '3.0']
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
- # uses: ruby/setup-ruby@v1
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://travis-ci.org/azuchi/bech32rb.svg?branch=master)](https://travis-ci.org/azuchi/bech32rb) [![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">
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: 000409190707111719041a120308120a060c161408110d091b090009021b1e1d150e190215010d0a1603091e1f0b100609090d1a1c1a13030c120c0c0f1c020d1d1a0e170b1c17021018121e0a021c121a1c0d161b16131d1609130f1d13180f1e081d041d0a110f110d1313081c11100600180b000c08140c1e130e0f090c160e0018191e1d1c0016060c11101214121e19070d1a0c1c15020414001f10100c09090600181806001809080e180507021913031100030216141b100908160c1213120f1912011f07021c13190d1900170c1d1203040f071c1908050504130e080d0a100d130b03000a0a08191310090918120f11031104120c15180c15120a0f1919070c1c12060c1911091010120a0d191b040c1613020e1911081f000b1a0b0514150c1613010d0c1c030b17011805090119061e0b051e130c00030419000112030110181d011a000f020c0901080c1113060201041a0e1b060a15160506181c11140c1c1d0b1b14020c131e0b0f071519181e0b1f0e0a08170f1d0c05060e1c1b0e160c0f0a171d0b101a1a1f1417160e0d1414030e161b0a000709100d15020c000e010c00000000
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
- bech32 = Bech32.decode(bech32_str, bech32_str.length)
22
- if bech32
21
+ hrp, data, spec = Bech32.decode(bech32_str, bech32_str.length)
22
+ if hrp
23
23
  puts <<~EOS
24
- HRP: #{bech32[0]}
25
- DATA: #{bech32[1].pack('C*').unpack1('H*')}
26
- TYPE: #{bech32[2] == Bech32::Encoding::BECH32 ? 'bech32' : 'bech32m'}
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
@@ -0,0 +1,8 @@
1
+ module Bech32
2
+ module Nostr
3
+ autoload :NIP19, 'bech32/nostr/nip19'
4
+ autoload :BareEntity, 'bech32/nostr/entity'
5
+ autoload :TLVEntity, 'bech32/nostr/entity'
6
+ end
7
+
8
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bech32
4
- VERSION = '1.3.0'
4
+ VERSION = '1.4.1'
5
5
  end
data/lib/bech32.rb CHANGED
@@ -6,6 +6,7 @@ module Bech32
6
6
  end
7
7
 
8
8
  autoload :SegwitAddr, 'bech32/segwit_addr'
9
+ autoload :Nostr, 'bech32/nostr'
9
10
 
10
11
  SEPARATOR = '1'
11
12
 
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.3.0
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: 2022-10-28 00:00:00.000000000 Z
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