sibit 0.13.1 → 0.14.0

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
  SHA256:
3
- metadata.gz: 217f15565505e50e8f5d019458edb7a1ced3ae311d3aa8a3600ee9c00f9636fe
4
- data.tar.gz: c98b24bb8acdb59652eea6eab03397487ee0e9be66e9791fac3a2397b5a108fc
3
+ metadata.gz: 3f6e5104f2435ac68861792d65d9032cc0b41e3fd817e0aa78d0fac816498dfd
4
+ data.tar.gz: ab243ba18cdb2d3b12cce59f610cc613f74a52cc5f1e231bce9079cd0793e894
5
5
  SHA512:
6
- metadata.gz: ad0fa1e2cc7589eb8f0016b7ba8735750e033dbf1dd8776f6b6ac0e7e2acbb5f8ae4d6d69918cb35fb1031ab1218b4523cc6c6e27602cb29d0643d9bb990b9a8
7
- data.tar.gz: 43fc86116a93558807dfea80b9af201a9958c2b5a788309287a72591d9e44bfdb6abf6441d8a6ea3fce952a2a5168c191b1500732b8ac8a5fa8bb03c3b906fd3
6
+ metadata.gz: 8c213d7ab111da0b3e5e04b1ebb6493d6a51161b95c1cfa7390b16ab712c2db048c17d0bc05a2653579f0dd4bd7ea1dec9f77e63d7e580699233af1d39105fd8
7
+ data.tar.gz: d165148dfdc656e97e672c98a1a9839d34214ef0c2484d795103aac45e15b026459cd3191fee1d9e336fc588e366c5f0b6bee8e458c8ae34d3eaed91ade7d3c3
data/.simplecov CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2019 Yegor Bugayenko
1
+ # Copyright (c) 2019-2020 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2019 Yegor Bugayenko
3
+ Copyright (c) 2019-2020 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the 'Software'), to deal
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -66,7 +66,7 @@ Cucumber::Rake::Task.new(:'features:html') do |t|
66
66
  end
67
67
 
68
68
  task :copyright do
69
- sh "grep -q -r '#{Date.today.strftime('%Y')}' \
69
+ sh "grep -q -r '2019-#{Date.today.strftime('%Y')}' \
70
70
  --include '*.rb' \
71
71
  --include '*.txt' \
72
72
  --include 'Rakefile' \
data/bin/sibit CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Copyright (c) 2019 Yegor Bugayenko
4
+ # Copyright (c) 2019-2020 Yegor Bugayenko
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the 'Software'), to deal
@@ -30,6 +30,7 @@ require_relative '../lib/sibit'
30
30
  require_relative '../lib/sibit/version'
31
31
  require_relative '../lib/sibit/blockchain'
32
32
  require_relative '../lib/sibit/btc'
33
+ require_relative '../lib/sibit/bitcoinchain'
33
34
 
34
35
  begin
35
36
  begin
@@ -70,6 +71,8 @@ Options are:"
70
71
  api = Sibit::Blockchain.new(http: http, log: log, dry: opts[:dry])
71
72
  elsif a == 'btc'
72
73
  api = Sibit::Btc.new(http: http, log: log, dry: opts[:dry])
74
+ elsif a == 'bitcoinchain'
75
+ api = Sibit::Bitcoinchain.new(http: http, log: log, dry: opts[:dry])
73
76
  else
74
77
  raise Sibit::Error, "Unknown API \"#{a}\""
75
78
  end
data/features/cli.feature CHANGED
@@ -15,7 +15,7 @@ Feature: Command Line Processing
15
15
  Then Exit code is zero
16
16
 
17
17
  Scenario: Bitcoin latest block hash can be retrieved via Btc
18
- When I run bin/sibit with "latest --api=btc"
18
+ When I run bin/sibit with "latest --api=bitcoinchain"
19
19
  Then Exit code is zero
20
20
 
