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,464 @@
1
+ # Implementation of BIP32 "Hierarchical Deterministic Wallets" (HD Wallets)
2
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
3
+ #
4
+ # Keychain encapsulates either a pair of "extended" keys (private and public),
5
+ # or only a public extended key.
6
+ # "Extended key" means the key (private or public) is accompanied by an extra
7
+ # 256 bits of entropy called "chain code" and some metadata about its position
8
+ # in a tree of keys (depth, parent fingerprint, index).
9
+ #
10
+ # Keychain has two modes of operation:
11
+ #
12
+ # 1. "Normal derivation" which allows to derive public keys separately
13
+ # from the private ones (internally i below 0x80000000).
14
+ #
15
+ # 2. "Hardened derivation" which derives only private keys (for i >= 0x80000000).
16
+ #
17
+ # Derivation can be treated as a single key or as a new branch of keychains.
18
+ # BIP43 and BIP44 propose a way for wallets to organize streams of keys using Keychain.
19
+ #
20
+ module BTC
21
+ class Keychain
22
+
23
+ MAX_INDEX = 0x7fffffff
24
+
25
+ PUBLIC_MAINNET_VERSION = 0x0488B21E # xpub
26
+ PRIVATE_MAINNET_VERSION = 0x0488ADE4 # xprv
27
+ PUBLIC_TESNET_VERSION = 0x043587CF # tpub
28
+ PRIVATE_TESTNET_VERSION = 0x04358394 # tprv
29
+
30
+ # Instance of BTC::Key that is a "head" of this keychain.
31
+ # If the keychain is public-only, key does not have a private component.
32
+ attr_reader :key
33
+
34
+ # 32-byte binary "chain code" string.
35
+ attr_reader :chain_code
36
+
37
+ # Base58Check-encoded extended public key.
38
+ attr_reader :extended_public_key
39
+ attr_reader :xpub
40
+
41
+ # Base58Check-encoded extended private key.
42
+ # Returns nil if it's a public-only keychain.
43
+ attr_reader :extended_private_key
44
+ attr_reader :xprv
45
+
46
+ # 160-bit binary identifier (aka "hash") of the keychain (RIPEMD160(SHA256(pubkey)))
47
+ attr_reader :identifier
48
+
49
+ # Base58Check-encoded identifier.
50
+ attr_reader :identifier_base58
51
+
52
+ # Fingerprint of the keychain (integer).
53
+ attr_reader :fingerprint
54
+
55
+ # Fingerprint of the parent keychain (integer).
56
+ # For master keychain it is always 0.
57
+ attr_reader :parent_fingerprint
58
+
59
+ # Index in the parent keychain (integer).
60
+ # Returns 0 for master keychain.
61
+ attr_reader :index
62
+
63
+ # Depth in the hierarchy (integer).
64
+ # Returns 0 for master keychain.
65
+ attr_reader :depth
66
+
67
+ # Network.mainnet or Network.testnet.
68
+ # Default is BTC::Network.default (mainnet if not overriden).
69
+ attr_accessor :network
70
+
71
+ # Returns true if the keychain can derive private keys (opposite of #public?).
72
+ def private?
73
+ !!@private_key
74
+ end
75
+
76
+ # Returns true if the keychain can only derive public keys (opposite of #private?).
77
+ def public?
78
+ !@private_key
79
+ end
80
+
81
+ # Returns true if the keychain was derived via hardened derivation from its parent.
82
+ # This means internally parameter i = 0x80000000 | self.index.
83
+ # For the master keychain index is zero and hardened? returns false.
84
+ def hardened?
85
+ !!@hardened
86
+ end
87
+
88
+ def network
89
+ @network
90
+ end
91
+
92
+ def network=(network)
93
+ @network = network || Network.default
94
+ @extended_public_key = nil
95
+ @extended_private_key = nil
96
+ end
97
+
98
+ # Returns true if this keychain is intended for mainnet.
99
+ def mainnet?
100
+ network.mainnet?
101
+ end
102
+
103
+ # Returns true if this keychain is intended for testnet.
104
+ def testnet?
105
+ network.testnet?
106
+ end
107
+
108
+ def key
109
+ @key ||= if @private_key
110
+ BTC::Key.new(private_key: @private_key, public_key_compressed: true)
111
+ else
112
+ BTC::Key.new(public_key: @public_key)
113
+ end
114
+ end
115
+
116
+ def xpub
117
+ extended_public_key
118
+ end
119
+
120
+ def xprv
121
+ extended_private_key
122
+ end
123
+
124
+ def extended_public_key
125
+ @extended_public_key ||= begin
126
+ prefix = _extended_key_prefix(mainnet? ? PUBLIC_MAINNET_VERSION : PUBLIC_TESNET_VERSION)
127
+ BTC::Base58.base58check_from_data(prefix + @public_key)
128
+ end
129
+ end
130
+
131
+ def extended_private_key
132
+ @extended_private_key ||= begin
133
+ return nil if !@private_key
134
+ prefix = _extended_key_prefix(mainnet? ? PRIVATE_MAINNET_VERSION : PRIVATE_TESTNET_VERSION)
135
+ BTC::Base58.base58check_from_data(prefix + "\x00" + @private_key)
136
+ end
137
+ end
138
+
139
+ def identifier
140
+ @identifier ||= BTC.hash160(@public_key)
141
+ end
142
+
143
+ def identifier_base58
144
+ @identifier_base58 ||= BTC::Base58.base58check_from_data(self.identifier)
145
+ end
146
+
147
+ def fingerprint
148
+ @fingerprint ||= BTC::WireFormat.read_uint32be(data:self.identifier).first
149
+ end
150
+
151
+ def ==(other)
152
+ self.identifier == other.identifier &&
153
+ self.private? == other.private? &&
154
+ self.mainnet? == other.mainnet?
155
+ end
156
+ alias_method :eql?, :==
157
+
158
+ def dup
159
+ Keychain.new(extended_key: self.xprv || self.xpub)
160
+ end
161
+
162
+ # Returns a copy of the keychain stripped of the private key.
163
+ # Equivalent to BTC::Keychain.new(xpub: keychain.xpub)
164
+ def public_keychain
165
+ self.class.new(_components: [
166
+ nil, # private_key
167
+ @public_key,
168
+ @chain_code,
169
+ @fingerprint,
170
+ @parent_fingerprint,
171
+ @index,
172
+ @depth,
173
+ @hardened,
174
+ @network])
175
+ end
176
+
177
+ # Initializes master keychain from a seed.
178
+ # This is the "root" keychain of the entire hierarchy.
179
+ def self.with_seed(seed, network: nil)
180
+ raise ArgumentError, "Use Keychain.new(seed: ...) instead"
181
+ end
182
+
183
+ # Initializes keychain from a Base58Check-encoded extended public or private key.
184
+ def self.with_extended_key(xkey)
185
+ raise ArgumentError, "Use Keychain.new(extended_key/xpub/xprv: ...) instead"
186
+ end
187
+
188
+ # Instantiates Keychain with a binary seed or a base58-encoded extended public/private key.
189
+ # Usage:
190
+ # * Keychain.new(seed: ...[, network: ...])
191
+ # * Keychain.new(extended_key: "xpub..." or "xprv...")
192
+ # * Keychain.new(xpub: "xpub...")
193
+ # * Keychain.new(xprv: "xprv...")
194
+ def initialize(seed: nil,
195
+ extended_key: nil,
196
+ xpub: nil,
197
+ xprv: nil,
198
+ network: nil,
199
+ _components: nil # private API
200
+ )
201
+
202
+ if seed
203
+ init_with_seed(seed, network: network)
204
+ elsif xkey = (xprv || xpub || extended_key)
205
+ if network
206
+ raise ArgumentError, "Cannot use network argument with extended key to initialize BTC::Keychain (network type is already encoded in the key)"
207
+ end
208
+ if [xprv, xpub, extended_key].compact.size != 1
209
+ raise ArgumentError, "Only one of xpub/xprv/extended_key arguments could be used to initialize BTC::Keychain"
210
+ end
211
+ init_with_extended_key(xkey)
212
+ elsif _components
213
+ init_with_components(*_components)
214
+ else
215
+ raise ArgumentError, "Either seed or an extended " if !private_key && !public_key
216
+ end
217
+ end
218
+
219
+ def init_with_seed(seed, network: nil)
220
+ hmac = BTC.hmac_sha512(data: seed, key: "Bitcoin seed".encode(Encoding::ASCII))
221
+ @private_key = hmac[0,32]
222
+ @public_key = BTC::Key.new(private_key: @private_key, public_key_compressed: true).public_key
223
+ @chain_code = hmac[32,32]
224
+ @fingerprint = nil
225
+ @parent_fingerprint = 0
226
+ @index = 0
227
+ @depth = 0
228
+ @network = network || BTC::Network.default
229
+ end
230
+ private :init_with_seed
231
+
232
+
233
+ def init_with_extended_key(xkey)
234
+ xkeydata = BTC::Base58.data_from_base58check(xkey)
235
+
236
+ if xkeydata.bytesize != 78
237
+ raise BTCError, "Invalid extended key length: must be 78 bytes (received #{xkeydata.bytesize} bytes)"
238
+ end
239
+
240
+ version, _ = BTC::WireFormat.read_uint32be(data: xkeydata)
241
+
242
+ bytes = xkeydata.bytes
243
+ keyprefix = bytes[45]
244
+
245
+ @network = Network.mainnet # not using Network.default because we set it explicitly based on xkey version.
246
+
247
+ # Check if it's a private key
248
+ if version == PRIVATE_MAINNET_VERSION || version == PRIVATE_TESTNET_VERSION
249
+
250
+ # Should have 0x00-prefixed private key (1 + 32 bytes).
251
+ if keyprefix != 0x00
252
+ raise BTCError, "Extended private key must be padded with 0x00 byte (received #{keyprefix})"
253
+ end
254
+
255
+ @private_key = xkeydata[46, 32]
256
+ @public_key = BTC::Key.new(private_key: @private_key, public_key_compressed: true).public_key
257
+ if version == PRIVATE_TESTNET_VERSION
258
+ @network = Network.testnet
259
+ end
260
+
261
+ elsif version == PUBLIC_MAINNET_VERSION || version == PUBLIC_TESNET_VERSION
262
+ # Should have a 33-byte public key with non-zero first byte.
263
+ if keyprefix == 0x00
264
+ raise BTCError, "Extended public key must have non-zero first byte (received #{keyprefix})"
265
+ end
266
+ @public_key = xkeydata[45, 33]
267
+ if version == PUBLIC_TESNET_VERSION
268
+ @network = Network.testnet
269
+ end
270
+ else
271
+ raise BTCError, "Unknown extended key version: 0x#{version.to_s(16).rjust(8, "0")}"
272
+ end
273
+
274
+ @depth = bytes[4] # 0 for master keychain, 1 for first level derived keychain etc.
275
+
276
+ @parent_fingerprint, _ = BTC::WireFormat.read_uint32be(data: xkeydata, offset: 5)
277
+ raise BTCError, "Cannot read uint32be parent_fingerprint" if !@parent_fingerprint
278
+
279
+ @index, _ = BTC::WireFormat.read_uint32be(data: xkeydata, offset: 9)
280
+ raise BTCError, "Cannot read uint32be index" if !@index
281
+
282
+ @hardened = false
283
+ if (0x80000000 & index) != 0
284
+ @index = (~0x80000000) & @index
285
+ @hardened = true
286
+ end
287
+
288
+ @chain_code = xkeydata[13, 32]
289
+ end
290
+ private :init_with_extended_key
291
+
292
+ def init_with_components(private_key,
293
+ public_key,
294
+ chain_code,
295
+ fingerprint,
296
+ parent_fingerprint,
297
+ index,
298
+ depth,
299
+ hardened,
300
+ network)
301
+ raise ArgumentError, "Either private or public key must be present" if !private_key && !public_key
302
+ @private_key = private_key
303
+ @public_key = public_key || BTC::Key.new(private_key: private_key, public_key_compressed: true).public_key
304
+ @chain_code = chain_code
305
+ @fingerprint = fingerprint
306
+ @parent_fingerprint = parent_fingerprint
307
+ @index = index
308
+ @depth = depth
309
+ @hardened = hardened
310
+ @network = network
311
+ end
312
+ private :init_with_components
313
+
314
+ def _extended_key_prefix(version)
315
+ data = [version,
316
+ @depth,
317
+ @parent_fingerprint,
318
+ @hardened ? (0x80000000 | @index) : @index
319
+ ].pack(
320
+ WireFormat::PACK_FORMAT_UINT32BE +
321
+ WireFormat::PACK_FORMAT_UINT8 +
322
+ WireFormat::PACK_FORMAT_UINT32BE +
323
+ WireFormat::PACK_FORMAT_UINT32BE)
324
+
325
+ data + @chain_code
326
+ end
327
+ private :_extended_key_prefix
328
+
329
+ # Returns a derived keychain at a given index.
330
+ # If hardened = true, uses hardened derivation (possible only when private
331
+ # key is present; otherwise returns nil).
332
+ # Index must be less of equal BTC::Keychain::MAX_INDEX, otherwise raises ArgumentError.
333
+ # Raises BTCError for some indexes (when hashing leads to invalid EC points)
334
+ # which is very rare (chance is below 2^-127), but must be expected.
335
+ # In such case, simply use next index.
336
+ # By default, a normal (non-hardened) derivation is used.
337
+ def derived_keychain(index_or_path, hardened: nil)
338
+
339
+ if index_or_path.is_a?(String)
340
+ if hardened != nil
341
+ raise ArgumentError, "Ambiguous use of `hardened` flag when deriving keychain with a string path"
342
+ end
343
+ return derived_keychain_with_path(index_or_path)
344
+ end
345
+
346
+ index = index_or_path
347
+
348
+ raise ArgumentError, "Index must not be nil" if !index
349
+
350
+ # As we use explicit "hardened" argument, do not allow higher bit set.
351
+ if index < 0 || index > 0x7fffffff || (0x80000000 & index) != 0
352
+ raise ArgumentError, "Index >= 0x80000000 is not valid. Use `hardened: true` argument instead."
353
+ end
354
+
355
+ if hardened && !@private_key
356
+ # Not possible to derive hardened keychain without a private key.
357
+ raise BTCError, "Not possible to derive a hardened keychain without a private key (index: #{index})."
358
+ end
359
+
360
+ private_key = nil
361
+ public_key = nil
362
+ chain_code = nil
363
+
364
+ data = "".b
365
+
366
+ if hardened
367
+ data << "\x00" << @private_key
368
+ else
369
+ data << @public_key
370
+ end
371
+
372
+ data << BTC::WireFormat.encode_uint32be(hardened ? (0x80000000 | index) : index)
373
+
374
+ digest = BTC.hmac_sha512(data: data, key: @chain_code)
375
+
376
+ chain_code = digest[32,32]
377
+
378
+ lib = BTC::OpenSSL
379
+ lib.autorelease do |pool|
380
+
381
+ factor = pool.new_bn(digest[0,32])
382
+ n = lib.group_order
383
+
384
+ if lib.BN_cmp(factor, n) >= 0
385
+ raise BTCError, "Factor for index #{index} is greater than curve order."
386
+ end
387
+
388
+ if @private_key
389
+ pk = pool.new_bn(@private_key)
390
+ lib.BN_mod_add_quick(pk, pk, factor, n) # pk = (pk + factor) % n
391
+
392
+ # Check for invalid derivation.
393
+
394
+ if lib.BN_cmp(pk, pool.new_bn("\x00")) == 0
395
+ raise BTCError, "Private key is zero for index #{index}."
396
+ end
397
+
398
+ private_key = lib.data_from_bn(pk, min_length: 32)
399
+ else
400
+
401
+ # Convert pubkey to a EC point
402
+ pubkey_x = pool.new_bn(@public_key)
403
+ pubkey_point = pool.new_ec_point
404
+ lib.EC_POINT_bn2point(lib.group, pubkey_x, pubkey_point, pool.bn_ctx)
405
+
406
+ # Compute point = pubkey + factor*G
407
+ point = pool.new_ec_point
408
+ # /** Computes r = generator * n + q * m
409
+ # * \param group underlying EC_GROUP object
410
+ # * \param r EC_POINT object for the result
411
+ # * \param n BIGNUM with the multiplier for the group generator (optional)
412
+ # * \param q EC_POINT object with the first factor of the second summand
413
+ # * \param m BIGNUM with the second factor of the second summand
414
+ # * \param ctx BN_CTX object (optional)
415
+ # * \return 1 on success and 0 if an error occured
416
+ # */
417
+ # int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *n, const EC_POINT *q, const BIGNUM *m, BN_CTX *ctx);
418
+ lib.EC_POINT_mul(lib.group, point, factor, pubkey_point, pool.new_bn("\x01"), pool.bn_ctx)
419
+
420
+ # Check for invalid derivation.
421
+ if 1 == lib.EC_POINT_is_at_infinity(lib.group, point)
422
+ raise BTCError, "Resulting point is at infinity for index #{index}."
423
+ end
424
+
425
+ lib.EC_POINT_point2bn(lib.group, point, BTC::OpenSSL::POINT_CONVERSION_COMPRESSED, pubkey_x, pool.bn_ctx)
426
+
427
+ public_key = lib.data_from_bn(pubkey_x, required_length: 33)
428
+ end
429
+ end
430
+
431
+ self.class.new(_components: [
432
+ private_key,
433
+ public_key,
434
+ chain_code,
435
+ nil,
436
+ self.fingerprint,
437
+ index,
438
+ @depth + 1,
439
+ !!hardened,
440
+ @network])
441
+ end
442
+
443
+ # Returns a derived BTC::Key from this keychain.
444
+ # This is a convenient way to do keychain.derived_keychain(i).key
445
+ # If the receiver contains a private key, child key will also contain a private key.
446
+ # If the receiver contains only a public key, child key will only contain a public key.
447
+ # (Or nil will be returned if hardened = true.)
448
+ # By default, a normal (non-hardened) derivation is used.
449
+ def derived_key(index_or_path, hardened: nil)
450
+ self.derived_keychain(index_or_path, hardened: hardened).key
451
+ end
452
+
453
+ def derived_keychain_with_path(path)
454
+ path.gsub(/^m\/?/, "").split("/").inject(self) do |keychain, segment|
455
+ if segment =~ /^\d+\'?$/
456
+ keychain.derived_keychain(segment.to_i, hardened: (segment[-1] == "'"))
457
+ else
458
+ raise ArgumentError, "Incorrect path format. Should be (\d+'?) separated by '/'."
459
+ end
460
+ end
461
+ end
462
+
463
+ end # Keychain
464
+ end # BTC
@@ -0,0 +1,73 @@
1
+ module BTC
2
+ class Network
3
+
4
+ # Name of the network (mainnet or testnet3)
5
+ attr_accessor :name
6
+
7
+ # Is testnet (true/false). Alias: testnet?
8
+ attr_accessor :testnet
9
+
10
+ # Is mainnet (true/false). Alias: mainnet?
11
+ attr_accessor :mainnet
12
+
13
+ # Genesis block for this network
14
+ attr_accessor :genesis_block
15
+
16
+ # Genesis block header for this network
17
+ attr_accessor :genesis_block_header
18
+
19
+ # Maximum target (lowest difficulty) for this network.
20
+ attr_accessor :max_target
21
+
22
+ # Default network when it is not explicitly specified.
23
+ def self.default
24
+ return (@default ||= self.mainnet)
25
+ end
26
+
27
+ def self.default=(network)
28
+ @default = network
29
+ end
30
+
31
+ def self.mainnet
32
+ @mainnet ||= begin
33
+ network = self.new
34
+ network.name = "mainnet"
35
+ network.genesis_block = Block.genesis_mainnet
36
+ network.genesis_block_header = BlockHeader.genesis_mainnet
37
+ network.max_target = ProofOfWork::MAX_TARGET_MAINNET
38
+ network
39
+ end
40
+ end
41
+
42
+ def self.testnet
43
+ @testnet ||= begin
44
+ network = self.new
45
+ network.name = "testnet3"
46
+ network.testnet = true
47
+ network.genesis_block = Block.genesis_testnet
48
+ network.genesis_block_header = BlockHeader.genesis_testnet
49
+ network.max_target = ProofOfWork::MAX_TARGET_TESTNET
50
+ network
51
+ end
52
+ end
53
+
54
+ def testnet?; @testnet || false; end
55
+ def testnet; @testnet || false; end
56
+
57
+ def mainnet?; !testnet?; end
58
+ def mainnet; !testnet?; end
59
+ def mainnet=(flag)
60
+ self.testnet = !flag
61
+ end
62
+
63
+ def dup
64
+ network = Network.new
65
+ network.name = self.name.dup
66
+ network.testnet = self.testnet
67
+ network.genesis_block = self.genesis_block
68
+ network.genesis_block_header = self.genesis_block_header
69
+ network
70
+ end
71
+
72
+ end
73
+ end