block_io 1.0.6 → 3.0.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.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.appveyor.yml-disabled +26 -0
  3. data/.gitignore +5 -1
  4. data/.rspec +1 -0
  5. data/.travis.yml +14 -0
  6. data/LICENSE +1 -1
  7. data/README.md +22 -13
  8. data/block_io.gemspec +9 -8
  9. data/examples/basic.rb +38 -10
  10. data/examples/dtrust.rb +60 -42
  11. data/examples/proxy.rb +36 -0
  12. data/examples/sweeper.rb +24 -14
  13. data/lib/block_io.rb +16 -399
  14. data/lib/block_io/api_exception.rb +11 -0
  15. data/lib/block_io/chainparams/BTC.yml +8 -0
  16. data/lib/block_io/chainparams/BTCTEST.yml +8 -0
  17. data/lib/block_io/chainparams/DOGE.yml +8 -0
  18. data/lib/block_io/chainparams/DOGETEST.yml +8 -0
  19. data/lib/block_io/chainparams/LTC.yml +8 -0
  20. data/lib/block_io/chainparams/LTCTEST.yml +8 -0
  21. data/lib/block_io/client.rb +243 -0
  22. data/lib/block_io/extended_bitcoinrb.rb +127 -0
  23. data/lib/block_io/helper.rb +262 -0
  24. data/lib/block_io/key.rb +38 -0
  25. data/lib/block_io/version.rb +1 -1
  26. data/spec/client_misc_spec.rb +76 -0
  27. data/spec/client_spec.rb +68 -0
  28. data/spec/dtrust_spec.rb +167 -0
  29. data/spec/helper_spec.rb +44 -0
  30. data/spec/key_spec.rb +92 -0
  31. data/spec/larger_transaction_spec.rb +351 -0
  32. data/spec/spec_helper.rb +5 -0
  33. data/spec/sweep_spec.rb +115 -0
  34. data/spec/test-cases/.gitignore +2 -0
  35. data/spec/test-cases/LICENSE +21 -0
  36. data/spec/test-cases/README.md +2 -0
  37. data/spec/test-cases/json/create_and_sign_transaction_response.json +61 -0
  38. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json +1261 -0
  39. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json +1266 -0
  40. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json +1271 -0
  41. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json +3816 -0
  42. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2SH_3of5_195inputs.json +2931 -0
  43. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2SH_4of5_195inputs.json +5 -0
  44. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_251inputs.json +3771 -0
  45. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_252inputs.json +3786 -0
  46. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_253inputs.json +3801 -0
  47. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_251inputs.json +5 -0
  48. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_252inputs.json +5 -0
  49. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_253inputs.json +5 -0
  50. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_251inputs.json +3771 -0
  51. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_252inputs.json +3786 -0
  52. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_253inputs.json +3801 -0
  53. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_251inputs.json +5 -0
  54. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_252inputs.json +5 -0
  55. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_253inputs.json +5 -0
  56. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json +21 -0
  57. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2sh_4_of_5_keys.json +5 -0
  58. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json +21 -0
  59. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_4_of_5_keys.json +5 -0
  60. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json +36 -0
  61. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_251outputs.json +591 -0
  62. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_252outputs.json +576 -0
  63. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_253outputs.json +531 -0
  64. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4_of_5_keys.json +5 -0
  65. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_251outputs.json +5 -0
  66. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_252outputs.json +5 -0
  67. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_253outputs.json +5 -0
  68. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2pkh.json +5 -0
  69. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh.json +5 -0
  70. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json +5 -0
  71. data/spec/test-cases/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +21 -0
  72. data/spec/test-cases/json/get_balance_response.json +8 -0
  73. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2SH_3of5_195inputs.json +1397 -0
  74. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2SH_4of5_195inputs.json +1397 -0
  75. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_251inputs.json +1795 -0
  76. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_252inputs.json +1802 -0
  77. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_253inputs.json +1809 -0
  78. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_251inputs.json +1789 -0
  79. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_252inputs.json +1802 -0
  80. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_253inputs.json +1809 -0
  81. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_251inputs.json +1795 -0
  82. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_252inputs.json +1802 -0
  83. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_253inputs.json +1809 -0
  84. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_251inputs.json +1795 -0
  85. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_252inputs.json +1802 -0
  86. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_253inputs.json +1809 -0
  87. data/spec/test-cases/json/prepare_dtrust_transaction_response_p2sh.json +45 -0
  88. data/spec/test-cases/json/prepare_dtrust_transaction_response_p2wsh_over_p2sh.json +45 -0
  89. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0.json +52 -0
  90. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_251outputs.json +1805 -0
  91. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_252outputs.json +1804 -0
  92. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_253outputs.json +1789 -0
  93. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_251outputs.json +1805 -0
  94. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_252outputs.json +1804 -0
  95. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_253outputs.json +1789 -0
  96. data/spec/test-cases/json/prepare_sweep_transaction_response_p2pkh.json +35 -0
  97. data/spec/test-cases/json/prepare_sweep_transaction_response_p2wpkh.json +35 -0
  98. data/spec/test-cases/json/prepare_sweep_transaction_response_p2wpkh_over_p2sh.json +35 -0
  99. data/spec/test-cases/json/prepare_transaction_response.json +164 -0
  100. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json +1796 -0
  101. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json +1803 -0
  102. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json +1810 -0
  103. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json +5367 -0
  104. data/spec/test-cases/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +76 -0
  105. data/spec/test-cases/json/summarize_prepared_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +6 -0
  106. metadata +249 -43