21
21
  Scenario: Bitcoin private key can be generated
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'uri'
24
+ require 'json'
25
+ require_relative 'version'
26
+ require_relative 'error'
27
+ require_relative 'log'
28
+ require_relative 'http'
29
+ require_relative 'json'
30
+
31
+ # Bitcoinchain.com API.
32
+ #
33
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
34
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
35
+ # License:: MIT
36
+ class Sibit
37
+ # Btc.com API.
38
+ class Bitcoinchain
39
+ # Constructor.
40
+ def initialize(log: Sibit::Log.new, http: Sibit::Http.new, dry: false)
41
+ @http = http
42
+ @log = log
43
+ @dry = dry
44
+ end
45
+
46
+ # Current price of BTC in USD (float returned).
47
+ def price(_currency)
48
+ raise Sibit::Error, 'Not implemented yet'
49
+ end
50
+
51
+ # Gets the balance of the address, in satoshi.
52
+ def balance(address)
53
+ Sibit::Json.new(http: @http, log: @log).get(
54
+ URI("https://api-r.bitcoinchain.com/v1/address/#{address}")
55
+ )[0]['balance']
56
+ end
57
+
58
+ # Get recommended fees, in satoshi per byte.
59
+ def fees
60
+ raise Sibit::Error, 'Not implemented yet'
61
+ end
62
+
63
+ # Sends a payment and returns the transaction hash.
64
+ def pay(_amount, _fee, _sources, _target, _change)
65
+ raise Sibit::Error, 'Not implemented yet'
66
+ end
67
+
68
+ # Gets the hash of the latest block.
69
+ def latest
70
+ Sibit::Json.new(http: @http, log: @log).get(
71
+ URI('https://api-r.bitcoinchain.com/v1/status')
72
+ )['hash']
73
+ end
74
+
75
+ # This method should fetch a Blockchain block and return as a hash.
76
+ def block(hash)
77
+ head = Sibit::Json.new(http: @http, log: @log).get(
78
+ URI("https://api-r.bitcoinchain.com/v1/block/#{hash}")
79
+ )[0]
80
+ nxt = head['next_block']
81
+ nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
82
+ {
83
+ hash: head['hash'],
84
+ orphan: !head['is_main'],
85
+ next: nxt,
86
+ previous: head['prev_block'],
87
+ txns: Sibit::Json.new(http: @http, log: @log).get(
88
+ URI("https://api-r.bitcoinchain.com/v1/block/txs/#{hash}")
89
+ )[0]['txs'].map do |t|
90
+ {
91
+ hash: t['self_hash'],
92
+ outputs: t['outputs'].select { |o| o['spent'] }.map do |o|
93
+ {
94
+ address: o['receiver'],
95
+ value: o['value']
96
+ }
97
+ end
98
+ }
99
+ end
100
+ }
101
+ end
102
+ end
103
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -34,7 +34,7 @@ require_relative 'json'
34
34
  # https://www.blockchain.com/api/blockchain_api
35
35
  #
36
36
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
37
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
37
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
38
38
  # License:: MIT
39
39
  class Sibit
40
40
  # Blockchain.info API.
@@ -133,7 +133,7 @@ class Sibit
133
133
  #{tx.outputs.map { |o| "out: #{o.script.bth} / #{num(o.value, p)}" }.join("\n ")}
134
134
  Min tx fee: #{num(Bitcoin.network[:min_tx_fee], p)}
135
135
  Fee requested: #{num(f, p)} as \"#{fee}\"
136
- Fee left: #{num(left, p)}
136
+ Fee actually paid: #{num(left, p)}
137
137
  Tx size: #{size} bytes
138
138
  Unspent: #{num(unspent, p)}
139
139
  Amount: #{num(satoshi, p)}
@@ -155,6 +155,11 @@ class Sibit
155
155
  )['hash']
156
156
  end
157
157
 
