onchain 0.4.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e90d6f43622f2013449f0c645899416c69cda45
4
- data.tar.gz: 0d2e9ee4ab58da945e8f8cb47c70659174a765bc
3
+ metadata.gz: 434e9d7b76fb77c75cd6ee42ee305c88ff1e654f
4
+ data.tar.gz: 3414885379272cc9c61a53a1310de8a30652c73b
5
5
  SHA512:
6
- metadata.gz: a86aac2c975f70495a05579fed758f0f8555cc05d40edbe093168e0a451ee19b82f47564c45e4d2d655004494eaa6b44f19a2dcdcfc743405bc21ca19caaa3bd
7
- data.tar.gz: d6935006cc73b8f8997d47a200d4dfe943125472f076c1774969f6e46b6cc1961461c79328c4c7860586adf7a1d3bbc579258f7a8ce04a8fcebf7374becb9572
6
+ metadata.gz: 15e32c96f9ad075a6d390ed51c82fede89aadc6d964414b224c92887bb81e027e0730ff2ff3a7fd22e73535470966d0e1e4a2ea442f3bab26c5d99725e025437
7
+ data.tar.gz: f38144aac4022320524fc41fb273b6f88afac0795f6b08598c4620d948f4faa035dedb287f9cd509205fae25b20eeae4f3023bb0d4615a858a29577b090955d3
@@ -0,0 +1,289 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ class OnChain
5
+ class << self
6
+
7
+ BALANCE_CACHE_FOR = 120
8
+ API_CACHE_FOR = 60
9
+
10
+ @@cache = {}
11
+
12
+ def cache_write(key, data, max_age=0)
13
+ @@cache[key] = [Time.now, data, max_age]
14
+ end
15
+
16
+ def cache_read(key)
17
+ # if the API URL exists as a key in cache, we just return it
18
+ # we also make sure the data is fresh
19
+ if @@cache.has_key? key
20
+ return @@cache[key][1] if Time.now-@@cache[key][0] < @@cache[key][2]
21
+ end
22
+ end
23
+
24
+
25
+ def get_all_balances(addresses)
26
+
27
+ if ! blockr_down?
28
+ bal = blockr_get_all_balances(addresses)
29
+ return bal
30
+ end
31
+
32
+ if ! blockchain_down?
33
+ bal = blockinfo_get_all_balances(addresses)
34
+ return bal
35
+ end
36
+ end
37
+
38
+ def get_balance(address)
39
+
40
+ # These guys make 300k per month so hammer them first.
41
+ if ! blockchain_down?
42
+ bal = blockinfo_balance(address)
43
+ if ! bal.instance_of? String
44
+ return bal
45
+ end
46
+ end
47
+
48
+ # Looks like blockchain is down, let's try blockr
49
+ if ! blockr_down?
50
+ bal = blockr_balance(address)
51
+ if ! bal.instance_of? String
52
+ return bal
53
+ end
54
+ end
55
+
56
+ # OK I give up.
57
+ 'Balance could not be retrieved'
58
+ end
59
+
60
+ def send_tx(tx_hex)
61
+ #payload = { :tx => tx_hex }
62
+ #HTTParty.post('http://blockchain.info/pushtx', {:body => payload})
63
+
64
+ # Blockchain doesn't support multi sig, so only use blockr
65
+ uri = URI.parse("http://btc.blockr.io/api/v1/tx/push")
66
+ http = Net::HTTP.new(uri.host, uri.port)
67
+
68
+ request = Net::HTTP::Post.new(uri.request_uri)
69
+ request.body = '{"hex":"' + tx_hex + '"}'
70
+ response = http.request(request)
71
+ return response
72
+ end
73
+
74
+ def get_history
75
+ return []
76
+ end
77
+
78
+ def get_unspent_outs(address)
79
+
80
+
81
+ # Looks like blockchain is down, let's try blockr
82
+ if ! blockr_down?
83
+ return blockr_unspent(address)
84
+ end
85
+
86
+ if ! blockchain_down?
87
+ return blockinfo_unspent(address)
88
+ end
89
+
90
+ # OK I give up.
91
+ 'Unspent outputs could not be retrieved'
92
+
93
+ end
94
+
95
+ def blockchain_is_down
96
+ cache_write(:blockchainapi, 'it is down', 60)
97
+ end
98
+
99
+ def blockchain_down?
100
+ if cache_read(:blockchainapi) != nil
101
+ return true
102
+ end
103
+ return false
104
+ end
105
+
106
+ def blockr_is_down
107
+ cache_write(:blockrapi, 'it is down', 60)
108
+ end
109
+
110
+ def blockr_down?
111
+ if cache_read(:blockrapi) != nil
112
+ return true
113
+ end
114
+ return false
115
+ end
116
+
117
+ def blockinfo_get_all_balances(addresses)
118
+ begin
119
+ base = "https://blockchain.info/multiaddr?&simple=true&active="
120
+
121
+ addresses.each do |address|
122
+ base = base + address + '|'
123
+ end
124
+
125
+ json = fetch_response(URI::encode(base))
126
+
127
+ addresses.each do |address|
128
+ bal = json[address]['final_balance'] / 100000000.0
129
+ cache_write(address, bal, BALANCE_CACHE_FOR)
130
+ end
131
+
132
+ rescue
133
+ 'Balance could not be retrieved'
134
+ end
135
+ end
136
+
137
+ def blockinfo_unspent(address)
138
+ begin
139
+ base_url = "http://blockchain.info/unspent?active=#{address}"
140
+ json = fetch_response(base_url, true)
141
+
142
+ unspent = []
143
+
144
+ json['unspent_outputs'].each do |data|
145
+ line = []
146
+ line << reverse_blockchain_tx(data['tx_hash'])
147
+ line << data['tx_output_n']
148
+ line << data['script']
149
+ line << data['value']
150
+ unspent << line
151
+ end
152
+
153
+ return unspent
154
+ rescue Exception => e
155
+ puts e.to_s
156
+ 'Unspent outputs could not be retrieved'
157
+ end
158
+ end
159
+
160
+ def blockinfo_balance(address)
161
+ begin
162
+ if cache_read(address) == nil
163
+ puts "cache is empty"
164
+ json = block_chain('address', address, "&limit=0")
165
+ if json.key?('final_balance')
166
+ bal = json['final_balance'] / 100000000.0
167
+ cache_write(address, bal, BALANCE_CACHE_FOR)
168
+ else
169
+ cache_write(address, 'Error', BALANCE_CACHE_FOR)
170
+ end
171
+ end
172
+ bal = cache_read(address)
173
+ if bal.class == Fixnum
174
+ bal = bal.to_f
175
+ end
176
+ return bal
177
+ rescue
178
+ 'Balance could not be retrieved'
179
+ end
180
+ end
181
+
182
+ def blockr_unspent(address)
183
+ begin
184
+ base_url = "http://btc.blockr.io/api/v1/address/unspent/#{address}"
185
+ json = fetch_response(base_url, true)
186
+
187
+ unspent = []
188
+
189
+ json['data']['unspent'].each do |data|
190
+ line = []
191
+ line << data['tx']
192
+ line << data['n']
193
+ line << data['script']
194
+ line << (data['amount'].to_f * 100000000).to_i
195
+ unspent << line
196
+ end
197
+
198
+ return unspent
199
+ rescue Exception => e
200
+ puts e.to_s
201
+ 'Unspent outputs could not be retrieved'
202
+ end
203
+ end
204
+
205
+ def blockr_get_all_balances(addresses)
206
+ begin
207
+ base = "https://blockr.io/api/v1/address/balance/"
208
+
209
+ addresses.each do |address|
210
+ base = base + address + ','
211
+ end
212
+
213
+ json = fetch_response(URI::encode(base))
214
+
215
+ json['data'].each do |data|
216
+ bal = data['balance']
217
+ addr = data['address']
218
+ cache_write(addr, bal, BALANCE_CACHE_FOR)
219
+ end
220
+
221
+ rescue
222
+ 'Balance could not be retrieved'
223
+ end
224
+ end
225
+
226
+ def blockr_balance(address)
227
+ begin
228
+ if cache_read(address) == nil
229
+ json = blockr('address/balance', address)
230
+ if json.key?('data')
231
+ bal = json['data']['balance'].to_f
232
+ cache_write(address, bal, BALANCE_CACHE_FOR)
233
+ else
234
+ cache_write(address, 'Error', BALANCE_CACHE_FOR)
235
+ end
236
+ end
237
+ return cache_read(address)
238
+ rescue Exception => e
239
+ puts e
240
+ 'Balance could not be retrieved'
241
+ end
242
+ end
243
+
244
+ def blockr(cmd, address, params = "")
245
+ if ! blockr_down?
246
+ begin
247
+ base_url = "http://blockr.io/api/v1/#{cmd}/#{address}" + params
248
+ fetch_response(base_url, true)
249
+ rescue
250
+ blockr_is_down
251
+ end
252
+ end
253
+ end
254
+
255
+ def block_chain(cmd, address, params = "")
256
+ if ! blockchain_down?
257
+ begin
258
+ base_url = "http://blockchain.info/#{cmd}/#{address}?format=json" + params
259
+
260
+ puts base_url
261
+ fetch_response(base_url, true)
262
+ rescue
263
+ blockchain_is_down
264
+ end
265
+ end
266
+ end
267
+
268
+ def fetch_response(url, do_json=true)
269
+ resp = Net::HTTP.get_response(URI.parse(url))
270
+ data = resp.body
271
+
272
+ if do_json
273
+ result = JSON.parse(data)
274
+ else
275
+ data
276
+ end
277
+ end
278
+
279
+ def reverse_blockchain_tx(hash)
280
+ bytes = hash.scan(/../).map { |x| x.hex.chr }.join
281
+
282
+ bytes = bytes.reverse
283
+
284
+ return hash.scan(/../).reverse.join
285
+ end
286
+
287
+
288
+ end
289
+ end
@@ -0,0 +1,41 @@
1
+ class OnChain
2
+ class << self
3
+
4
+
5
+ # With a bunch of HD wallet paths, build a transaction
6
+ # That pays all the coins to a certain address
7
+ def sweep(paths, mpk, destination_address)
8
+
9
+ master = MoneyTree::Node.from_serialized_address(mpk)
10
+
11
+ tx = Bitcoin::Protocol::Tx.new
12
+
13
+ amount = 0
14
+
15
+ paths.each do |path|
16
+
17
+ address = master.node_for_path(path).to_address
18
+
19
+ unspent = get_unspent_outs(address)
20
+
21
+ unspent.each do |spent|
22
+ txin = Bitcoin::Protocol::TxIn.new
23
+
24
+ txin.prev_out = spent[0]
25
+ txin.prev_out_index = spent[1]
26
+ amount += spent[3].to_i
27
+
28
+ tx.add_in(txin)
29
+ end
30
+ end
31
+
32
+ txout = Bitcoin::Protocol::TxOut.new(amount, Bitcoin::Script.from_string(
33
+ "OP_DUP OP_HASH160 #{destination_address} " +
34
+ "OP_EQUALVERIFY OP_CHECKSIG").to_payload)
35
+
36
+ tx.add_out(txout)
37
+ puts tx.to_json
38
+ end
39
+
40
+ end
41
+ end
data/lib/onchain.rb CHANGED
@@ -1,289 +1,4 @@
1
- require 'net/http'
2
- require 'json'
3
-
4
- class OnChain
5
- class << self
6
-
7
- BALANCE_CACHE_FOR = 120
8
- API_CACHE_FOR = 60
9
-
10
- @@cache = {}
11
-
12
- def cache_write(key, data, max_age=0)
13
- @@cache[key] = [Time.now, data, max_age]
14
- end
15
-
16
- def cache_read(key)
17
- # if the API URL exists as a key in cache, we just return it
18
- # we also make sure the data is fresh
19
- if @@cache.has_key? key
20
- return @@cache[key][1] if Time.now-@@cache[key][0] < @@cache[key][2]
21
- end
22
- end
23
-
24
-
25
- def get_all_balances(addresses)
26
-
27
- if ! blockr_down?
28
- bal = blockr_get_all_balances(addresses)
29
- return bal
30
- end
31
-
32
- if ! blockchain_down?
33
- bal = blockinfo_get_all_balances(addresses)
34
- return bal
35
- end
36
- end
37
-
38
- def get_balance(address)
39
-
40
- # These guys make 300k per month so hammer them first.
41
- if ! blockchain_down?
42
- bal = blockinfo_balance(address)
43
- if ! bal.instance_of? String
44
- return bal
45
- end
46
- end
47
-
48
- # Looks like blockchain is down, let's try blockr
49
- if ! blockr_down?
50
- bal = blockr_balance(address)
51
- if ! bal.instance_of? String
52
- return bal
53
- end
54
- end
55
-
56
- # OK I give up.
57
- 'Balance could not be retrieved'
58
- end
59
-
60
- def send_tx(tx_hex)
61
- #payload = { :tx => tx_hex }
62
- #HTTParty.post('http://blockchain.info/pushtx', {:body => payload})
63
-
64
- # Blockchain doesn't support multi sig, so only use blockr
65
- uri = URI.parse("http://btc.blockr.io/api/v1/tx/push")
66
- http = Net::HTTP.new(uri.host, uri.port)
67
-
68
- request = Net::HTTP::Post.new(uri.request_uri)
69
- request.body = '{"hex":"' + tx_hex + '"}'
70
- response = http.request(request)
71
- return response
72
- end
73
-
74
- def get_history
75
- return []
76
- end
77
-
78
- def get_unspent_outs(address)
79
-
80
-
81
- # Looks like blockchain is down, let's try blockr
82
- if ! blockr_down?
83
- return blockr_unspent(address)
84
- end
85
-
86
- if ! blockchain_down?
87
- return blockinfo_unspent(address)
88
- end
89
-
90
- # OK I give up.
91
- 'Unspent outputs could not be retrieved'
92
-
93
- end
94
-
95
- def blockchain_is_down
96
- cache_write(:blockchainapi, 'it is down', 60)
97
- end
98
-
99
- def blockchain_down?
100
- if cache_read(:blockchainapi) != nil
101
- return true
102
- end
103
- return false
104
- end
105
-
106
- def blockr_is_down
107
- cache_write(:blockrapi, 'it is down', 60)
108
- end
109
-
110
- def blockr_down?
111
- if cache_read(:blockrapi) != nil
112
- return true
113
- end
114
- return false
115
- end
116
-
117
- def blockinfo_get_all_balances(addresses)
118
- begin
119
- base = "https://blockchain.info/multiaddr?&simple=true&active="
120
-
121
- addresses.each do |address|
122
- base = base + address + '|'
123
- end
124
-
125
- json = fetch_response(URI::encode(base))
126
-
127
- addresses.each do |address|
128
- bal = json[address]['final_balance'] / 100000000.0
129
- cache_write(address, bal, BALANCE_CACHE_FOR)
130
- end
131
-
132
- rescue
133
- 'Balance could not be retrieved'
134
- end
135
- end
136
-
137
- def blockinfo_unspent(address)
138
- begin
139
- base_url = "http://blockchain.info/unspent?active=#{address}"
140
- json = fetch_response(base_url, true)
141
-
142
- unspent = []
143
-
144
- json['unspent_outputs'].each do |data|
145
- line = []
146
- line << reverse_blockchain_tx(data['tx_hash'])
147
- line << data['tx_output_n']
148
- line << data['script']
149
- line << data['value']
150
- unspent << line
151
- end
152
-
153
- return unspent
154
- rescue Exception => e
155
- puts e.to_s
156
- 'Unspent outputs could not be retrieved'
157
- end
158
- end
159
-
160
- def blockinfo_balance(address)
161
- begin
162
- if cache_read(address) == nil
163
- puts "cache is empty"
164
- json = block_chain('address', address, "&limit=0")
165
- if json.key?('final_balance')
166
- bal = json['final_balance'] / 100000000.0
167
- cache_write(address, bal, BALANCE_CACHE_FOR)
168
- else
169
- cache_write(address, 'Error', BALANCE_CACHE_FOR)
170
- end
171
- end
172
- bal = cache_read(address)
173
- if bal.class == Fixnum
174
- bal = bal.to_f
175
- end
176
- return bal
177
- rescue
178
- 'Balance could not be retrieved'
179
- end
180
- end
181
-
182
- def blockr_unspent(address)
183
- begin
184
- base_url = "http://btc.blockr.io/api/v1/address/unspent/#{address}"
185
- json = fetch_response(base_url, true)
186
-
187
- unspent = []
188
-
189
- json['data']['unspent'].each do |data|
190
- line = []
191
- line << data['tx']
192
- line << data['n']
193
- line << data['script']
194
- line << (data['amount'].to_f * 100000000).to_i
195
- unspent << line
196
- end
197
-
198
- return unspent
199
- rescue Exception => e
200
- puts e.to_s
201
- 'Unspent outputs could not be retrieved'
202
- end
203
- end
204
-
205
- def blockr_get_all_balances(addresses)
206
- begin
207
- base = "https://blockr.io/api/v1/address/balance/"
208
-
209
- addresses.each do |address|
210
- base = base + address + ','
211
- end
212
-
213
- json = fetch_response(URI::encode(base))
214
-
215
- json['data'].each do |data|
216
- bal = data['balance']
217
- addr = data['address']
218
- cache_write(addr, bal, BALANCE_CACHE_FOR)
219
- end
220
-
221
- rescue
222
- 'Balance could not be retrieved'
223
- end
224
- end
225
-
226
- def blockr_balance(address)
227
- begin
228
- if cache_read(address) == nil
229
- json = blockr('address/balance', address)
230
- if json.key?('data')
231
- bal = json['data']['balance'].to_f
232
- cache_write(address, bal, BALANCE_CACHE_FOR)
233
- else
234
- cache_write(address, 'Error', BALANCE_CACHE_FOR)
235
- end
236
- end
237
- return cache_read(address)
238
- rescue Exception => e
239
- puts e
240
- 'Balance could not be retrieved'
241
- end
242
- end
243
-
244
- def blockr(cmd, address, params = "")
245
- if ! blockr_down?
246
- begin
247
- base_url = "http://blockr.io/api/v1/#{cmd}/#{address}" + params
248
- fetch_response(base_url, true)
249
- rescue
250
- blockr_is_down
251
- end
252
- end
253
- end
254
-
255
- def block_chain(cmd, address, params = "")
256
- if ! blockchain_down?
257
- begin
258
- base_url = "http://blockchain.info/#{cmd}/#{address}?format=json" + params
259
-
260
- puts base_url
261
- fetch_response(base_url, true)
262
- rescue
263
- blockchain_is_down
264
- end
265
- end
266
- end
267
-
268
- def fetch_response(url, do_json=true)
269
- resp = Net::HTTP.get_response(URI.parse(url))
270
- data = resp.body
271
-
272
- if do_json
273
- result = JSON.parse(data)
274
- else
275
- data
276
- end
277
- end
278
-
279
- def reverse_blockchain_tx(hash)
280
- bytes = hash.scan(/../).map { |x| x.hex.chr }.join
281
-
282
- bytes = bytes.reverse
283
-
284
- return hash.scan(/../).reverse.join
285
- end
286
-
287
-
288
- end
289
- end
1
+ require 'onchain/block_chain.rb'
2
+ require 'onchain/sweeper.rb'
3
+ require 'money-tree'
4
+ require 'bitcoin'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onchain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Purton
@@ -11,7 +11,7 @@ cert_chain: []
11
11
  date: 2014-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec
14
+ name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,21 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: vcr
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: ir_b
28
+ name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - ">="
@@ -53,13 +39,13 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: guard-rspec
42
+ name: money-tree
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - ">="
60
46
  - !ruby/object:Gem::Version
61
47
  version: '0'
62
- type: :development
48
+ type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
@@ -67,13 +53,13 @@ dependencies:
67
53
  - !ruby/object:Gem::Version
68
54
  version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: webmock
56
+ name: bitcoin-ruby
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
59
  - - ">="
74
60
  - !ruby/object:Gem::Version
75
61
  version: '0'
76
- type: :development
62
+ type: :runtime
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
@@ -82,16 +68,14 @@ dependencies:
82
68
  version: '0'
83
69
  description: Call 3rd party API's but also switch API's if a 3rd party is down
84
70
  email:
85
- - ian@onchain.com
71
+ - ian@onchain.io
86
72
  executables: []
87
73
  extensions: []
88
74
  extra_rdoc_files: []
89
75
  files:
90
- - ".gitignore"
91
- - README.md
92
76
  - lib/onchain.rb
93
- - lib/onchain/version.rb
94
- - onchain.gemspec
77
+ - lib/onchain/block_chain.rb
78
+ - lib/onchain/sweeper.rb
95
79
  homepage: https://github.com/onchain/onchain-gem
