cash-addr 0.1.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +35 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +53 -0
  8. data/LICENSE.txt +13 -0
  9. data/README.md +73 -0
  10. data/Rakefile +8 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +6 -0
  13. data/cash-addr.gemspec +31 -0
  14. data/circle.yml +7 -0
  15. data/doc/CashAddr.html +110 -0
  16. data/doc/CashAddr/Converter.html +277 -0
  17. data/doc/CashAddr/InvalidAddress.html +103 -0
  18. data/doc/LICENSE_txt.html +99 -0
  19. data/doc/README_md.html +175 -0
  20. data/doc/cash-addr_gemspec.html +125 -0
  21. data/doc/created.rid +11 -0
  22. data/doc/css/fonts.css +167 -0
  23. data/doc/css/rdoc.css +590 -0
  24. data/doc/fonts/Lato-Light.ttf +0 -0
  25. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  26. data/doc/fonts/Lato-Regular.ttf +0 -0
  27. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  28. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  29. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  30. data/doc/images/add.png +0 -0
  31. data/doc/images/arrow_up.png +0 -0
  32. data/doc/images/brick.png +0 -0
  33. data/doc/images/brick_link.png +0 -0
  34. data/doc/images/bug.png +0 -0
  35. data/doc/images/bullet_black.png +0 -0
  36. data/doc/images/bullet_toggle_minus.png +0 -0
  37. data/doc/images/bullet_toggle_plus.png +0 -0
  38. data/doc/images/date.png +0 -0
  39. data/doc/images/delete.png +0 -0
  40. data/doc/images/find.png +0 -0
  41. data/doc/images/loadingAnimation.gif +0 -0
  42. data/doc/images/macFFBgHack.png +0 -0
  43. data/doc/images/package.png +0 -0
  44. data/doc/images/page_green.png +0 -0
  45. data/doc/images/page_white_text.png +0 -0
  46. data/doc/images/page_white_width.png +0 -0
  47. data/doc/images/plugin.png +0 -0
  48. data/doc/images/ruby.png +0 -0
  49. data/doc/images/tag_blue.png +0 -0
  50. data/doc/images/tag_green.png +0 -0
  51. data/doc/images/transparent.png +0 -0
  52. data/doc/images/wrench.png +0 -0
  53. data/doc/images/wrench_orange.png +0 -0
  54. data/doc/images/zoom.png +0 -0
  55. data/doc/index.html +175 -0
  56. data/doc/js/darkfish.js +161 -0
  57. data/doc/js/jquery.js +4 -0
  58. data/doc/js/navigation.js +142 -0
  59. data/doc/js/navigation.js.gz +0 -0
  60. data/doc/js/search.js +109 -0
  61. data/doc/js/search_index.js +1 -0
  62. data/doc/js/search_index.js.gz +0 -0
  63. data/doc/js/searcher.js +229 -0
  64. data/doc/js/searcher.js.gz +0 -0
  65. data/doc/table_of_contents.html +94 -0
  66. data/lib/cash_addr.rb +59 -0
  67. data/lib/cash_addr/address.rb +109 -0
  68. data/lib/cash_addr/crypto.rb +95 -0
  69. data/lib/cash_addr/version.rb +7 -0
  70. metadata +182 -0
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base58'
4
+ require 'digest'
5
+
6
+ module CashAddr
7
+ class Address
8
+ attr_accessor :version, :payload, :prefix, :digest
9
+
10
+ VERSION_MAP = {
11
+ 'legacy' => [
12
+ ['P2SH', 5],
13
+ ['P2PKH', 0],
14
+ ['P2SHTestnet', 196],
15
+ ['P2PKHTestnet', 111]
16
+ ],
17
+ 'cash' => [
18
+ ['P2SH', 8],
19
+ ['P2PKH', 0],
20
+ ['P2SHTestnet', 8],
21
+ ['P2PKHTestnet', 0]
22
+ ]
23
+ }.freeze
24
+ DEFAULT_PREFIX = 'bitcoincash'
25
+
26
+ def initialize(version, payload, prefix = nil, digest = nil)
27
+ @version = version
28
+ @payload = payload
29
+ @digest = digest
30
+
31
+ @prefix = prefix ? prefix : DEFAULT_PREFIX
32
+
33
+ @version = 'P2SHTestnet' if prefix == 'bchtest' && version == 'P2SH'
34
+ @version = 'P2PKHTestnet' if prefix == 'bchtest' && version == 'P2PKH'
35
+ @prefix = 'bchtest' if %w[P2SHTestnet P2PKHTestnet].include?(@version)
36
+ end
37
+
38
+ def legacy_address
39
+ version_int = self.class.address_type('legacy', version)[1]
40
+ input = self.class.code_list_to_string([version_int] + payload + Array(digest))
41
+ input += Digest::SHA256.digest(Digest::SHA256.digest(input))[0..3] unless digest
42
+ Base58.binary_to_base58(input, :bitcoin)
43
+ end
44
+
45
+ def cash_address
46
+ version_int = self.class.address_type('cash', version)[1]
47
+ p = [version_int] + payload
48
+ p = CashAddr::Crypto.convertbits(p, 8, 5)
49
+ checksum = CashAddr::Crypto.calculate_checksum(prefix, p)
50
+ prefix + ':' + CashAddr::Crypto.b32encode(p + checksum)
51
+ end
52
+
53
+ def self.code_list_to_string(code_list)
54
+ code_list.map { |i| Array(i).pack('C*') }.flatten.join
55
+ end
56
+
57
+ def self.address_type(address_type, version)
58
+ VERSION_MAP[address_type].each do |mapping|
59
+ return mapping if mapping.include?(version)
60
+ end
61
+
62
+ raise(CashAddr::InvalidAddress.new, 'Could not determine address version')
63
+ end
64
+
65
+ def self.from_string(address_string)
66
+ raise(CashAddr::InvalidAddress, 'Expected string as input') unless address_string.is_a?(String)
67
+ return legacy_string(address_string) unless address_string.include?(':')
68
+ cash_string(address_string)
69
+ end
70
+
71
+ def self.legacy_string(address_string)
72
+ decoded = nil
73
+ begin
74
+ decoded = Base58.base58_to_binary(address_string, :bitcoin).bytes
75
+ rescue StandardError
76
+ raise(CashAddr::InvalidAddress.new, 'Could not decode legacy address')
77
+ end
78
+ ver = address_type('legacy', decoded[0].to_i)[0]
79
+ payload = decoded[1..-5]
80
+ digest = decoded[-4..-1]
81
+
82
+ new(ver, payload, nil, digest)
83
+ end
84
+
85
+ def self.cash_string(address_string)
86
+ if address_string.upcase != address_string && address_string.downcase != address_string
87
+ raise(CashAddr::InvalidAddress.new, 'Cash address contains uppercase and lowercase characters')
88
+ end
89
+
90
+ address_string = address_string.downcase
91
+ if !address_string.include?(':')
92
+ address_string = DEFAULT_PREFIX + ':' + address_string
93
+ end
94
+
95
+ pre, base32string = address_string.split(':')
96
+ decoded = CashAddr::Crypto.b32decode(base32string)
97
+
98
+ if !CashAddr::Crypto.verify_checksum(pre, decoded)
99
+ raise(CashAddr::InvalidAddress.new, 'Bad cash address checksum')
100
+ end
101
+
102
+ converted = CashAddr::Crypto.convertbits(decoded, 5, 8)
103
+ ver = CashAddr::Address.address_type('cash', converted[0].to_i)[0]
104
+ p = converted[1..-7]
105
+
106
+ new(ver, p, pre, nil)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CashAddr
4
+ class Crypto
5
+ CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
6
+
7
+ def self.polymod(values)
8
+ chk = 1
9
+ generator = [
10
+ [0x01, 0x98f2bc8e61],
11
+ [0x02, 0x79b76d99e2],
12
+ [0x04, 0xf33e5fb3c4],
13
+ [0x08, 0xae2eabe2a8],
14
+ [0x10, 0x1e4f43e470]
15
+ ]
16
+ values.each do |value|
17
+ top = chk >> 35
18
+ chk = ((chk & 0x07ffffffff) << 5) ^ value
19
+ generator.each do |i|
20
+ chk ^= i[1] if (top & i[0]) != 0
21
+ end
22
+ end
23
+ chk ^ 1
24
+ end
25
+
26
+ def self.prefix_expand(prefix)
27
+ val = if prefix
28
+ prefix.split('').map do |i|
29
+ i.ord & 0x1f
30
+ end
31
+ else
32
+ []
33
+ end
34
+
35
+ val + [0]
36
+ end
37
+
38
+ def self.calculate_checksum(prefix, payload)
39
+ poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0])
40
+ out = []
41
+ 8.times do |i|
42
+ out.push((poly >> 5 * (7 - i)) & 0x1f)
43
+ end
44
+ out
45
+ end
46
+
47
+ def self.verify_checksum(prefix, payload)
48
+ polymod(prefix_expand(prefix) + payload) == 0
49
+ rescue TypeError
50
+ raise CashAddr::InvalidAddress
51
+ end
52
+
53
+ def self.b32decode(inputs)
54
+ out = []
55
+ return out unless inputs
56
+
57
+ inputs.split('').each do |letter|
58
+ out.push(CHARSET.index(letter))
59
+ end
60
+ out
61
+ end
62
+
63
+ def self.b32encode(inputs)
64
+ out = ''
65
+ inputs.each do |char_code|
66
+ out += CHARSET[char_code].to_s
67
+ end
68
+ out
69
+ end
70
+
71
+ def self.convertbits(data, frombits, tobits, pad = true)
72
+ acc = 0
73
+ bits = 0
74
+ ret = []
75
+ maxv = (1 << tobits) - 1
76
+ max_acc = (1 << (frombits + tobits - 1)) - 1
77
+ data.each do |value|
78
+ return nil if value < 0 || ((value >> frombits) != 0)
79
+
80
+ acc = ((acc << frombits) | value) & max_acc
81
+ bits += frombits
82
+ while bits >= tobits
83
+ bits -= tobits
84
+ ret.push((acc >> bits) & maxv)
85
+ end
86
+ end
87
+ if pad
88
+ ret.push((acc << (tobits - bits)) & maxv) if bits != 0
89
+ elsif bits >= frombits || (((acc << (tobits - bits)) & maxv) != 0)
90
+ return nil
91
+ end
92
+ ret
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CashAddr
4
+ ##
5
+ # Version of the cash-addr gem.
6
+ VERSION = '0.1.1'
7
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cash-addr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Josh Ellithorpe
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.52.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.52.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: base58
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.2
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.2
83
+ description:
84
+ email:
85
+ - josh.ellithorpe@coinbase.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".rubocop.yml"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/console
100
+ - bin/setup
101
+ - cash-addr.gemspec
102
+ - circle.yml
103
+ - doc/CashAddr.html
104
+ - doc/CashAddr/Converter.html
105
+ - doc/CashAddr/InvalidAddress.html
106
+ - doc/LICENSE_txt.html
107
+ - doc/README_md.html
108
+ - doc/cash-addr_gemspec.html
109
+ - doc/created.rid
110
+ - doc/css/fonts.css
111
+ - doc/css/rdoc.css
112
+ - doc/fonts/Lato-Light.ttf
113
+ - doc/fonts/Lato-LightItalic.ttf
114
+ - doc/fonts/Lato-Regular.ttf
115
+ - doc/fonts/Lato-RegularItalic.ttf
116
+ - doc/fonts/SourceCodePro-Bold.ttf
117
+ - doc/fonts/SourceCodePro-Regular.ttf
118
+ - doc/images/add.png
119
+ - doc/images/arrow_up.png
120
+ - doc/images/brick.png
121
+ - doc/images/brick_link.png
122
+ - doc/images/bug.png
123
+ - doc/images/bullet_black.png
124
+ - doc/images/bullet_toggle_minus.png
125
+ - doc/images/bullet_toggle_plus.png
126
+ - doc/images/date.png
127
+ - doc/images/delete.png
128
+ - doc/images/find.png
129
+ - doc/images/loadingAnimation.gif
130
+ - doc/images/macFFBgHack.png
131
+ - doc/images/package.png
132
+ - doc/images/page_green.png
133
+ - doc/images/page_white_text.png
134
+ - doc/images/page_white_width.png
135
+ - doc/images/plugin.png
136
+ - doc/images/ruby.png
137
+ - doc/images/tag_blue.png
138
+ - doc/images/tag_green.png
139
+ - doc/images/transparent.png
140
+ - doc/images/wrench.png
141
+ - doc/images/wrench_orange.png
142
+ - doc/images/zoom.png
143
+ - doc/index.html
144
+ - doc/js/darkfish.js
145
+ - doc/js/jquery.js
146
+ - doc/js/navigation.js
147
+ - doc/js/navigation.js.gz
148
+ - doc/js/search.js
149
+ - doc/js/search_index.js
150
+ - doc/js/search_index.js.gz
151
+ - doc/js/searcher.js
152
+ - doc/js/searcher.js.gz
153
+ - doc/table_of_contents.html
154
+ - lib/cash_addr.rb
155
+ - lib/cash_addr/address.rb
156
+ - lib/cash_addr/crypto.rb
157
+ - lib/cash_addr/version.rb
158
+ homepage: https://github.com/coinbase/cash-addr
159
+ licenses:
160
+ - Apache-2.0
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.7.4
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: Library to convert between base58 and CashAddr BCH addresses
182
+ test_files: []