onchain 1.0.13 → 1.0.14

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: 0534c875acd3eeef4038a0288de9efb4d66c448c
4
- data.tar.gz: f14ca67db4145a1fdd6b27fa430e1e9b7187406e
3
+ metadata.gz: 34dfbd268297f6be4f6cd1a23d766aecee706b9f
4
+ data.tar.gz: 5efa3f058778c38e0e0f1e5b38eefb6210ce845c
5
5
  SHA512:
6
- metadata.gz: dc1c9fbef6f40f83813feecd66b1be7d8febb0bef87d39d774a6a80875d79c9aa0b181f6419f3b0b83fd1bfc858905372363e06669bf0f409fc23a16f5476f00
7
- data.tar.gz: 7a0447f71b8e935a27bd176972fbc81a411456211ba0146456623175876388476175da60ce8cb28dd50a7af157da77f25464ca3f24c42cfdf8e6e62bdefa48cb
6
+ metadata.gz: c934cdae3ac301001fddc90c702eac3518d99ff82e7b9fd752dfc6a4e73072bc2de89d40d535f18abaa9d7f93410848b850e8f6f916e3ac6c5958646087c12ce
7
+ data.tar.gz: 953098816da0ce65180dee31cb1d0dba97cf30e2c7f6799bcb337759d45faae9a71164e86172998d3198404ae58a79da4804f7ba251af76195883818d7933b82
@@ -1,292 +1,81 @@
1
1
  require 'net/http'
2
2
  require 'json'
3
3
 
4
+ # We support a number of blockchain API providers,
5
+ # if one goes down we automatically switch over to another.
6
+ #
7
+ # Each provider has to support the following methods
8
+ #
9
+ # get_balance(address)
10
+ # get_all_balances([])
11
+ # send_tx(tx_hex)
12
+ # get_transactions(address)
13
+ #
4
14
  class OnChain
15
+ end
16
+
17
+ class OnChain::BlockChain
5
18
  class << self
6
19
 
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
-
20
+ #ALL_SUPPLIERS = [:chaincom, :blockr, :blockinfo ]
21
+ ALL_SUPPLIERS = [ :blockr, :blockinfo ]
24
22
 
25
- def get_all_balances(addresses)
23
+ def method_missing (method_name, *args, &block)
24
+
25
+ get_available_suppliers.each do |supplier|
26
26
 
27
- if ! blockr_down?
28
- bal = blockr_get_all_balances(addresses)
29
- return bal
27
+ real_method = "#{supplier.to_s}_#{method_name}"
28
+ begin
29
+ method = self.method(real_method)
30
+ begin
31
+ result = method.call(*args)
32
+ return result
33
+ rescue => e2
34
+ # We have the method but it errored. Assume
35
+ # service is down.
36
+ cache_write(supplier.to_s, 'down', SERVICE_DOWN_FOR)
37
+ puts e2.to_s
38
+ end
39
+ rescue => e
40
+ puts "there's no method called '#{real_method}'"
41
+ puts e.backtrace
42
+ end
30
43
  end
31
44
 
32
- if ! blockchain_down?
33
- bal = blockinfo_get_all_balances(addresses)
34
- return bal
35
- end
36
45
  end
37
46
 
38
47
  def get_balance_satoshi(address)
39
48
  return (get_balance(address).to_f * 100000000).to_i
40
49
  end
41
50
 
42
- def get_balance(address)
43
-
44
- # These guys make 300k per month so hammer them first.
45
- if ! blockchain_down?
46
- bal = blockinfo_balance(address)
47
- if ! bal.instance_of? String
48
- return bal
49
- end
50
- end
51
-
52
- # Looks like blockchain is down, let's try blockr
53
- if ! blockr_down?
54
- bal = blockr_balance(address)
55
- if ! bal.instance_of? String
56
- return bal
51
+ def get_available_suppliers
52
+ available = []
53
+ ALL_SUPPLIERS.each do |supplier|
54
+ if cache_read(supplier.to_s) == nil
55
+ available << supplier
57
56
  end
58
57
  end
59
-
60
- # OK I give up.
61
- 'Balance could not be retrieved'
58
+ return available
62
59
  end
63
60
 
64
- def send_tx(tx_hex)
65
- #payload = { :tx => tx_hex }
66
- #HTTParty.post('http://blockchain.info/pushtx', {:body => payload})
67
-
68
- # Blockchain doesn't support multi sig, so only use blockr
69
- uri = URI.parse("http://btc.blockr.io/api/v1/tx/push")
70
- http = Net::HTTP.new(uri.host, uri.port)
71
-
72
- request = Net::HTTP::Post.new(uri.request_uri)
73
- request.body = '{"hex":"' + tx_hex + '"}'
74
- response = http.request(request)
75
- return response
76
- end
61
+ BALANCE_CACHE_FOR = 120
62
+ API_CACHE_FOR = 60
63
+ SERVICE_DOWN_FOR = 60
77
64
 
