sibit 0.0.1 → 0.1.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 +6 -0
- data/README.md +17 -14
- data/bin/sibit +12 -2
- data/features/step_definitions/steps.rb +1 -3
- data/lib/sibit/version.rb +1 -1
- data/lib/sibit.rb +120 -16
- data/sibit.gemspec +3 -1
- data/test/test_sibit.rb +43 -0
- metadata +44 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a297184fe1eeb6405104af89ad5eb25288fc99e1fc0635596f0ed61649f3c248
|
|
4
|
+
data.tar.gz: b386b08577c46b662de0b78737e364411cea207f203b6c78196caa545407bf0b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b088ebce459844e583f03a160490cec3a56299bf021c0e953597e6323f2d84377a47ca1f069810bc7db04d54bc4761929b706cc10f32a0c349753ecd826eb17
|
|
7
|
+
data.tar.gz: 8de79264a592d6bdf387223a4443d7950441a06c553b926640ac4f9c91f9fc61df0f97ed14d878a76de2be02e0eccbf238dcb7f139f26ed4467b1fc545f1e176
|
data/.rubocop.yml
CHANGED
|
@@ -5,6 +5,8 @@ AllCops:
|
|
|
5
5
|
DisplayCopNames: true
|
|
6
6
|
TargetRubyVersion: 2.3
|
|
7
7
|
|
|
8
|
+
Metrics/LineLength:
|
|
9
|
+
Max: 100
|
|
8
10
|
Layout/EndOfLine:
|
|
9
11
|
EnforcedStyle: lf
|
|
10
12
|
Metrics/CyclomaticComplexity:
|
|
@@ -13,6 +15,10 @@ Metrics/MethodLength:
|
|
|
13
15
|
Enabled: false
|
|
14
16
|
Layout/MultilineMethodCallIndentation:
|
|
15
17
|
Enabled: false
|
|
18
|
+
Metrics/ParameterLists:
|
|
19
|
+
Max: 6
|
|
20
|
+
Metrics/ClassLength:
|
|
21
|
+
Max: 120
|
|
16
22
|
Metrics/AbcSize:
|
|
17
23
|
Enabled: false
|
|
18
24
|
Metrics/BlockLength:
|
data/README.md
CHANGED
|
@@ -16,20 +16,18 @@ this [short video](https://www.youtube.com/watch?v=IV9pRBq5A4g).
|
|
|
16
16
|
This is a simple Bitcoin client, to use from the command line
|
|
17
17
|
or from your Ruby app. You don't need to run any Bitcoin software,
|
|
18
18
|
no need to install anything, and so on. All you need is just a command line
|
|
19
|
-
and [Ruby](https://www.ruby-lang.org/en/) 2.3+.
|
|
19
|
+
and [Ruby](https://www.ruby-lang.org/en/) 2.3+. The purpose of this
|
|
20
|
+
client is to simplify most typical operations with Bitcoin. If you need
|
|
21
|
+
something more complex, I would recommend using
|
|
22
|
+
[bitcoin-ruby](https://github.com/lian/bitcoin-ruby) for Ruby and
|
|
23
|
+
[Electrum](https://electrum.org/) as a GUI client.
|
|
20
24
|
|
|
21
|
-
This is Ruby gem, install it first:
|
|
25
|
+
This is a Ruby gem, install it first:
|
|
22
26
|
|
|
23
27
|
```bash
|
|
24
28
|
$ gem install sibit
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
Run it and read its output:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
$ sibit --help
|
|
31
|
-
```
|
|
32
|
-
|
|
33
31
|
Then, you generate a [private key](https://en.bitcoin.it/wiki/Private_key):
|
|
34
32
|
|
|
35
33
|
```bash
|
|
@@ -56,8 +54,8 @@ $ sibit balance 1CC3X2gu58d6wXUWMffpuzN9JAfTUWu4Kj
|
|
|
56
54
|
To send a payment from a few addresses to a new address:
|
|
57
55
|
|
|
58
56
|
```
|
|
59
|
-
$ sibit pay KEY AMOUNT FEE
|
|
60
|
-
|
|
57
|
+
$ sibit pay KEY AMOUNT FEE SOURCE1,SOURCE2,... TARGET CHANGE
|
|
58
|
+
e87f138c9ebf5986151667719825c28458a28cc66f69fed4f1032a93b399fdf8
|
|
61
59
|
```
|
|
62
60
|
|
|
63
61
|
Here,
|
|
@@ -65,9 +63,12 @@ Here,
|
|
|
65
63
|
`AMOUNT` is the amount of [satoshi](https://en.bitcoin.it/wiki/Satoshi_%28unit%29) you are sending,
|
|
66
64
|
`FEE` is the [miner fee](https://en.bitcoin.it/wiki/Miner_fees) you are ready to spend to make this transaction delivered
|
|
67
65
|
(you can say `S`, `M`, `L`, or `XL` if you want it to be calculated automatically),
|
|
68
|
-
`
|
|
69
|
-
`
|
|
70
|
-
|
|
66
|
+
`SOURCE1,SOURCE2,...` is a comma-separated list of addresses you are sending your coins from,
|
|
67
|
+
`TARGET` is the address you are sending to,
|
|
68
|
+
`CHANGE` is the address where the change will be sent to.
|
|
69
|
+
The transaction hash will be returned.
|
|
70
|
+
Not all [UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output)
|
|
71
|
+
will be used, but only the necessary amount of them.
|
|
71
72
|
|
|
72
73
|
All operations are performed through the
|
|
73
74
|
[Blockchain API](https://www.blockchain.com/api/blockchain_api).
|
|
@@ -84,7 +85,9 @@ sibit = Sibit.new
|
|
|
84
85
|
pkey = sibit.generate
|
|
85
86
|
address = sibit.create(pkey)
|
|
86
87
|
balance = sibit.balance(address)
|
|
87
|
-
|
|
88
|
+
target = sibit.create(pkey) # where to send coins to
|
|
89
|
+
change = sibit.create(pkey) # where the change will sent to
|
|
90
|
+
tx = sibit.pay(pkey, 10_000_000, 'XL', [address], target, change)
|
|
88
91
|
```
|
|
89
92
|
|
|
90
93
|
Should work.
|
data/bin/sibit
CHANGED
|
@@ -24,11 +24,18 @@ STDOUT.sync = true
|
|
|
24
24
|
require 'slop'
|
|
25
25
|
require 'backtrace'
|
|
26
26
|
require_relative '../lib/sibit'
|
|
27
|
+
require_relative '../lib/sibit/version'
|
|
27
28
|
|
|
28
29
|
begin
|
|
29
30
|
begin
|
|
30
31
|
opts = Slop.parse(ARGV, strict: true, help: true) do |o|
|
|
31
|
-
o.banner = "Usage (#{Sibit::VERSION}): sibit [options]
|
|
32
|
+
o.banner = "Usage (#{Sibit::VERSION}): sibit [options] command [args]
|
|
33
|
+
Commands are:
|
|
34
|
+
generate: Generate a new private key
|
|
35
|
+
create: Create a public Bitcoin address from the key
|
|
36
|
+
balance: Check the balance of the Bitcoin address
|
|
37
|
+
pay: Send a new Bitcoin transaction
|
|
38
|
+
Options are:"
|
|
32
39
|
o.bool '--help', 'Read this: https://github.com/yegor256/sibit' do
|
|
33
40
|
puts o
|
|
34
41
|
exit
|
|
@@ -37,6 +44,7 @@ begin
|
|
|
37
44
|
rescue Slop::Error => ex
|
|
38
45
|
raise "#{ex.message}"
|
|
39
46
|
end
|
|
47
|
+
raise 'Try --help' if opts.arguments.empty?
|
|
40
48
|
sibit = Sibit.new
|
|
41
49
|
case opts.arguments[0]
|
|
42
50
|
when 'generate'
|
|
@@ -60,7 +68,9 @@ begin
|
|
|
60
68
|
raise 'Addresses argument is required' if sources.nil?
|
|
61
69
|
target = opts.arguments[5]
|
|
62
70
|
raise 'Target argument is required' if target.nil?
|
|
63
|
-
|
|
71
|
+
change = opts.arguments[6]
|
|
72
|
+
raise 'Change argument is required' if change.nil?
|
|
73
|
+
puts sibit.pay(pvt, amount, fee, sources.split(','), target, change)
|
|
64
74
|
else
|
|
65
75
|
raise "Command #{opts.arguments[0]} is not supported"
|
|
66
76
|
end
|
|
@@ -56,9 +56,7 @@ When(%r{^I run bin/sibit with "([^"]*)"$}) do |arg|
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
Then(/^Stdout contains "([^"]*)"$/) do |txt|
|
|
59
|
-
unless @stdout.include?(txt)
|
|
60
|
-
raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}"
|
|
61
|
-
end
|
|
59
|
+
raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}" unless @stdout.include?(txt)
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
Then(/^Stdout is empty$/) do
|
data/lib/sibit/version.rb
CHANGED
data/lib/sibit.rb
CHANGED
|
@@ -25,38 +25,142 @@ require 'typhoeus'
|
|
|
25
25
|
require 'json'
|
|
26
26
|
|
|
27
27
|
# Sibit main class.
|
|
28
|
+
#
|
|
29
|
+
# It works through the Blockchain API at the moment:
|
|
30
|
+
# https://www.blockchain.com/api/blockchain_api
|
|
31
|
+
#
|
|
28
32
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
29
33
|
# Copyright:: Copyright (c) 2019 Yegor Bugayenko
|
|
30
34
|
# License:: MIT
|
|
31
35
|
class Sibit
|
|
32
|
-
#
|
|
36
|
+
# Constructor.
|
|
37
|
+
def initialize(log: STDOUT)
|
|
38
|
+
@log = log
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Generate new Bitcon private key and returns in Hash160 format.
|
|
33
42
|
def generate
|
|
34
|
-
|
|
35
|
-
key.priv
|
|
43
|
+
Bitcoin::Key.generate.priv
|
|
36
44
|
end
|
|
37
45
|
|
|
38
|
-
# Create Bitcon address using the private key.
|
|
46
|
+
# Create Bitcon address using the private key in Hash160 format.
|
|
39
47
|
def create(pvt)
|
|
40
|
-
key
|
|
41
|
-
key.priv = pvt
|
|
42
|
-
key.addr
|
|
48
|
+
key(pvt).addr
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
# Get the balance of the address, in satoshi.
|
|
46
52
|
def balance(address)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
get_json("https://blockchain.info/rawaddr/#{address}")['final_balance']
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Send a payment and return the transaction hash.
|
|
57
|
+
#
|
|
58
|
+
# +pvt+: the private key as a Hash160 string
|
|
59
|
+
# +amount+: the amount either in satoshis or ending with 'BTC', like '0.7BTC'
|
|
60
|
+
# +fee+: the miners fee in satoshis (as integer) or S/M/X/XL as a string
|
|
61
|
+
# +sources+: the array of bitcoin addresses where the coins are now
|
|
62
|
+
# +target+: the target address to send to
|
|
63
|
+
# +change+: the address where the change has to be sent to
|
|
64
|
+
def pay(pvt, amount, fee, sources, target, change)
|
|
65
|
+
satoshi = satoshi(amount)
|
|
66
|
+
builder = Bitcoin::Builder::TxBuilder.new
|
|
67
|
+
unspent = 0
|
|
68
|
+
size = 100
|
|
69
|
+
utxos(sources).each do |utxo|
|
|
70
|
+
unspent += utxo['value']
|
|
71
|
+
builder.input do |i|
|
|
72
|
+
i.prev_out(utxo['tx_hash_big_endian'])
|
|
73
|
+
i.prev_out_index(utxo['tx_output_n'])
|
|
74
|
+
i.prev_out_script = [utxo['script']].pack('H*')
|
|
75
|
+
i.signature_key(key(pvt))
|
|
76
|
+
end
|
|
77
|
+
size += 180
|
|
78
|
+
break if unspent > satoshi
|
|
79
|
+
end
|
|
80
|
+
raise "Not enough funds to send #{amount}, only #{unspent} left" if unspent < satoshi
|
|
81
|
+
builder.output(satoshi, target)
|
|
82
|
+
tx = builder.tx(
|
|
83
|
+
input_value: unspent,
|
|
84
|
+
leave_fee: mfee(fee, size),
|
|
85
|
+
change_address: change
|
|
51
86
|
)
|
|
87
|
+
post_tx(tx.to_payload.bth)
|
|
88
|
+
tx.hash
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Retrieve all unspent outputs of the given list of
|
|
94
|
+
# addresses.
|
|
95
|
+
def utxos(sources)
|
|
96
|
+
offset = 0
|
|
97
|
+
txns = []
|
|
98
|
+
loop do
|
|
99
|
+
uri = [
|
|
100
|
+
'https://blockchain.info/unspent?',
|
|
101
|
+
"active=#{sources.join('|')}",
|
|
102
|
+
offset.positive? ? "&offset=#{offset}" : ''
|
|
103
|
+
].join
|
|
104
|
+
list = get_json(uri)['unspent_outputs']
|
|
105
|
+
txns += list
|
|
106
|
+
break if list.empty?
|
|
107
|
+
offset += list.count
|
|
108
|
+
end
|
|
109
|
+
txns
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Convert text to amount.
|
|
113
|
+
def satoshi(amount)
|
|
114
|
+
return (amount.gsub(/BTC$/, '').to_f * 100_000_000).to_i if amount.end_with?('BTC')
|
|
115
|
+
amount.to_i
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def mfee(fee, size)
|
|
119
|
+
return fee.to_i if fee.is_a?(Integer) || /^[0-9]+$/.match?(fee)
|
|
120
|
+
case fee
|
|
121
|
+
when 'S'
|
|
122
|
+
return 10 * size
|
|
123
|
+
when 'M'
|
|
124
|
+
return 50 * size
|
|
125
|
+
when 'L'
|
|
126
|
+
return 100 * size
|
|
127
|
+
when 'XL'
|
|
128
|
+
return 250 * size
|
|
129
|
+
else
|
|
130
|
+
raise "Can't understand the fee: #{fee.inspect}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Make key from private key string in Hash160.
|
|
135
|
+
def key(hash160)
|
|
136
|
+
key = Bitcoin::Key.new
|
|
137
|
+
key.priv = hash160
|
|
138
|
+
key
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def post_tx(body)
|
|
142
|
+
uri = 'https://blockchain.info/pushtx'
|
|
143
|
+
request = Typhoeus::Request.new(uri, method: :post, body: { tx: body })
|
|
144
|
+
request.run
|
|
145
|
+
response = request.response
|
|
146
|
+
raise "Invalid response at #{uri}: #{response.code}" unless response.code == 200
|
|
147
|
+
debug("POST #{uri}: #{response.code}")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def get_json(uri)
|
|
151
|
+
request = Typhoeus::Request.new(uri, method: :get, headers: {})
|
|
52
152
|
request.run
|
|
53
153
|
response = request.response
|
|
54
|
-
|
|
55
|
-
|
|
154
|
+
raise "Invalid response at #{uri}: #{response.code}" unless response.code == 200
|
|
155
|
+
debug("GET #{uri}: #{response.code}")
|
|
156
|
+
JSON.parse(response.body)
|
|
56
157
|
end
|
|
57
158
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
159
|
+
def debug(msg)
|
|
160
|
+
if @log.respond_to?(:debug)
|
|
161
|
+
@log.debug(msg)
|
|
162
|
+
elsif @log.respond_to?(:puts)
|
|
163
|
+
@log.puts(msg)
|
|
164
|
+
end
|
|
61
165
|
end
|
|
62
166
|
end
|
data/sibit.gemspec
CHANGED
|
@@ -55,10 +55,12 @@ and Ruby 2.3+.'
|
|
|
55
55
|
s.add_runtime_dependency 'typhoeus', '1.3.1'
|
|
56
56
|
s.add_development_dependency 'codecov', '0.1.10'
|
|
57
57
|
s.add_development_dependency 'cucumber', '1.3.17'
|
|
58
|
+
s.add_development_dependency 'debase', '0.2.2'
|
|
58
59
|
s.add_development_dependency 'minitest', '5.5.0'
|
|
59
60
|
s.add_development_dependency 'rake', '12.0.0'
|
|
60
|
-
# s.add_development_dependency 'rdoc', '4.2.0'
|
|
61
61
|
s.add_development_dependency 'rspec-rails', '3.1.0'
|
|
62
62
|
s.add_development_dependency 'rubocop', '0.61.0'
|
|
63
63
|
s.add_development_dependency 'rubocop-rspec', '1.31.0'
|
|
64
|
+
s.add_development_dependency 'ruby-debug-ide', '0.6.1'
|
|
65
|
+
s.add_development_dependency 'webmock', '3.4.2'
|
|
64
66
|
end
|
data/test/test_sibit.rb
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
23
|
require 'minitest/autorun'
|
|
24
|
+
require 'webmock/minitest'
|
|
25
|
+
require 'json'
|
|
24
26
|
require_relative '../lib/sibit'
|
|
25
27
|
|
|
26
28
|
# Sibit.
|
|
@@ -38,14 +40,55 @@ class TestSibit < Minitest::Test
|
|
|
38
40
|
def test_create_address
|
|
39
41
|
sibit = Sibit.new
|
|
40
42
|
pkey = sibit.generate
|
|
43
|
+
puts "key: #{pkey}"
|
|
41
44
|
address = sibit.create(pkey)
|
|
45
|
+
puts "address: #{address}"
|
|
42
46
|
assert(!address.nil?)
|
|
43
47
|
assert(/^1[0-9a-zA-Z]+$/.match?(address))
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def test_gets_balance
|
|
51
|
+
stub_request(
|
|
52
|
+
:get,
|
|
53
|
+
'https://blockchain.info/rawaddr/1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f'
|
|
54
|
+
).to_return(status: 200, body: '{"final_balance": 100}')
|
|
47
55
|
sibit = Sibit.new
|
|
48
56
|
balance = sibit.balance('1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f')
|
|
49
57
|
assert(balance.is_a?(Integer))
|
|
58
|
+
assert_equal(100, balance)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_sends_payment
|
|
62
|
+
json = {
|
|
63
|
+
unspent_outputs: [
|
|
64
|
+
{
|
|
65
|
+
tx_hash: 'fc8fb1a526aef220b54a66bbb3e0549bf34db4f25e1aebc3feb87e86d341e65d',
|
|
66
|
+
tx_hash_big_endian: '5de641d3867eb8fec3eb1a5ef2b44df39b54e0b3bb664ab520f2ae26a5b18ffc',
|
|
67
|
+
tx_output_n: 0,
|
|
68
|
+
script: '76a914c48a1737b35a9f9d9e3b624a910f1e22f7e80bbc88ac',
|
|
69
|
+
value: 100_000
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
stub_request(
|
|
74
|
+
:get,
|
|
75
|
+
'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi'
|
|
76
|
+
).to_return(status: 200, body: JSON.pretty_generate(json))
|
|
77
|
+
stub_request(
|
|
78
|
+
:get,
|
|
79
|
+
'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi&offset=1'
|
|
80
|
+
).to_return(status: 200, body: '{"unspent_outputs": []}')
|
|
81
|
+
stub_request(:post, 'https://blockchain.info/pushtx').to_return(status: 200)
|
|
82
|
+
sibit = Sibit.new
|
|
83
|
+
target = sibit.create(sibit.generate)
|
|
84
|
+
change = sibit.create(sibit.generate)
|
|
85
|
+
tx = sibit.pay(
|
|
86
|
+
'fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2',
|
|
87
|
+
'0.0001BTC', 'S',
|
|
88
|
+
['1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi'],
|
|
89
|
+
target, change
|
|
90
|
+
)
|
|
91
|
+
assert(!tx.nil?)
|
|
92
|
+
assert(tx.length > 30, tx)
|
|
50
93
|
end
|
|
51
94
|
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.0
|
|
4
|
+
version: 0.1.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-04-
|
|
11
|
+
date: 2019-04-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: backtrace
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - '='
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: 1.3.17
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: debase
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - '='
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: 0.2.2
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - '='
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 0.2.2
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: minitest
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -178,6 +192,34 @@ dependencies:
|
|
|
178
192
|
- - '='
|
|
179
193
|
- !ruby/object:Gem::Version
|
|
180
194
|
version: 1.31.0
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: ruby-debug-ide
|
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
|
198
|
+
requirements:
|
|
199
|
+
- - '='
|
|
200
|
+
- !ruby/object:Gem::Version
|
|
201
|
+
version: 0.6.1
|
|
202
|
+
type: :development
|
|
203
|
+
prerelease: false
|
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - '='
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: 0.6.1
|
|
209
|
+
- !ruby/object:Gem::Dependency
|
|
210
|
+
name: webmock
|
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
|
212
|
+
requirements:
|
|
213
|
+
- - '='
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: 3.4.2
|
|
216
|
+
type: :development
|
|
217
|
+
prerelease: false
|
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - '='
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: 3.4.2
|
|
181
223
|
description: |-
|
|
182
224
|
This is a simple Bitcoin client, to use from command line \
|
|
183
225
|
or from your Ruby app. You don't need to run any Bitcoin software, \
|