data/lib/block_io.rb CHANGED
@@ -1,406 +1,23 @@
1
- require 'block_io/version'
2
- require 'httpclient'
3
- require 'json'
4
- require 'connection_pool'
5
- require 'ecdsa'
6
- require 'openssl'
7
- require 'digest'
8
- require 'pbkdf2'
9
- require 'securerandom'
10
- require 'base64'
1
+ require "http"
2
+ require "oj"
3
+ require "bitcoin"
4
+ require "openssl"
5
+ require "securerandom"
6
+ require "connection_pool"
7
+
8
+ require_relative "block_io/version"
9
+ require_relative "block_io/helper"
10
+ require_relative "block_io/key"
11
+ require_relative "block_io/client"
12
+ require_relative "block_io/api_exception"
13
+ require_relative "block_io/extended_bitcoinrb"
11
14
 
12
15
  module BlockIo
13
16
 
14
- @api_key = nil
15
- @base_url = "https://block.io/api/VERSION/API_CALL/?api_key="
16
- @pin = nil
17
- @encryptionKey = 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
- @encryptionKey = Helper.pinToAesKey(@pin) if !@pin.nil?
26
-
27
- @conn_pool = ConnectionPool.new(size: 5, timeout: 300) { HTTPClient.new }
28
-
29
- @version = args[:version] || 2 # default version is 2
30
-
31
- self.api_call(['get_balance',""])
32
- end
33
-
34
- def self.method_missing(m, *args, &block)
35
-
36
- method_name = m.to_s
37
-
38
- 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
39
- # need to withdraw from an address
40
- self.withdraw(args.first, m.to_s)
41
-
42
- elsif ['sweep_from_address'].include?(m.to_s) then
43
- # need to sweep from an address
44
- self.sweep(args.first, m.to_s)
45
- else
46
- params = get_params(args.first)
47
- self.api_call([method_name, params])
48
- end
49
-
50
- end
51
-
52
- def self.withdraw(args = {}, method_name = 'withdraw')
53
- # validate arguments for withdrawal of funds TODO
54
-
55
- raise Exception.new("PIN not set. Use BlockIo.set_options(:api_key=>'API KEY',:pin=>'SECRET PIN',:version=>'API VERSION')") if @pin.nil?
56
-
57
- params = get_params(args)
58
-
59
- params += "&pin=#{@pin}" if @version == 1 # Block.io handles the Secret PIN in the legacy API (v1)
60
-
61
- response = self.api_call([method_name, params])
62
-
63
- if response['data'].has_key?('reference_id') then
64
- # Block.io's asking us to provide some client-side signatures, let's get to it
65
-
66
- # extract the passphrase
67
- encrypted_passphrase = response['data']['encrypted_passphrase']['passphrase']
68
-
69
- # let's get our private key
70
- key = Helper.extractKey(encrypted_passphrase, @encryptionKey)
71
-
72
- 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']
73
-
74
- # let's sign all the inputs we can
75
- inputs = response['data']['inputs']
76
-
77
- inputs.each do |input|
78
- # iterate over all signers
79
-
80
- input['signers'].each do |signer|
81
- # if our public key matches this signer's public key, sign the data
82
-
83
- signer['signed_data'] = key.sign(input['data_to_sign']) if signer['signer_public_key'] == key.public_key
84
-
85
- end
86
-
87
- end
88
-
89
- # the response object is now signed, let's stringify it and finalize this withdrawal
90
-
91
- response = self.api_call(['sign_and_finalize_withdrawal',{:signature_data => response['data'].to_json}])
92
-
93
- # if we provided all the required signatures, this transaction went through
94
- # otherwise Block.io responded with data asking for more signatures
95
- # the latter will be the case for dTrust addresses
96
- end
97
-
98
- return response
99
-
100
- end
101
-
102
- def self.sweep(args = {}, method_name = 'sweep_from_address')
103
- # sweep coins from a given address + key
104
-
105
- raise Exception.new("No private_key provided.") unless args.has_key?(:private_key)
106
-
107
- key = Key.from_wif(args[:private_key])
108
-
109
- args[:public_key] = key.public_key # so Block.io can match things up
110
- args.delete(:private_key) # the key must never leave this machine
111
-
112
- params = get_params(args)
113
-
114
- response = self.api_call([method_name, params])
115
-
116
- if response['data'].has_key?('reference_id') then
117
- # Block.io's asking us to provide some client-side signatures, let's get to it
118
-
119
- # let's sign all the inputs we can
120
- inputs = response['data']['inputs']
121
-
122
- inputs.each do |input|
123
- # iterate over all signers
124
-
125
- input['signers'].each do |signer|
126
- # if our public key matches this signer's public key, sign the data
127
-
128
- signer['signed_data'] = key.sign(input['data_to_sign']) if signer['signer_public_key'] == key.public_key
129
-
130
- end
131
-
132
- end
133
-
134
- # the response object is now signed, let's stringify it and finalize this withdrawal
135
-
136
- response = self.api_call(['sign_and_finalize_sweep',{:signature_data => response['data'].to_json}])
137
-
138
- # if we provided all the required signatures, this transaction went through
139
- # otherwise Block.io responded with data asking for more signatures
140
- # the latter will be the case for dTrust addresses
141
- end
142
-
143
- return response
144
-
17
+ def self.version
18
+ BlockIo::VERSION
145
19
  end