158
+ # This method should fetch a Blockchain block and return as a hash.
159
+ def block(_hash)
160
+ raise Sibit::Error, 'Not implemented yet'
161
+ end
162
+
158
163
  private
159
164
 
160
165
  def num(satoshi, usd)
data/lib/sibit/btc.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -31,7 +31,7 @@ require_relative 'json'
31
31
  # Btc.com API.
32
32
  #
33
33
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
34
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
34
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
35
35
  # License:: MIT
36
36
  class Sibit
37
37
  # Btc.com API.
@@ -81,5 +81,48 @@ class Sibit
81
81
  @log.info("The hash of the latest block is #{hash}")
82
82
  hash
83
83
  end
84
+
85
+ # This method should fetch a Blockchain block and return as a hash.
86
+ def block(hash)
87
+ head = Sibit::Json.new(http: @http, log: @log).get(
88
+ URI("https://chain.api.btc.com/v3/block/#{hash}")
89
+ )
90
+ nxt = head['data']['next_block_hash']
91
+ nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
92
+ {
93
+ hash: head['data']['hash'],
94
+ orphan: head['data']['is_orphan'],
95
+ next: nxt,
96
+ previous: head['data']['prev_block_hash'],
97
+ txns: txns(hash)
98
+ }
99
+ end
100
+
101
+ private
102
+
103
+ def txns(hash)
104
+ page = 1
105
+ psize = 50
106
+ all = []
107
+ loop do
108
+ txns = Sibit::Json.new(http: @http, log: @log).get(
109
+ URI("https://chain.api.btc.com/v3/block/#{hash}/tx?page=#{page}&pagesize=#{psize}")
110
+ )['data']['list'].map do |t|
111
+ {
112
+ hash: t['hash'],
113
+ outputs: t['outputs'].reject { |o| o['spent_by_tx'] }.map do |o|
114
+ {
115
+ address: o['addresses'][0],
116
+ value: o['value']
117
+ }
118
+ end
119
+ }
120
+ end
121
+ all += txns
122
+ page += 1
123
+ break if txns.length < psize
124
+ end
125
+ all
126
+ end
84
127
  end
85
128
  end
data/lib/sibit/error.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -23,7 +23,7 @@
23
23
  # The error.
24
24
  #
25
25
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
26
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
26
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
27
27
  # License:: MIT
28
28
  class Sibit
29
29
  # The error.
data/lib/sibit/fake.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require_relative 'version'
25
25
  # Fake API.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  class Sibit
31
31
  # Fake API
data/lib/sibit/http.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -25,7 +25,7 @@ require 'net/http'
25
25
  # HTTP interface.
26
26
  #
27
27
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
28
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
29
29
  # License:: MIT
30
30
  class Sibit
31
31
  # This HTTP client will be used by default.
@@ -33,6 +33,7 @@ class Sibit
33
33
  def client(uri)
34
34
  http = Net::HTTP.new(uri.host, uri.port)
35
35
  http.use_ssl = true
36
+ http.read_timeout = 120
36
37
  http
37
38
  end
38
39
  end
@@ -46,6 +47,7 @@ class Sibit
46
47
  def client(uri)
47
48
  http = Net::HTTP.new(uri.host, uri.port, @host, @port.to_i)
48
49
  http.use_ssl = true
50
+ http.read_timeout = 120
49
51
  http
50
52
  end
51
53
  end
data/lib/sibit/json.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -22,9 +22,11 @@
22
22
 
23
23
  require 'json'
24
24
  require 'uri'
25
+ require 'cgi'
25
26
  require_relative 'version'
26
27
  require_relative 'error'
27
28
  require_relative 'http'
29
+ require_relative 'log'
28
30
 
29
31
  # Json SDK.
30
32
  #
@@ -32,7 +34,7 @@ require_relative 'http'
32
34
  # https://www.blockchain.com/api/blockchain_api
33
35
  #
34
36
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
35
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
37
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
36
38
  # License:: MIT
