btcruby 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.travis.yml +7 -0
  4. data/FAQ.md +7 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +18 -0
  7. data/HOWTO.md +17 -0
  8. data/LICENSE +19 -0
  9. data/README.md +59 -0
  10. data/Rakefile +6 -0
  11. data/TODO.txt +40 -0
  12. data/bin/console +19 -0
  13. data/btcruby.gemspec +20 -0
  14. data/documentation/address.md +73 -0
  15. data/documentation/base58.md +52 -0
  16. data/documentation/block.md +127 -0
  17. data/documentation/block_header.md +120 -0
  18. data/documentation/constants.md +88 -0
  19. data/documentation/data.md +54 -0
  20. data/documentation/diagnostics.md +90 -0
  21. data/documentation/extensions.md +76 -0
  22. data/documentation/hash_functions.md +58 -0
  23. data/documentation/hash_id.md +22 -0
  24. data/documentation/index.md +230 -0
  25. data/documentation/key.md +177 -0
  26. data/documentation/keychain.md +180 -0
  27. data/documentation/network.md +75 -0
  28. data/documentation/opcode.md +220 -0
  29. data/documentation/openssl.md +7 -0
  30. data/documentation/p2pkh.md +71 -0
  31. data/documentation/p2sh.md +64 -0
  32. data/documentation/proof_of_work.md +84 -0
  33. data/documentation/script.md +280 -0
  34. data/documentation/signature.md +71 -0
  35. data/documentation/transaction.md +213 -0
  36. data/documentation/transaction_builder.md +188 -0
  37. data/documentation/transaction_input.md +133 -0
  38. data/documentation/transaction_output.md +130 -0
  39. data/documentation/wif.md +72 -0
  40. data/documentation/wire_format.md +70 -0
  41. data/lib/btcruby/address.rb +296 -0
  42. data/lib/btcruby/base58.rb +108 -0
  43. data/lib/btcruby/big_number.rb +47 -0
  44. data/lib/btcruby/block.rb +170 -0
  45. data/lib/btcruby/block_header.rb +231 -0
  46. data/lib/btcruby/constants.rb +59 -0
  47. data/lib/btcruby/currency_formatter.rb +64 -0
  48. data/lib/btcruby/data.rb +98 -0
  49. data/lib/btcruby/diagnostics.rb +92 -0
  50. data/lib/btcruby/errors.rb +8 -0
  51. data/lib/btcruby/extensions.rb +65 -0
  52. data/lib/btcruby/hash_functions.rb +54 -0
  53. data/lib/btcruby/hash_id.rb +18 -0
  54. data/lib/btcruby/key.rb +517 -0
  55. data/lib/btcruby/keychain.rb +464 -0
  56. data/lib/btcruby/network.rb +73 -0
  57. data/lib/btcruby/opcode.rb +197 -0
  58. data/lib/btcruby/open_assets/asset.rb +35 -0
  59. data/lib/btcruby/open_assets/asset_address.rb +49 -0
  60. data/lib/btcruby/open_assets/asset_definition.rb +75 -0
  61. data/lib/btcruby/open_assets/asset_id.rb +24 -0
  62. data/lib/btcruby/open_assets/asset_marker.rb +94 -0
  63. data/lib/btcruby/open_assets/asset_processor.rb +377 -0
  64. data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
  65. data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
  66. data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
  67. data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
  68. data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
  69. data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
  70. data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
  71. data/lib/btcruby/open_assets.rb +26 -0
  72. data/lib/btcruby/openssl.rb +536 -0
  73. data/lib/btcruby/proof_of_work.rb +110 -0
  74. data/lib/btcruby/safety.rb +26 -0
  75. data/lib/btcruby/script.rb +733 -0
  76. data/lib/btcruby/signature_hashtype.rb +37 -0
  77. data/lib/btcruby/transaction.rb +511 -0
  78. data/lib/btcruby/transaction_builder/errors.rb +15 -0
  79. data/lib/btcruby/transaction_builder/provider.rb +54 -0
  80. data/lib/btcruby/transaction_builder/result.rb +73 -0
  81. data/lib/btcruby/transaction_builder/signer.rb +28 -0
  82. data/lib/btcruby/transaction_builder.rb +520 -0
  83. data/lib/btcruby/transaction_input.rb +298 -0
  84. data/lib/btcruby/transaction_outpoint.rb +30 -0
  85. data/lib/btcruby/transaction_output.rb +315 -0
  86. data/lib/btcruby/version.rb +3 -0
  87. data/lib/btcruby/wif.rb +118 -0
  88. data/lib/btcruby/wire_format.rb +362 -0
  89. data/lib/btcruby.rb +44 -2
  90. data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
  91. data/sample_code/creating_a_transaction_manually.rb +44 -0
  92. data/sample_code/generating_an_address.rb +20 -0
  93. data/sample_code/using_transaction_builder.rb +49 -0
  94. data/spec/address_spec.rb +206 -0
  95. data/spec/all.rb +6 -0
  96. data/spec/base58_spec.rb +83 -0
  97. data/spec/block_header_spec.rb +18 -0
  98. data/spec/block_spec.rb +18 -0
  99. data/spec/currency_formatter_spec.rb +46 -0
  100. data/spec/data_spec.rb +50 -0
  101. data/spec/diagnostics_spec.rb +41 -0
  102. data/spec/key_spec.rb +205 -0
  103. data/spec/keychain_spec.rb +261 -0
  104. data/spec/network_spec.rb +48 -0
  105. data/spec/open_assets/asset_address_spec.rb +33 -0
  106. data/spec/open_assets/asset_id_spec.rb +15 -0
  107. data/spec/open_assets/asset_marker_spec.rb +47 -0
  108. data/spec/open_assets/asset_processor_spec.rb +567 -0
  109. data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
  110. data/spec/open_assets/asset_transaction_spec.rb +70 -0
  111. data/spec/proof_of_work_spec.rb +53 -0
  112. data/spec/script_spec.rb +66 -0
  113. data/spec/spec_helper.rb +8 -0
  114. data/spec/transaction_builder_spec.rb +338 -0
  115. data/spec/transaction_spec.rb +162 -0
  116. data/spec/wire_format_spec.rb +283 -0
  117. metadata +141 -7
