cashaddress 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 657f955260080576cc0fb9031bc36f5ae4b67bd0
4
+ data.tar.gz: f71f4aeccc2c5db452697c657bb302db084d1f88
5
+ SHA512:
6
+ metadata.gz: 8f558e7b8331a7e8e38d228dbd73616e7120b1895da98b930eff5dd0e48c775e7d72014eb290890469f4319bfcca15ec21654ee1f3d0855f4e8dbfb71fa6a325
7
+ data.tar.gz: cb07eb8951d5e6d922fd20f25c7ee0bfc8fd609fc29aa19c20cf4cdb175e702c874c3e2787eedaecd256cc5f969715cf54ff9938951e63c8d8a5b975bae37a2e
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cashaddress.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 nubis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ # Cashaddress
2
+
3
+ Converts between bitcoin cash cashaddress and legacy bitcoin address format.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'cashaddress'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install cashaddress
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ > Cashaddress.from_legacy('1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu')
25
+ => 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a',
26
+ > Cashaddress.to_legacy('bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a')
27
+ => '1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu'
28
+ ```
29
+
30
+ ## Development
31
+
32
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
33
+
34
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bitex-la/cashaddress-ruby
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cashaddress"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "cashaddress/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cashaddress"
8
+ spec.version = Cashaddress::VERSION
9
+ spec.authors = ["nubis"]
10
+ spec.email = ["yo@nubis.im"]
11
+
12
+ spec.summary = %q{Convert between bitcoin legacy and cashaddress format}
13
+ spec.description = %q{Converts between bitcoin legacy and cashaddress formats}
14
+ spec.homepage = "https://github.com/bitex-la/cashaddress-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.15"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ spec.add_development_dependency "byebug"
28
+ end
@@ -0,0 +1,193 @@
1
+ require "cashaddress/version"
2
+ require 'digest'
3
+
4
+ module Cashaddress
5
+ def self.to_lookup(array)
6
+ Hash[*array.each_with_index.to_a.flatten]
7
+ end
8
+
9
+ CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l".split('')
10
+ CHARSET_LOOKUP = to_lookup(CHARSET)
11
+
12
+ # ALPHABET is missing some confusing characters like O and I
13
+ ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".split('')
14
+ ALPHABET_LOOKUP = to_lookup(ALPHABET)
15
+
16
+ MAINNET_PREFIX = [2, 9, 20, 3, 15, 9, 14, 3, 1, 19, 8, 0]
17
+ TESTNET_PREFIX = [2, 3, 8, 20, 5, 19, 20, 0]
18
+
19
+ def self.to_legacy(cashaddress)
20
+ unless match = /(.+?):(.+)/.match(cashaddress)
21
+ raise Error.new("Malformed cashaddress: #{cashaddress}")
22
+ end
23
+
24
+ is_mainnet = match.captures.first == 'bitcoincash'
25
+ prefix = is_mainnet ? MAINNET_PREFIX.clone : TESTNET_PREFIX.clone
26
+ raw_payload = match.captures.last.split('').map{|m| CHARSET_LOOKUP[m] }
27
+
28
+ if polymod(prefix + raw_payload) != 0
29
+ raise Error.new("Address checksum invalid: #{cashaddress}")
30
+ end
31
+
32
+ payload = convert_bits(raw_payload[0..-9], 5, 8, false)
33
+ if payload.empty?
34
+ raise Error.new("Converted payload was empty")
35
+ end
36
+
37
+ version = if (payload.first >> 3) == 0
38
+ is_mainnet ? 0x00 : 0x6f
39
+ else
40
+ is_mainnet ? 0x05 : 0xc4
41
+ end
42
+
43
+ make_old_address(payload[1..21], version)
44
+ end
45
+
46
+ def self.make_old_address(payload, version)
47
+ raw = [version] + payload
48
+ digest = Digest::SHA256.digest(Digest::SHA256.digest(raw.pack("C*"))).unpack('C*')
49
+ encode_base58( raw + digest[0..3] )
50
+ end
51
+
52
+ def self.encode_base58(bytes)
53
+ digits = [0]
54
+ bytes.each do |byte|
55
+ carry = byte
56
+ digits.each_with_index do |digit, index|
57
+ carry += digit << 8
58
+ digits[index] = carry % 58
59
+ carry = ( carry / 58 ) | 0
60
+ end
61
+
62
+ while carry > 0
63
+ digits << carry % 58
64
+ carry = (carry / 58) | 0
65
+ end
66
+ end
67
+
68
+ encoded = ''
69
+ bytes.each do |byte|
70
+ break if byte != 0
71
+ encoded << "1"
72
+ end
73
+
74
+ encoded << digits.reverse.map{|d| ALPHABET[d] }.join('')
75
+
76
+ encoded
77
+ end
78
+
79
+ def self.from_legacy(legacy_address)
80
+ bytes = [0]
81
+ characters = legacy_address.split('')
82
+
83
+ characters.each_with_index do |char, i|
84
+ unless value = ALPHABET_LOOKUP[char]
85
+ raise Error.new("Invalid char #{char}")
86
+ end
87
+
88
+ carry = value
89
+ bytes.each_with_index do |byte, j|
90
+ carry += byte * 58
91
+ bytes[j] = carry & 0xff
92
+ carry = carry >> 8
93
+ end
94
+
95
+ while carry > 0
96
+ bytes << ( carry & 0xff )
97
+ carry = carry >> 8
98
+ end
99
+ end
100
+
101
+ characters.each do |char|
102
+ break if char != "1"
103
+ bytes << 0
104
+ end
105
+
106
+ if bytes.size < 5
107
+ raise Error.new("Decoded less than 5 bytes from #{legacy_address}")
108
+ end
109
+
110
+ bytes = bytes.reverse
111
+ version = bytes.first
112
+
113
+ digest = Digest::SHA256.digest(Digest::SHA256.digest(bytes[0..-5].pack("C*"))).unpack('C*')
114
+
115
+ if digest[0..3] != bytes[-4..-1]
116
+ raise Error.new("Invalid checksum")
117
+ end
118
+
119
+ kind, is_main_net = case version
120
+ when 0x00, 0x1c then [0, true]
121
+ when 0x05, 0x28 then [1, true]
122
+ when 0x6f then [0, false]
123
+ when 0xc4 then [1, false]
124
+ else raise Error.new("Unexpected version #{version}")
125
+ end
126
+
127
+ make_cashaddress(kind, bytes[1..-5], is_main_net)
128
+ end
129
+
130
+ def self.make_cashaddress(kind, hash, main_net)
131
+ packed = convert_bits( [ kind << 3] + hash, 8, 5, true)
132
+ raise Error.new("Can't pack Cashaddress") unless packed
133
+
134
+ prefix = main_net ? MAINNET_PREFIX.clone : TESTNET_PREFIX.clone
135
+ encoded = prefix.concat(packed)
136
+ moduled = polymod(encoded + [0] * 8)
137
+ checksum = 8.times.map do |i|
138
+ shifted = ('%0*b' % [ 5 * (i+1), moduled >> ( 5 * (7-i))])[-5..-1]
139
+ .split('').map(&:to_i)
140
+ to_five_bit_array(shifted).first
141
+ end
142
+
143
+ address = main_net ? "bitcoincash:" : "bchtest:"
144
+
145
+ address << (packed + checksum).map{|b| CHARSET[b] }.join('')
146
+
147
+ if address.size != 54 && address.size != 50
148
+ raise Error.new("Address is not 50 or 54 characters long #{address}")
149
+ end
150
+
151
+ address
152
+ end
153
+
154
+ def self.to_five_bit_array(array)
155
+ array.each_slice(5).collect do |slice|
156
+ slice.zip([16, 8, 4, 2, 1]).map{|a,b| a * b}.sum
157
+ end
158
+ end
159
+
160
+ def self.convert_bits(data, from, to, pad)
161
+ converted = data
162
+ .map{|i| '%0*b' % [from, i] } # Get chunks <from> bits long
163
+ .join('') # In a long row of zeros and ones.
164
+ .chars.each_slice(to) # Then in bits that are <
165
+ .map{|bits| bits.join('').ljust(to, '0').to_i(2) }
166
+
167
+ converted = converted[0..-2] if converted.last == 0 && !pad
168
+ converted
169
+ end
170
+
171
+ # Polymod checksum is defined in the cashaddr spec
172
+ # https://github.com/Bitcoin-UAHF/spec/blob/master/cashaddr.md
173
+ def self.polymod(vector)
174
+ accum = 1
175
+ vector.each do |byte|
176
+ pivot = accum >> 35
177
+ accum = ((accum & 0x07ffffffff) << 5) ^ byte
178
+
179
+ [
180
+ [0x01, 0x98f2bc8e61],
181
+ [0x02, 0x79b76d99e2],
182
+ [0x04, 0xf33e5fb3c4],
183
+ [0x08, 0xae2eabe2a8],
184
+ [0x10, 0x1e4f43e470],
185
+ ].each do |bit, mask|
186
+ accum ^= mask if (pivot & bit) != 0
187
+ end
188
+ end
189
+ accum ^ 1
190
+ end
191
+
192
+ class Error < StandardError; end;
193
+ end
@@ -0,0 +1,3 @@
1
+ module Cashaddress
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cashaddress
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - nubis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-18 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.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
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: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Converts between bitcoin legacy and cashaddress formats
70
+ email:
71
+ - yo@nubis.im
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - cashaddress.gemspec
86
+ - lib/cashaddress.rb
87
+ - lib/cashaddress/version.rb
88
+ homepage: https://github.com/bitex-la/cashaddress-ruby
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.6.11
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Convert between bitcoin legacy and cashaddress format
112
+ test_files: []