37
39
  class Sibit
38
40
  # JSON processing.
@@ -50,8 +52,9 @@ class Sibit
50
52
  start = Time.now
51
53
  res = @http.client(uri).get(
52
54
  "#{uri.path}?#{uri.query}",
53
- 'Accept' => 'text/plain',
55
+ 'Accept' => 'application/json',
54
56
  'User-Agent' => user_agent,
57
+ 'Accept-Charset' => 'UTF-8',
55
58
  'Accept-Encoding' => ''
56
59
  )
57
60
  unless res.code == '200'
@@ -68,6 +71,7 @@ class Sibit
68
71
  "tx=#{CGI.escape(body)}",
69
72
  'Accept' => 'text/plain',
70
73
  'User-Agent' => user_agent,
74
+ 'Accept-Charset' => 'UTF-8',
71
75
  'Accept-Encoding' => '',
72
76
  'Content-Type' => 'application/x-www-form-urlencoded'
73
77
  )
data/lib/sibit/log.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -23,7 +23,7 @@
23
23
  # The log.
24
24
  #
25
25
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
26
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
26
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
27
27
  # License:: MIT
28
28
  class Sibit
29
29
  # Log.
data/lib/sibit/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -22,9 +22,9 @@
22
22
 
23
23
  # Sibit main class.
24
24
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
25
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
25
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  class Sibit
28
28
  # Current version of the library.
29
- VERSION = '0.13.1'
29
+ VERSION = '0.14.0'
30
30
  end
data/lib/sibit.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -28,7 +28,7 @@ require_relative 'sibit/blockchain'
28
28
  # Sibit main class.
29
29
  #
30
30
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
31
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
32
32
  # License:: MIT
33
33
  class Sibit
34
34
  # Constructor.
@@ -115,6 +115,62 @@ class Sibit
115
115
  first_one(&:latest)
116
116
  end
117
117
 
118
+ # You call this method and provide a callback. You provide the hash
119
+ # of the starting block. The method will go through the Blockchain,
120
+ # fetch blocks and find transactions, one by one, passing them to the
121
+ # callback provided. When finished, the method returns the hash of
122
+ # a new block, seen last.
123
+ #
124
+ # The callback will be called with three arguments:
125
+ # 1) Bitcoin address of the receiver, 2) transaction hash, 3) amount
126
+ # in satoshi.
127
+ def scan(start, max: 4)
128
+ block = start
129
+ count = 0
130
+ wrong = []
131
+ loop do
132
+ json = first_one { |api| api.block(block) }
133
+ if json[:orphan]
134
+ steps = 4
135
+ @log.info("Orphan block found at #{block}, moving #{steps} steps back...")
136
+ wrong << block
137
+ steps.times do
138
+ block = json[:previous]
139
+ wrong << block
140
+ @log.info("Moved back to #{block}")
141
+ json = first_one { |api| api.block(block) }
142
+ end
143
+ next
144
+ end
145
+ checked = 0
146
+ checked_outputs = 0
147
+ json[:txns].each do |t|
148
+ t[:outputs].each_with_index do |o, i|
149
+ address = o[:address]
150
+ checked_outputs += 1
151
+ hash = "#{t[:hash]}:#{i}"
152
+ satoshi = o[:value]
153
+ yield(address, hash, satoshi)
154
+ @log.info("Bitcoin tx found at #{hash} for #{satoshi} sent to #{address}")
155
+ end
156
+ checked += 1
157
+ end
158
+ @log.info("We checked #{checked} txns and #{checked_outputs} outputs in block #{block}")
159
+ n = json[:next]
160
+ if n.nil?
161
+ @log.info("The next_block is empty in block #{block}, this is the end of Blockchain")
162
+ break
163
+ end
164
+ block = n
165
+ count += 1
166
+ if count > max
167
+ @log.info("Too many blocks (#{count}) in one go, let's get back to it next time")
168
+ break
169
+ end
170
+ end
171
+ block
172
+ end
173
+
118
174
  private
