bech32 1.3.0 → 1.4.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 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