96
80
  licenses: []
97
81
  metadata: {}
data/.gitignore DELETED
@@ -1,14 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .DS_Store
4
- .bundle
5
- .rvmrc
6
- .ruby-version
7
- .yardoc
8
- .rake_tasks~
9
- Gemfile.lock
10
- coverage/*
11
- doc/*
12
- log/*
13
- pkg/*
14
- .idea/*
data/README.md DELETED
@@ -1,4 +0,0 @@
1
- onchain-gem
2
- ===========
3
-
4
- Ruby gem to access 3rd party block chain API and unchain services.
@@ -1,3 +0,0 @@
1
- module Onchain
2
- VERSION = "0.4.2"
3
- end
data/onchain.gemspec DELETED
@@ -1,21 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/onchain/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Ian Purton"]
6
- gem.email = ["ian@onchain.com"]
7
- gem.summary = %q{Ruby wrapper for various 3rd-party bitcoin APIs}
8
- gem.description = %q{Call 3rd party API's but also switch API's if a 3rd party is down}
9
- gem.homepage = "https://github.com/onchain/onchain-gem"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "onchain"
15
- gem.require_paths = ["lib"]
16
- gem.version = Onchain::VERSION
17
-
18
- %w{rspec vcr ir_b guard-rspec webmock}.each do |gem_library|
19
- gem.add_development_dependency gem_library
20
- end
21
- end