146
-
147
-
148
- private
149
20
 
150
- def self.api_call(endpoint)
151
-
152
- body = nil
153
-
154
- @conn_pool.with do |hc|
155
- # prevent initiation of HTTPClients every time we make this call, use a connection_pool
156
-
157
- hc.ssl_config.ssl_version = :TLSv1
158
- response = hc.post("#{@base_url.gsub('API_CALL',endpoint[0]).gsub('VERSION', 'v'+@version.to_s) + @api_key}", endpoint[1])
159
-
160
- begin
161
- body = JSON.parse(response.body)
162
- raise Exception.new(body['data']['error_message']) if !body['status'].eql?('success')
163
- rescue
164
- raise Exception.new('Unknown error occurred. Please report this.')
165
- end
166
- end
167
-
168
- body
169
- end
170
-
171
- private
172
-
173
- def self.get_params(args = {})
174
- # construct the parameter string
175
- params = ""
176
- args = {} if args.nil?
177
-
178
- args.each do |k,v|
179
- params += '&' if params.length > 0
180
- params += "#{k.to_s}=#{v.to_s}"
181
- end
182
-
183
- return params
184
- end
185
-
186
- public
187
-
188
- class Key
189
-
190
- def initialize(privkey = nil, compressed = true)
191
- # the privkey must be in hex if at all provided
192
-
193
- @group = ECDSA::Group::Secp256k1
194
- @private_key = privkey.to_i(16) || 1 + SecureRandom.random_number(group.order - 1)
195
- @public_key = @group.generator.multiply_by_scalar(@private_key)
196
- @compressed = compressed
197
-
198
- end
199
-
200
- def private_key
201
- # returns private key in hex form
202
- return @private_key.to_s(16)
203
- end
204
-
205
- def public_key
206
- # returns the compressed form of the public key to save network fees (shorter scripts)
207
-
208
- return ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
209
- end
210
-
211
- def sign(data)
212
- # signed the given hexadecimal string
213
-
214
- nonce = deterministicGenerateK([data].pack("H*"), @private_key) # RFC6979
215
-
216
- signature = ECDSA.sign(@group, @private_key, data.to_i(16), nonce)
217
-
218
- # BIP0062 -- use lower S values only
219
- r, s = signature.components
220
-
221
- over_two = @group.order >> 1 # half of what it was
222
- s = @group.order - s if (s > over_two)
223
-
224
- signature = ECDSA::Signature.new(r, s)
225
-
226
- # DER encode this, and return it in hex form
227
- return ECDSA::Format::SignatureDerString.encode(signature).unpack("H*")[0]
228
- end
229
-
230
- def self.from_passphrase(passphrase)
231
- # create a private+public key pair from a given passphrase
232
- # think of this as your brain wallet. be very sure to use a sufficiently long passphrase
233
- # if you don't want a passphrase, just use Key.new and it will generate a random key for you
234
-
235
- raise Exception.new('Must provide passphrase at least 8 characters long.') if passphrase.nil? or passphrase.length < 8
236
-
237
- hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
238
-
239
- return Key.new(hashed_key)
240
- end
241
-
242
- def self.from_wif(wif)
243
- # returns a new key extracted from the Wallet Import Format provided
244
- # TODO check against checksum
245
-
246
- hexkey = Helper.decode_base58(wif)
247
- actual_key = hexkey[2...66]
248
-
249
- compressed = hexkey[2..hexkey.length].length-8 > 64 and hexkey[2..hexkey.length][64...66] == '01'
250
-
251
- return Key.new(actual_key, compressed)
252
-
253
- end
254
-
255
- def isPositive(i)
256
- sig = "!+-"[i <=> 0]
257
-
258
- return sig.eql?("+")
259
- end
260
-
261
- def deterministicGenerateK(data, privkey, group = ECDSA::Group::Secp256k1)
262
- # returns a deterministic K -- RFC6979
263
-
264
- hash = data.bytes.to_a
265
-
266
- x = [privkey.to_s(16)].pack("H*").bytes.to_a
267
-
268
- k = []
269
- 32.times { k.insert(0, 0) }
270
-
271
- v = []
272
- 32.times { v.insert(0, 1) }
273
-
274
- # step D
275
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).concat(x).concat(hash).pack("C*")).bytes.to_a
276
-
277
- # step E
278
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
279
-
280
- # puts "E: " + v.pack("C*").unpack("H*")[0]
281
-
282
- # step F
283
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([1]).concat(x).concat(hash).pack("C*")).bytes.to_a
284
-
285
- # step G
286
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
287
-
288
- # step H2b (Step H1/H2a ignored)
289
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
290
-
291
- h2b = v.pack("C*").unpack("H*")[0]
292
- tNum = h2b.to_i(16)
293
-
294
- # step H3
295
- while (!isPositive(tNum) or tNum >= group.order) do
296
- # k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k)
297
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).pack("C*")).bytes.to_a
298
-
299
- # v = crypto.HmacSHA256(v, k)
300
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
301
-
302
- # T = BigInteger.fromBuffer(v)
303
- tNum = v.pack("C*").unpack("H*")[0].to_i(16)
304
- end
305
-
306
- return tNum
307
- end
308
-
309
- end
310
-
311
- module Helper
312
-
313
- def self.extractKey(encrypted_data, b64_enc_key)
314
- # passphrase is in plain text
315
- # encrypted_data is in base64, as it was stored on Block.io
316
- # returns the private key extracted from the given encrypted data
317
-
318
- decrypted = self.decrypt(encrypted_data, b64_enc_key)
319
-
320
- return Key.from_passphrase(decrypted)
321
- end
322
-
323
- def self.sha256(value)
324
- # returns the hex of the hash of the given value
325
- hash = Digest::SHA2.new(256)
326
- hash << value
327
- hash.hexdigest # return hex
328
- end
329
-
330
- def self.pinToAesKey(secret_pin, iterations = 2048)
331
- # converts the pincode string to PBKDF2
332
- # returns a base64 version of PBKDF2 pincode
333
- salt = ""
334
-
335
- # pbkdf2-ruby gem uses SHA256 as the default hash function
336
- aes_key_bin = PBKDF2.new(:password => secret_pin, :salt => salt, :iterations => iterations/2, :key_length => 128/8).value
337
- aes_key_bin = PBKDF2.new(:password => aes_key_bin.unpack("H*")[0], :salt => salt, :iterations => iterations/2, :key_length => 256/8).value
338
-
339
- return Base64.strict_encode64(aes_key_bin) # the base64 encryption key
340
- end
341
-
342
- # Decrypts a block of data (encrypted_data) given an encryption key
343
- def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
344
-
345
- response = nil
346
-
347
- begin
348
- aes = OpenSSL::Cipher::Cipher.new(cipher_type)
349
- aes.decrypt
350
- aes.key = Base64.strict_decode64(b64_enc_key)
351
- aes.iv = iv if iv != nil
352
- response = aes.update(Base64.strict_decode64(encrypted_data)) + aes.final
353
- rescue Exception => e
354
- # decryption failed, must be an invalid Secret PIN
355
- raise Exception.new('Invalid Secret PIN provided.')
356
- end
357
-
358
- return response
359
- end
360
-
361
- # Encrypts a block of data given an encryption key
362
- def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
363
- aes = OpenSSL::Cipher::Cipher.new(cipher_type)
364
- aes.encrypt
365
- aes.key = Base64.strict_decode64(b64_enc_key)
366
- aes.iv = iv if iv != nil
367
- Base64.strict_encode64(aes.update(data) + aes.final)
368
- end
21
+ end
369
22
 
