sibit 0.8.0 → 0.9.0

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
  SHA256:
3
- metadata.gz: fd8d72ab658b0424c135882a0787001395a69db720bc8283cf6b2155729c5a96
4
- data.tar.gz: 37c2fab8a0bd840620e57c2a38ac831dcab13232f6274299c11477675e41734a
3
+ metadata.gz: 420c1f144c205270b6147f9f7841dc623f406da2229515491a6e62394b666a7a
4
+ data.tar.gz: 4038585222353146bcfb9095edea86cec8f16b4ec8bf2561c134e5e09df8c3a1
5
5
  SHA512:
6
- metadata.gz: 9e70e784fdf68578e1919ce2989cdfb53c8fc50289e41b57d5df0429fdb28d6679eaf0e558f34112517019f372b16f0c2212b45b7c5104ad3bafd704cf3516b8
7
- data.tar.gz: 11123bef8e29dde1689907f4536325f807510e9dd3dd10002afea284925176b37543060083ea17a8934e72d43a88cb2fc51c86697938d565bcfceb0d1487b937
6
+ metadata.gz: 2327d071a6deecf933409c9a56869889adcbece633fba1072e269130e247ea24a841e8f4b6698eaa91b0b2c6c11c54a654c10fb1049e52245270bc3449890b35
7
+ data.tar.gz: 590ff2cd99e4f060043a5d067b38013d5bf0fd0dfb09c6def61559a993d4b8c175b94122f4680b1db99e313ea33d34df6c7e8d54e7be7ad2fed57e3ea6f8c1a9
data/.rubocop.yml CHANGED
@@ -18,7 +18,7 @@ Layout/MultilineMethodCallIndentation:
18
18
  Metrics/ParameterLists:
19
19
  Max: 6
20
20
  Metrics/ClassLength:
21
- Max: 150
21
+ Max: 200
22
22
  Metrics/AbcSize:
23
23
  Enabled: false
24
24
  Metrics/BlockLength:
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <img src="https://en.bitcoin.it/w/images/en/2/29/BC_Logo_.png" width="64px"/>
1
+ <img src="/logo.png" width="64px"/>
2
2
 
