bitcoin-addrgen 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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