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 +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +4 -1
- data/Rakefile +0 -3
- data/appveyor.yml +1 -1
- data/bin/sibit +3 -1
- data/features/cli.feature +4 -0
- data/lib/sibit/version.rb +1 -1
- data/lib/sibit.rb +21 -13
- data/logo.png +0 -0
- data/test/test_sibit.rb +43 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 420c1f144c205270b6147f9f7841dc623f406da2229515491a6e62394b666a7a
|
|
4
|
+
data.tar.gz: 4038585222353146bcfb9095edea86cec8f16b4ec8bf2561c134e5e09df8c3a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2327d071a6deecf933409c9a56869889adcbece633fba1072e269130e247ea24a841e8f4b6698eaa91b0b2c6c11c54a654c10fb1049e52245270bc3449890b35
|
|
7
|
+
data.tar.gz: 590ff2cd99e4f060043a5d067b38013d5bf0fd0dfb09c6def61559a993d4b8c175b94122f4680b1db99e313ea33d34df6c7e8d54e7be7ad2fed57e3ea6f8c1a9
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<img src="
|
|
1
|
+
<img src="/logo.png" width="64px"/>
|
|
2
2
|
|
|
3
3
|
[](http://www.elegantobjects.org)
|
|
4
4
|
[](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
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
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(
|
|
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)}
|
|
180
|
-
Min tx fee: #{num(Bitcoin.network[:min_tx_fee])}
|
|
181
|
-
Fee left: #{num(
|
|
182
|
-
Tx size: #{
|
|
183
|
-
Unspent: #{num(unspent)}
|
|
184
|
-
Amount: #{num(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(
|
|
215
|
-
|
|
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.
|
|
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-
|
|
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
|