blockchain-ruby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +41 -0
- data/LICENSE.txt +20 -0
- data/README.md +96 -0
- data/blockchain-ruby.gemspec +27 -0
- data/lib/address.rb +48 -0
- data/lib/block.rb +27 -0
- data/lib/blockchain.rb +35 -0
- data/lib/realtime.rb +53 -0
- data/lib/transaction.rb +59 -0
- data/spec/address_spec.rb +45 -0
- data/spec/block_spec.rb +42 -0
- data/spec/reatime_spec.rb +82 -0
- data/spec/spec_helper.rb +84 -0
- data/spec/transaction_spec.rb +87 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 741747d1cc360130278facebe80df167706167a2
|
4
|
+
data.tar.gz: 6eed1fa2d389cd2e2ddd1d983a2db76dc9bc543d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9d07d23a07df7a27941b5242784045ad6045da4fff26d286521173f5b92c843a38ee584140eed0c5c4f9f684750a797c983897709309882c3f20eebb4bcbd4d
|
7
|
+
data.tar.gz: 414e3b362df7b07dd800de6168f4e397a5e52dedf85de88b30a92bd09d358f905b2ea279b04a4de01e9cc5d3685c860b86046a8254f432a52b248c3991930ff2
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
blockchain-ruby (1.0.0)
|
5
|
+
bitcoin-money (~> 0.5.1)
|
6
|
+
monetize (~> 0.3.0)
|
7
|
+
money (~> 6.1.1)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
bitcoin-money (0.5.1)
|
13
|
+
diff-lcs (1.2.5)
|
14
|
+
fakeweb (1.3.0)
|
15
|
+
i18n (0.6.11)
|
16
|
+
monetize (0.3.0)
|
17
|
+
money (~> 6.1.0.beta1)
|
18
|
+
money (6.1.1)
|
19
|
+
i18n (~> 0.6.4)
|
20
|
+
rake (10.3.2)
|
21
|
+
rspec (3.0.0)
|
22
|
+
rspec-core (~> 3.0.0)
|
23
|
+
rspec-expectations (~> 3.0.0)
|
24
|
+
rspec-mocks (~> 3.0.0)
|
25
|
+
rspec-core (3.0.1)
|
26
|
+
rspec-support (~> 3.0.0)
|
27
|
+
rspec-expectations (3.0.1)
|
28
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
+
rspec-support (~> 3.0.0)
|
30
|
+
rspec-mocks (3.0.1)
|
31
|
+
rspec-support (~> 3.0.0)
|
32
|
+
rspec-support (3.0.0)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
blockchain-ruby!
|
39
|
+
fakeweb (~> 1.3.0)
|
40
|
+
rake
|
41
|
+
rspec (~> 3.0.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
blockchain-ruby
|
2
|
+
===============
|
3
|
+
|
4
|
+
Gives Rubyists the ability to traverse the blockchain in a semantic way.
|
5
|
+
|
6
|
+
The following functionality has been implemented:
|
7
|
+
|
8
|
+
Addresses
|
9
|
+
---------
|
10
|
+
Search for addresses with hash160 or address
|
11
|
+
|
12
|
+
returns `nil` if not found
|
13
|
+
~~~ruby
|
14
|
+
a = Blockchain::Address.find('143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr')
|
15
|
+
a.hash160 # => '217810f57fd7cdf79274c5d5e4a891fadc74887b'
|
16
|
+
a.address # => '143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr'
|
17
|
+
a.n_tx # => 143
|
18
|
+
a.n_unredeemed # => 56
|
19
|
+
a.total_received # => #<Btc BTC:0.00124124 mBTC:1.24124 uBTC:1241.24 Satoshis:124124.0>
|
20
|
+
a.total_sent # => #<Btc BTC:5.829e-05 mBTC:0.05829 uBTC:58.29 Satoshis:5829.0>
|
21
|
+
a.final_balance # => #<Btc BTC:0.00090215 mBTC:0.90215 uBTC:902.15 Satoshis:90215.0>
|
22
|
+
a.refresh! # refreshes the address
|
23
|
+
a.to_s # => '217810f57fd7cdf79274c5d5e4a891fadc74887b'
|
24
|
+
a.first_seen # => #<DateTime: 2014-08-22T18:05:32-04:00 ((2456892j,79532s,800786000n),-14400s,2299161j)>
|
25
|
+
txs = a.txs
|
26
|
+
~~~
|
27
|
+
|
28
|
+
Transactions
|
29
|
+
------------
|
30
|
+
Search for transactions with transaction index or hash
|
31
|
+
|
32
|
+
returns `nil` if not found
|
33
|
+
~~~ruby
|
34
|
+
t = txs[0]
|
35
|
+
t.hsh # => 'bebc1f8db50669d8ab7c7181aa92ffe3f170510d5894cb0a4100af23e335210f'
|
36
|
+
t.ver # => 1
|
37
|
+
t.vin_sz # => 1
|
38
|
+
t.vout_sz # => 2
|
39
|
+
t.time # => #<DateTime: 2014-08-22T18:05:32-04:00 ((2456892j,79532s,800786000n),-14400s,2299161j)>
|
40
|
+
t.size # => 258
|
41
|
+
t.result # => 0
|
42
|
+
t.relayed_by # => "127.0.01"
|
43
|
+
t.double_spend # => false
|
44
|
+
t.block_height # => 317010
|
45
|
+
t.tx_index # => 62922222
|
46
|
+
|
47
|
+
t.txtotalbtcoutput # => #<Btc BTC:0.00124124 mBTC:1.24124 uBTC:1241.24 Satoshis:124124.0>
|
48
|
+
t.txtotalbtcinput # => #<Btc BTC:5.829e-05 mBTC:0.05829 uBTC:58.29 Satoshis:5829.0>
|
49
|
+
t.txfee # => #<Btc BTC:0.00090215 mBTC:0.90215 uBTC:902.15 Satoshis:90215.0>
|
50
|
+
|
51
|
+
inputs = t.inputs # array of Blockchain::Transaction::Input
|
52
|
+
out = t.out # array of Blockchain::Transaction::Output
|
53
|
+
|
54
|
+
# INPUTs
|
55
|
+
i = inputs[0]
|
56
|
+
i.script # => '76a9141f689f046aae32021905eeec37baef94dd591ff588ac'
|
57
|
+
i.prev_out # => Blockchain::Transaction.find(62918382) | the tx_index of last tx
|
58
|
+
|
59
|
+
# OUTPUTs
|
60
|
+
o = out[0]
|
61
|
+
o.script # => '76a9141f689f046aae32021905eeec37baef94dd591ff588ac'
|
62
|
+
o.n # => 0
|
63
|
+
o.value # => 10000
|
64
|
+
o.addr # => '13s5H9qsCDxsbCuZF5mGbg6rMLziQAnrxr'
|
65
|
+
o.tx_index # => 62922222
|
66
|
+
o.spent # => true
|
67
|
+
o.type # => 0
|
68
|
+
~~~
|
69
|
+
|
70
|
+
Blocks
|
71
|
+
------
|
72
|
+
Search for blocks with block index or block hash
|
73
|
+
|
74
|
+
returns `nil` if not found
|
75
|
+
~~~ruby
|
76
|
+
b = Blockchain::Block.find(458453)
|
77
|
+
b.hsh # => '000000000000000024b816f859d85a955dfe8db09e3735d51755cb5f9cf2f63f'
|
78
|
+
b.ver # => 2
|
79
|
+
b.prev_block # => '00000000000000000894f4f2cbd1dbc8dd0b6d2f3a9a89582f5456516f2bc7d9'
|
80
|
+
b.mrkl_root # => 'baab986e40ac0ea11b7fff8f207b9c8cdbcf49b6b58e66829f8fe9cc5c017e09'
|
81
|
+
b.time # => #<DateTime: 2014-08-22T22:12:23+00:00 ((2456892j,79943s,0n),+0s,2299161j)>
|
82
|
+
b.bits # => 405675096
|
83
|
+
b.fee # => #<Btc BTC:0.16328705 mBTC:163.28705 uBTC:163287.05 Satoshis:16328705.0>
|
84
|
+
b.nonce # => 2030590116
|
85
|
+
b.n_tx # => 979
|
86
|
+
b.size # => 539809
|
87
|
+
b.block_index # => 458453
|
88
|
+
b.main_chain # => true
|
89
|
+
b.height # => 317016
|
90
|
+
b.received_time # => #<DateTime: 2014-08-22T22:12:23+00:00 ((2456892j,79943s,0n),+0s,2299161j)>
|
91
|
+
b.relayed_by # => "193.187.79.76"
|
92
|
+
b.tx # => array of Blockchain::Transaction in the block
|
93
|
+
~~~
|
94
|
+
|
95
|
+
|
96
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "blockchain-ruby"
|
7
|
+
gem.version = "1.0.0"
|
8
|
+
gem.authors = ["James Larisch"]
|
9
|
+
gem.email = ["root@jameslarisch.com"]
|
10
|
+
gem.description = "Convenient wrapper around public Blockchain.info API."
|
11
|
+
gem.summary = "Convenient wrapper around public Blockchain.info API."
|
12
|
+
gem.homepage = "https://github.com/zenchilabs/blockchain-ruby"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec", "~> 3.0.0"
|
21
|
+
gem.add_development_dependency "fakeweb", "~> 1.3.0"
|
22
|
+
gem.add_development_dependency "rake"
|
23
|
+
|
24
|
+
gem.add_dependency "money", "~> 6.1.1"
|
25
|
+
gem.add_dependency "monetize", "~> 0.3.0"
|
26
|
+
gem.add_dependency "bitcoin-money", "~> 0.5.1"
|
27
|
+
end
|
data/lib/address.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Blockchain
|
2
|
+
class Address
|
3
|
+
attr_reader :hash160, :address, :total_received
|
4
|
+
attr_reader :total_sent, :final_balance, :txs
|
5
|
+
|
6
|
+
def self.find(address)
|
7
|
+
self.new(Blockchain.rawaddr(address)) rescue nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(a)
|
11
|
+
meta_init(a)
|
12
|
+
end
|
13
|
+
|
14
|
+
private def meta_init(a)
|
15
|
+
a.each_key { |k| instance_variable_set("@#{k}", a[k]) }
|
16
|
+
@total_received = Btc.from_satoshis(@total_received || 0)
|
17
|
+
@total_sent = Btc.from_satoshis(@total_sent || 0)
|
18
|
+
@final_balance = Btc.from_satoshis(@final_balance || 0)
|
19
|
+
@txs.map! { |t| Blockchain::Transaction.new(t) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def n_tx
|
23
|
+
@n_tx || 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def n_unredeemed
|
27
|
+
@n_unredeemed || 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def firstseen
|
31
|
+
DateTime.strptime(Blockchain.q("addressfirstseen/#{@address}"), '%s')
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
@address
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(o)
|
39
|
+
o.instance_variables == self.instance_variables
|
40
|
+
end
|
41
|
+
|
42
|
+
def refresh!
|
43
|
+
address = @address
|
44
|
+
instance_variables.each { |i| instance_variable_set(i, nil) }
|
45
|
+
meta_init(Blockchain.rawaddr(address))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/block.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Blockchain
|
2
|
+
class Block
|
3
|
+
attr_reader :hsh, :ver, :prev_block, :mrkl_root
|
4
|
+
attr_reader :time, :bits, :fee, :nonce, :n_tx, :size
|
5
|
+
attr_reader :block_index, :main_chain, :height, :received_time
|
6
|
+
attr_reader :relayed_by, :tx
|
7
|
+
|
8
|
+
# id can be block_hash or block_index
|
9
|
+
def self.find(id)
|
10
|
+
self.new(Blockchain.rawblock(id)) rescue nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(b)
|
14
|
+
@hsh = b.delete('hash') # hash is an unfortunate word
|
15
|
+
b.each_key { |k| instance_variable_set("@#{k}", b[k]) }
|
16
|
+
@time = DateTime.strptime(@time.to_s, '%s')
|
17
|
+
@received_time = DateTime.strptime(@received_time.to_s, '%s')
|
18
|
+
@tx.map! { |t| Blockchain::Transaction.new(t) }
|
19
|
+
@fee = Btc.from_satoshis(fee)
|
20
|
+
end
|
21
|
+
|
22
|
+
def previous
|
23
|
+
Block.find(@prev_block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/blockchain.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Blockchain
|
2
|
+
ROOT = 'https://blockchain.info'
|
3
|
+
|
4
|
+
def self.get(path)
|
5
|
+
response = open("#{ROOT}/#{path}")
|
6
|
+
response.read if response.status.first == '200'
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.q(path)
|
10
|
+
self.get("q/#{path}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.rawblock(block)
|
14
|
+
JSON.parse(self.get("rawblock/#{block}"))
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.rawtx(tx)
|
18
|
+
JSON.parse(self.get("rawtx/#{tx}"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.rawaddr(address)
|
22
|
+
JSON.parse(self.get("rawaddr/#{address}"))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'money'
|
27
|
+
require 'bigdecimal'
|
28
|
+
require 'open-uri'
|
29
|
+
require 'date'
|
30
|
+
require 'btc'
|
31
|
+
require 'realtime'
|
32
|
+
require 'transaction'
|
33
|
+
require 'address'
|
34
|
+
require 'block'
|
35
|
+
|
data/lib/realtime.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Blockchain
|
2
|
+
def self.difficulty
|
3
|
+
BigDecimal.new(self.q('getdifficulty'))
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.block_count
|
7
|
+
self.q('getblockcount').to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.latest_hash
|
11
|
+
self.q('latesthash')
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.bc_per_block
|
15
|
+
Btc.new(self.q('bcperblock'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.total
|
19
|
+
Btc.new(self.q('totalbc'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.probability
|
23
|
+
BigDecimal.new(self.q('probability'))
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.hashes_to_win
|
27
|
+
self.q('hashestowin').to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.next_retarget
|
31
|
+
self.q('nextretarget').to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.avg_tx_size
|
35
|
+
self.q('avgtxsize').to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.avg_tx_value
|
39
|
+
self.q('avgtxvalue').to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.interval
|
43
|
+
BigDecimal.new(self.q('interval'))
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.eta
|
47
|
+
BigDecimal.new(self.q('eta'))
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.avg_tx_number
|
51
|
+
self.q('avgtxnumber').to_i
|
52
|
+
end
|
53
|
+
end
|
data/lib/transaction.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blockchain
|
2
|
+
class Transaction
|
3
|
+
attr_reader :hsh, :ver, :vin_sz, :vout_sz, :time, :size, :result, :double_spend
|
4
|
+
attr_reader :relayed_by, :block_height, :tx_index, :inputs, :out
|
5
|
+
|
6
|
+
def self.find(transaction)
|
7
|
+
self.new(Blockchain.rawtx(transaction))
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(t)
|
11
|
+
@hsh = t.delete('hash')
|
12
|
+
t.each_key { |k| instance_variable_set("@#{k}", t[k]) }
|
13
|
+
@inputs.map! { |i| Input.new(i) }
|
14
|
+
@out.map! { |o| Output.new(o) }
|
15
|
+
@time = DateTime.strptime(@time.to_s, '%s')
|
16
|
+
end
|
17
|
+
|
18
|
+
def txtotalbtcoutput
|
19
|
+
Btc.from_satoshis(Blockchain.q("txtotalbtcoutput/#{@hsh}"))
|
20
|
+
end
|
21
|
+
|
22
|
+
def txtotalbtcinput
|
23
|
+
Btc.from_satoshis(Blockchain.q("txtotalbtcinput/#{@hsh}"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def txfee
|
27
|
+
Btc.from_satoshis(Blockchain.q("txfee/#{@hsh}"))
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(o)
|
31
|
+
o.instance_variables == self.instance_variables
|
32
|
+
end
|
33
|
+
|
34
|
+
class Input
|
35
|
+
attr_reader :script, :addr, :n, :tx_index
|
36
|
+
|
37
|
+
def initialize(i)
|
38
|
+
@script = i['script']
|
39
|
+
@prev_out = i['prev_out']
|
40
|
+
@addr = @prev_out['addr']
|
41
|
+
@n = @prev_out['n']
|
42
|
+
@tx_index = @prev_out['tx_index']
|
43
|
+
end
|
44
|
+
|
45
|
+
def prev_out
|
46
|
+
Transaction.find(@tx_index)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Output
|
51
|
+
attr_reader :n, :addr, :tx_index, :spent, :type, :script
|
52
|
+
|
53
|
+
def initialize(o)
|
54
|
+
o.each_key { |k| instance_variable_set"@#{k}", o[k] }
|
55
|
+
@value = Btc.from_satoshis(@value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Blockchain::Address do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
FakeWeb.allow_net_connect = false
|
7
|
+
@test = '1AJbsFZ64EpEfS5UAjAfcUG8pH8Jn3rn1F'
|
8
|
+
fake_raw(TEST_ADDRESS)
|
9
|
+
@a = Blockchain::Address.find(@test)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should get first seen' do
|
13
|
+
fake_q('addressfirstseen', '1331482301')
|
14
|
+
expect(@a.firstseen).to eq(DateTime.strptime('1331482301', '%s'))
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should init all fields' do
|
18
|
+
expect(@a.hash160).to eq('660d4ef3a743e3e696ad990364e555c271ad504b')
|
19
|
+
expect(@a.address).to eq('1AJbsFZ64EpEfS5UAjAfcUG8pH8Jn3rn1F')
|
20
|
+
expect(@a.n_tx).to eq(17)
|
21
|
+
expect(@a.n_unredeemed).to eq(2)
|
22
|
+
expect(@a.total_received).to eq(Btc.from_satoshis(1031350000))
|
23
|
+
expect(@a.total_sent).to eq(Btc.from_satoshis(931250000))
|
24
|
+
expect(@a.final_balance).to eq(Btc.from_satoshis(100100000))
|
25
|
+
expect(@a.txs).to eq([Blockchain::Transaction.new(JSON.parse(TEST_TRANSACTION))])
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should refresh to same thing' do
|
29
|
+
a1 = @a.clone
|
30
|
+
@a.refresh!
|
31
|
+
expect(@a).to eq(a1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def fake_q(path, body, address = @test)
|
35
|
+
FakeWeb.register_uri(:get,
|
36
|
+
"#{Blockchain::ROOT}/q/#{path}/#{address}",
|
37
|
+
body: body, status: 200)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fake_raw(body, address = @test)
|
41
|
+
FakeWeb.register_uri(:get,
|
42
|
+
"#{Blockchain::ROOT}/rawaddr/#{address}",
|
43
|
+
body: body, status: 200)
|
44
|
+
end
|
45
|
+
end
|
data/spec/block_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Blockchain do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
FakeWeb.allow_net_connect = false
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.find' do
|
10
|
+
it 'should return nil if invalid raw block' do
|
11
|
+
FakeWeb.allow_net_connect = true
|
12
|
+
expect(Blockchain::Block.find('nonsense')).to be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'it should init all fields' do
|
16
|
+
fake('someid', TEST_BLOCK)
|
17
|
+
b = Blockchain::Block.find('someid')
|
18
|
+
expect(b.hsh).to eq('0000000000000bae09a7a393a8acded75aa67e46cb81f7acaa5ad94f9eacd103')
|
19
|
+
expect(b.ver).to eq(1)
|
20
|
+
expect(b.prev_block).to eq('00000000000007d0f98d9edca880a6c124e25095712df8952e0439ac7409738a')
|
21
|
+
expect(b.mrkl_root).to eq('935aa0ed2e29a4b81e0c995c39e06995ecce7ddbebb26ed32d550a72e8200bf5')
|
22
|
+
expect(b.time).to eq(DateTime.strptime('1322131230', '%s'))
|
23
|
+
expect(b.bits).to eq(437129626)
|
24
|
+
expect(b.nonce).to eq(2964215930)
|
25
|
+
expect(b.n_tx).to eq(22)
|
26
|
+
expect(b.size).to eq(9195)
|
27
|
+
expect(b.block_index).to eq(818044)
|
28
|
+
expect(b.main_chain).to eq(true)
|
29
|
+
expect(b.height).to eq(154595)
|
30
|
+
expect(b.fee).to eq(Btc.from_satoshis(12345678))
|
31
|
+
expect(b.received_time).to eq(DateTime.strptime('1322131301', '%s'))
|
32
|
+
expect(b.relayed_by).to eq('108.60.208.156')
|
33
|
+
expect(b.tx).to eq([Blockchain::Transaction.new(JSON.parse(TEST_TRANSACTION))])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def fake(id, body)
|
38
|
+
FakeWeb.register_uri(:get, "#{Blockchain::ROOT}/rawblock/#{id}",
|
39
|
+
body: body, status: 200)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Blockchain do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
FakeWeb.allow_net_connect = false
|
7
|
+
@test_i = 123456
|
8
|
+
@test_i_s = '123456'
|
9
|
+
@test_f = 12345.12345
|
10
|
+
@test_f_s = '12345.12345'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should get difficulty' do
|
14
|
+
fake('getdifficulty', @test_f_s)
|
15
|
+
expect(Blockchain.difficulty.to_f).to eq(@test_f)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should get blockcount' do
|
19
|
+
fake('getblockcount', @test_i_s)
|
20
|
+
expect(Blockchain.block_count).to eq(@test_i)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should get latesthash' do
|
24
|
+
fake('latesthash', @test_i_s)
|
25
|
+
expect(Blockchain.latest_hash).to eq(@test_i_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should get bcperblock' do
|
29
|
+
fake('bcperblock', @test_i_s)
|
30
|
+
expect(Blockchain.bc_per_block.btc.to_f).to eq(@test_i.to_f)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should get total' do
|
34
|
+
fake('totalbc', @test_i_s)
|
35
|
+
expect(Blockchain.total.btc.to_f).to eq(@test_i.to_f)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should get probability' do
|
39
|
+
fake('probability', @test_f_s)
|
40
|
+
expect(Blockchain.probability.to_f).to eq(@test_f)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should get hashestowin' do
|
44
|
+
fake('hashestowin', @test_i_s)
|
45
|
+
expect(Blockchain.hashes_to_win).to eq(@test_i)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should get nextretarget' do
|
49
|
+
fake('nextretarget', @test_i_s)
|
50
|
+
expect(Blockchain.next_retarget).to eq(@test_i)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should get avg_tx_size' do
|
54
|
+
fake('avgtxsize', @test_i_s)
|
55
|
+
expect(Blockchain.avg_tx_size).to eq(@test_i)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should get avg_tx_value' do
|
59
|
+
fake('avgtxvalue', @test_i_s)
|
60
|
+
expect(Blockchain.avg_tx_value).to eq(@test_i)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should get avg_tx_number' do
|
64
|
+
fake('avgtxnumber', @test_i_s)
|
65
|
+
expect(Blockchain.avg_tx_number).to eq(@test_i)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should get interval' do
|
69
|
+
fake('interval', @test_f_s)
|
70
|
+
expect(Blockchain.interval.to_f).to eq(@test_f)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should get eta' do
|
74
|
+
fake('eta', @test_f_s)
|
75
|
+
expect(Blockchain.eta.to_f).to eq(@test_f)
|
76
|
+
end
|
77
|
+
|
78
|
+
def fake(path, body)
|
79
|
+
FakeWeb.register_uri(:get, "#{Blockchain::ROOT}/q/#{path}",
|
80
|
+
body: body, status: 200)
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fakeweb'
|
2
|
+
require 'blockchain'
|
3
|
+
|
4
|
+
TEST_TRANSACTION = <<-eos
|
5
|
+
{
|
6
|
+
"hash":"b6f6991d03df0e2e04dafffcd6bc418aac66049e2cd74b80f14ac86db1e3f0da",
|
7
|
+
"ver":1,
|
8
|
+
"vin_sz":1,
|
9
|
+
"vout_sz":2,
|
10
|
+
"time":1408744310,
|
11
|
+
"size":258,
|
12
|
+
"relayed_by":"64.179.201.80",
|
13
|
+
"block_height": 12200,
|
14
|
+
"tx_index":"12563028",
|
15
|
+
"result": 0,
|
16
|
+
"inputs":[
|
17
|
+
{
|
18
|
+
"prev_out":{
|
19
|
+
"n":1,
|
20
|
+
"addr":"143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr",
|
21
|
+
"value":"100000000",
|
22
|
+
"tx_index":12554260,
|
23
|
+
"type":0,
|
24
|
+
"script":"76a914641ad5051edd97029a003fe9efb29359fcee409d88ac"
|
25
|
+
},
|
26
|
+
"script":"76a914641ad5051edd97029a003fe9efb29359fcee409d88ac"
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"out":[
|
30
|
+
{
|
31
|
+
"n": 0,
|
32
|
+
"value": 100000,
|
33
|
+
"addr": "13s5H9qsCDxsbCuZF5mGbg6rMLziQAnrxr",
|
34
|
+
"tx_index": 62922222,
|
35
|
+
"spent": true,
|
36
|
+
"type": 0,
|
37
|
+
"script": "76a9141f689f046aae32021905eeec37baef94dd591ff588ac"
|
38
|
+
},
|
39
|
+
{
|
40
|
+
"n": 1,
|
41
|
+
"value": 1883055,
|
42
|
+
"addr": "143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr",
|
43
|
+
"tx_index": 62922222,
|
44
|
+
"spent": true,
|
45
|
+
"type": 0,
|
46
|
+
"script": "76a914217810f57fd7cdf79274c5d5e4a891fadc74887b88ac"
|
47
|
+
}
|
48
|
+
]
|
49
|
+
}
|
50
|
+
eos
|
51
|
+
|
52
|
+
TEST_BLOCK = <<-eos
|
53
|
+
{
|
54
|
+
"hash":"0000000000000bae09a7a393a8acded75aa67e46cb81f7acaa5ad94f9eacd103",
|
55
|
+
"ver":1,
|
56
|
+
"prev_block":"00000000000007d0f98d9edca880a6c124e25095712df8952e0439ac7409738a",
|
57
|
+
"mrkl_root":"935aa0ed2e29a4b81e0c995c39e06995ecce7ddbebb26ed32d550a72e8200bf5",
|
58
|
+
"time":1322131230,
|
59
|
+
"bits":437129626,
|
60
|
+
"nonce":2964215930,
|
61
|
+
"fee":12345678,
|
62
|
+
"n_tx":22,
|
63
|
+
"size":9195,
|
64
|
+
"block_index":818044,
|
65
|
+
"main_chain":true,
|
66
|
+
"height":154595,
|
67
|
+
"received_time":1322131301,
|
68
|
+
"relayed_by":"108.60.208.156",
|
69
|
+
"tx": [#{TEST_TRANSACTION}]
|
70
|
+
}
|
71
|
+
eos
|
72
|
+
|
73
|
+
TEST_ADDRESS = <<-eos
|
74
|
+
{
|
75
|
+
"hash160":"660d4ef3a743e3e696ad990364e555c271ad504b",
|
76
|
+
"address":"1AJbsFZ64EpEfS5UAjAfcUG8pH8Jn3rn1F",
|
77
|
+
"n_tx":17,
|
78
|
+
"n_unredeemed":2,
|
79
|
+
"total_received":1031350000,
|
80
|
+
"total_sent":931250000,
|
81
|
+
"final_balance":100100000,
|
82
|
+
"txs": [#{TEST_TRANSACTION}]
|
83
|
+
}
|
84
|
+
eos
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe Blockchain::Transaction do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
FakeWeb.allow_net_connect = true
|
8
|
+
@test = 'b6f6991d03df0e2e04dafffcd6bc418aac66049e2cd74b80f14ac86db1e3f0da'
|
9
|
+
fake(@test, TEST_TRANSACTION)
|
10
|
+
@t = Blockchain::Transaction.find(@test)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should return txtotalbtcoutput' do
|
14
|
+
fake_q("txtotalbtcoutput/#{@test}", '12345')
|
15
|
+
expect(@t.txtotalbtcoutput).to eq(Btc.from_satoshis('12345'))
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should return txtotalbtcinput' do
|
19
|
+
fake_q("txtotalbtcinput/#{@test}", '12345')
|
20
|
+
expect(@t.txtotalbtcinput).to eq(Btc.from_satoshis('12345'))
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should return txfee' do
|
24
|
+
fake_q("txfee/#{@test}", '12345')
|
25
|
+
expect(@t.txfee).to eq(Btc.from_satoshis('12345'))
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should parse and store all basic fields' do
|
29
|
+
expect(@t.hsh).to eq('b6f6991d03df0e2e04dafffcd6bc418aac66049e2cd74b80f14ac86db1e3f0da')
|
30
|
+
expect(@t.ver).to eq (1)
|
31
|
+
expect(@t.vin_sz).to eq(1)
|
32
|
+
expect(@t.vout_sz).to eq(2)
|
33
|
+
expect(@t.time).to eq(DateTime.strptime('1408744310', '%s'))
|
34
|
+
expect(@t.size).to eq(258)
|
35
|
+
expect(@t.relayed_by).to eq("64.179.201.80")
|
36
|
+
expect(@t.block_height).to eq(12200)
|
37
|
+
expect(@t.tx_index).to eq("12563028")
|
38
|
+
expect(@t.result).to eq(0)
|
39
|
+
expect(@t.double_spend).to be_falsey
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Blockchain::Transaction::Input do
|
43
|
+
before :all do
|
44
|
+
@is = @t.inputs
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should assign prev out fields correctly' do
|
48
|
+
expect(@is[0].script).to eq('76a914641ad5051edd97029a003fe9efb29359fcee409d88ac')
|
49
|
+
expect(@is[0].addr).to eq('143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr')
|
50
|
+
expect(@is[0].n).to eq(1)
|
51
|
+
expect(@is[0].tx_index).to eq(12554260)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return prevout as a transaction' do
|
55
|
+
expect(@is[0].prev_out).to be_a(Blockchain::Transaction)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Blockchain::Transaction::Output do
|
61
|
+
before :all do
|
62
|
+
@os = @t.out
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should assign second out correctly' do
|
66
|
+
o = @os[1]
|
67
|
+
expect(o.n).to eq(1)
|
68
|
+
expect(o.addr).to eq('143y8gQbhmgePi4QJyL6QBi8zk4PSM5KQr')
|
69
|
+
expect(o.tx_index).to eq(62922222)
|
70
|
+
expect(o.spent).to eq(true)
|
71
|
+
expect(o.type).to eq(0)
|
72
|
+
expect(o.script).to eq('76a914217810f57fd7cdf79274c5d5e4a891fadc74887b88ac')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def fake(t, body)
|
77
|
+
FakeWeb.register_uri(:get,
|
78
|
+
"#{Blockchain::ROOT}/rawtx/#{t}",
|
79
|
+
body: body, status: 200)
|
80
|
+
end
|
81
|
+
|
82
|
+
def fake_q(path, body)
|
83
|
+
FakeWeb.register_uri(:get,
|
84
|
+
"#{Blockchain::ROOT}/q/#{path}",
|
85
|
+
body: body, status: 200)
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blockchain-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Larisch
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fakeweb
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.3.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: money
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 6.1.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 6.1.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: monetize
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bitcoin-money
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.5.1
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.5.1
|
97
|
+
description: Convenient wrapper around public Blockchain.info API.
|
98
|
+
email:
|
99
|
+
- root@jameslarisch.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- blockchain-ruby.gemspec
|
110
|
+
- lib/address.rb
|
111
|
+
- lib/block.rb
|
112
|
+
- lib/blockchain.rb
|
113
|
+
- lib/realtime.rb
|
114
|
+
- lib/transaction.rb
|
115
|
+
- spec/address_spec.rb
|
116
|
+
- spec/block_spec.rb
|
117
|
+
- spec/reatime_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
- spec/transaction_spec.rb
|
120
|
+
homepage: https://github.com/zenchilabs/blockchain-ruby
|
121
|
+
licenses: []
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.2.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Convenient wrapper around public Blockchain.info API.
|
143
|
+
test_files:
|
144
|
+
- spec/address_spec.rb
|
145
|
+
- spec/block_spec.rb
|
146
|
+
- spec/reatime_spec.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
- spec/transaction_spec.rb
|
149
|
+
has_rdoc:
|