block_io 1.0.8 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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"