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_history_for_addresses(addresses) ⇒ Object
- .blockinfo_get_unspent_outs(address) ⇒ Object
- .blockinfo_parse_address_tx(address, json) ⇒ 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_history_for_addresses(addresses) ⇒ Object
- .chaincom_get_transactions(address) ⇒ Object
- .chaincom_get_unspent_outs(address) ⇒ Object
- .chaincom_parse_address_tx(addresses, txs) ⇒ 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.
- .get_unspent_for_amount(addresses, amount_in_satoshi) ⇒ Object
-
.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
118 119 120 121 122 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 118 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
16 17 18 19 20 21 22 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 16 def blockinfo_address_history(address) base_url = "http://blockchain.info/address/#{address}?format=json" json = fetch_response(base_url, true) blockinfo_parse_address_tx(address, json) end |
.blockinfo_get_all_balances(addresses) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 62 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
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 101 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_history_for_addresses(addresses) ⇒ Object
5 6 7 8 9 10 11 12 13 14 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 5 def blockinfo_get_history_for_addresses(addresses) history = [] addresses.each do |address| res = blockinfo_address_history(address) res.each do |r| history << r end end return history end |
.blockinfo_get_unspent_outs(address) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 83 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 |
.blockinfo_parse_address_tx(address, json) ⇒ Object
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 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 24 def blockinfo_parse_address_tx(address, json) 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 return hist 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
149 150 151 152 153 154 155 |
# File 'lib/onchain/block_chain.rb', line 149 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
145 146 147 |
# File 'lib/onchain/block_chain.rb', line 145 def cache_write(key, data, max_age=0) @@cache[key] = [Time.now, data, max_age] end |
.chaincom_address_history(address) ⇒ Object
13 14 15 16 17 18 |
# File 'lib/onchain/providers/chaincom_api.rb', line 13 def chaincom_address_history(address) txs = Chain.get_address_transactions(address) return chaincom_parse_address_tx([address], txs) end |
.chaincom_get_all_balances(addresses) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/onchain/providers/chaincom_api.rb', line 112 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["total"]["balance"] / 100000000.0 cache_write(address["address"], bal, BALANCE_CACHE_FOR) end end |
.chaincom_get_balance(address) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/onchain/providers/chaincom_api.rb', line 65 def chaincom_get_balance(address) if cache_read(address) == nil addr = Chain.get_address(address) if addr.count > 0 bal = addr[0]["total"]["balance"].to_i / 100000000.0 cache_write(address, bal, BALANCE_CACHE_FOR) end end return cache_read(address) end |
.chaincom_get_history_for_addresses(addresses) ⇒ Object
7 8 9 10 11 |
# File 'lib/onchain/providers/chaincom_api.rb', line 7 def chaincom_get_history_for_addresses(addresses) txs = Chain.get_addresses_transactions(addresses) return chaincom_parse_address_tx(addresses, txs) end |
.chaincom_get_transactions(address) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/onchain/providers/chaincom_api.rb', line 78 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
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/onchain/providers/chaincom_api.rb', line 94 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_parse_address_tx(addresses, txs) ⇒ Object
20 21 22 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/providers/chaincom_api.rb', line 20 def chaincom_parse_address_tx(addresses, txs) hist = [] txs.each do |tx| row = {} row[:time] = Date.parse(tx["block_time"]).to_s row[:addr] = {} row[:outs] = {} inputs = tx['inputs'] val = 0 recv = "Y" inputs.each do |input| row[:addr][input["addresses"][0]] = input["addresses"][0] if addresses.include? input["addresses"][0] recv = "N" end end tx["outputs"].each do |out| row[:outs][out["addresses"][0] ] = out["addresses"][0] if recv == "Y" and addresses.include? out["addresses"][0] val = val + out["value"].to_f / 100000000.0 elsif recv == "N" and addresses.include? out["addresses"][0] val = val + out["value"].to_f / 100000000.0 end end row[:total] = val row[:recv] = recv hist << row end return hist end |
.chaincom_send_tx(tx_hex) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/onchain/providers/chaincom_api.rb', line 52 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
157 158 159 160 161 162 163 164 165 166 |
# File 'lib/onchain/block_chain.rb', line 157 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
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/onchain/block_chain.rb', line 113 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.to_s == 'send_tx' next end if supplier == :blockinfo and method_name.to_s == 'get_transactions' next end if supplier == :blockr and method_name.to_s == 'address_history' next end if supplier == :blockr and method_name.to_s == 'get_history_for_addresses' next end available << supplier end end return available end |
.get_balance_satoshi(address) ⇒ Object
109 110 111 |
# File 'lib/onchain/block_chain.rb', line 109 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.
68 69 70 71 72 73 74 75 76 |
# File 'lib/onchain/block_chain.rb', line 68 def get_uncached_addresses(addresses) ret = [] addresses.each do |address| if cache_read(address) == nil ret << address end end return ret end |
.get_unspent_for_amount(addresses, amount_in_satoshi) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/onchain/block_chain.rb', line 78 def get_unspent_for_amount(addresses, amount_in_satoshi) unspents = [] indexes = [] amount_so_far = 0 addresses.each_with_index do |address, index| if amount_so_far >= amount_in_satoshi break end unspent_outs = get_unspent_outs(address) unspent_outs.each do |spent| unspents << spent indexes << index amount_so_far = amount_so_far + spent[3].to_i if amount_so_far >= amount_in_satoshi break end end end change = amount_so_far - amount_in_satoshi return unspents, indexes, change end |
.method_missing(method_name, *args, &block) ⇒ Object
ALL_SUPPLIERS = [ :blockr, :blockinfo ]
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/onchain/block_chain.rb', line 33 def method_missing (method_name, *args, &block) if ENV['CHAIN-API-KEY-ID'] != nil begin Chain.api_key_id = ENV['CHAIN-API-KEY-ID'] Chain.api_key_secret = ENV['CHAIN-API-KEY-SECRET'] rescue # Had problems setting this so get arou d it. end 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
124 125 126 127 128 129 130 |
# File 'lib/onchain/providers/blockchaininfo_api.rb', line 124 def reverse_blockchain_tx(hash) bytes = hash.scan(/../).map { |x| x.hex.chr }.join bytes = bytes.reverse return hash.scan(/../).reverse.join end |