3
3
  [![EO principles respected here](http://www.elegantobjects.org/badge.svg)](http://www.elegantobjects.org)
4
4
  [![Managed by Zerocracy](https://www.0crat.com/badge/C3RFVLU72.svg)](https://www.0crat.com/p/C3RFVLU72)
@@ -26,6 +26,9 @@ something more complex, I would recommend using
26
26
  [bitcoin-ruby](https://github.com/lian/bitcoin-ruby) for Ruby and
27
27
  [Electrum](https://electrum.org/) as a GUI client.
28
28
 
29
+ You may want to discuss this tool at [Bitcointalk](https://bitcointalk.org/index.php?topic=5130324)
30
+ and give the thread a few merits.
31
+
29
32
  This is a Ruby gem, install it first (if doesn't work, there are
30
33
  some hints at the bottom of this page):
31
34
 
data/Rakefile CHANGED
@@ -36,7 +36,6 @@ end
36
36
  task default: %i[clean test features rubocop copyright]
37
37
 
38
38
  require 'rake/testtask'
39
- desc 'Run all unit tests'
40
39
  Rake::TestTask.new(:test) do |test|
41
40
  Rake::Cleaner.cleanup_files(['coverage'])
42
41
  test.libs << 'lib' << 'test'
@@ -45,7 +44,6 @@ Rake::TestTask.new(:test) do |test|
45
44
  end
46
45
 
47
46
  require 'rdoc/task'
48
- desc 'Build RDoc documentation'
49
47
  Rake::RDocTask.new do |rdoc|
50
48
  rdoc.rdoc_dir = 'rdoc'
51
49
  rdoc.title = "#{name} #{version}"
@@ -54,7 +52,6 @@ Rake::RDocTask.new do |rdoc|
54
52
  end
55
53
 
56
54
  require 'rubocop/rake_task'
57
- desc 'Run RuboCop on all directories'
58
55
  RuboCop::RakeTask.new(:rubocop) do |task|
59
56
  task.fail_on_error = true
60
57
  task.requires << 'rubocop-rspec'
data/appveyor.yml CHANGED
@@ -15,7 +15,7 @@ build_script:
15
15
  - bundle update
16
16
  - bundle install
17
17
  test_script:
18
- - rake
18
+ - bundle exec rake
19
19
  cache:
20
20
  - C:\Ruby200\bin -> sibit.gemspec
21
21
  - C:\Ruby200\lib\ruby\gems\2.0.0 -> sibit.gemspec
data/bin/sibit CHANGED
@@ -39,6 +39,7 @@ Commands are:
39
39
  pay: Send a new Bitcoin transaction
40
40
  Options are:"
41
41
  o.string '--proxy', 'HTTPS proxy for all requests, e.g. "localhost:3128"'
42
+ o.bool '--dry', 'Don\'t send a real payment, run in a read-only mode'
42
43
  o.bool '--help', 'Read this: https://github.com/yegor256/sibit' do
43
44
  puts o
44
45
  exit
@@ -51,7 +52,8 @@ Options are:"
51
52
  raise 'Try --help' if opts.arguments.empty?
52
53
  sibit = Sibit.new(
53
54
  log: opts[:verbose] ? STDOUT : nil,
54
- http: opts[:proxy] ? Sibit.proxy_http(opts[:proxy]) : Sibit.default_http
55
+ http: opts[:proxy] ? Sibit.proxy_http(opts[:proxy]) : Sibit.default_http,
56
+ dry: opts[:dry]
55
57
  )
56
58
  case opts.arguments[0]
57
59
  when 'price'
data/features/cli.feature CHANGED
@@ -25,3 +25,7 @@ Feature: Command Line Processing
25
25
  Scenario: Bitcoin balance can be checked
26
26
  When I run bin/sibit with "balance 1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f --verbose"
27
27
  Then Exit code is zero
28
+
29
+ Scenario: Bitcoin transaction can be sent
30
+ When I run bin/sibit with "--verbose --dry pay 100000 S 1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi:fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2 1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi 1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi"
31
+ Then Exit code is zero
data/lib/sibit/version.rb CHANGED
@@ -26,5 +26,5 @@
26
26
  # License:: MIT
27
27
  class Sibit
28
28
  # Current version of the library.
29
- VERSION = '0.8.0'
29
+ VERSION = '0.9.0'
30
30
  end
data/lib/sibit.rb CHANGED
@@ -25,6 +25,7 @@ require 'uri'
25
25
  require 'bitcoin'
26
26
  require 'json'
27
27
  require 'cgi'
28
+ require_relative 'sibit/version'
28
29
 
29
30
  # Sibit main class.
30
31
  #
@@ -90,9 +91,10 @@ class Sibit
90
91
  # provide anything, the console will be used. The object you provide
91
92
  # has to respond to the method +info+ or +puts+ in order to receive logging
92
93
  # messages.
93
- def initialize(log: STDOUT, http: Sibit.default_http)
94
+ def initialize(log: STDOUT, http: Sibit.default_http, dry: false)
94
95
  @log = log
95
96
  @http = http
97
+ @dry = dry
96
98
  end
97
99
 
98
100
  # Current price of 1 BTC.
@@ -138,6 +140,7 @@ class Sibit
138
140
  # +target+: the target address to send to
139
141
  # +change+: the address where the change has to be sent to
140
142
  def pay(amount, fee, sources, target, change)
143
+ p = price
141
144
  satoshi = satoshi(amount)
142
145
  builder = Bitcoin::Builder::TxBuilder.new
143
146
  unspent = 0
@@ -157,11 +160,11 @@ class Sibit
157
160
  i.signature_key(key(sources[address]))
158
161
  end
159
162
  size += 180
160
- info(" #{num(utxo['value'])}/#{utxo['confirmations']} at #{utxo['tx_hash_big_endian']}")
163
+ info(" #{num(utxo['value'], p)}/#{utxo['confirmations']} at #{utxo['tx_hash_big_endian']}")
161
164
  break if unspent > satoshi
162
165
  end
163
166
  if unspent < satoshi
164
- raise Error, "Not enough funds to send #{num(amount)}, only #{num(unspent)} left"
167
+ raise Error, "Not enough funds to send #{num(satoshi, p)}, only #{num(unspent, p)} left"
165
168
  end
166
169
  builder.output(satoshi, target)
167
170
  f = mfee(fee, size)
@@ -171,20 +174,21 @@ class Sibit
171
174
  extra_fee: f - Bitcoin.network[:min_tx_fee],
172
175
  change_address: change
173
176
  )
177
+ left = unspent - tx.outputs.map(&:value).inject(&:+)
174
178
  info("A new Bitcoin transaction #{tx.hash} prepared:
175
179
  #{tx.in.count} input#{tx.in.count > 1 ? 's' : ''}:
176
180
  #{tx.inputs.map { |i| " in: #{i.prev_out.bth}:#{i.prev_out_index}" }.join("\n ")}
177
181
  #{tx.out.count} output#{tx.out.count > 1 ? 's' : ''}:
178
- #{tx.outputs.map { |o| "out: #{o.script.bth} / #{num(o.value)}" }.join("\n ")}
179
- Fee required: #{num(f)} satoshi
180
- Min tx fee: #{num(Bitcoin.network[:min_tx_fee])} satoshi
181
- Fee left: #{num(unspent - tx.outputs.map(&:value).inject(&:+))} satoshi
182
- Tx size: #{num(size)} bytes
183
- Unspent: #{num(unspent)} satoshi
184
- Amount: #{num(satoshi)} satoshi
182
+ #{tx.outputs.map { |o| "out: #{o.script.bth} / #{num(o.value, p)}" }.join("\n ")}
183
+ Fee required: #{num(f, p)}
184
+ Min tx fee: #{num(Bitcoin.network[:min_tx_fee], p)}
185
+ Fee left: #{num(left, p)}
186
+ Tx size: #{size} bytes
187
+ Unspent: #{num(unspent, p)}
188
+ Amount: #{num(satoshi, p)}
185
189
  Target address: #{target}
186
190
  Change address is #{change}")
187
- post_tx(tx.to_payload.bth)
191
+ post_tx(tx.to_payload.bth) unless @dry
188
192
  tx.hash
189
193
  end
190
194
 
@@ -211,8 +215,12 @@ class Sibit
211
215
 
212
216
  private
213
217
 
214
- def num(int)
215
- int.to_s.gsub(/\d(?=(...)+$)/, '\0,')
218
+ def num(satoshi, usd)
219
+ format(
220
+ '%<satoshi>ss/$%<dollars>0.2f',
221
+ satoshi: satoshi.to_s.gsub(/\d(?=(...)+$)/, '\0,'),
222
+ dollars: satoshi * usd / 100_000_000
223
+ )
216
224
  end
217
225
 
218
226
  # Convert text to amount.
data/logo.png ADDED
Binary file
data/test/test_sibit.rb CHANGED
@@ -80,6 +80,9 @@ class TestSibit < Minitest::Test
80
80
  end
81
81
 
82
82
  def test_send_payment
83
+ stub_request(
84
+ :get, 'https://blockchain.info/ticker'
85
+ ).to_return(status: 200, body: '{"USD" : {"15m" : 5160.04}}')
83
86
  json = {
84
87
  unspent_outputs: [
85
88
  {
@@ -110,4 +113,44 @@ class TestSibit < Minitest::Test
110
113
  assert(!tx.nil?)
111
114
  assert(tx.length > 30, tx)
112
115
  end
116
+
117
+ def test_fail_if_not_enough_funds
118
+ stub_request(
119
+ :get, 'https://blockchain.info/ticker'
120
+ ).to_return(status: 200, body: '{"USD" : {"15m" : 5160.04}}')
121
+ json = {
122
+ unspent_outputs: []
123
+ }
124
+ stub_request(
125
+ :get,
126
+ 'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi&limit=1000'
127
+ ).to_return(status: 200, body: JSON.pretty_generate(json))
128
+ sibit = Sibit.new
129
+ target = sibit.create(sibit.generate)
130
+ change = sibit.create(sibit.generate)
131
+ assert_raises Sibit::Error do
132
+ sibit.pay(
133
+ '0.0001BTC', 'XL',
134
+ {
135
+ '1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi' =>
136
+ 'fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2'
137
+ },
138
+ target, change
139
+ )
140
+ end
141
+ end
142
+
143
+ def test_fake_object_works
144
+ sibit = Sibit::Fake.new
145
+ assert_equal(4_000, sibit.price)
146
+ assert_equal('fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2', sibit.generate)
147
+ assert_equal('1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi', sibit.create(''))
148
+ assert_equal(100_000_000, sibit.balance(''))
149
+ assert_equal(
150
+ '9dfe55a30b5ee732005158c589179a398117117a68d21531fb6c78b85b544c54',
151
+ sibit.pay(0, 'M', {}, '', '')
152
+ )
153
+ assert_equal('00000000000000000008df8a6e1b61d1136803ac9791b8725235c9f780b4ed71', sibit.latest)
154
+ assert_equal({}, sibit.get_json('/'))
155
+ end
113
156
  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.8.0
4
+ version: 0.9.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-05-03 00:00:00.000000000 Z
11
+ date: 2019-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -240,6 +240,7 @@ files:
240
240
  - features/support/env.rb
241
241
  - lib/sibit.rb
242
242
  - lib/sibit/version.rb
243
+ - logo.png
243
244
  - sibit.gemspec
244
245
  - test/test__helper.rb
245
246
  - test/test_sibit.rb