78
- def get_history
79
- return []
80
- end
65
+ @@cache = {}
81
66
 
82
- def get_transactions(address)
83
- begin
84
- base_url = "http://btc.blockr.io/api/v1/address/txs/#{address}"
85
- json = fetch_response(base_url, true)
86
-
87
- unspent = []
88
-
89
- json['data']['txs'].each do |data|
90
- line = []
91
- line << data['tx']
92
- line << (data['amount'].to_f * 100000000).to_i
93
- unspent << line
94
- end
95
-
96
- return unspent
97
- rescue Exception => e
98
- puts e.to_s
99
- 'Unspent outputs could not be retrieved'
100
- end
67
+ def cache_write(key, data, max_age=0)
68
+ @@cache[key] = [Time.now, data, max_age]
101
69
  end
102
70
 
103
- def get_unspent_outs(address)
104
-
105
-
106
- # Looks like blockchain is down, let's try blockr
107
- if ! blockr_down?
108
- return blockr_unspent(address)
109
- end
110
-
111
- if ! blockchain_down?
112
- return blockinfo_unspent(address)
113
- end
114
-
115
- # OK I give up.
116
- 'Unspent outputs could not be retrieved'
117
-
118
- end
119
-
120
- def blockchain_is_down
121
- cache_write(:blockchainapi, 'it is down', 60)
122
- end
123
-
124
- def blockchain_down?
125
- if cache_read(:blockchainapi) != nil
126
- return true
127
- end
128
- return false
129
- end
130
-
131
- def blockr_is_down
132
- cache_write(:blockrapi, 'it is down', 60)
133
- end
134
-
135
- def blockr_down?
136
- if cache_read(:blockrapi) != nil
137
- return true
138
- end
139
- return false
140
- end
141
-
142
- def blockinfo_get_all_balances(addresses)
143
- begin
144
- base = "https://blockchain.info/multiaddr?&simple=true&active="
145
-
146
- addresses.each do |address|
147
- base = base + address + '|'
148
- end
149
-
150
- json = fetch_response(URI::encode(base))
151
-
152
- addresses.each do |address|
153
- bal = json[address]['final_balance'] / 100000000.0
154
- cache_write(address, bal, BALANCE_CACHE_FOR)
155
- end
156
-
157
- rescue
158
- 'Balance could not be retrieved'
159
- end
160
- end
161
-
162
- def blockinfo_unspent(address)
163
- begin
164
- base_url = "http://blockchain.info/unspent?active=#{address}"
165
- json = fetch_response(base_url, true)
166
-
167
- unspent = []
168
-
169
- json['unspent_outputs'].each do |data|
170
- line = []
171
- line << reverse_blockchain_tx(data['tx_hash'])
172
- line << data['tx_output_n']
173
- line << data['script']
174
- line << data['value']
175
- unspent << line
176
- end
177
-
178
- return unspent
179
- rescue Exception => e
180
- puts e.to_s
181
- 'Unspent outputs could not be retrieved'
182
- end
183
- end
184
-
185
- def blockinfo_balance(address)
186
- begin
187
- if cache_read(address) == nil
188
- json = block_chain('address', address, "&limit=0")
189
- if json.key?('final_balance')
190
- bal = json['final_balance'] / 100000000.0
191
- cache_write(address, bal, BALANCE_CACHE_FOR)
192
- else
193
- cache_write(address, 'Error', BALANCE_CACHE_FOR)
194
- end
195
- end
196
- bal = cache_read(address)
197
- if bal.class == Fixnum
198
- bal = bal.to_f
199
- end
200
- return bal
201
- rescue
202
- 'Balance could not be retrieved'
203
- end
204
- end
205
-
206
- def blockr_unspent(address)
207
- begin
208
- base_url = "http://btc.blockr.io/api/v1/address/unspent/#{address}"
209
- json = fetch_response(base_url, true)
210
-
211
- unspent = []
212
-
213
- json['data']['unspent'].each do |data|
214
- line = []
215
- line << data['tx']
216
- line << data['n']
217
- line << data['script']
218
- line << (data['amount'].to_f * 100000000).to_i
219
- unspent << line
220
- end
221
-
222
- return unspent
223
- rescue Exception => e
224
- puts e.to_s
225
- 'Unspent outputs could not be retrieved'
226
- end
227
- end
228
-
229
- def blockr_get_all_balances(addresses)
230
- begin
231
- base = "https://blockr.io/api/v1/address/balance/"
232
-
233
- addresses.each do |address|
234
- base = base + address + ','
235
- end
236
-
237
- json = fetch_response(URI::encode(base))
238
-
239
- json['data'].each do |data|
240
- bal = data['balance']
241
- addr = data['address']
242
- cache_write(addr, bal, BALANCE_CACHE_FOR)
243
- end
244
-
245
- rescue
246
- 'Balance could not be retrieved'
247
- end
248
- end
249
-
250
- def blockr_balance(address)
251
- begin
252
- if cache_read(address) == nil
253
- json = blockr('address/balance', address)
254
- if json.key?('data')
255
- bal = json['data']['balance'].to_f
256
- cache_write(address, bal, BALANCE_CACHE_FOR)
257
- else
258
- cache_write(address, 'Error', BALANCE_CACHE_FOR)
259
- end
260
- end
261
- return cache_read(address)
262
- rescue Exception => e
263
- 'Balance could not be retrieved'
264
- end
265
- end
266
-
267
- def blockr(cmd, address, params = "")
268
- if ! blockr_down?
269
- begin
270
- base_url = "http://blockr.io/api/v1/#{cmd}/#{address}" + params
271
- fetch_response(base_url, true)
272
- rescue
273
- blockr_is_down
274
- end
275
- end
276
- end
277
-
278
- def block_chain(cmd, address, params = "")
279
- if ! blockchain_down?
280
- begin
281
- base_url = "http://blockchain.info/#{cmd}/#{address}?format=json" + params
282
-
283
- fetch_response(base_url, true)
284
- rescue
285
- blockchain_is_down
286
- end
287
- end
71
+ def cache_read(key)
72
+ # if the API URL exists as a key in cache, we just return it
73
+ # we also make sure the data is fresh
74
+ if @@cache.has_key? key
75
+ return @@cache[key][1] if Time.now-@@cache[key][0] < @@cache[key][2]
76
+ end
288
77
  end
