block_io 1.0.8 → 3.0.1

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 (107) 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 +23 -13
  8. data/block_io.gemspec +9 -9
  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 -408
  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 +244 -0
  22. data/lib/block_io/extended_bitcoinrb.rb +127 -0
  23. data/lib/block_io/helper.rb +322 -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 +154 -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 +264 -73
  107. data/examples/change.rb +0 -117
data/lib/block_io.rb CHANGED
@@ -1,415 +1,23 @@
1
- require 'block_io/version'
2
- require 'httpclient'
3
- require 'oj'
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: 1, timeout: 300) { h = HTTPClient.new; h.tcp_keepalive = true; h.ssl_config.ssl_version = :auto; h }
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
- Helper.signData(inputs, [key])
78
-
79
- # the response object is now signed, let's stringify it and finalize this withdrawal
80
- response = self.api_call(['sign_and_finalize_withdrawal',{:signature_data => Oj.dump(response['data'])}])
81
-
82
- # if we provided all the required signatures, this transaction went through
83
- # otherwise Block.io responded with data asking for more signatures
84
- # the latter will be the case for dTrust addresses
85
- end
86
-
87
- return response
88
-
89
- end
90
-
91
- def self.sweep(args = {}, method_name = 'sweep_from_address')
92
- # sweep coins from a given address + key
93
-
94
- raise Exception.new("No private_key provided.") unless args.has_key?(:private_key)
95
-
96
- key = Key.from_wif(args[:private_key])
97
-
98
- args[:public_key] = key.public_key # so Block.io can match things up
99
- args.delete(:private_key) # the key must never leave this machine
100
-
101
- params = get_params(args)
102
-
103
- response = self.api_call([method_name, params])
104
-
105
- if response['data'].has_key?('reference_id') then
106
- # Block.io's asking us to provide some client-side signatures, let's get to it
107
-
108
- # let's sign all the inputs we can
109
- inputs = response['data']['inputs']
110
- Helper.signData(inputs, [key])
111
-
112
- # the response object is now signed, let's stringify it and finalize this withdrawal
113
- response = self.api_call(['sign_and_finalize_sweep',{:signature_data => Oj.dump(response['data'])}])
114
-
115
- # if we provided all the required signatures, this transaction went through
116
- # otherwise Block.io responded with data asking for more signatures
117
- # the latter will be the case for dTrust addresses
118
- end
119
-
120
- return response
121
-
17
+ def self.version
18
+ BlockIo::VERSION
122
19
  end
123
-
124
-
125
- private
126
20
 
