bitcoin-addrgen 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,46 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+ *.gem
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+
31
+ .DS_Store
32
+
33
+ # For TextMate
34
+ *.tmproj
35
+ tmtags
36
+ .tmtags
37
+
38
+ # For emacs:
39
+ *~
40
+ \#*
41
+ .\#*
42
+
43
+ # For vim:
44
+ *.swp
45
+
46
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bitcoin-addrgen (0.0.1)
5
+ gmp (~> 0.6.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ diff-lcs (1.2.4)
11
+ gmp (0.6.31)
12
+ rake (10.0.4)
13
+ rspec (2.13.0)
14
+ rspec-core (~> 2.13.0)
15
+ rspec-expectations (~> 2.13.0)
16
+ rspec-mocks (~> 2.13.0)
17
+ rspec-core (2.13.1)
18
+ rspec-expectations (2.13.0)
19
+ diff-lcs (>= 1.1.3, < 2.0)
20
+ rspec-mocks (2.13.1)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bitcoin-addrgen!
27
+ rake (~> 10.0.4)
28
+ rspec (~> 2.13.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Matyas Danter
2
+ Copyright (c) 2012 Chris Savery
3
+ Copyright (c) 2013 Pavol Rusnak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the "Software"),
7
+ to deal in the Software without restriction, including without limitation
8
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ and/or sell copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
19
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Bitcoin Addrgen - Deterministic Bitcoin Address Generator
2
+
3
+ ## Installation
4
+
5
+ ```sh
6
+ ~$ gem install bitcoin-addrgen
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ require 'bitcoin-addrgen'
13
+
14
+ master_public_key = "675b7041a347223984750fe3ab229df0c9f960e7ec98226b7182a2cb1990e39901feecf5a670f1d788ab29f626e20de424f049d216fc6f4c6ec42506763fa28e"
15
+ first_ten_addresses = 10.times.collect do |address_index|
16
+ BitcoinAddrgen.generate_public_address(master_public_key, address_index)
17
+ end
18
+
19
+ puts first_ten_addresses
20
+ # 13EfJ1hQBGMBbEwvPa3MLeH6buBhiMSfCC
21
+ # 1AZW6GGmsUizkHhrtg853Qnxk68vCx2gFq
22
+ # 1M7ReiRrcYygYYLdyPS2cKQi7p9s9ViFS1
23
+ # 1NcawzHFoECpDhz8hUCZp43hGjvdEB8DBA
24
+ # 1PjcUN9kEBn3kvUmT2BXezwTG4RRvrqjRw
25
+ # 16QNfbdLoKkMKtQR3MK8uisss7YAF88Yv4
26
+ # 1jmA5ySdFz7cDwWb15rWQe63ZUo8spiBa
27
+ # 1BsHKTsi3umme8xv4GbrPxGCfQ2feJYZAV
28
+ # 16uCFEcanBtRPAwn6GhkFtmVeurrkbgt1U
29
+ # 1J7yTE8Cm9fMV9nqCjnM6kTTzTkksVic98
30
+
31
+ ```
32
+
33
+ # Credits
34
+
35
+ * Pure PHP Elliptic Curve Cryptography Library by Matej Danter, https://github.com/mdanter/phpecc
36
+ * mpkgen by Chris Savery, https://github.com/bkkcoins/misc/tree/master/mpkgen/php
37
+
38
+ Donations welcome at 1PuRV7zVXrajGxHJ6LJLccgDYz4hNcVPfS
39
+
40
+ # Copyright
41
+
42
+ See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Run Specs'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "bitcoin_addrgen/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "bitcoin-addrgen"
8
+ s.version = BitcoinAddrgen::VERSION
9
+ s.authors = ["Pavol Rusnak"]
10
+ s.email = ["stick@gk2.sk"]
11
+ s.homepage = "https://github.com/prusnak/addrgen"
12
+ s.summary = "Deterministic Bitcoin Address Generator"
13
+ s.description = "Deterministic Bitcoin Address Generator."
14
+
15
+ s.rubyforge_project = "bitcoin-addrgen"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'gmp', '~> 0.6.0'
22
+ s.add_development_dependency 'rspec', '~> 2.13.0'
23
+ s.add_development_dependency 'rake', '~> 10.0.4'
24
+ end
@@ -0,0 +1,9 @@
1
+ module BitcoinAddrgen; end
2
+
3
+ require 'bitcoin_addrgen/addrgen'
4
+
5
+ module BitcoinAddrgen
6
+ def self.generate_public_address(master_public_key, address_index)
7
+ BitcoinAddrgen::Addrgen.addr_from_mpk(master_public_key, address_index)
8
+ end
9
+ end
@@ -0,0 +1,205 @@
1
+ #
2
+ # Copyright (c) 2012 Matyas Danter
3
+ # Copyright (c) 2012 Chris Savery
4
+ # Copyright (c) 2013 Pavol Rusnak
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the "Software"),
8
+ # to deal in the Software without restriction, including without limitation
9
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ # and/or sell copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included
14
+ # in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
20
+ # OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ # OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ require 'digest'
25
+ require 'gmp'
26
+
27
+ module BitcoinAddrgen
28
+ class Curve
29
+
30
+ attr_reader :prime, :a, :b
31
+
32
+ def initialize(prime, a, b)
33
+ @prime = prime
34
+ @a = a
35
+ @b = b
36
+ end
37
+
38
+ def contains(x, y)
39
+ GMP::Z.new(0) == (y**2 - (x ** 3 + @a * x + @b)).fmod(@prime)
40
+ end
41
+
42
+ def self.cmp(cp1, cp2)
43
+ if cp1.a == cp2.a and cp1.b == cp2.b and cp1.prime == cp2.prime
44
+ return 0
45
+ else
46
+ return 1
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ class Point
53
+
54
+ attr_reader :curve, :x, :y, :order
55
+
56
+ def initialize(curve, x, y, order = nil)
57
+ @curve = curve
58
+ @x = x
59
+ @y = y
60
+ @order = order
61
+ if @curve and @curve.instance_of?(Curve)
62
+ raise Exception, 'Curve does not contain point' if !@curve.contains(@x, @y)
63
+ if @order != nil
64
+ raise Exception, 'Self*Order must equal infinity' if (Point.cmp(Point.mul(order, self), :infinity) != 0)
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.cmp(p1, p2)
70
+ if !p1.instance_of?(Point)
71
+ return 1 if p2.instance_of?(Point)
72
+ return 0 if !p2.instance_of?(Point)
73
+ end
74
+ if !p2.instance_of?(Point)
75
+ return 1 if p1.instance_of?(Point)
76
+ return 0 if !p1.instance_of?(Point)
77
+ end
78
+ if p1.x == p2.x and p1.y == p2.y and Curve.cmp(p1.curve, p2.curve)
79
+ return 0
80
+ else
81
+ return 1
82
+ end
83
+ end
84
+
85
+ def self.add(p1, p2)
86
+
87
+ return p1 if Point.cmp(p2, :infinity) == 0 and p1.instance_of?(Point)
88
+ return p2 if Point.cmp(p1, :infinity) == 0 and p2.instance_of?(Point)
89
+ return :infinity if Point.cmp(p1, :infinity) == 0 and Point.cmp(p2, :infinity) == 0
90
+
91
+ if Curve.cmp(p1.curve, p2.curve) == 0
92
+ if p1.x == p2.x
93
+ if (p1.y + p2.y).fmod(p1.curve.prime) == 0
94
+ return :infinity
95
+ else
96
+ return Point.double(p1)
97
+ end
98
+ end
99
+ p = p1.curve.prime
100
+ l = (p2.y - p1.y) * (p2.x - p1.x).invert(p)
101
+ x3 = (l ** 2 - p1.x - p2.x).fmod(p)
102
+ y3 = (l * (p1.x - x3) - p1.y).fmod(p)
103
+ p3 = Point.new(p1.curve, x3, y3)
104
+ return p3
105
+ else
106
+ raise Exception, 'Elliptic curves do not match'
107
+ end
108
+ end
109
+
110
+ def self.mul(x2, p1)
111
+ e = x2
112
+ return :infinity if Point.cmp(p1, :infinity) == 0
113
+ e = e.fmod(p1.order) if p1.order != nil
114
+ return :infinity if e == GMP::Z.new(0)
115
+ if e > GMP::Z.new(0)
116
+ e3 = 3 * e
117
+ negative_self = Point.new(p1.curve, p1.x, -p1.y, p1.order)
118
+ i = Point.leftmost_bit(e3).tdiv(2)
119
+ result = p1
120
+ while i > GMP::Z.new(1)
121
+ result = Point.double(result)
122
+ result = Point.add(result, p1) if (e3 & i) != GMP::Z.new(0) and (e & i) == GMP::Z.new(0)
123
+ result = Point.add(result, negative_self) if (e3 & i) == GMP::Z.new(0) and (e & i) != GMP::Z.new(0)
124
+ i = i.tdiv(2)
125
+ end
126
+ return result
127
+ end
128
+ end
129
+
130
+ def self.leftmost_bit(x)
131
+ if x > GMP::Z.new(0)
132
+ result = GMP::Z.new(1)
133
+ while result <= x
134
+ result *= 2
135
+ end
136
+ return result.tdiv(2)
137
+ end
138
+ end
139
+
140
+ def self.double(p1)
141
+ p = p1.curve.prime
142
+ a = p1.curve.a
143
+ inverse = (2 * p1.y).invert(p)
144
+ three_x2 = 3 * (p1.x ** 2)
145
+ l = ((three_x2 + a) * inverse).fmod(p)
146
+ x3 = (l ** 2 - 2 * p1.x).fmod(p)
147
+ y3 = (l * (p1.x - x3) - p1.y).fmod(p)
148
+ y3 = p + y3 if 0 > y3
149
+ p3 = Point.new(p1.curve, x3, y3)
150
+ p3
151
+ end
152
+
153
+ end
154
+
155
+ class Addrgen
156
+ def self.hex_to_bin(s)
157
+ [s].pack('H*')
158
+ end
159
+
160
+ def self.sha256_raw(data)
161
+ Digest::SHA256.digest(data)
162
+ end
163
+
164
+ def self.sha256(data)
165
+ Digest::SHA256.hexdigest(data)
166
+ end
167
+
168
+ def self.ripemd160(data)
169
+ Digest::RMD160.hexdigest(data)
170
+ end
171
+
172
+ def self.addr_from_mpk(mpk, idx)
173
+ _p = GMP::Z.new('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)
174
+ _r = GMP::Z.new('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)
175
+ _b = GMP::Z.new('0000000000000000000000000000000000000000000000000000000000000007', 16)
176
+ _Gx = GMP::Z.new('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
177
+ _Gy = GMP::Z.new('483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
178
+ curve = Curve.new(_p, 0, _b)
179
+ gen = Point.new(curve, _Gx, _Gy, _r)
180
+
181
+ # prepare the input values
182
+ x = GMP::Z.new(mpk[0, 64], 16)
183
+ y = GMP::Z.new(mpk[64, 64], 16)
184
+ z = GMP::Z.new(sha256(sha256_raw(idx.to_s + ':0:' + hex_to_bin(mpk))), 16)
185
+
186
+ # generate the new public key based off master and sequence points
187
+ pt = Point.add(Point.new(curve, x, y), Point.mul(z, gen))
188
+ keystr = hex_to_bin('04' + pt.x.to_s(16).rjust(64, '0') + pt.y.to_s(16).rjust(64, '0'))
189
+ vh160 = '00' + ripemd160(sha256_raw(keystr))
190
+ addr = vh160 + sha256(sha256_raw(hex_to_bin(vh160)))[0, 8]
191
+
192
+ num = GMP::Z.new(addr, 16).to_s(58)
193
+ num = num.tr('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv', '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz')
194
+
195
+ pad = ''
196
+ n = 0
197
+ while addr[n] == '0' and addr[n+1] == '0'
198
+ pad += '1'
199
+ n += 2
200
+ end
201
+
202
+ pad + num
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,3 @@
1
+ module BitcoinAddrgen
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe BitcoinAddrgen do
4
+
5
+ let(:master_public_key) { '675b7041a347223984750fe3ab229df0c9f960e7ec98226b7182a2cb1990e39901feecf5a670f1d788ab29f626e20de424f049d216fc6f4c6ec42506763fa28e' }
6
+
7
+ context "generate_public_address" do
8
+ subject do
9
+ BitcoinAddrgen.generate_public_address(master_public_key, address_index)
10
+ end
11
+
12
+ context "with first address index" do
13
+ let(:address_index) { 0 }
14
+
15
+ it "generates the correct public address" do
16
+ expect(subject).to eq('13EfJ1hQBGMBbEwvPa3MLeH6buBhiMSfCC')
17
+ end
18
+ end
19
+
20
+ context "with seventh address index" do
21
+ let(:address_index) { 6 }
22
+
23
+ it "generates the correct public address" do
24
+ expect(subject).to eq('1jmA5ySdFz7cDwWb15rWQe63ZUo8spiBa')
25
+ end
26
+ end
27
+
28
+ context "with 10th address index" do
29
+ let(:address_index) { 9 }
30
+
31
+ it "generates the correct public address" do
32
+ expect(subject).to eq('1J7yTE8Cm9fMV9nqCjnM6kTTzTkksVic98')
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'bitcoin-addrgen'
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitcoin-addrgen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pavol Rusnak
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: gmp
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.6.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.13.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.13.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 10.0.4
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 10.0.4
62
+ description: Deterministic Bitcoin Address Generator.
63
+ email:
64
+ - stick@gk2.sk
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - Gemfile.lock
72
+ - LICENSE.txt
73
+ - README.md
74
+ - Rakefile
75
+ - bitcoin-addrgen.gemspec
76
+ - lib/bitcoin-addrgen.rb
77
+ - lib/bitcoin_addrgen/addrgen.rb
78
+ - lib/bitcoin_addrgen/version.rb
79
+ - spec/bitcoin_addrgen_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: https://github.com/prusnak/addrgen
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project: bitcoin-addrgen
101
+ rubygems_version: 1.8.23
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Deterministic Bitcoin Address Generator
105
+ test_files:
106
+ - spec/bitcoin_addrgen_spec.rb
107
+ - spec/spec_helper.rb