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,536 @@
1
+ require 'ffi'
2
+
3
+ # This is a collection of binding to OpenSSL that are missing in standard library in Ruby 2.0.
4
+ # You need an 'ffi' gem to make it work.
5
+ module BTC
6
+ module OpenSSL
7
+ include ::FFI::Library
8
+ extend self
9
+
10
+ if FFI::Platform.windows?
11
+ ffi_lib 'libeay32', 'ssleay32'
12
+ else
13
+ ffi_lib 'ssl'
14
+ end
15
+
16
+ NID_secp256k1 = 714
17
+
18
+ POINT_CONVERSION_COMPRESSED = 0x02
19
+ POINT_CONVERSION_UNCOMPRESSED = 0x04
20
+
21
+ attach_function :SSL_library_init, [], :int
22
+ attach_function :ERR_load_crypto_strings, [], :void
23
+ attach_function :SSL_load_error_strings, [], :void
24
+ attach_function :RAND_poll, [], :int
25
+
26
+ attach_function :BN_CTX_free, [:pointer], :int
27
+ attach_function :BN_CTX_new, [], :pointer
28
+ attach_function :BN_new, [], :pointer
29
+ attach_function :BN_free, [:pointer], :int
30
+ attach_function :BN_copy, [:pointer, :pointer], :pointer
31
+ attach_function :BN_dup, [:pointer], :pointer
32
+ attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
33
+ attach_function :BN_bn2bin, [:pointer, :pointer], :void
34
+ attach_function :BN_num_bits, [:pointer], :int
35
+ attach_function :BN_cmp, [:pointer, :pointer], :int
36
+ attach_function :BN_set_word, [:pointer, :int], :int
37
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
38
+ attach_function :BN_sub, [:pointer, :pointer, :pointer], :int
39
+ attach_function :BN_div, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
40
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
41
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
42
+ attach_function :BN_mod_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
43
+ attach_function :BN_mod_add_quick, [:pointer, :pointer, :pointer, :pointer], :int
44
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
45
+ attach_function :BN_mul_word, [:pointer, :int], :int
46
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
47
+ attach_function :BN_rshift1, [:pointer, :pointer], :int
48
+
49
+ attach_function :EC_GROUP_new_by_curve_name, [:int], :pointer
50
+ attach_function :EC_GROUP_free, [:pointer], :void
51
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
52
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
53
+ attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
54
+ attach_function :EC_GROUP_get0_generator, [:pointer], :pointer
55
+
56
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
57
+ attach_function :EC_KEY_free, [:pointer], :void
58
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
59
+ attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
60
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
61
+ attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
62
+ attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
63
+
64
+ attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
65
+ attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
66
+ attach_function :o2i_ECPublicKey, [:pointer, :pointer, :long], :pointer
67
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
68
+
69
+ attach_function :EC_POINT_new, [:pointer], :pointer
70
+ attach_function :EC_POINT_free, [:pointer], :int
71
+ attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
72
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
73
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
74
+ attach_function :EC_POINT_copy, [:pointer, :pointer], :int
75
+ attach_function :EC_POINT_get_affine_coordinates_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
76
+ attach_function :EC_POINT_point2bn, [:pointer, :pointer, :int, :pointer, :pointer], :pointer
77
+ attach_function :EC_POINT_bn2point, [:pointer, :pointer, :pointer, :pointer], :pointer
78
+
79
+ attach_function :ECDSA_SIG_new, [], :pointer
80
+ attach_function :ECDSA_SIG_free, [:pointer], :void
81
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
82
+ attach_function :ECDSA_verify, [:int, :pointer, :int, :pointer, :int, :pointer], :int
83
+
84
+ attach_function :i2d_ECDSA_SIG, [:pointer, :pointer], :int
85
+ attach_function :d2i_ECDSA_SIG, [:pointer, :pointer, :long], :pointer
86
+
87
+ def BN_num_bytes(a) # in openssl this is defined by a macro
88
+ (BN_num_bits(a)+7)/8
89
+ end
90
+
91
+ def prepare_if_needed
92
+ if !@prepared_openssl
93
+ SSL_library_init()
94
+ ERR_load_crypto_strings()
95
+ SSL_load_error_strings()
96
+ RAND_poll()
97
+ @prepared_openssl = true
98
+ end
99
+ end
100
+
101
+ def group
102
+ @group ||= EC_GROUP_new_by_curve_name(NID_secp256k1)
103
+ end
104
+
105
+ def group_order
106
+ @group_order ||= begin
107
+ n = BN_new()
108
+ bn_ctx = BN_CTX_new()
109
+ EC_GROUP_get_order(self.group, n, bn_ctx)
110
+ BN_CTX_free(bn_ctx)
111
+ n
112
+ end
113
+ end
114
+
115
+ def group_half_order
116
+ @group_half_order ||= begin
117
+ halforder = BN_new()
118
+ BN_rshift1(halforder, self.group_order)
119
+ halforder
120
+ end
121
+ end
122
+
123
+ # Creates autorelease pool from which various objects can be created.
124
+ # When block returns, pool deallocates all created objects.
125
+ # Available methods on pool instance:
126
+ # - ec_key - last EC_KEY (created lazily if needed)
127
+ # - group - group of the ec_key
128
+ # - bn_ctx - lazily created single instance of BN_CTX
129
+ # - new_ec_key - creates new instance of EC_KEY
130
+ # - new_bn - creates new instance of BIGNUM
131
+ # - new_ec_point - creates new instance of EC_POINT
132
+ def autorelease(&block) # {|pool| }
133
+ prepare_if_needed
134
+ result = nil
135
+ begin
136
+ pool = AutoreleasePool.new
137
+ result = yield(pool)
138
+ ensure
139
+ pool.drain
140
+ end
141
+ result
142
+ end
143
+
144
+ def public_key_with_compression(pubkey, compressed)
145
+ raise ArgumentError, "Public key is missing" if !pubkey
146
+
147
+ autorelease do |pool|
148
+
149
+ eckey = pool.new_ec_key
150
+
151
+ # 1. Load EC_KEY with pubkey binary data.
152
+ buf = FFI::MemoryPointer.from_string(pubkey)
153
+ eckey = o2i_ECPublicKey(pointer_to_pointer(eckey), pointer_to_pointer(buf), buf.size-1)
154
+ if eckey.null?
155
+ raise BTCError, "OpenSSL failed to create EC_KEY with public key: #{BTC::Data.hex_from_data(pubkey).inspect}"
156
+ end
157
+
158
+ # 2. Extract re-compressed pubkey from EC_KEY
159
+ EC_KEY_set_conv_form(eckey, compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
160
+
161
+ length = i2o_ECPublicKey(eckey, nil)
162
+ buf = FFI::MemoryPointer.new(:uint8, length)
163
+ if i2o_ECPublicKey(eckey, pointer_to_pointer(buf)) == length
164
+ public_key = buf.read_string(length)
165
+ else
166
+ raise BTCError, "OpenSSL failed to regenerate a public key."
167
+ end
168
+
169
+ public_key
170
+ end
171
+ end
172
+
173
+ # Returns a pair of private key, public key
174
+ def regenerate_keypair(private_key, public_key_compressed: false)
175
+
176
+ autorelease do |pool|
177
+
178
+ eckey = pool.new_ec_key
179
+
180
+ priv_bn = pool.new_bn(private_key)
181
+
182
+ pub_key = pool.new_ec_point
183
+ EC_POINT_mul(self.group, pub_key, priv_bn, nil, nil, pool.bn_ctx)
184
+ EC_KEY_set_private_key(eckey, priv_bn)
185
+ EC_KEY_set_public_key(eckey, pub_key)
186
+
187
+ length = i2d_ECPrivateKey(eckey, nil)
188
+ buf = FFI::MemoryPointer.new(:uint8, length)
189
+ if i2d_ECPrivateKey(eckey, pointer_to_pointer(buf)) == length
190
+ # We have a full DER representation of private key, it contains a length
191
+ # of a private key at offset 8 and private key at offset 9.
192
+ size = buf.get_array_of_uint8(8, 1)[0]
193
+ private_key2 = buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00")
194
+ else
195
+ raise BTCError, "OpenSSL failed to convert private key to DER format"
196
+ end
197
+
198
+ if private_key2 != private_key
199
+ raise BTCError, "OpenSSL somehow regenerated a wrong private key."
200
+ end
201
+
202
+ EC_KEY_set_conv_form(eckey, public_key_compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
203
+
204
+ length = i2o_ECPublicKey(eckey, nil)
205
+ buf = FFI::MemoryPointer.new(:uint8, length)
206
+ if i2o_ECPublicKey(eckey, pointer_to_pointer(buf)) == length
207
+ public_key = buf.read_string(length)
208
+ else
209
+ raise BTCError, "OpenSSL failed to regenerate a public key."
210
+ end
211
+
212
+ [ private_key2, public_key ]
213
+ end
214
+ end
215
+
216
+ # Returns k value computed deterministically from message hash and privkey.
217
+ # See https://tools.ietf.org/html/rfc6979
218
+ def rfc6979_ecdsa_nonce(hash, privkey)
219
+ raise ArgumentError, "Hash must be 32 bytes long" if hash.bytesize != 32
220
+ raise ArgumentError, "Private key must be 32 bytes long" if privkey.bytesize != 32
221
+
222
+ autorelease do |pool|
223
+ order = self.group_order
224
+
225
+ # Step 3.2.a. hash = H(message). Already performed by the caller.
226
+
227
+ # Step 3.2.b. V = 0x01 0x01 0x01 ... 0x01 (32 bytes equal 0x01)
228
+ v = "\x01".b*32
229
+
230
+ # Step 3.2.c. K = 0x00 0x00 0x00 ... 0x00 (32 bytes equal 0x00)
231
+ k = "\x00".b*32
232
+
233
+ # Step 3.2.d. K = HMAC-SHA256(key: K, data: V || 0x00 || int2octets(privkey) || bits2octets(hash))
234
+ h1 = pool.new_bn(hash)
235
+ BN_div(nil, h1, h1, order, pool.bn_ctx) # h1 = h1 % order
236
+ h1data = data_from_bn(h1, min_length: 32)
237
+ k = BTC.hmac_sha256(key: k, data: v + "\x00".b + privkey + h1data)
238
+
239
+ # Step 3.2.e. V = HMAC-SHA256(key: K, data: V)
240
+ v = BTC.hmac_sha256(key: k, data: v)
241
+
242
+ # Step 3.2.f. K = HMAC-SHA256(key: K, data: V || 0x01 || int2octets(privkey) || bits2octets(hash))
243
+ k = BTC.hmac_sha256(key: k, data: v + "\x01".b + privkey + h1data)
244
+
245
+ # Step 3.2.g. V = HMAC-SHA256(key: K, data: V)
246
+ v = BTC.hmac_sha256(key: k, data: v)
247
+
248
+ # Step 3.2.h.
249
+ zero32 = "\x00".b*32
250
+ 10000.times do
251
+ t = BTC.hmac_sha256(key: k, data: v)
252
+ tn = pool.new_bn(t)
253
+ if BN_cmp(tn, order) < 0
254
+ nonce = data_from_bn(tn, min_length: 32)
255
+ if nonce != zero32
256
+ return nonce
257
+ end
258
+ end
259
+ # Note: the probability of not succeeding at the first try is about 2^-127.
260
+ k = BTC.hmac_sha256(key: k, data: v + zero32)
261
+ v = BTC.hmac_sha256(key: k, data: v)
262
+ end
263
+ # we generated 10000 numbers, none of them is good -> fail.
264
+ raise "Cannot find any good ECDSA nonce after 10000 iterations of RFC6979."
265
+ end
266
+ end
267
+
268
+ # Computes a deterministic ECDSA signature with canonical (lowest) S value.
269
+ # Nonce k is equal to HMAC-SHA256(data: hash, key: privkey)
270
+ def ecdsa_signature(hash, privkey, normalized: true)
271
+ raise ArgumentError, "Hash is missing" if !hash
272
+ raise ArgumentError, "Cannot make a ECDSA signature without the private key" if !privkey
273
+
274
+ # ECDSA signature is a pair of numbers: (Kx, s)
275
+ # Where Kx = x coordinate of k*G mod n (n is the order of secp256k1).
276
+ # And s = (k^-1)*(h + Kx*privkey).
277
+ # By default, k is chosen randomly on interval [0, n - 1].
278
+ # But this makes signatures harder to test and allows faulty or
279
+ # backdoored RNGs to leak private keys from ECDSA signatures.
280
+ # To avoid these issues, we'll generate k = Hash256(hash || privatekey)
281
+ # and make all computations by hand.
282
+
283
+ autorelease do |pool|
284
+
285
+ # Order of our curve
286
+ n = self.group_order
287
+ halfn = self.group_half_order
288
+
289
+ # Generate k deterministically from private key and message using HMAC-SHA256
290
+ # This is an important point #1.
291
+ kdata = rfc6979_ecdsa_nonce(hash, privkey)
292
+ k = pool.new_bn(kdata)
293
+
294
+ # Enforce k within group order: k = k % n
295
+ BN_div(nil, k, k, n, pool.bn_ctx)
296
+
297
+ # Compute K = k*G
298
+ #(can't use K variable name because Ruby does not allow
299
+ # constant assignment in methods)
300
+ kG = pool.new_ec_point
301
+ EC_POINT_mul(self.group, kG, k, nil, nil, pool.bn_ctx)
302
+
303
+ # Compute r = K.x. This is first half of the signature.
304
+ r = pool.new_bn
305
+ EC_POINT_get_affine_coordinates_GFp(self.group, kG, r, nil, pool.bn_ctx)
306
+
307
+ # Compute s = (k^-1)*(h + r*privkey).
308
+ h = pool.new_bn(hash)
309
+ p = pool.new_bn(privkey)
310
+ tmp = pool.new_bn
311
+ s = pool.new_bn
312
+ BN_mod_mul(tmp, r, p, n, pool.bn_ctx) # tmp = r*privkey
313
+ BN_mod_add_quick(s, tmp, h, n) # s = h + tmp = h + r*privkey
314
+ BN_mod_inverse(k, k, n, pool.bn_ctx) # k' = k^-1
315
+ BN_mod_mul(s, s, k, n, pool.bn_ctx) # s = k'*(h + r*privkey)
316
+
317
+ # Enforce low S values, by negating the value (modulo the order) if above order/2.
318
+ # This is an important point #2. Not doing that would yield (sometimes)
319
+ # non-canonical signatures that will be rejected by many relaying nodes.
320
+ if normalized
321
+ if BN_cmp(s, halfn) > 0
322
+ BN_sub(s, n, s)
323
+ end
324
+ end
325
+
326
+ # Fill in ECDSA_SIG structure so we can convert it into a proper DER format.
327
+ sig = ECDSA_SIG.new
328
+ sig[:r] = r
329
+ sig[:s] = s
330
+
331
+ # Encode signature in DER format.
332
+
333
+ sig_size = 72 # typical size of a signature (when both numbers are 33 bytes).
334
+
335
+ # allocate a bit more memory just in case (cargo cult)
336
+ buffer = FFI::MemoryPointer.new(:uint8, sig_size + 16)
337
+ sig_size = i2d_ECDSA_SIG(sig.pointer, pointer_to_pointer(buffer))
338
+
339
+ # read actual number of bytes composed by OpenSSL
340
+ signature = buffer.read_string(sig_size)
341
+ signature
342
+ end
343
+ end
344
+
345
+ # Normalizes S value of the signature and returns normalized signature.
346
+ # Returns nil if signature is completely invalid.
347
+ def ecdsa_normalized_signature(signature)
348
+ raise ArgumentError, "Signature is missing" if !signature
349
+
350
+ autorelease do |pool|
351
+
352
+ # Order of our curve
353
+ n = self.group_order
354
+ halfn = self.group_half_order
355
+
356
+ # ECDSA_SIG *psig = NULL;
357
+ # d2i_ECDSA_SIG(&psig, &input, vchSig.size());
358
+ buf = FFI::MemoryPointer.from_string(signature)
359
+ psig = d2i_ECDSA_SIG(nil, pointer_to_pointer(buf), buf.size-1)
360
+ if psig.null?
361
+ raise BTCError, "OpenSSL failed to read ECDSA signature with DER: #{BTC::Data.hex_from_data(signature).inspect}"
362
+ end
363
+
364
+ sig = ECDSA_SIG.new(psig) # read sig from its pointer
365
+ s = sig[:s]
366
+
367
+ # Enforce low S values, by negating the value (modulo the order) if above order/2.
368
+ if BN_cmp(s, halfn) > 0
369
+ BN_sub(s, n, s)
370
+ end
371
+
372
+ # Note: we'll place new s value back to s bignum,
373
+ # so we don't need another sig structure.
374
+
375
+ # Encode signature in DER format.
376
+ sig_size = 72 # typical size of a signature (when both numbers are 33 bytes).
377
+
378
+ # allocate a bit more memory just in case (cargo cult)
379
+ buffer = FFI::MemoryPointer.new(:uint8, sig_size + 16)
380
+ sig_size = i2d_ECDSA_SIG(sig.pointer, pointer_to_pointer(buffer))
381
+
382
+ # read actual number of bytes composed by OpenSSL
383
+ signature = buffer.read_string(sig_size)
384
+
385
+ # Free the signature created by d2i_ECDSA_SIG above.
386
+ ECDSA_SIG_free(psig)
387
+
388
+ signature
389
+ end
390
+ end
391
+
392
+ def ecdsa_verify(signature, hash, public_key)
393
+ raise ArgumentError, "Signature is missing" if !signature
394
+ raise ArgumentError, "Hash is missing" if !hash
395
+ raise ArgumentError, "Public key is missing" if !public_key
396
+
397
+ autorelease do |pool|
398
+ eckey = pool.new_ec_key
399
+
400
+ buf = FFI::MemoryPointer.from_string(public_key)
401
+ eckey = o2i_ECPublicKey(pointer_to_pointer(eckey), pointer_to_pointer(buf), buf.size - 1)
402
+ if eckey.null?
403
+ raise BTCError, "OpenSSL failed to create EC_KEY with public key: #{BTC::Data.hex_from_data(public_key).inspect}"
404
+ end
405
+
406
+ # -1 = error, 0 = bad sig, 1 = good
407
+ hash_buf = FFI::MemoryPointer.from_string(hash)
408
+ sig_buf = FFI::MemoryPointer.from_string(signature)
409
+ result = ECDSA_verify(0, hash_buf, hash_buf.size-1, sig_buf, sig_buf.size-1, eckey)
410
+
411
+ if result == 1
412
+ return true
413
+ end
414
+
415
+ if result == 0
416
+ Diagnostics.current.add_message("OpenSSL detected invalid ECDSA signature. Signature: #{BTC::Data.hex_from_data(signature).inspect}; Hash: #{BTC::Data.hex_from_data(hash).inspect}; Pubkey: #{BTC::Data.hex_from_data(public_key).inspect}")
417
+ else
418
+ raise BTCError, "OpenSSL failed with error while verifying ECDSA signature. Signature: #{BTC::Data.hex_from_data(signature).inspect}; Hash: #{BTC::Data.hex_from_data(hash).inspect}; Pubkey: #{BTC::Data.hex_from_data(public_key).inspect}"
419
+ end
420
+ return false
421
+ end
422
+ false
423
+ end
424
+
425
+ # extract private key from uncompressed DER format
426
+ def private_key_from_der_format(der_key)
427
+ raise ArgumentError, "Missing DER private key" if !der_key
428
+
429
+ prepare_if_needed
430
+
431
+ buf = FFI::MemoryPointer.from_string(der_key)
432
+ ec_key = d2i_ECPrivateKey(nil, pointer_to_pointer(buf), buf.size-1)
433
+ if ec_key.null?
434
+ raise BTCError, "OpenSSL failed to create EC_KEY with DER private key"
435
+ end
436
+ bn = EC_KEY_get0_private_key(ec_key)
437
+ BN_bn2bin(bn, buf)
438
+ buf.read_string(32)
439
+ end
440
+
441
+ # Returns data from bignum
442
+ def data_from_bn(bn, min_length: nil, required_length: nil)
443
+ raise ArgumentError, "Missing big number" if !bn
444
+
445
+ length = BN_num_bytes(bn)
446
+ buf = FFI::MemoryPointer.from_string("\x00"*length)
447
+ BN_bn2bin(bn, buf)
448
+ s = buf.read_string(length)
449
+ s = s.rjust(min_length, "\x00") if min_length
450
+ if required_length && s.bytesize != required_length
451
+ raise BTCError, "Non-matching length of the number: #{s.bytesize} bytes vs required #{required_length}"
452
+ end
453
+ s
454
+ end
455
+
456
+ protected
457
+
458
+ # Returns instance of **SomeType for input of type *SomeType.
459
+ def pointer_to_pointer(pointer)
460
+ FFI::MemoryPointer.new(:pointer).put_pointer(0, pointer)
461
+ end
462
+
463
+
464
+
465
+ # typedef struct ECDSA_SIG_st {
466
+ # BIGNUM *r;
467
+ # BIGNUM *s;
468
+ # } ECDSA_SIG;
469
+ class ECDSA_SIG < ::FFI::Struct
470
+ layout :r, :pointer,
471
+ :s, :pointer
472
+ end
473
+
474
+ class AutoreleasePool
475
+
476
+ LIB = BTC::OpenSSL
477
+
478
+ # Returns last created EC_KEY or creates one on the fly.
479
+ attr_reader :ec_key
480
+
481
+ # Returns current BN_CTX object or creates one on the fly.
482
+ attr_reader :bn_ctx
483
+
484
+ def initialize
485
+ @ec_keys = []
486
+ @bns = []
487
+ @ec_points = []
488
+ end
489
+
490
+ def ec_key
491
+ @ec_keys.last || new_ec_key
492
+ end
493
+
494
+ def bn_ctx
495
+ @bn_ctx ||= LIB.BN_CTX_new()
496
+ end
497
+
498
+ def new_ec_key
499
+ eckey = LIB.EC_KEY_new_by_curve_name(NID_secp256k1)
500
+ @ec_keys << eckey
501
+ return eckey
502
+ end
503
+
504
+ # Creates new bignum object optionally initialized with binary data (bin2bn)
505
+ def new_bn(data = nil)
506
+ bn = LIB.BN_new()
507
+ if data && data.size > 0
508
+ data_ptr = FFI::MemoryPointer.from_string(data)
509
+ # size-1 to skip \0 terminator
510
+ bn = LIB.BN_bin2bn(data_ptr, data_ptr.size - 1, bn)
511
+ end
512
+ @bns << bn
513
+ return bn
514
+ end
515
+
516
+ def new_ec_point
517
+ p = LIB.EC_POINT_new(LIB.group)
518
+ @ec_points << p
519
+ return p
520
+ end
521
+
522
+ def drain
523
+ @ec_keys.each {|eckey| LIB.EC_KEY_free(eckey) }; @ec_keys = nil
524
+ @bns.each {|bn| LIB.BN_free(bn) }; @bns = nil
525
+ @ec_points.each {|p| LIB.EC_POINT_free(p) }; @ec_points = nil
526
+ if @bn_ctx
527
+ LIB.BN_CTX_free(@bn_ctx)
528
+ @bn_ctx = nil
529
+ end
530
+ return nil
531
+ end
532
+
533
+ end
534
+
535
+ end
536
+ end
@@ -0,0 +1,110 @@
1
+ module BTC
2
+ # Proof of work is specified using several terms.
3
+ # 1. `target` is big unsigned integer derived from 256-bit hash (interpreted as little-endian integer).
4
+ # Hash of a valid block should be below target.
5
+ # 2. `bits` is a 'satoshi compact' representation of a target as uint32.
6
+ # 3. `difficulty` is a floating point multiple of the minimum difficulty.
7
+ # Difficulty = 2 means the block is 2x more difficult than the minimal difficulty.
8
+ module ProofOfWork
9
+ extend self
10
+
11
+ MAX_TARGET_MAINNET = 0x00000000ffff0000000000000000000000000000000000000000000000000000
12
+ MAX_TARGET_TESTNET = 0x00000007fff80000000000000000000000000000000000000000000000000000
13
+
14
+ # Note on Satoshi Compact format (used for 'bits' value).
15
+ #
16
+ # The "compact" format is a representation of a whole
17
+ # number N using an unsigned 32bit number similar to a
18
+ # floating point format.
19
+ # The most significant 8 bits are the unsigned exponent of base 256.
20
+ # This exponent can be thought of as "number of bytes of N".
21
+ # The lower 23 bits are the mantissa.
22
+ # Bit number 24 (0x800000) represents the sign of N.
23
+ # N = (-1^sign) * mantissa * 256^(exponent-3)
24
+ #
25
+ # Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
26
+ # MPI uses the most significant bit of the first byte as sign.
27
+ # Thus 0x1234560000 is compact (0x05123456)
28
+ # and 0xc0de000000 is compact (0x0600c0de)
29
+ # (0x05c0de00) would be -0x40de000000
30
+
31
+ # Converts 256-bit integer to 32-bit compact representation.
32
+ def bits_from_target(target)
33
+ exponent = 3
34
+ signed = (target < 0)
35
+ target = -target if signed
36
+ while target > 0x7fffff
37
+ target >>= 8
38
+ exponent += 1
39
+ end
40
+ # The 0x00800000 bit denotes the sign.
41
+ # Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
42
+ if (target & 0x00800000) > 0
43
+ target >>= 8
44
+ exponent += 1
45
+ end
46
+ result = (exponent << 24) + target
47
+ result = result | 0x00800000 if signed
48
+ result
49
+ end
50
+
51
+ # Converts 32-bit compact representation to a 256-bit integer.
52
+ def target_from_bits(bits)
53
+ exponent = ((bits >> 24) & 0xff)
54
+ mantissa = bits & 0x7fffff
55
+ mantissa *= -1 if (bits & 0x800000) > 0
56
+ (mantissa * (256**(exponent-3))).to_i
57
+ end
58
+
59
+ # Computes bits from difficulty.
60
+ # Could be inaccurate since difficulty is a limited-precision floating-point number.
61
+ # Default max_target is for Bitcoin mainnet.
62
+ def bits_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
63
+ bits_from_target(target_from_difficulty(difficulty, max_target: max_target))
64
+ end
65
+
66
+ # Computes difficulty from bits.
67
+ # Default max_target is for Bitcoin mainnet.
68
+ def difficulty_from_bits(bits, max_target: MAX_TARGET_MAINNET)
69
+ difficulty_from_target(target_from_bits(bits), max_target: max_target)
70
+ end
71
+
72
+ # Computes target from difficulty.
73
+ # Could be inaccurate since difficulty is a limited-precision floating-point number.
74
+ # Default max_target is for Bitcoin mainnet.
75
+ def target_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
76
+ (max_target / difficulty).round.to_i
77
+ end
78
+
79
+ # Compute relative difficulty from a given target.
80
+ # E.g. returns 2.5 if target is 2.5 times harder to reach than the max_target.
81
+ # Default max_target is for Bitcoin mainnet.
82
+ def difficulty_from_target(target, max_target: MAX_TARGET_MAINNET)
83
+ (max_target / target.to_f)
84
+ end
85
+
86
+ # Converts target integer to a binary 32-byte hash.
87
+ def hash_from_target(target)
88
+ bytes = []
89
+ while target > 0
90
+ bytes << (target % 256)
91
+ target /= 256
92
+ end
93
+ BTC::Data.data_from_bytes(bytes).ljust(32, "\x00".b)
94
+ end
95
+
96
+ # Converts 32-byte hash to target integer (hash is treated as little-endian integer)
97
+ def target_from_hash(hash)
98
+ target = 0
99
+ i = 0
100
+ hash.each_byte do |byte|
101
+ target += byte * (256**i)
102
+ i += 1
103
+ end
104
+ target
105
+ end
106
+
107
+ # TODO: add retargeting calculation routines
108
+
109
+ end # ProofOfWork
110
+ end # BTC
@@ -0,0 +1,26 @@
1
+ module BTC
2
+
3
+ # Several functions intended to detect bad data in runtime and throw exceptions.
4
+ # These are for programmer's errors, not for bad user input.
5
+ # Bad user input should never raise exceptions.
6
+ module Safety
7
+ def AssertType(value, type)
8
+ if !value.is_a?(type)
9
+ raise ArgumentError, "Value #{value.inspect} must be of type #{type}!"
10
+ end
11
+ end
12
+ def AssertTypeOrNil(value, type)
13
+ return if value == nil
14
+ AssertType(value, type)
15
+ end
16
+
17
+ # Checks invariant and raises an exception.
18
+ def Invariant(condition, message)
19
+ if !condition
20
+ raise RuntimeError, "BTC Invariant Failure: #{message}"
21
+ end
22
+ end
23
+ end
24
+
25
+ include Safety
26
+ end