119
175
 
120
176
  def first_one
data/sibit.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
data/test/test__helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'webmock/minitest'
25
+ require 'json'
26
+ require_relative '../lib/sibit'
27
+ require_relative '../lib/sibit/bitcoinchain'
28
+
29
+ # Sibit::Bitcoinchain test.
30
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
32
+ # License:: MIT
33
+ class TestBitcoinchain < Minitest::Test
34
+ def test_fetch_hash
35
+ stub_request(
36
+ :get,
37
+ 'https://api-r.bitcoinchain.com/v1/status'
38
+ ).to_return(body: '{"hash": "test"}')
39
+ WebMock.allow_net_connect!
40
+ sibit = Sibit::Bitcoinchain.new
41
+ hash = sibit.latest
42
+ assert_equal('test', hash)
43
+ end
44
+
45
+ def test_fetch_balance
46
+ hash = '1Chain4asCYNnLVbvG6pgCLGBrtzh4Lx4b'
47
+ stub_request(:get, "https://api-r.bitcoinchain.com/v1/address/#{hash}")
48
+ .to_return(body: '[{"balance": 123}]')
49
+ sibit = Sibit::Bitcoinchain.new
50
+ satoshi = sibit.balance(hash)
51
+ assert_equal(123, satoshi)
52
+ end
53
+
54
+ def test_fetch_block
55
+ hash = '000000000000000007341915521967247f1dec17b3a311b8a8f4495392f1439b'
56
+ stub_request(:get, "https://api-r.bitcoinchain.com/v1/block/#{hash}")
57
+ .to_return(
58
+ body: '[{"next_block": "nn", "prev_block": "pp", "hash": "hh"}]'
59
+ )
60
+ stub_request(:get, "https://api-r.bitcoinchain.com/v1/block/txs/#{hash}")
61
+ .to_return(
62
+ body: '[{"txs":[{"self_hash":"hash123",
63
+ "outputs":[{"value": 123, "receiver": "a1"}]}]}]'
64
+ )
65
+ sibit = Sibit::Bitcoinchain.new
66
+ json = sibit.block(hash)
67
+ assert_equal('nn', json[:next])
68
+ assert_equal('pp', json[:previous])
69
+ assert_equal('hh', json[:hash])
70
+ assert(json[:txns].is_a?(Array))
71
+ assert_equal('hash123', json[:txns][0][:hash])
72
+ assert(json[:txns][0][:outputs].is_a?(Array))
73
+ end
74
+ end
data/test/test_btc.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -28,7 +28,7 @@ require_relative '../lib/sibit/btc'
28
28
 
29
29
  # Sibit::Btc test.
30
30
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
31
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
32
32
  # License:: MIT
33
33
  class TestBtc < Minitest::Test
34
34
  def test_get_zero_balance
@@ -63,4 +63,21 @@ class TestBtc < Minitest::Test
63
63
  assert(balance.is_a?(Integer))
64
64
  assert_equal(123, balance)
65
65
  end
66
+
67
+ def test_fetch_block
68
+ hash = '000000000000000007341915521967247f1dec17b3a311b8a8f4495392f1439b'
69
+ stub_request(:get, "https://chain.api.btc.com/v3/block/#{hash}")
70
+ .to_return(body: '{"data": {"next_block_hash": "n", "hash": "h", "prev_block_hash": "p"}}')
71
+ stub_request(:get, "https://chain.api.btc.com/v3/block/#{hash}/tx?page=1&pagesize=50")
72
+ .to_return(body: '{"data": {"list":[{"hash": "thash",
73
+ "outputs": [{"addresses": ["a1"], "value": 123}]}]}}')
74
+ sibit = Sibit::Btc.new
75
+ json = sibit.block(hash)
76
+ assert(json[:next])
77
+ assert(json[:previous])
78
+ assert_equal('h', json[:hash])
79
+ assert(json[:txns].is_a?(Array))
80
+ assert_equal('thash', json[:txns][0][:hash])
81
+ assert(json[:txns][0][:outputs].is_a?(Array))
82
+ end
66
83
  end
