ci_block_io 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: df3f290034d9d589c52a407a53d22bf8b47377f75eca1735efa619e2540c2fcb
4
+ data.tar.gz: 2a2828ce15a32faa36e97d64b5c429af77e2783fd5f54755ff1059c788dd0aba
5
+ SHA512:
6
+ metadata.gz: 95b1149b0f041f2af71b1c51384979e4649b28737c78bdcefb2d7c359b475baa0ab7525da497c92a2cf0ce51624ae35726dd09145aab857b94659206aab00a84
7
+ data.tar.gz: 393160136ac00104b62cf9f4c2c978fcac43dd610de8b4c7433b5ef822c95600cecb4fe123c00084342b2e1d3a8665e9668b8b3417393e1d0de1b0d5439773d4
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ pkg
3
+ vendor/
4
+ .bundle/
5
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in block_io.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 BlockIo
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 NONINFRINGEMENT. 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.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # BlockIo
2
+
3
+ This Ruby Gem is the official reference client for the Block.io payments API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from <a href="https://block.io" target="_blank">Block.io</a>. Go ahead, sign up :)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'block_io'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install block_io -v=1.2.0
18
+
19
+ ## Changelog
20
+
21
+ *06/25/18*: Remove support for Ruby < 1.9.3 (OpenSSL::Cipher::Cipher). Remove connection_pool dependency.
22
+ *01/21/15*: Added ability to sweep coins from one address to another.
23
+ *11/04/14*: Fix issue with nil parameters in an API call.
24
+ *11/03/14*: Reduce dependence on OpenSSL. PBKDF2 function is now Ruby-based. Should work well with Heroku's libraries.
25
+ *10/18/14*: Now using deterministic signatures (RFC6979), and BIP62 to hinder transaction malleability.
26
+
27
+
28
+ ## Usage
29
+
30
+ It's super easy to get started. In your Ruby shell ($ irb), for example, do this:
31
+
32
+ require 'block_io'
33
+ BlockIo.set_options :api_key => 'API KEY', :pin => 'SECRET PIN', :version => 2
34
+
35
+ And you're good to go:
36
+
37
+ BlockIo.get_new_address
38
+ BlockIo.get_my_addresses
39
+
40
+ For more information, see https://block.io/api/simple/ruby
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it ( https://github.com/BlockIo/gem-block-io/fork )
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ci_block_io/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ci_block_io"
8
+ spec.version = CiBlockIo::VERSION
9
+ spec.authors = ["Atsuhiro Tsuruta feat. Atif Nazir"]
10
+ spec.email = ["a.tsuruta@1-box.co.jp"]
11
+ spec.summary = %q{An easy to use Dogecoin, Bitcoin, Litecoin wallet API by Block.io. Sign up required at Block.io.}
12
+ spec.description = %q{This Ruby Gem is the official reference client for the Block.io payments API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from Block.io. Go ahead, sign up :)}
13
+ spec.homepage = "https://block.io/api/simple/ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 0"
23
+ spec.add_runtime_dependency "ecdsa", "~> 1.2", '>= 1.2.0'
24
+ spec.add_runtime_dependency "httpclient", "~> 2.8", '>= 2.8.0'
25
+ spec.add_runtime_dependency "oj", "~> 3.3", '>= 3.3.5'
26
+ spec.add_runtime_dependency "pbkdf2-ruby", '~> 0.2', '>= 0.2.1'
27
+ end
@@ -0,0 +1,3 @@
1
+ module CiBlockIo
2
+ VERSION = "1.3.0"
3
+ end
@@ -0,0 +1,418 @@
1
+ require 'ci_block_io/version'
2
+ require 'httpclient'
3
+ require 'oj'
4
+ require 'ecdsa'
5
+ require 'openssl'
6
+ require 'digest'
7
+ require 'pbkdf2'
8
+ require 'securerandom'
9
+ require 'base64'
10
+
11
+ module CiBlockIo
12
+
13
+ @api_key = nil
14
+ @base_url = nil
15
+ @pin = nil
16
+ @encryptionKey = nil
17
+ @client = nil
18
+ @conn_pool = nil
19
+ @version = nil
20
+
21
+ def self.set_options(args = {})
22
+ # initialize BlockIo
23
+ @api_key = args[:api_key]
24
+ @pin = args[:pin]
25
+
26
+ @encryptionKey = Helper.pinToAesKey(@pin) if !@pin.nil?
27
+
28
+ hostname = args[:hostname] || "block.io"
29
+ @base_url = "https://" << hostname << "/api/VERSION/API_CALL/?api_key="
30
+
31
+ @client = HTTPClient.new
32
+ @client.tcp_keepalive = true
33
+ @client.ssl_config.ssl_version = :auto
34
+
35
+ @version = args[:version] || 2 # default version is 2
36
+
37
+ self.api_call(['get_balance',""])
38
+ end
39
+
40
+ def self.method_missing(m, *args, &block)
41
+
42
+ method_name = m.to_s
43
+
44
+ if ['withdraw', 'withdraw_from_address', 'withdraw_from_addresses', 'withdraw_from_user', 'withdraw_from_users', 'withdraw_from_label', 'withdraw_from_labels'].include?(m.to_s) then
45
+ # need to withdraw from an address
46
+ self.withdraw(args.first, m.to_s)
47
+ elsif ['sweep_from_address'].include?(m.to_s) then
48
+ # need to sweep from an address
49
+ self.sweep(args.first, m.to_s)
50
+ else
51
+ params = get_params(args.first)
52
+ self.api_call([method_name, params])
53
+ end
54
+
55
+ end
56
+
57
+ def self.withdraw(args = {}, method_name = 'withdraw')
58
+ # validate arguments for withdrawal of funds TODO
59
+
60
+ raise Exception.new("PIN not set. Use BlockIo.set_options(:api_key=>'API KEY',:pin=>'SECRET PIN',:version=>'API VERSION')") if @pin.nil?
61
+
62
+ params = get_params(args)
63
+
64
+ params << "&pin=" << @pin if @version == 1 # Block.io handles the Secret PIN in the legacy API (v1)
65
+
66
+ response = self.api_call([method_name, params])
67
+
68
+ if response['data'].has_key?('reference_id') then
69
+ # Block.io's asking us to provide some client-side signatures, let's get to it
70
+
71
+ # extract the passphrase
72
+ encrypted_passphrase = response['data']['encrypted_passphrase']['passphrase']
73
+
74
+ # let's get our private key
75
+ key = Helper.extractKey(encrypted_passphrase, @encryptionKey)
76
+
77
+ raise Exception.new('Public key mismatch for requested signer and ourselves. Invalid Secret PIN detected.') if key.public_key != response['data']['encrypted_passphrase']['signer_public_key']
78
+
79
+ # let's sign all the inputs we can
80
+ inputs = response['data']['inputs']
81
+
82
+ Helper.signData(inputs, [key])
83
+
84
+ # the response object is now signed, let's stringify it and finalize this withdrawal
85
+ response = self.api_call(['sign_and_finalize_withdrawal',{:signature_data => Oj.dump(response['data'])}])
86
+
87
+ # if we provided all the required signatures, this transaction went through
88
+ # otherwise Block.io responded with data asking for more signatures
89
+ # the latter will be the case for dTrust addresses
90
+ end
91
+
92
+ return response
93
+
94
+ end
95
+
96
+ def self.sweep(args = {}, method_name = 'sweep_from_address')
97
+ # sweep coins from a given address + key
98
+
99
+ raise Exception.new("No private_key provided.") unless args.has_key?(:private_key)
100
+
101
+ key = Key.from_wif(args[:private_key])
102
+
103
+ args[:public_key] = key.public_key # so Block.io can match things up
104
+ args.delete(:private_key) # the key must never leave this machine
105
+
106
+ params = get_params(args)
107
+
108
+ response = self.api_call([method_name, params])
109
+
110
+ if response['data'].has_key?('reference_id') then
111
+ # Block.io's asking us to provide some client-side signatures, let's get to it
112
+
113
+ # let's sign all the inputs we can
114
+ inputs = response['data']['inputs']
115
+ Helper.signData(inputs, [key])
116
+
117
+ # the response object is now signed, let's stringify it and finalize this withdrawal
118
+ response = self.api_call(['sign_and_finalize_sweep',{:signature_data => Oj.dump(response['data'])}])
119
+
120
+ # if we provided all the required signatures, this transaction went through
121
+ # otherwise Block.io responded with data asking for more signatures
122
+ # the latter will be the case for dTrust addresses
123
+ end
124
+
125
+ return response
126
+
127
+ end
128
+
129
+
130
+ private
131
+
132
+ def self.api_call(endpoint)
133
+
134
+ body = nil
135
+ base_url = @base_url.gsub('API_CALL',endpoint[0]).gsub('VERSION', 'v'+@version.to_s)
136
+ return nil if base_url.blank?
137
+
138
+ response = @client.post("#{base_url + @api_key}", endpoint[1])
139
+
140
+ begin
141
+ body = Oj.load(response.body)
142
+ raise Exception.new(body['data']['error_message']) if !body['status'].eql?('success')
143
+ rescue
144
+ raise Exception.new('Unknown error occurred. Please report this.')
145
+ end
146
+
147
+ body
148
+ end
149
+
150
+ private
151
+
152
+ def self.get_params(args = {})
153
+ # construct the parameter string
154
+ params = ""
155
+ args = {} if args.nil?
156
+
157
+ args.each do |k,v|
158
+ params += '&' if params.length > 0
159
+ params += "#{k.to_s}=#{v.to_s}"
160
+ end
161
+
162
+ return params
163
+ end
164
+
165
+ public
166
+
167
+ class Key
168
+
169
+ def initialize(privkey = nil, compressed = true)
170
+ # the privkey must be in hex if at all provided
171
+
172
+ @group = ECDSA::Group::Secp256k1
173
+ @private_key = privkey.to_i(16) || 1 + SecureRandom.random_number(group.order - 1)
174
+ @public_key = @group.generator.multiply_by_scalar(@private_key)
175
+ @compressed = compressed
176
+
177
+ end
178
+
179
+ def private_key
180
+ # returns private key in hex form
181
+ return @private_key.to_s(16)
182
+ end
183
+
184
+ def public_key
185
+ # returns the compressed form of the public key to save network fees (shorter scripts)
186
+
187
+ return ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
188
+ end
189
+
190
+ def sign(data)
191
+ # signed the given hexadecimal string
192
+
193
+ nonce = deterministicGenerateK([data].pack("H*"), @private_key) # RFC6979
194
+
195
+ signature = ECDSA.sign(@group, @private_key, data.to_i(16), nonce)
196
+
197
+ # BIP0062 -- use lower S values only
198
+ r, s = signature.components
199
+
200
+ over_two = @group.order >> 1 # half of what it was
201
+ s = @group.order - s if (s > over_two)
202
+
203
+ signature = ECDSA::Signature.new(r, s)
204
+
205
+ # DER encode this, and return it in hex form
206
+ return ECDSA::Format::SignatureDerString.encode(signature).unpack("H*")[0]
207
+ end
208
+
209
+ def self.from_passphrase(passphrase)
210
+ # create a private+public key pair from a given passphrase
211
+ # think of this as your brain wallet. be very sure to use a sufficiently long passphrase
212
+ # if you don't want a passphrase, just use Key.new and it will generate a random key for you
213
+
214
+ raise Exception.new('Must provide passphrase at least 8 characters long.') if passphrase.nil? or passphrase.length < 8
215
+
216
+ hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
217
+
218
+ return Key.new(hashed_key)
219
+ end
220
+
221
+ def self.from_wif(wif)
222
+ # returns a new key extracted from the Wallet Import Format provided
223
+ # TODO check against checksum
224
+
225
+ hexkey = Helper.decode_base58(wif)
226
+ actual_key = hexkey[2...66]
227
+
228
+ compressed = hexkey[2..hexkey.length].length-8 > 64 and hexkey[2..hexkey.length][64...66] == '01'
229
+
230
+ return Key.new(actual_key, compressed)
231
+
232
+ end
233
+
234
+ def isPositive(i)
235
+ sig = "!+-"[i <=> 0]
236
+
237
+ return sig.eql?("+")
238
+ end
239
+
240
+ def deterministicGenerateK(data, privkey, group = ECDSA::Group::Secp256k1)
241
+ # returns a deterministic K -- RFC6979
242
+
243
+ hash = data.bytes.to_a
244
+
245
+ x = [privkey.to_s(16)].pack("H*").bytes.to_a
246
+
247
+ k = []
248
+ 32.times { k.insert(0, 0) }
249
+
250
+ v = []
251
+ 32.times { v.insert(0, 1) }
252
+
253
+ # step D
254
+ k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).concat(x).concat(hash).pack("C*")).bytes.to_a
255
+
256
+ # step E
257
+ v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
258
+
259
+ # puts "E: " + v.pack("C*").unpack("H*")[0]
260
+
261
+ # step F
262
+ k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([1]).concat(x).concat(hash).pack("C*")).bytes.to_a
263
+
264
+ # step G
265
+ v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
266
+
267
+ # step H2b (Step H1/H2a ignored)
268
+ v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
269
+
270
+ h2b = v.pack("C*").unpack("H*")[0]
271
+ tNum = h2b.to_i(16)
272
+
273
+ # step H3
274
+ while (!isPositive(tNum) or tNum >= group.order) do
275
+ # k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k)
276
+ k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).pack("C*")).bytes.to_a
277
+
278
+ # v = crypto.HmacSHA256(v, k)
279
+ v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
280
+
281
+ # T = BigInteger.fromBuffer(v)
282
+ tNum = v.pack("C*").unpack("H*")[0].to_i(16)
283
+ end
284
+
285
+ return tNum
286
+ end
287
+
288
+ end
289
+
290
+ module Helper
291
+
292
+ def self.signData(inputs, keys)
293
+ # sign the given data with the given keys
294
+ # TODO loop is O(n^3), make it better
295
+
296
+ raise Exception.new('Keys object must be an array of keys, without at least one key inside it.') unless keys.is_a?(Array) and keys.size >= 1
297
+
298
+ i = 0
299
+ while i < inputs.size do
300
+ # iterate over all signers
301
+ input = inputs[i]
302
+
303
+ j = 0
304
+ while j < input['signers'].size do
305
+ # if our public key matches this signer's public key, sign the data
306
+ signer = inputs[i]['signers'][j]
307
+
308
+ k = 0
309
+ while k < keys.size do
310
+ # sign for each key provided, if we can
311
+ key = keys[k]
312
+ signer['signed_data'] = key.sign(input['data_to_sign']) if signer['signer_public_key'] == key.public_key
313
+ k = k + 1
314
+ end
315
+
316
+ j = j + 1
317
+ end
318
+
319
+ i = i + 1
320
+ end
321
+
322
+ inputs
323
+ end
324
+
325
+ def self.extractKey(encrypted_data, b64_enc_key)
326
+ # passphrase is in plain text
327
+ # encrypted_data is in base64, as it was stored on Block.io
328
+ # returns the private key extracted from the given encrypted data
329
+
330
+ decrypted = self.decrypt(encrypted_data, b64_enc_key)
331
+
332
+ return Key.from_passphrase(decrypted)
333
+ end
334
+
335
+ def self.sha256(value)
336
+ # returns the hex of the hash of the given value
337
+ hash = Digest::SHA2.new(256)
338
+ hash << value
339
+ hash.hexdigest # return hex
340
+ end
341
+
342
+ def self.pinToAesKey(secret_pin, iterations = 2048)
343
+ # converts the pincode string to PBKDF2
344
+ # returns a base64 version of PBKDF2 pincode
345
+ salt = ""
346
+
347
+ # pbkdf2-ruby gem uses SHA256 as the default hash function
348
+ aes_key_bin = PBKDF2.new(:password => secret_pin, :salt => salt, :iterations => iterations/2, :key_length => 128/8).value
349
+ aes_key_bin = PBKDF2.new(:password => aes_key_bin.unpack("H*")[0], :salt => salt, :iterations => iterations/2, :key_length => 256/8).value
350
+
351
+ return Base64.strict_encode64(aes_key_bin) # the base64 encryption key
352
+ end
353
+
354
+ # Decrypts a block of data (encrypted_data) given an encryption key
355
+ def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
356
+
357
+ response = nil
358
+
359
+ begin
360
+ aes = OpenSSL::Cipher.new(cipher_type)
361
+ aes.decrypt
362
+ aes.key = Base64.strict_decode64(b64_enc_key)
363
+ aes.iv = iv if iv != nil
364
+ response = aes.update(Base64.strict_decode64(encrypted_data)) + aes.final
365
+ rescue Exception => e
366
+ # decryption failed, must be an invalid Secret PIN
367
+ raise Exception.new('Invalid Secret PIN provided.')
368
+ end
369
+
370
+ return response
371
+ end
372
+
373
+ # Encrypts a block of data given an encryption key
374
+ def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
375
+ aes = OpenSSL::Cipher.new(cipher_type)
376
+ aes.encrypt
377
+ aes.key = Base64.strict_decode64(b64_enc_key)
378
+ aes.iv = iv if iv != nil
379
+ Base64.strict_encode64(aes.update(data) + aes.final)
380
+ end
381
+
382
+ # courtesy bitcoin-ruby
383
+
384
+ def self.int_to_base58(int_val, leading_zero_bytes=0)
385
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
386
+ base58_val, base = '', alpha.size
387
+ while int_val > 0
388
+ int_val, remainder = int_val.divmod(base)
389
+ base58_val = alpha[remainder] + base58_val
390
+ end
391
+ base58_val
392
+ end
393
+
394
+ def self.base58_to_int(base58_val)
395
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
396
+ int_val, base = 0, alpha.size
397
+ base58_val.reverse.each_char.with_index do |char,index|
398
+ raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
399
+ int_val += char_index*(base**index)
400
+ end
401
+ int_val
402
+ end
403
+
404
+ def self.encode_base58(hex)
405
+ leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
406
+ ("1"*leading_zero_bytes) + Helper.int_to_base58( hex.to_i(16) )
407
+ end
408
+
409
+ def self.decode_base58(base58_val)
410
+ s = Helper.base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
411
+ s = '' if s == '00'
412
+ leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
413
+ s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
414
+ s
415
+ end
416
+ end
417
+
418
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ci_block_io
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Atsuhiro Tsuruta feat. Atif Nazir
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ecdsa
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.2.0
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '1.2'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.2.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: httpclient
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.8'
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.8.0
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '2.8'
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.8.0
81
+ - !ruby/object:Gem::Dependency
82
+ name: oj
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '3.3'
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 3.3.5
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3.3'
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 3.3.5
101
+ - !ruby/object:Gem::Dependency
102
+ name: pbkdf2-ruby
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '0.2'
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 0.2.1
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.2'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 0.2.1
121
+ description: This Ruby Gem is the official reference client for the Block.io payments
122
+ API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from
123
+ Block.io. Go ahead, sign up :)
124
+ email:
125
+ - a.tsuruta@1-box.co.jp
126
+ executables: []
127
+ extensions: []
128
+ extra_rdoc_files: []
129
+ files:
130
+ - ".gitignore"
131
+ - Gemfile
132
+ - LICENSE
133
+ - README.md
134
+ - Rakefile
135
+ - ci_block_io.gemspec
136
+ - lib/ci_block_io.rb
137
+ - lib/ci_block_io/version.rb
138
+ homepage: https://block.io/api/simple/ruby
139
+ licenses:
140
+ - MIT
141
+ metadata: {}
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.7.3
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: An easy to use Dogecoin, Bitcoin, Litecoin wallet API by Block.io. Sign up
162
+ required at Block.io.
163
+ test_files: []