onchain 1.0.13 → 1.0.14

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.
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: []