@@ -0,0 +1,517 @@
1
+ # Key encapsulates EC public and private keypair (or only public part) on curve secp256k1.
2
+ # You can sign data and verify signatures.
3
+ # When instantiated with a public key, only signature verification is possible.
4
+ # When instantiated with a private key, all operations are available.
5
+ module BTC
6
+ class Key
7
+
8
+ # Flag specifying if the public key should be compressed.
9
+ # Default is true.
10
+ attr_reader :public_key_compressed
11
+
12
+ # Returns a copy of BTC::Key instance with public_key_compressed == true
13
+ attr_reader :compressed_key
14
+
15
+ # Returns a copy of BTC::Key instance with public_key_compressed == false
16
+ attr_reader :uncompressed_key
17
+
18
+ # A binary string containing a private key. Returns nil if there is only public key.
19
+ attr_reader :private_key
20
+
21
+ # A binary string containing compressed or uncompressed public key (depends on public_key_compressed flag).
22
+ attr_reader :public_key
23
+
24
+ # A binary string containing a compressed public key.
25
+ attr_reader :compressed_public_key
26
+
27
+ # A binary string containing an uncompressed public key.
28
+ attr_reader :uncompressed_public_key
29
+
30
+ # A network to which this key belongs.
31
+ # Affects how addresses and WIFs are formatted.
32
+ # Default is BTC::Network.default (mainnet if not overriden).
33
+ attr_accessor :network
34
+
35
+ COMPRESSED_PUBKEY_LENGTH = 33
36
+ UNCOMPRESSED_PUBKEY_LENGTH = 65
37
+
38
+ # Initializes a key with one of the given keys (public or private).
39
+ # Usage:
40
+ # * Key.new(private_key: ...[, public_key_compressed: ...][, network: ...])
41
+ # * Key.new(public_key: ...[, network: ...])
42
+ # * Key.new(wif: ...)
43
+ def initialize(private_key: nil,
44
+ public_key: nil,
45
+ public_key_compressed: true,
46
+ wif: nil,
47
+ network: nil)
48
+
49
+ @public_key_compressed = public_key_compressed
50
+ @network = network || BTC::Network.default
51
+
52
+ if private_key
53
+ if !Key.validate_private_key_range(private_key)
54
+ raise FormatError, "Private key is outside the valid range"
55
+ end
56
+ @private_key = private_key
57
+ elsif public_key
58
+ if !Key.valid_pubkey?(public_key)
59
+ raise FormatError, "Invalid public key: #{public_key.inspect}"
60
+ end
61
+ @public_key_compressed = (self.class.length_for_pubkey(public_key) == COMPRESSED_PUBKEY_LENGTH)
62
+ @public_key = public_key
63
+ elsif wif
64
+ wif = wif.is_a?(WIF) ? wif : Address.parse(wif)
65
+ if !wif.is_a?(WIF)
66
+ raise FormatError, "Invalid WIF string"
67
+ end
68
+ key = wif.key
69
+ @private_key = key.private_key
70
+ @public_key = key.public_key
71
+ @public_key_compressed = key.public_key_compressed
72
+ @network = wif.network
73
+ else
74
+ raise ArgumentError, "Must specify either private_key or public_key"
75
+ end
76
+ end
77
+
78
+ # Creates a randomly-generated key pair.
79
+ def self.random(public_key_compressed: true, network: nil)
80
+ # Chances that we'll enter the loop are below 2^-127.
81
+ privkey = BTC::Data.random_data(32)
82
+ while !self.validate_private_key_range(privkey)
83
+ privkey = BTC::Data.random_data(32)
84
+ end
85
+ return self.new(private_key: privkey,
86
+ public_key_compressed: public_key_compressed,
87
+ network: network)
88
+ end
89
+
90
+ # Creates an instance with public key data (compressed or not).
91
+ # You can verify signatures with that instance, but cannot sign messages.
92
+ # If the public key is compressed, public_key_compressed attribute will be set to true.
93
+ def self.with_public_key(public_key, network: nil)
94
+ raise ArgumentError, "Use Key.new(public_key: ...) instead"
95
+ end
96
+
97
+ # Creates an instance with private key data (32 bytes).
98
+ # You can sign messages and verify signatures with that instance.
99
+ # public_key_compressed is set to true as a default.
100
+ def self.with_private_key(private_key, public_key_compressed: true, network: nil)
101
+ raise ArgumentError, "Use Key.new(private_key: ...) instead"
102
+ end
103
+
104
+ # Creates an instance with a private key encoded in WIF format.
105
+ # Uses WIF internally.
106
+ def self.with_wif(wif_string)
107
+ raise ArgumentError, "Use Key.new(wif: ...) instead"
108
+ end
109
+
110
+
111
+ # Accessors
112
+ # ---------
113
+
114
+ def network
115
+ @network || BTC::Network.default
116
+ end
117
+
118
+ def compressed_key
119
+ self.class.new(private_key: @private_key,
120
+ public_key: self.compressed_public_key,
121
+ public_key_compressed: true,
122
+ network: @network)
123
+ end
124
+
125
+ def uncompressed_key
126
+ self.class.new(private_key: @private_key,
127
+ public_key: self.uncompressed_public_key,
128
+ public_key_compressed: false,
129
+ network: @network)
130
+ end
131
+
132
+ def public_key
133
+ if !@public_key
134
+ regenerate_key_pair
135
+ end
136
+ @public_key
137
+ end
138
+
139
+ def compressed_public_key
140
+ BTC::OpenSSL.public_key_with_compression(self.public_key, true)
141
+ end
142
+
143
+ def uncompressed_public_key
144
+ BTC::OpenSSL.public_key_with_compression(self.public_key, false)
145
+ end
146
+
147
+ # Returns a PublicKeyAddress instance that encodes a public key hash.
148
+ def address(network: nil)
149
+ PublicKeyAddress.new(public_key: self.public_key, network: network)
150
+ end
151
+
152
+ # Returns a WIF instance that encodes private key.
153
+ def to_wif_object(network: nil)
154
+ return nil if !self.private_key
155
+ WIF.new(key: self, network: network)
156
+ end
157
+
158
+ # Returns private key encoded in WIF format (aka Sipa format).
159
+ def to_wif(network: nil)
160
+ return nil if !self.private_key
161
+ self.to_wif_object(network: network).to_s
162
+ end
163
+
164
+ def dup
165
+ self.class.new(
166
+ private_key: @private_key,
167
+ public_key: @public_key,
168
+ public_key_compressed: @public_key_compressed,
169
+ network: @network)
170
+ end
171
+
172
+ # Two keypairs are equal only when they are equally complete (both with or
173
+ # without a private key), have matching keys and compression.
174
+ def ==(other)
175
+ self.public_key == other.public_key &&
176
+ self.private_key == other.private_key
177
+ end
178
+ alias_method :eql?, :==
179
+
180
+ # Multiplies a public key of the receiver with a given private key and
181
+ # returns resulting curve point as BTC::Key object (pubkey only).
182
+ # Pubkey compression flag is the same as on receiver.
183
+ def diffie_hellman(key2)
184
+
185
+ lib = BTC::OpenSSL
186
+ lib.autorelease do |pool|
187
+
188
+ pk = pool.new_bn(key2.private_key)
189
+ n = lib.group_order
190
+
191
+ # Convert pubkey to a EC point
192
+ pubkey_x = pool.new_bn(self.compressed_public_key)
193
+ pubkey_point = pool.new_ec_point
194
+ lib.EC_POINT_bn2point(lib.group, pubkey_x, pubkey_point, pool.bn_ctx)
195
+
196
+ # Compute point = pubkey*pk + 0*G
197
+ point = pool.new_ec_point
198
+ # /** Computes r = generator * n + q * m
199
+ # * \param group underlying EC_GROUP object
200
+ # * \param r EC_POINT object for the result
201
+ # * \param n BIGNUM with the multiplier for the group generator (optional)
202
+ # * \param q EC_POINT object with the first factor of the second summand
203
+ # * \param m BIGNUM with the second factor of the second summand
204
+ # * \param ctx BN_CTX object (optional)
205
+ # * \return 1 on success and 0 if an error occured
206
+ # */
207
+ # int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
208
+ lib.EC_POINT_mul(lib.group, point, nil, pubkey_point, pk, pool.bn_ctx)
209
+
210
+ # Check for invalid derivation.
211
+ if 1 == lib.EC_POINT_is_at_infinity(lib.group, point)
212
+ raise MathError, "Resulting point is at infinity."
213
+ end
214
+
215
+ lib.EC_POINT_point2bn(
216
+ lib.group,
217
+ point,
218
+ self.public_key_compressed ?
219
+ BTC::OpenSSL::POINT_CONVERSION_COMPRESSED :
220
+ BTC::OpenSSL::POINT_CONVERSION_UNCOMPRESSED,
221
+ pubkey_x,
222
+ pool.bn_ctx
223
+ )
224
+
225
+ result_pubkey = lib.data_from_bn(pubkey_x, required_length: 33)
226
+ return Key.new(public_key: result_pubkey)
227
+ end
228
+ end
229
+
230
+
231
+
232
+ # Signatures
233
+ # ----------
234
+
235
+
236
+ # Standard ECDSA signature for a given hash. Used by OP_CHECKSIG and friends.
237
+ # Ensures canonical lower S value and makes a deterministic signature
238
+ # (k = HMAC-SHA256(key: privkey, data: hash))
239
+ def ecdsa_signature(hash, normalized: true)
240
+ BTC::OpenSSL.ecdsa_signature(hash, @private_key, normalized: normalized)
241
+ end
242
+
243
+ # Returns true if ECDSA signature is valid for a given hash
244
+ def verify_ecdsa_signature(signature, hash)
245
+ BTC::OpenSSL.ecdsa_verify(signature, hash, self.public_key)
246
+ end
247
+
248
+ def self.normalized_signature(signature)
249
+ BTC::OpenSSL.ecdsa_normalized_signature(signature)
250
+ end
251
+
252
+ # Validates and normalizes script signature to make it canonical.
253
+ # Note: signature must have hashtype byte appended.
254
+ # Returns nil if signature is invalid and cannot be normalized.
255
+ # Returns original signature if it is canonical.
256
+ # Returns normalized signature script if signature can be normalized.
257
+ def self.validate_and_normalize_script_signature(data)
258
+ raise ArgumentError, "Missing script signature data" if !data || data.size == 0
259
+ if validate_script_signature(data)
260
+ return data
261
+ end
262
+ data = BTC::Data.ensure_binary_encoding(data)
263
+ normalized_sig = normalized_signature(data[0, data.size-1])
264
+ return nil if !normalized_sig
265
+ return normalized_sig + data[data.size-1, 1]
266
+ end
267
+
268
+ # Checks if this signature with appended script hash type is well-formed.
269
+ # Logs detailed info using Diagnostics and returns true or false.
270
+ # Set verify_lower_s:false when processing incoming blocks.
271
+ def self.validate_script_signature(data, verify_lower_s: true)
272
+ # See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
273
+ # A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
274
+ # Where R and S are not negative (their first byte has its highest bit not set), and not
275
+ # excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
276
+ # in which case a single 0 byte is necessary and even required).
277
+
278
+ raise ArgumentError, "Missing script signature data" if !data
279
+
280
+ data = BTC::Data.ensure_binary_encoding(data) # so we can use #[] on byte level.
281
+
282
+ length = data.bytesize
283
+
284
+ # Non-canonical signature: too short
285
+ if length < 9
286
+ Diagnostics.current.add_message("Non-canonical signature: too short.")
287
+ return false
288
+ end
289
+
290
+ # Non-canonical signature: too long
291
+ if length > 73
292
+ Diagnostics.current.add_message("Non-canonical signature: too long.")
293
+ return false
294
+ end
295
+
296
+ bytes = data.bytes
297
+
298
+ hashtype = bytes[length - 1] & (~(SIGHASH_ANYONECANPAY))
299
+
300
+ if hashtype < SIGHASH_ALL || hashtype > SIGHASH_SINGLE
301
+ Diagnostics.current.add_message("Non-canonical signature: unknown hashtype byte.")
302
+ return false
303
+ end
304
+
305
+ if bytes[0] != 0x30
306
+ Diagnostics.current.add_message("Non-canonical signature: wrong type.")
307
+ return false
308
+ end
309
+
310
+ if bytes[1] != length-3
311
+ Diagnostics.current.add_message("Non-canonical signature: wrong length marker.")
312
+ return false
313
+ end
314
+
315
+ lenR = bytes[3]
316
+
317
+ if (5 + lenR) >= length
318
+ Diagnostics.current.add_message("Non-canonical signature: S length misplaced.")
319
+ return false
320
+ end
321
+
322
+ lenS = bytes[5 + lenR]
323
+
324
+ if (lenR + lenS + 7) != length
325
+ Diagnostics.current.add_message("Non-canonical signature: R+S length mismatch")
326
+ return false
327
+ end
328
+
329
+ bufR = bytes[4, lenR]
330
+ if bytes[4 - 2] != 0x02
331
+ Diagnostics.current.add_message("Non-canonical signature: R value type mismatch")
332
+ return false
333
+ end
334
+
335
+ if lenR == 0
336
+ Diagnostics.current.add_message("Non-canonical signature: R length is zero")
337
+ return false
338
+ end
339
+
340
+ if bufR[0] & 0x80 != 0
341
+ Diagnostics.current.add_message("Non-canonical signature: R value negative")
342
+ return false
343
+ end
344
+
345
+ if lenR > 1 && (bufR[0] == 0x00) && ((bufR[1] & 0x80) == 0)
346
+ Diagnostics.current.add_message("Non-canonical signature: R value excessively padded")
347
+ return false
348
+ end
349
+
350
+ bufS = bytes[6 + lenR, lenS]
351
+ s = data[6 + lenR, lenS]
352
+ if bytes[6 + lenR - 2] != 0x02
353
+ Diagnostics.current.add_message("Non-canonical signature: S value type mismatch")
354
+ return false
355
+ end
356
+
357
+ if lenS == 0
358
+ Diagnostics.current.add_message("Non-canonical signature: S length is zero")
359
+ return false
360
+ end
361
+
362
+ if bufS[0] & 0x80 != 0
363
+ return false
364
+ Diagnostics.current.add_message("Non-canonical signature: S value is negative")
365
+ end
366
+
367
+ if lenS > 1 && (bufS[0] == 0x00) && ((bufS[1] & 0x80) == 0)
368
+ Diagnostics.current.add_message("Non-canonical signature: S value excessively padded")
369
+ return false
370
+ end
371
+
372
+ if verify_lower_s
373
+ if !self_validate_signature_element(s, check_half: true)
374
+ Diagnostics.current.add_message("Non-canonical signature: S value is unnecessarily high")
375
+ return false
376
+ end
377
+ end
378
+
379
+ return true
380
+ end
381
+
382
+ # Zero-filled 32-byte buffer
383
+ KEY_ZERO = "\x00"*32
384
+
385
+ # Order of secp256k1's generator minus 1.
386
+ KEY_MAX_MOD_ORDER =("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +
387
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE" +
388
+ "\xBA\xAE\xDC\xE6\xAF\x48\xA0\x3B" +
389
+ "\xBF\xD2\x5E\x8C\xD0\x36\x41\x40").b.freeze
390
+
391
+ # Half of the order of secp256k1's generator minus 1.
392
+ KEY_MAX_MOD_HALF_ORDER =("\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +
393
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" +
394
+ "\x5D\x57\x6E\x73\x57\xA4\x50\x1D" +
395
+ "\xDF\xE9\x2F\x46\x68\x1B\x20\xA0").b.freeze
396
+
397
+ # Private helper to compare two big numbers in big-endian notation.
398
+ # Higher byte has higher value, but strings can be of different length.
399
+ def self.compare_big_endian(s1, s2)
400
+ s1 = BTC::Data.ensure_binary_encoding(s1)
401
+ s2 = BTC::Data.ensure_binary_encoding(s2)
402
+
403
+ if s1.bytesize < s2.bytesize
404
+ s1 = "\x00"*(s2.bytesize - s1.bytesize) + s1
405
+ end
406
+
407
+ if s2.bytesize < s1.bytesize
408
+ s2 = "\x00"*(s1.bytesize - s2.bytesize) + s2
409
+ end
410
+
411
+ s1 <=> s2
412
+ end
413
+
414
+ # Private helper to validate portion of a signate. Follows style of bitcoind.
415
+ def self.self_validate_signature_element(data, check_half: false)
416
+ return self.compare_big_endian(data, KEY_ZERO) > 0 &&
417
+ self.compare_big_endian(data, check_half ? KEY_MAX_MOD_HALF_ORDER : KEY_MAX_MOD_ORDER) <= 0
418
+ end
419
+
420
+ # Returns true if data representing a private key is within a valid range.
421
+ def self.validate_private_key_range(private_key)
422
+ return self.compare_big_endian(private_key, KEY_ZERO) > 0 &&
423
+ self.compare_big_endian(private_key, KEY_MAX_MOD_ORDER) <= 0
424
+ end
425
+
426
+ # Checks if this public key is valid and well-formed.
427
+ # Logs detailed info using Diagnostics and returns true or false.
428
+ def self.validate_public_key(data)
429
+ raise ArgumentError, "Missing public key" if !data
430
+
431
+ length = data.bytesize
432
+
433
+ # Non-canonical public key: too short
434
+ if length < 33
435
+ Diagnostics.current.add_message("Non-canonical public key: too short.")
436
+ return false
437
+ end
438
+
439
+ bytes = data.bytes
440
+
441
+ if bytes[0] == 0x04
442
+ # Length of an uncompressed key must be 65 bytes.
443
+ return true if length == 65
444
+ Diagnostics.current.add_message("Non-canonical public key: length of uncompressed key must be 65 bytes.")
445
+ return false
446
+ elsif bytes[0] == 0x02 || bytes[0] == 0x03
447
+ # Length of compressed key must be 33 bytes.
448
+ return true if length == 33
449
+ Diagnostics.current.add_message("Non-canonical public key: length of compressed key must be 33 bytes.")
450
+ return false
451
+ end
452
+
453
+ # Unknown public key format.
454
+ Diagnostics.current.add_message("Unknown non-canonical public key.")
455
+ return false
456
+ end
457
+
458
+ # Non-standard "compact" signature used for Bitcoin signed messages.
459
+ # It features fixed length and allows efficient extraction of a public key from it.
460
+ def compact_signature(hash)
461
+ raise BTCError, "Not implemented"
462
+ end
463
+
464
+ # Returns true if compact signature is valid for a given hash
465
+ def verify_compact_signature(signature, hash)
466
+ raise BTCError, "Not implemented"
467
+ end
468
+
469
+ # Compact signature for a given message. Prepends it with standard prefix
470
+ # "\x18Bitcoin Signed Message:\n" and encodes message in wire format.
471
+ def message_signature(message)
472
+ raise BTCError, "Not implemented"
473
+ end
474
+
475
+ def verify_message_signature(signature, message)
476
+ raise BTCError, "Not implemented"
477
+ end
478
+
479
+
480
+
481
+
482
+ protected
483
+
484
+
485
+ # Helpers
486
+
487
+ def regenerate_key_pair
488
+ if @private_key
489
+ privkey, pubkey = BTC::OpenSSL.regenerate_keypair(@private_key, public_key_compressed: @public_key_compressed)
490
+ if privkey && pubkey
491
+ @private_key = privkey
492
+ @public_key = pubkey
493
+ end
494
+ end
495
+ self
496
+ end
497
+
498
+ def self.length_for_pubkey(data)
499
+ return 0 if data.bytesize == 0
500
+ header = data.bytes[0];
501
+ if header == 2 || header == 3
502
+ return COMPRESSED_PUBKEY_LENGTH
503
+ end
504
+ if header == 4 || header == 6 || header == 7
505
+ return UNCOMPRESSED_PUBKEY_LENGTH
506
+ end
507
+ return 0
508
+ end
509
+
510
+ def self.valid_pubkey?(data)
511
+ raise ArgumentError, "Pubkey is missing" if !data
512
+ length = data.bytesize
513
+ return length > 0 && self.length_for_pubkey(data) == length
514
+ end
515
+
516
+ end
517
+ end