370
- # courtesy bitcoin-ruby
371
-
372
- def self.int_to_base58(int_val, leading_zero_bytes=0)
373
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
374
- base58_val, base = '', alpha.size
375
- while int_val > 0
376
- int_val, remainder = int_val.divmod(base)
377
- base58_val = alpha[remainder] + base58_val
378
- end
379
- base58_val
380
- end
381
-
382
- def self.base58_to_int(base58_val)
383
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
384
- int_val, base = 0, alpha.size
385
- base58_val.reverse.each_char.with_index do |char,index|
386
- raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
387
- int_val += char_index*(base**index)
388
- end
389
- int_val
390
- end
391
-
392
- def self.encode_base58(hex)
393
- leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
394
- ("1"*leading_zero_bytes) + Helper.int_to_base58( hex.to_i(16) )
395
- end
396
-
397
- def self.decode_base58(base58_val)
398
- s = Helper.base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
399
- s = '' if s == '00'
400
- leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
401
- s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
402
- s
403
- end
404
- end
405
23
 
406
- end
@@ -0,0 +1,11 @@
1
+ module BlockIo
2
+ class APIException < Exception
3
+
4
+ attr_reader :raw_data
5
+
6
+ def set_raw_data(data)
7
+ @raw_data = data
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "BTC"
3
+ address_version: "00"
4
+ p2sh_version: "05"
5
+ bech32_hrp: 'bc'
6
+ privkey_version: "80"
7
+ extended_privkey_version: "0488ade4"
8
+ extended_pubkey_version: "0488b21e"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "BTCTEST"
3
+ address_version: "6f"
4
+ p2sh_version: "c4"
5
+ bech32_hrp: 'tb'
6
+ privkey_version: "ef"
7
+ extended_privkey_version: "04358394"
8
+ extended_pubkey_version: "043587cf"