data/test/test_fake.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -27,7 +27,7 @@ require_relative '../lib/sibit/fake'
27
27
 
28
28
  # Sibit::Fake test.
29
29
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
30
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
31
31
  # License:: MIT
32
32
  class TestFake < Minitest::Test
33
33
  def test_fake_object_works
data/test/test_json.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'uri'
25
+ require_relative '../lib/sibit/json'
26
+
27
+ # Sibit::Json test.
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
30
+ # License:: MIT
31
+ class TestJson < Minitest::Test
32
+ def test_loads_hash
33
+ WebMock.allow_net_connect!
34
+ json = Sibit::Json.new.get(URI('https://api-r.bitcoinchain.com/v1/status'))
35
+ assert(!json['hash'].nil?)
36
+ end
37
+ end
data/test/test_sibit.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2019 Yegor Bugayenko
3
+ # Copyright (c) 2019-2020 Yegor Bugayenko
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the 'Software'), to deal
@@ -27,7 +27,7 @@ require_relative '../lib/sibit'
27
27
 
28
28
  # Sibit.
29
29
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
- # Copyright:: Copyright (c) 2019 Yegor Bugayenko
30
+ # Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
31
31
  # License:: MIT
32
32
  class TestSibit < Minitest::Test
33
33
  def test_loads_fees
@@ -167,4 +167,37 @@ class TestSibit < Minitest::Test
167
167
  )
168
168
  end
169
169
  end
170
+
171
+ def test_scan
172
+ api = Object.new
173
+ def api.block(hash)
174
+ {
175
+ hash: hash,
176
+ orphan: false,
177
+ next: 'next',
178
+ previous: 'previous',
179
+ txns: [
180
+ {
181
+ hash: 'hash',
182
+ outputs: [
183
+ {
184
+ address: 'addr',
185
+ value: 123
186
+ }
187
+ ]
188
+ }
189
+ ]
190
+ }
191
+ end
192
+ sibit = Sibit.new(api: api)
193
+ found = false
194
+ tail = sibit.scan('head') do |addr, tx, satoshi|
195
+ assert_equal(123, satoshi)
196
+ assert_equal('addr', addr)
197
+ assert_equal('hash:0', tx)
198
+ found = true
199
+ end
200
+ assert(found)
201
+ assert_equal('next', tail)
202
+ end
170
203
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sibit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-15 00:00:00.000000000 Z
11
+ date: 2020-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -267,6 +267,7 @@ files:
267
267
  - features/step_definitions/steps.rb
268
268
  - features/support/env.rb
269
269
  - lib/sibit.rb
270
+ - lib/sibit/bitcoinchain.rb
270
271
  - lib/sibit/blockchain.rb
271
272
  - lib/sibit/btc.rb
272
273
  - lib/sibit/error.rb
@@ -278,8 +279,10 @@ files:
278
279
  - logo.svg
279
280
  - sibit.gemspec
280
281
  - test/test__helper.rb
282
+ - test/test_bitcoinchain.rb
281
283
  - test/test_btc.rb
282
284
  - test/test_fake.rb
285
+ - test/test_json.rb
283
286
  - test/test_sibit.rb
284
287
  homepage: http://github.com/yegor256/sibit
285
288
  licenses:
@@ -311,6 +314,8 @@ test_files:
311
314
  - features/step_definitions/steps.rb
312
315
  - features/support/env.rb
313
316
  - test/test__helper.rb
317
+ - test/test_bitcoinchain.rb
314
318
  - test/test_btc.rb
315
319
  - test/test_fake.rb
320
+ - test/test_json.rb
316
321
  - test/test_sibit.rb