127
- def self.api_call(endpoint)
128
-
129
- body = nil
130
-
131
- @conn_pool.with do |hc|
132
- # prevent initiation of HTTPClients every time we make this call, use a connection_pool
133
-
134
- response = hc.post("#{@base_url.gsub('API_CALL',endpoint[0]).gsub('VERSION', 'v'+@version.to_s) + @api_key}", endpoint[1])
135
-
136
- begin
137
- body = Oj.load(response.body)
138
- raise Exception.new(body['data']['error_message']) if !body['status'].eql?('success')
139
- rescue
140
- raise Exception.new('Unknown error occurred. Please report this.')
141
- end
142
- end
143
-
144
- body
145
- end
146
-
147
- private
148
-
149
- def self.get_params(args = {})
150
- # construct the parameter string
151
- params = ""
152
- args = {} if args.nil?
153
-
154
- args.each do |k,v|
155
- params += '&' if params.length > 0
156
- params += "#{k.to_s}=#{v.to_s}"
157
- end
158
-
159
- return params
160
- end
161
-
162
- public
163
-
164
- class Key
165
-
166
- def initialize(privkey = nil, compressed = true)
167
- # the privkey must be in hex if at all provided
168
-
169
- @group = ECDSA::Group::Secp256k1
170
- @private_key = privkey.to_i(16) || 1 + SecureRandom.random_number(group.order - 1)
171
- @public_key = @group.generator.multiply_by_scalar(@private_key)
172
- @compressed = compressed
173
-
174
- end
175
-
176
- def private_key
177
- # returns private key in hex form
178
- return @private_key.to_s(16)
179
- end
180
-
181
- def public_key
182
- # returns the compressed form of the public key to save network fees (shorter scripts)
183
-
184
- return ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
185
- end
186
-
187
- def sign(data)
188
- # signed the given hexadecimal string
189
-
190
- nonce = deterministicGenerateK([data].pack("H*"), @private_key) # RFC6979
191
-
192
- signature = ECDSA.sign(@group, @private_key, data.to_i(16), nonce)
193
-
194
- # BIP0062 -- use lower S values only
195
- r, s = signature.components
196
-
197
- over_two = @group.order >> 1 # half of what it was
198
- s = @group.order - s if (s > over_two)
199
-
200
- signature = ECDSA::Signature.new(r, s)
201
-
202
- # DER encode this, and return it in hex form
203
- return ECDSA::Format::SignatureDerString.encode(signature).unpack("H*")[0]
204
- end
205
-
206
- def self.from_passphrase(passphrase)
207
- # create a private+public key pair from a given passphrase
208
- # think of this as your brain wallet. be very sure to use a sufficiently long passphrase
209
- # if you don't want a passphrase, just use Key.new and it will generate a random key for you
210
-
211
- raise Exception.new('Must provide passphrase at least 8 characters long.') if passphrase.nil? or passphrase.length < 8
212
-
213
- hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
214
-
215
- return Key.new(hashed_key)
216
- end
217
-
218
- def self.from_wif(wif)
219
- # returns a new key extracted from the Wallet Import Format provided
220
- # TODO check against checksum
221
-
222
- hexkey = Helper.decode_base58(wif)
223
- actual_key = hexkey[2...66]
224
-
225
- compressed = hexkey[2..hexkey.length].length-8 > 64 and hexkey[2..hexkey.length][64...66] == '01'
226
-
227
- return Key.new(actual_key, compressed)
228
-
229
- end
230
-
231
- def isPositive(i)
232
- sig = "!+-"[i <=> 0]
233
-
234
- return sig.eql?("+")
235
- end
236
-
237
- def deterministicGenerateK(data, privkey, group = ECDSA::Group::Secp256k1)
238
- # returns a deterministic K -- RFC6979
239
-
240
- hash = data.bytes.to_a
241
-
242
- x = [privkey.to_s(16)].pack("H*").bytes.to_a
243
-
244
- k = []
245
- 32.times { k.insert(0, 0) }
246
-
247
- v = []
248
- 32.times { v.insert(0, 1) }
249
-
250
- # step D
251
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).concat(x).concat(hash).pack("C*")).bytes.to_a
252
-
253
- # step E
254
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
255
-
256
- # puts "E: " + v.pack("C*").unpack("H*")[0]
257
-
258
- # step F
259
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([1]).concat(x).concat(hash).pack("C*")).bytes.to_a
260
-
261
- # step G
262
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
263
-
264
- # step H2b (Step H1/H2a ignored)
265
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
266
-
267
- h2b = v.pack("C*").unpack("H*")[0]
268
- tNum = h2b.to_i(16)
269
-
270
- # step H3
271
- while (!isPositive(tNum) or tNum >= group.order) do
272
- # k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k)
273
- k = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), [].concat(v).concat([0]).pack("C*")).bytes.to_a
274
-
275
- # v = crypto.HmacSHA256(v, k)
276
- v = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), k.pack("C*"), v.pack("C*")).bytes.to_a
277
-
278
- # T = BigInteger.fromBuffer(v)
279
- tNum = v.pack("C*").unpack("H*")[0].to_i(16)
280
- end
281
-
282
- return tNum
283
- end
284
-
285
- end
286
-
287
- module Helper
288
-
289
- def self.signData(inputs, keys)
290
- # sign the given data with the given keys
291
- # TODO loop is O(n^3), make it better
292
-
293
- 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
294
-
295
- i = 0
296
- while i < inputs.size do
297
- # iterate over all signers
298
- input = inputs[i]
299
-
300
- j = 0
301
- while j < input['signers'].size do
302
- # if our public key matches this signer's public key, sign the data
303
- signer = inputs[i]['signers'][j]
304
-
305
- k = 0
306
- while k < keys.size do
307
- # sign for each key provided, if we can
308
- key = keys[k]
309
- signer['signed_data'] = key.sign(input['data_to_sign']) if signer['signer_public_key'] == key.public_key
310
- k = k + 1
311
- end
312
-
313
- j = j + 1
314
- end
315
-
316
- i = i + 1
317
- end
318
-
319
- inputs
320
- end
321
-
322
- def self.extractKey(encrypted_data, b64_enc_key)
323
- # passphrase is in plain text
324
- # encrypted_data is in base64, as it was stored on Block.io
325
- # returns the private key extracted from the given encrypted data
326
-
327
- decrypted = self.decrypt(encrypted_data, b64_enc_key)
328
-
329
- return Key.from_passphrase(decrypted)
330
- end
331
-
332
- def self.sha256(value)
333
- # returns the hex of the hash of the given value
334
- hash = Digest::SHA2.new(256)
335
- hash << value
336
- hash.hexdigest # return hex
337
- end
338
-
339
- def self.pinToAesKey(secret_pin, iterations = 2048)
340
- # converts the pincode string to PBKDF2
341
- # returns a base64 version of PBKDF2 pincode
342
- salt = ""
343
-
344
- # pbkdf2-ruby gem uses SHA256 as the default hash function
345
- aes_key_bin = PBKDF2.new(:password => secret_pin, :salt => salt, :iterations => iterations/2, :key_length => 128/8).value
346
- aes_key_bin = PBKDF2.new(:password => aes_key_bin.unpack("H*")[0], :salt => salt, :iterations => iterations/2, :key_length => 256/8).value
347
-
348
- return Base64.strict_encode64(aes_key_bin) # the base64 encryption key
349
- end
350
-
351
- # Decrypts a block of data (encrypted_data) given an encryption key
352
- def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
353
-
354
- response = nil
355
-
356
- begin
357
- aes = OpenSSL::Cipher::Cipher.new(cipher_type)
358
- aes.decrypt
359
- aes.key = Base64.strict_decode64(b64_enc_key)
360
- aes.iv = iv if iv != nil
361
- response = aes.update(Base64.strict_decode64(encrypted_data)) + aes.final
362
- rescue Exception => e
363
- # decryption failed, must be an invalid Secret PIN
364
- raise Exception.new('Invalid Secret PIN provided.')
365
- end
366
-
367
- return response
368
- end
369
-
370
- # Encrypts a block of data given an encryption key
371
- def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB')
372
- aes = OpenSSL::Cipher::Cipher.new(cipher_type)
373
- aes.encrypt
374
- aes.key = Base64.strict_decode64(b64_enc_key)
375
- aes.iv = iv if iv != nil
376
- Base64.strict_encode64(aes.update(data) + aes.final)
377
- end
21
+ end
378
22
 
379
- # courtesy bitcoin-ruby
380
-
381
- def self.int_to_base58(int_val, leading_zero_bytes=0)
382
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
383
- base58_val, base = '', alpha.size
384
- while int_val > 0
385
- int_val, remainder = int_val.divmod(base)
386
- base58_val = alpha[remainder] + base58_val
387
- end
388
- base58_val
389
- end
390
-
391
- def self.base58_to_int(base58_val)
392
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
393
- int_val, base = 0, alpha.size
394
- base58_val.reverse.each_char.with_index do |char,index|
395
- raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
396
- int_val += char_index*(base**index)
397
- end
398
- int_val
399
- end
400
-
401
- def self.encode_base58(hex)
402
- leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
403
- ("1"*leading_zero_bytes) + Helper.int_to_base58( hex.to_i(16) )
404
- end
405
-
406
- def self.decode_base58(base58_val)
407
- s = Helper.base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
408
- s = '' if s == '00'
409
- leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
410
- s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
411
- s
412
- end
413
- end
414
23
 
415
- 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"