289
-
78
+
290
79
  def fetch_response(url, do_json=true)
291
80
  resp = Net::HTTP.get_response(URI.parse(url))
292
81
  data = resp.body
@@ -298,14 +87,5 @@ class OnChain
298
87
  end
299
88
  end
300
89
 
301
- def reverse_blockchain_tx(hash)
302
- bytes = hash.scan(/../).map { |x| x.hex.chr }.join
303
-
304
- bytes = bytes.reverse
305
-
306
- return hash.scan(/../).reverse.join
307
- end
308
-
309
-
310
90
  end
311
91
  end
@@ -36,7 +36,7 @@ class OnChain
36
36
  total_amount = total_amount + payment[1]
37
37
  end
38
38
 
39
- total_in_fund = get_balance_satoshi(fund_address)
39
+ total_in_fund = OnChain::BlockChain.get_balance_satoshi(fund_address)
40
40
 
41
41
  # Do we have enough in the fund.
42
42
  if(total_amount > total_in_fund)
@@ -45,7 +45,7 @@ class OnChain
45
45
 
46
46
  # OK, let's get some inputs
47
47
  amount_so_far = 0
48
- unspent = get_unspent_outs(fund_address)
48
+ unspent = OnChain::BlockChain.get_unspent_outs(fund_address)
49
49
  unspent.each do |spent|
50
50
 
51
51
  txin = Bitcoin::Protocol::TxIn.new
