block_io 1.2.1 → 3.0.2

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 +4 -4
  2. data/.appveyor.yml-disabled +26 -0
  3. data/.gitignore +2 -1
  4. data/.rspec +1 -0
  5. data/.travis.yml +14 -0
  6. data/LICENSE +1 -1
  7. data/README.md +24 -15
  8. data/block_io.gemspec +9 -7
  9. data/examples/basic.rb +32 -10
  10. data/examples/dtrust.rb +59 -42
  11. data/examples/proxy.rb +36 -0
  12. data/examples/sweeper.rb +24 -14
  13. data/lib/block_io.rb +16 -411
  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 +270 -44
  107. data/examples/change.rb +0 -117
data/lib/block_io.rb CHANGED
@@ -1,418 +1,23 @@
1
- require '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'
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"
10
14
 
11
15
  module BlockIo
12
16
 
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
- # make sure pins don't get passed inadvertently
63
- args.delete(:pin)
64
- args.delete('pin')
65
-
66
- params = get_params(args)
67
-
68
- response = self.api_call([method_name, params])
69
-
70
- if response['data'].has_key?('reference_id') then
71
- # Block.io's asking us to provide some client-side signatures, let's get to it
72
-
73
- # extract the passphrase
74
- encrypted_passphrase = response['data']['encrypted_passphrase']['passphrase']
75
-
76
- # let's get our private key
77
- key = Helper.extractKey(encrypted_passphrase, @encryptionKey)
78
-
79
- 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']
80
-
81
- # let's sign all the inputs we can
82
- inputs = response['data']['inputs']
83
-
84
- Helper.signData(inputs, [key])
85
-
86
- # the response object is now signed, let's stringify it and finalize this withdrawal
87
- response = self.api_call(['sign_and_finalize_withdrawal',{:signature_data => Oj.dump(response['data'])}])
88
-
89
- # if we provided all the required signatures, this transaction went through
90
- # otherwise Block.io responded with data asking for more signatures
91
- # the latter will be the case for dTrust addresses
92
- end
93
-
94
- return response
95
-
17
+ def self.version
18
+ BlockIo::VERSION
96
19
  end
97
-
98
- def self.sweep(args = {}, method_name = 'sweep_from_address')
99
- # sweep coins from a given address + key
100
-
101
- raise Exception.new("No private_key provided.") unless args.has_key?(:private_key)
102
-
103
- key = Key.from_wif(args[:private_key])
104
-
105
- args[:public_key] = key.public_key # so Block.io can match things up
106
- args.delete(:private_key) # the key must never leave this machine
107
-
108
- params = get_params(args)
109
-
110
- response = self.api_call([method_name, params])
111
-
112
- if response['data'].has_key?('reference_id') then
113
- # Block.io's asking us to provide some client-side signatures, let's get to it
114
-
115
- # let's sign all the inputs we can
116
- inputs = response['data']['inputs']
117
- Helper.signData(inputs, [key])
118
-
119
- # the response object is now signed, let's stringify it and finalize this withdrawal
120
- response = self.api_call(['sign_and_finalize_sweep',{:signature_data => Oj.dump(response['data'])}])
121
-
122
- # if we provided all the required signatures, this transaction went through
123
- # otherwise Block.io responded with data asking for more signatures
124
- # the latter will be the case for dTrust addresses
125
- end
126
-
127
- return response
128
-
129
- end
130
-
131
-
132
- private
133
20
 
134
- def self.api_call(endpoint)
135
-
136
- body = nil
137
-
138
- response = @client.post("#{@base_url.gsub('API_CALL',endpoint[0]).gsub('VERSION', 'v'+@version.to_s) + @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
21
+ end
381
22
 
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
23
 
418
- 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"