Class: OnChain::BlockChain
- Inherits:
-
Object
- Object
- OnChain::BlockChain
- Defined in:
- lib/onchain/block_chain.rb,
lib/onchain/providers/blockr_api.rb,
lib/onchain/providers/chaincom_api.rb,
lib/onchain/providers/blockchaininfo_api.rb
Constant Summary collapse
- ALL_SUPPLIERS =
[:chaincom, :blockr, :blockinfo ]
- BALANCE_CACHE_FOR =
120
- API_CACHE_FOR =
60
- SERVICE_DOWN_FOR =
60
- @@cache =
{}
Class Method Summary collapse
- .block_chain(cmd, address, params = "") ⇒ Object
- .blockinfo_address_history(address) ⇒ Object
- .blockinfo_get_all_balances(addresses) ⇒ Object
- .blockinfo_get_balance(address) ⇒ Object
- .blockinfo_get_unspent_outs(address) ⇒ Object
- .blockr(cmd, address, params = "") ⇒ Object
- .blockr_get_all_balances(addresses) ⇒ Object
- .blockr_get_balance(address) ⇒ Object
- .blockr_get_transactions(address) ⇒ Object
- .blockr_get_unspent_outs(address) ⇒ Object
- .blockr_send_tx(tx_hex) ⇒ Object
- .cache_read(key) ⇒ Object
- .cache_write(key, data, max_age = 0) ⇒ Object
- .chaincom_address_history(address) ⇒ Object
- .chaincom_get_all_balances(addresses) ⇒ Object
- .chaincom_get_balance(address) ⇒ Object
- .chaincom_get_transactions(address) ⇒ Object
- .chaincom_get_unspent_outs(address) ⇒ Object
- .chaincom_send_tx(tx_hex) ⇒ Object
- .fetch_response(url, do_json = true) ⇒ Object
- .get_available_suppliers(method_name) ⇒ Object
- .get_balance_satoshi(address) ⇒ Object
-
.get_uncached_addresses(addresses) ⇒ Object
Given a list of addresses, return those that don’t have balances in the cahce.
-
.method_missing(method_name, *args, &block) ⇒ Object
ALL_SUPPLIERS = [ :blockr, :blockinfo ].
- .reverse_blockchain_tx(hash) ⇒ Object
Class Method Details
.block_chain(cmd, address, params = "") ⇒ Object
101 102 103 104 105 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 101 def block_chain(cmd, address, params = "") base_url = "http://blockchain.info/#{cmd}/#{address}?format=json" + params fetch_response(base_url, true) end |
.blockinfo_address_history(address) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 5 def blockinfo_address_history(address) base_url = "http://blockchain.info/address/#{address}?format=json" json = fetch_response(base_url, true) hist = [] if json.key?('txs') txs = json['txs'] txs.each do |tx| row = {} row[:time] = tx["time"] row[:addr] = {} row[:outs] = {} inputs = tx['inputs'] val = 0 recv = "Y" inputs.each do |input| row[:addr][input["prev_out"]["addr"]] = input["prev_out"]["addr"] if input["prev_out"]["addr"] == address recv = "N" end end tx["out"].each do |out| row[:outs][out["addr"]] = out["addr"] if recv == "Y" and out["addr"] == address val = val + out["value"].to_f / 100000000.0 elsif recv == "N" and out["addr"] != address val = val + out["value"].to_f / 100000000.0 end end row[:total] = val row[:recv] = recv hist << row end return hist else 'Error' end end |
.blockinfo_get_all_balances(addresses) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 45 def blockinfo_get_all_balances(addresses) base = "https://blockchain.info/multiaddr?&simple=true&active=" addr = get_uncached_addresses(addresses) if addr.length == 0 return end addr.each do |address| base = base + address + '|' end json = fetch_response(URI::encode(base)) addresses.each do |address| bal = json[address]['final_balance'] / 100000000.0 cache_write(address, bal, BALANCE_CACHE_FOR) end end |
.blockinfo_get_balance(address) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 84 def blockinfo_get_balance(address) if cache_read(address) == nil json = block_chain('address', address, "&limit=0") if json.key?('final_balance') bal = json['final_balance'] / 100000000.0 cache_write(address, bal, BALANCE_CACHE_FOR) else cache_write(address, 'Error', BALANCE_CACHE_FOR) end end bal = cache_read(address) if bal.class == Fixnum bal = bal.to_f end return bal end |
.blockinfo_get_unspent_outs(address) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 66 def blockinfo_get_unspent_outs(address) base_url = "http://blockchain.info/unspent?active=#{address}" json = fetch_response(base_url, true) unspent = [] json['unspent_outputs'].each do |data| line = [] line << reverse_blockchain_tx(data['tx_hash']) line << data['tx_output_n'] line << data['script'] line << data['value'] unspent << line end return unspent end |
.blockr(cmd, address, params = "") ⇒ Object
95 96 97 98 99 100 |
# File 'lib/onchain/providers/blockr_api.rb', line 95 def blockr(cmd, address, params = "") base_url = "http://blockr.io/api/v1/#{cmd}/#{address}" + params fetch_response(base_url, true) end |
.blockr_get_all_balances(addresses) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/onchain/providers/blockr_api.rb', line 72 def blockr_get_all_balances(addresses) addr = get_uncached_addresses(addresses) if addr.length == 0 return end base = "https://blockr.io/api/v1/address/balance/" addr.each do |address| base = base + address + ',' end json = fetch_response(URI::encode(base)) json['data'].each do |data| bal = data['balance'].to_f addr = data['address'] cache_write(addr, bal, BALANCE_CACHE_FOR) end end |
.blockr_get_balance(address) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/onchain/providers/blockr_api.rb', line 25 def blockr_get_balance(address) if cache_read(address) == nil json = blockr('address/balance', address) if json.key?('data') bal = json['data']['balance'].to_f cache_write(address, bal, BALANCE_CACHE_FOR) else cache_write(address, 'Error', BALANCE_CACHE_FOR) end end return cache_read(address) end |
.blockr_get_transactions(address) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/onchain/providers/blockr_api.rb', line 38 def blockr_get_transactions(address) base_url = "http://btc.blockr.io/api/v1/address/txs/#{address}" json = fetch_response(base_url, true) unspent = [] json['data']['txs'].each do |data| line = [] line << data['tx'] line << data['amount'].to_f unspent << line end return unspent end |
.blockr_get_unspent_outs(address) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/onchain/providers/blockr_api.rb', line 54 def blockr_get_unspent_outs(address) base_url = "http://btc.blockr.io/api/v1/address/unspent/#{address}" json = fetch_response(base_url, true) unspent = [] json['data']['unspent'].each do |data| line = [] line << data['tx'] line << data['n'] line << data['script'] line << (data['amount'].to_f * 100000000).to_i unspent << line end return unspent end |
.blockr_send_tx(tx_hex) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/onchain/providers/blockr_api.rb', line 4 def blockr_send_tx(tx_hex) uri = URI.parse("http://btc.blockr.io/api/v1/tx/push") http = Net::HTTP.new(uri.host, uri.port) request = Net::HTTP::Post.new(uri.request_uri) request.body = '{"hex":"' + tx_hex + '"}' response = http.request(request) res = JSON.parse(response.body) mess = res["message"] stat = res["status"] if stat == 'fail' stat = 'failure' end tx_hash = res["data"] ret = "{\"status\":\"#{stat}\",\"data\":\"#{tx_hash}\",\"code\":200,\"message\":\"#{mess}\"}" return JSON.parse(ret) end |
.cache_read(key) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/onchain/block_chain.rb', line 101 def cache_read(key) # if the API URL exists as a key in cache, we just return it # we also make sure the data is fresh if @@cache.has_key? key return @@cache[key][1] if Time.now-@@cache[key][0] < @@cache[key][2] end end |
.cache_write(key, data, max_age = 0) ⇒ Object
97 98 99 |
# File 'lib/onchain/block_chain.rb', line 97 def cache_write(key, data, max_age=0) @@cache[key] = [Time.now, data, max_age] end |
.chaincom_address_history(address) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/onchain/providers/chaincom_api.rb', line 6 def chaincom_address_history(address) txs = Chain.get_address_transactions(address) hist = [] txs.each do |tx| row = {} row[:time] = tx["block_time"] row[:addr] = {} row[:outs] = {} inputs = tx['inputs'] val = 0 recv = "Y" inputs.each do |input| row[:addr][input["addresses"][0]] = input["addresses"][0] if input["addresses"][0] == address recv = "N" end end tx["outputs"].each do |out| row[:outs][out["addresses"][0] ] = out["addresses"][0] if recv == "Y" and out["addresses"][0] == address val = val + out["value"].to_f / 100000000.0 elsif recv == "N" and out["addresses"][0] != address val = val + out["value"].to_f / 100000000.0 end end row[:total] = val row[:recv] = recv hist << row end return hist end |
.chaincom_get_all_balances(addresses) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/onchain/providers/chaincom_api.rb', line 98 def chaincom_get_all_balances(addresses) addr = get_uncached_addresses(addresses) if addr.length == 0 return end res = Chain.get_addresses(addr) if ! res.kind_of?(Array) res = [res] end res.each do |address| bal = address["balance"] / 100000000.0 cache_write(address["hash"], bal, BALANCE_CACHE_FOR) end end |
.chaincom_get_balance(address) ⇒ Object
53 54 55 56 57 58 59 60 61 62 |
# File 'lib/onchain/providers/chaincom_api.rb', line 53 def chaincom_get_balance(address) if cache_read(address) == nil addr = Chain.get_address(address) bal = addr["balance"] / 100000000.0 cache_write(address, bal, BALANCE_CACHE_FOR) end return cache_read(address) end |
.chaincom_get_transactions(address) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/onchain/providers/chaincom_api.rb', line 64 def chaincom_get_transactions(address) txs = Chain.get_address_transactions(address) unspent = [] txs.each do |data| line = [] line << data['hash'] line << data["outputs"][0]["value"] / 100000000.0 unspent << line end return unspent end |
.chaincom_get_unspent_outs(address) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/onchain/providers/chaincom_api.rb', line 80 def chaincom_get_unspent_outs(address) uns = Chain.get_address_unspents(address) unspent = [] uns.each do |data| line = [] line << data['transaction_hash'] line << data['output_index'] line << data['script_hex'] line << data['value'] unspent << line end return unspent end |
.chaincom_send_tx(tx_hex) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/onchain/providers/chaincom_api.rb', line 40 def chaincom_send_tx(tx_hex) begin tx = Chain.send_transaction(tx_hex) tx_hash = tx["transaction_hash"] ret = "{\"status\":\"success\",\"data\":\"#{tx_hash}\",\"code\":200,\"message\":\"\"}" return JSON.parse(ret) rescue => e ret = "{\"status\":\"failure\",\"data\":\"#{tx_hash}\",\"code\":200,\"message\":\"#{e.to_s}\"}" return JSON.parse(ret) end end |
.fetch_response(url, do_json = true) ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/onchain/block_chain.rb', line 109 def fetch_response(url, do_json=true) resp = Net::HTTP.get_response(URI.parse(url)) data = resp.body if do_json result = JSON.parse(data) else data end end |
.get_available_suppliers(method_name) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/onchain/block_chain.rb', line 68 def get_available_suppliers(method_name) available = [] ALL_SUPPLIERS.each do |supplier| if cache_read(supplier.to_s) == nil if supplier == :blockinfo and method_name == 'send_tx' next end if supplier == :blockinfo and method_name == 'get_transactions' next end if supplier == :blockr and method_name == 'address_history' next end available << supplier end end return available end |
.get_balance_satoshi(address) ⇒ Object
64 65 66 |
# File 'lib/onchain/block_chain.rb', line 64 def get_balance_satoshi(address) return (get_balance(address).to_f * 100000000).to_i end |
.get_uncached_addresses(addresses) ⇒ Object
Given a list of addresses, return those that don’t have balances in the cahce.
54 55 56 57 58 59 60 61 62 |
# File 'lib/onchain/block_chain.rb', line 54 def get_uncached_addresses(addresses) ret = [] addresses.each do |address| if cache_read(address) == nil ret << address end end return ret end |
.method_missing(method_name, *args, &block) ⇒ Object
ALL_SUPPLIERS = [ :blockr, :blockinfo ]
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/onchain/block_chain.rb', line 23 def method_missing (method_name, *args, &block) if ENV['CHAIN-API-KEY-ID'] != nil Chain.api_key_id = ENV['CHAIN-API-KEY-ID'] Chain.api_key_secret = ENV['CHAIN-API-KEY-SECRET'] end get_available_suppliers(method_name).each do |supplier| real_method = "#{supplier.to_s}_#{method_name}" begin method = self.method(real_method) begin result = method.call(*args) return result rescue => e2 # We have the method but it errored. Assume # service is down. cache_write(supplier.to_s, 'down', SERVICE_DOWN_FOR) puts e2.to_s end rescue => e puts "there's no method called '#{real_method}'" puts e.backtrace end end end |
.reverse_blockchain_tx(hash) ⇒ Object
107 108 109 110 111 112 113 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 107 def reverse_blockchain_tx(hash) bytes = hash.scan(/../).map { |x| x.hex.chr }.join bytes = bytes.reverse return hash.scan(/../).reverse.join end |