@@ -0,0 +1,70 @@
1
+ class OnChain::BlockChain
2
+ class << self
3
+
4
+
5
+ def blockinfo_get_all_balances(addresses)
6
+ base = "https://blockchain.info/multiaddr?&simple=true&active="
7
+
8
+ addresses.each do |address|
9
+ base = base + address + '|'
10
+ end
11
+
12
+ json = fetch_response(URI::encode(base))
13
+
14
+ addresses.each do |address|
15
+ bal = json[address]['final_balance'] / 100000000.0
16
+ cache_write(address, bal, BALANCE_CACHE_FOR)
17
+ end
18
+ end
19
+
20
+ def blockinfo_get_unspent_outs(address)
21
+ base_url = "http://blockchain.info/unspent?active=#{address}"
22
+ json = fetch_response(base_url, true)
23
+
24
+ unspent = []
25
+
26
+ json['unspent_outputs'].each do |data|
27
+ line = []
28
+ line << reverse_blockchain_tx(data['tx_hash'])
29
+ line << data['tx_output_n']
30
+ line << data['script']
31
+ line << data['value']
32
+ unspent << line
33
+ end
34
+
35
+ return unspent
36
+ end
37
+
38
+ def blockinfo_get_balance(address)
39
+ if cache_read(address) == nil
40
+ json = block_chain('address', address, "&limit=0")
41
+ if json.key?('final_balance')
42
+ bal = json['final_balance'] / 100000000.0
43
+ cache_write(address, bal, BALANCE_CACHE_FOR)
44
+ else
45
+ cache_write(address, 'Error', BALANCE_CACHE_FOR)
46
+ end
47
+ end
48
+ bal = cache_read(address)
49
+ if bal.class == Fixnum
50
+ bal = bal.to_f
51
+ end
52
+ return bal
53
+ end
54
+
55
+ def block_chain(cmd, address, params = "")
56
+ base_url = "http://blockchain.info/#{cmd}/#{address}?format=json" + params
57
+
58
+ fetch_response(base_url, true)
59
+ end
60
+
61
+ def reverse_blockchain_tx(hash)
62
+ bytes = hash.scan(/../).map { |x| x.hex.chr }.join
63
+
64
+ bytes = bytes.reverse
65
+
66
+ return hash.scan(/../).reverse.join
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,74 @@
1
+ class OnChain::BlockChain
2
+ class << self
3
+
4
+ def blockr_get_balance(address)
5
+ if cache_read(address) == nil
6
+ json = blockr('address/balance', address)
7
+ if json.key?('data')
8
+ bal = json['data']['balance'].to_f
9
+ cache_write(address, bal, BALANCE_CACHE_FOR)
10
+ else
11
+ cache_write(address, 'Error', BALANCE_CACHE_FOR)
12
+ end
13
+ end
14
+ return cache_read(address)
15
+ end
16
+
17
+ def blockr_get_transactions(address)
18
+ base_url = "http://btc.blockr.io/api/v1/address/txs/#{address}"
19
+ json = fetch_response(base_url, true)
20
+
21
+ unspent = []
22
+
23
+ json['data']['txs'].each do |data|
24
+ line = []
25
+ line << data['tx']
26
+ line << (data['amount'].to_f * 100000000).to_i
27
+ unspent << line
28
+ end
29
+
30
+ return unspent
31
+ end
32
+
33
+ def blockr_get_unspent_outs(address)
34
+ base_url = "http://btc.blockr.io/api/v1/address/unspent/#{address}"
35
+ json = fetch_response(base_url, true)
36
+
37
+ unspent = []
38
+
39
+ json['data']['unspent'].each do |data|
40
+ line = []
41
+ line << data['tx']
42
+ line << data['n']
43
+ line << data['script']
44
+ line << (data['amount'].to_f * 100000000).to_i
45
+ unspent << line
46
+ end
47
+
48
+ return unspent
49
+ end
50
+
51
+ def blockr_get_all_balances(addresses)
52
+ base = "https://blockr.io/api/v1/address/balance/"
53
+
54
+ addresses.each do |address|
55
+ base = base + address + ','
56
+ end
57
+
58
+ json = fetch_response(URI::encode(base))
59
+
60
+ json['data'].each do |data|
61
+ bal = data['balance']
62
+ addr = data['address']
63
+ cache_write(addr, bal, BALANCE_CACHE_FOR)
64
+ end
65
+ end
66
+
67
+ def blockr(cmd, address, params = "")
68
+
69
+ base_url = "http://blockr.io/api/v1/#{cmd}/#{address}" + params
70
+ fetch_response(base_url, true)
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+ class OnChain::BlockChain
2
+ class << self
3
+ end
4
+ end
@@ -16,7 +16,7 @@ class OnChain
16
16
 
17
17
  address = master.node_for_path(path).to_address
18
18
 
19
- unspent = get_unspent_outs(address)
19
+ unspent = OnChain::BlockChain.get_unspent_outs(address)
20
20
 
21
21
  unspent.each do |spent|
22
22
  txin = Bitcoin::Protocol::TxIn.new
data/lib/onchain.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  require 'onchain/block_chain.rb'
2
+ require 'onchain/providers/blockchaininfo_api.rb'
3
+ require 'onchain/providers/blockr_api.rb'
4
+ require 'onchain/providers/chaincom_api.rb'
2
5
  require 'onchain/sweeper.rb'
3
6
  require 'onchain/payments.rb'
4
7
  require 'money-tree'
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onchain
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.13
4
+ version: 1.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Purton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-07 00:00:00.000000000 Z
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: money-tree
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bitcoin-ruby
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  description: Call 3rd party API's but also switch API's if a 3rd party is down
@@ -76,6 +76,9 @@ files:
76
76
  - lib/onchain.rb
77
77
  - lib/onchain/block_chain.rb
78
78
  - lib/onchain/payments.rb
79
+ - lib/onchain/providers/blockchaininfo_api.rb
80
+ - lib/onchain/providers/blockr_api.rb
81
+ - lib/onchain/providers/chaincom_api.rb
79
82
  - lib/onchain/sweeper.rb
80
83
  homepage: https://github.com/onchain/onchain-gem
81
84
  licenses: []