bitcoinrb 0.2.9 → 0.5.0

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +3 -2
  4. data/README.md +7 -6
  5. data/bitcoinrb.gemspec +4 -4
  6. data/exe/bitcoinrbd +5 -0
  7. data/lib/bitcoin.rb +33 -1
  8. data/lib/bitcoin/bip85_entropy.rb +111 -0
  9. data/lib/bitcoin/block_header.rb +2 -0
  10. data/lib/bitcoin/chain_params.rb +0 -8
  11. data/lib/bitcoin/chainparams/regtest.yml +1 -1
  12. data/lib/bitcoin/chainparams/testnet.yml +1 -1
  13. data/lib/bitcoin/constants.rb +3 -10
  14. data/lib/bitcoin/descriptor.rb +147 -0
  15. data/lib/bitcoin/ext.rb +5 -0
  16. data/lib/bitcoin/ext/json_parser.rb +46 -0
  17. data/lib/bitcoin/ext_key.rb +19 -4
  18. data/lib/bitcoin/key.rb +9 -5
  19. data/lib/bitcoin/key_path.rb +12 -5
  20. data/lib/bitcoin/message.rb +7 -0
  21. data/lib/bitcoin/message/base.rb +1 -0
  22. data/lib/bitcoin/message/cf_parser.rb +16 -0
  23. data/lib/bitcoin/message/cfcheckpt.rb +36 -0
  24. data/lib/bitcoin/message/cfheaders.rb +40 -0
  25. data/lib/bitcoin/message/cfilter.rb +35 -0
  26. data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
  27. data/lib/bitcoin/message/get_cfheaders.rb +24 -0
  28. data/lib/bitcoin/message/get_cfilters.rb +25 -0
  29. data/lib/bitcoin/message/network_addr.rb +31 -12
  30. data/lib/bitcoin/message/version.rb +14 -22
  31. data/lib/bitcoin/mnemonic.rb +5 -5
  32. data/lib/bitcoin/network/peer.rb +12 -11
  33. data/lib/bitcoin/network/peer_discovery.rb +3 -1
  34. data/lib/bitcoin/node/cli.rb +14 -10
  35. data/lib/bitcoin/node/spv.rb +1 -1
  36. data/lib/bitcoin/out_point.rb +14 -7
  37. data/lib/bitcoin/payment_code.rb +92 -0
  38. data/lib/bitcoin/psbt.rb +3 -1
  39. data/lib/bitcoin/psbt/input.rb +7 -16
  40. data/lib/bitcoin/psbt/tx.rb +18 -12
  41. data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
  42. data/lib/bitcoin/rpc/request_handler.rb +3 -3
  43. data/lib/bitcoin/script/script.rb +18 -10
  44. data/lib/bitcoin/script/script_interpreter.rb +3 -5
  45. data/lib/bitcoin/secp256k1.rb +1 -0
  46. data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
  47. data/lib/bitcoin/secp256k1/ruby.rb +4 -35
  48. data/lib/bitcoin/slip39.rb +93 -0
  49. data/lib/bitcoin/slip39/share.rb +122 -0
  50. data/lib/bitcoin/slip39/sss.rb +245 -0
  51. data/lib/bitcoin/slip39/wordlist/english.txt +1024 -0
  52. data/lib/bitcoin/store.rb +2 -1
  53. data/lib/bitcoin/store/chain_entry.rb +1 -0
  54. data/lib/bitcoin/store/db/level_db.rb +2 -2
  55. data/lib/bitcoin/store/utxo_db.rb +226 -0
  56. data/lib/bitcoin/tx.rb +6 -10
  57. data/lib/bitcoin/tx_in.rb +4 -5
  58. data/lib/bitcoin/util.rb +29 -1
  59. data/lib/bitcoin/version.rb +1 -1
  60. data/lib/bitcoin/wallet.rb +1 -0
  61. data/lib/bitcoin/wallet/account.rb +1 -0
  62. data/lib/bitcoin/wallet/base.rb +3 -3
  63. data/lib/bitcoin/wallet/db.rb +1 -1
  64. data/lib/bitcoin/wallet/master_key.rb +1 -0
  65. data/lib/bitcoin/wallet/utxo.rb +37 -0
  66. metadata +45 -26
@@ -2,6 +2,7 @@ module Bitcoin
2
2
  module PSBT
3
3
 
4
4
  class GlobalXpub
5
+ include Bitcoin::HexConverter
5
6
 
6
7
  attr_reader :xpub # Bitcoin::ExtPubkey
7
8
  attr_reader :info # Bitcoin::PSBT::KeyOriginInfo
@@ -16,7 +17,7 @@ module Bitcoin
16
17
  end
17
18
 
18
19
  def to_h
19
- {xpub: xpub.to_payload.bth}.merge(info.to_h)
20
+ {xpub: xpub.to_hex}.merge(info.to_h)
20
21
  end
21
22
 
22
23
  def to_s
@@ -25,11 +26,14 @@ module Bitcoin
25
26
  end
26
27
 
27
28
  class Tx
29
+ include Bitcoin::HexConverter
30
+
28
31
  attr_accessor :tx
29
32
  attr_accessor :xpubs
30
33
  attr_reader :inputs
31
34
  attr_reader :outputs
32
35
  attr_accessor :unknowns
36
+ attr_accessor :version_number
33
37
 
34
38
  def initialize(tx = nil)
35
39
  @tx = tx
@@ -82,6 +86,9 @@ module Bitcoin
82
86
  info = Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)
83
87
  raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
84
88
  partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
89
+ when PSBT_GLOBAL_TYPES[:ver]
90
+ partial_tx.version_number = value.unpack('V').first
91
+ raise ArgumentError, "An unsupported version was detected." if SUPPORT_VERSION < partial_tx.version_number
85
92
  else
86
93
  raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]
87
94
  partial_tx.unknowns[([key_type].pack('C') + key).bth] = value
@@ -113,13 +120,15 @@ module Bitcoin
113
120
 
114
121
  raise ArgumentError, 'Outputs provided does not match the number of outputs in transaction.' unless partial_tx.outputs.size == partial_tx.tx.out.size
115
122
 
116
- partial_tx.inputs.each do |input|
117
- raise ArgumentError, 'PSBT is not sane.' unless input.sane?
118
- end
119
-
120
123
  partial_tx
121
124
  end
122
125
 
126
+ # get PSBT version
127
+ # @return [Integer] PSBT version number
128
+ def version
129
+ version_number ? version_number : 0
130
+ end
131
+
123
132
  # Finds the UTXO for a given input index
124
133
  # @param [Integer] index input_index Index of the input to retrieve the UTXO of
125
134
  # @return [Bitcoin::TxOut] The UTXO of the input if found.
@@ -138,11 +147,10 @@ module Bitcoin
138
147
 
139
148
  payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:unsigned_tx], value: tx.to_payload)
140
149
  payload << xpubs.map(&:to_payload).join
141
-
150
+ payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:ver], value: [version_number].pack('V')) if version_number
142
151
  payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join
143
152
 
144
153
  payload << PSBT_SEPARATOR.itb
145
-
146
154
  payload << inputs.map(&:to_payload).join
147
155
  payload << outputs.map(&:to_payload).join
148
156
  payload
@@ -166,14 +174,12 @@ module Bitcoin
166
174
  utxo = prev_tx.out[tx_in.out_point.index]
167
175
  raise ArgumentError, 'redeem script does not match utxo.' if redeem_script && !utxo.script_pubkey.include?(redeem_script.to_hash160)
168
176
  raise ArgumentError, 'witness script does not match redeem script.' if redeem_script && witness_script && !redeem_script.include?(witness_script.to_sha256)
169
- if utxo.script_pubkey.witness_program? || (redeem_script && redeem_script.witness_program?)
170
- inputs[i].witness_utxo = utxo
171
- else
172
- inputs[i].non_witness_utxo = prev_tx
173
- end
177
+ inputs[i].witness_utxo = utxo if utxo.script_pubkey.witness_program? || redeem_script&.witness_program?
178
+ inputs[i].non_witness_utxo = prev_tx
174
179
  inputs[i].redeem_script = redeem_script if redeem_script
175
180
  inputs[i].witness_script = witness_script if witness_script
176
181
  inputs[i].hd_key_paths = hd_key_paths.map(&:pubkey).zip(hd_key_paths).to_h
182
+ break
177
183
  end
178
184
  end
179
185
  end
@@ -1,4 +1,4 @@
1
- require 'rest-client'
1
+ require 'net/http'
2
2
 
3
3
  module Bitcoin
4
4
  module RPC
@@ -53,20 +53,30 @@ module Bitcoin
53
53
  :params => params,
54
54
  :id => 'jsonrpc'
55
55
  }
56
- post(server_url, @config[:timeout], @config[:open_timeout], data.to_json, content_type: :json) do |respdata, request, result|
57
- raise result.message if !result.kind_of?(Net::HTTPSuccess) && respdata.empty?
58
- response = JSON.parse(respdata.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') })
59
- raise response['error'] if response['error']
60
- response['result']
61
- end
56
+ uri = URI.parse(server_url)
57
+ http = Net::HTTP.new(uri.hostname, uri.port)
58
+ http.use_ssl = uri.scheme === "https"
59
+ request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
60
+ request.basic_auth(uri.user, uri.password)
61
+ request.content_type = 'application/json'
62
+ request.body = data.to_json
63
+ response = http.request(request)
64
+ body = response.body
65
+ response = Bitcoin::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
66
+ raise response['error'].to_s if response['error']
67
+ response['result']
62
68
  end
63
-
64
- def post(url, timeout, open_timeout, payload, headers={}, &block)
65
- RestClient::Request.execute(method: :post, url: url, timeout: timeout,
66
- open_timeout: open_timeout, payload: payload, headers: headers, &block)
69
+
70
+ # Call CLI command on Ruby-like method names.
71
+ # e.g. generate_to_address, send_to_address, get_wallet_info
72
+ def method_missing(name, *args)
73
+ if name.to_s.include?('_')
74
+ send(name.to_s.gsub('_', '').to_sym, args)
75
+ else
76
+ super
77
+ end
67
78
  end
68
79
 
69
80
  end
70
-
71
81
  end
72
82
  end
@@ -42,14 +42,14 @@ module Bitcoin
42
42
  nextblockhash: node.chain.next_hash(block_hash).rhex
43
43
  }
44
44
  else
45
- entry.header.to_payload.bth
45
+ entry.header.to_hex
46
46
  end
47
47
  end
48
48
 
49
49
  # Returns connected peer information.
50
50
  def getpeerinfo
51
51
  node.pool.peers.map do |peer|
52
- local_addr = peer.remote_version.remote_addr[0..peer.remote_version.remote_addr.rindex(':')] + '18333'
52
+ local_addr = "#{peer.remote_version.remote_addr.ip}:18333"
53
53
  {
54
54
  id: peer.id,
55
55
  addr: "#{peer.host}:#{peer.port}",
@@ -96,7 +96,7 @@ module Bitcoin
96
96
  script = Bitcoin::Script.parse_from_payload(hex_script.htb)
97
97
  h = script.to_h
98
98
  h.delete(:hex)
99
- h[:p2sh] = script.to_p2sh.addresses.first unless script.p2sh?
99
+ h[:p2sh] = script.to_p2sh.to_addr unless script.p2sh?
100
100
  h
101
101
  rescue Exception
102
102
  raise ArgumentError.new('Script decode failed')
@@ -6,6 +6,7 @@ module Bitcoin
6
6
  # bitcoin script
7
7
  class Script
8
8
  include Bitcoin::Opcodes
9
+ include Bitcoin::HexConverter
9
10
 
10
11
  attr_accessor :chunks
11
12
 
@@ -54,7 +55,8 @@ module Bitcoin
54
55
  # @param [String] m the number of signatures required for multisig
55
56
  # @param [Array] pubkeys array of public keys that compose multisig
56
57
  # @return [Script] multisig script.
57
- def self.to_multisig_script(m, pubkeys)
58
+ def self.to_multisig_script(m, pubkeys, sort: false)
59
+ pubkeys = pubkeys.sort if sort
58
60
  new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
59
61
  end
60
62
 
@@ -147,7 +149,9 @@ module Bitcoin
147
149
  chunks.size == 0
148
150
  end
149
151
 
152
+ # @deprecated
150
153
  def addresses
154
+ puts "WARNING: Bitcoin::Script#addresses is deprecated. Use Bitcoin::Script#to_addr instead."
151
155
  return [p2pkh_addr] if p2pkh?
152
156
  return [p2sh_addr] if p2sh?
153
157
  return [bech32_addr] if witness_program?
@@ -155,6 +159,15 @@ module Bitcoin
155
159
  []
156
160
  end
157
161
 
162
+ # convert to address
163
+ # @return [String] if script type is p2pkh or p2sh or witness program, return address, otherwise nil.
164
+ def to_addr
165
+ return p2pkh_addr if p2pkh?
166
+ return p2sh_addr if p2sh?
167
+ return bech32_addr if witness_program?
168
+ nil
169
+ end
170
+
158
171
  # check whether standard script.
159
172
  def standard?
160
173
  p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
@@ -323,6 +336,7 @@ module Bitcoin
323
336
  when Integer
324
337
  opcode_to_name(c)
325
338
  when String
339
+ return c if c.empty?
326
340
  if c.pushdata?
327
341
  v = Opcodes.opcode_to_small_int(c.ord)
328
342
  if v
@@ -350,7 +364,7 @@ module Bitcoin
350
364
 
351
365
  # generate hash160 hash for payload
352
366
  def to_hash160
353
- Bitcoin.hash160(to_payload.bth)
367
+ Bitcoin.hash160(to_hex)
354
368
  end
355
369
 
356
370
  # script size
@@ -487,7 +501,7 @@ module Bitcoin
487
501
  end
488
502
 
489
503
  def to_h
490
- h = {asm: to_s, hex: to_payload.bth, type: type}
504
+ h = {asm: to_s, hex: to_hex, type: type}
491
505
  addrs = addresses
492
506
  unless addrs.empty?
493
507
  h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
@@ -503,12 +517,6 @@ module Bitcoin
503
517
  (size > 0 && op_return?) || size > Bitcoin::MAX_SCRIPT_SIZE
504
518
  end
505
519
 
506
- # convert payload to hex data.
507
- # @return [String] script with hex format.
508
- def to_hex
509
- to_payload.bth
510
- end
511
-
512
520
  private
513
521
 
514
522
  # generate p2pkh address. if script dose not p2pkh, return nil.
@@ -541,7 +549,7 @@ module Bitcoin
541
549
  def bech32_addr
542
550
  segwit_addr = Bech32::SegwitAddr.new
543
551
  segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
544
- segwit_addr.script_pubkey = to_payload.bth
552
+ segwit_addr.script_pubkey = to_hex
545
553
  segwit_addr.addr
546
554
  end
547
555
 
@@ -61,7 +61,6 @@ module Bitcoin
61
61
  # Additional validation for spend-to-script-hash transactions
62
62
  if flag?(SCRIPT_VERIFY_P2SH) && script_pubkey.p2sh?
63
63
  return set_error(SCRIPT_ERR_SIG_PUSHONLY) unless script_sig.push_only?
64
- tmp = stack
65
64
  @stack = stack_copy
66
65
  raise 'stack cannot be empty.' if stack.empty?
67
66
  begin
@@ -76,7 +75,7 @@ module Bitcoin
76
75
  if flag?(SCRIPT_VERIFY_WITNESS) && redeem_script.witness_program?
77
76
  had_witness = true
78
77
  # The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we reintroduce malleability.
79
- return set_error(SCRIPT_ERR_WITNESS_MALLEATED_P2SH) unless script_sig == (Bitcoin::Script.new << redeem_script.to_payload.bth)
78
+ return set_error(SCRIPT_ERR_WITNESS_MALLEATED_P2SH) unless script_sig == (Bitcoin::Script.new << redeem_script.to_hex)
80
79
 
81
80
  version, program = redeem_script.witness_data
82
81
  return false unless verify_witness_program(witness, version, program)
@@ -133,7 +132,7 @@ module Bitcoin
133
132
 
134
133
  return false unless eval_script(script_pubkey, :witness_v0)
135
134
 
136
- return set_error(SCRIPT_ERR_EVAL_FALSE) unless stack.size == 1
135
+ return set_error(SCRIPT_ERR_CLEANSTACK) unless stack.size == 1
137
136
  return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last)
138
137
  true
139
138
  end
@@ -147,7 +146,7 @@ module Bitcoin
147
146
  op_count = 0
148
147
 
149
148
  script.chunks.each_with_index do |c, index|
150
- need_exec = !flow_stack.include?(false)
149
+ need_exec = flow_stack.rindex(false).nil?
151
150
 
152
151
  return set_error(SCRIPT_ERR_PUSH_SIZE) if c.pushdata? && c.pushed_data.bytesize > MAX_SCRIPT_ELEMENT_SIZE
153
152
 
@@ -609,7 +608,6 @@ module Bitcoin
609
608
  return false if sig.empty?
610
609
  s = sig.unpack('C*')
611
610
  hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
612
- hash_type &= (~(Bitcoin::SIGHASH_FORK_ID)) if Bitcoin.chain_params.fork_chain? # for fork coin.
613
611
  return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single]
614
612
  true
615
613
  end
@@ -6,6 +6,7 @@ module Bitcoin
6
6
 
7
7
  autoload :Ruby, 'bitcoin/secp256k1/ruby'
8
8
  autoload :Native, 'bitcoin/secp256k1/native'
9
+ autoload :RFC6979, 'bitcoin/secp256k1/rfc6979'
9
10
 
10
11
  end
11
12
 
@@ -0,0 +1,43 @@
1
+ module Bitcoin
2
+ module Secp256k1
3
+ module RFC6979
4
+
5
+ INITIAL_V = '0101010101010101010101010101010101010101010101010101010101010101'.htb
6
+ INITIAL_K = '0000000000000000000000000000000000000000000000000000000000000000'.htb
7
+ ZERO_B = '00'.htb
8
+ ONE_B = '01'.htb
9
+
10
+ module_function
11
+
12
+ # generate temporary key k to be used when ECDSA sign.
13
+ # https://tools.ietf.org/html/rfc6979#section-3.2
14
+ # @param [String] key_data a data contains private key and message.
15
+ # @param [String] extra_entropy extra entropy with binary format.
16
+ # @return [Integer] a nonce.
17
+ def generate_rfc6979_nonce(key_data, extra_entropy)
18
+ v = INITIAL_V # 3.2.b
19
+ k = INITIAL_K # 3.2.c
20
+ # 3.2.d
21
+ k = Bitcoin.hmac_sha256(k, v + ZERO_B + key_data + extra_entropy)
22
+ # 3.2.e
23
+ v = Bitcoin.hmac_sha256(k, v)
24
+ # 3.2.f
25
+ k = Bitcoin.hmac_sha256(k, v + ONE_B + key_data + extra_entropy)
26
+ # 3.2.g
27
+ v = Bitcoin.hmac_sha256(k, v)
28
+ # 3.2.h
29
+ t = ''
30
+ 10000.times do
31
+ v = Bitcoin.hmac_sha256(k, v)
32
+ t = (t + v)
33
+ t_num = t.bth.to_i(16)
34
+ return t_num if 1 <= t_num && t_num < Bitcoin::Secp256k1::GROUP.order
35
+ k = Bitcoin.hmac_sha256(k, v + '00'.htb)
36
+ v = Bitcoin.hmac_sha256(k, v)
37
+ end
38
+ raise 'A valid nonce was not found.'
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -12,8 +12,8 @@ module Bitcoin
12
12
  private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
13
13
  public_key = GROUP.generator.multiply_by_scalar(private_key)
14
14
  privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
15
- pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
16
- [privkey.bth, pubkey.bth]
15
+ pubkey = public_key.to_hex(compressed)
16
+ [privkey.bth, pubkey]
17
17
  end
18
18
 
19
19
  # generate bitcoin key object
@@ -24,7 +24,7 @@ module Bitcoin
24
24
 
25
25
  def generate_pubkey(privkey, compressed: true)
26
26
  public_key = ECDSA::Group::Secp256k1.generator.multiply_by_scalar(privkey.to_i(16))
27
- ECDSA::Format::PointOctetString.encode(public_key, compression: compressed).bth
27
+ public_key.to_hex(compressed)
28
28
  end
29
29
 
30
30
  # sign data.
@@ -35,7 +35,7 @@ module Bitcoin
35
35
  privkey = privkey.htb
36
36
  private_key = ECDSA::Format::IntegerOctetString.decode(privkey)
37
37
  extra_entropy ||= ''
38
- nonce = generate_rfc6979_nonce(data, privkey, extra_entropy)
38
+ nonce = RFC6979.generate_rfc6979_nonce(privkey + data, extra_entropy)
39
39
 
40
40
  # port form ecdsa gem.
41
41
  r_point = GROUP.new_point(nonce)
@@ -87,37 +87,6 @@ module Bitcoin
87
87
  end
88
88
  end
89
89
 
90
- INITIAL_V = '0101010101010101010101010101010101010101010101010101010101010101'.htb
91
- INITIAL_K = '0000000000000000000000000000000000000000000000000000000000000000'.htb
92
- ZERO_B = '00'.htb
93
- ONE_B = '01'.htb
94
-
95
- # generate temporary key k to be used when ECDSA sign.
96
- # https://tools.ietf.org/html/rfc6979#section-3.2
97
- def generate_rfc6979_nonce(data, privkey, extra_entropy)
98
- v = INITIAL_V # 3.2.b
99
- k = INITIAL_K # 3.2.c
100
- # 3.2.d
101
- k = Bitcoin.hmac_sha256(k, v + ZERO_B + privkey + data + extra_entropy)
102
- # 3.2.e
103
- v = Bitcoin.hmac_sha256(k, v)
104
- # 3.2.f
105
- k = Bitcoin.hmac_sha256(k, v + ONE_B + privkey + data + extra_entropy)
106
- # 3.2.g
107
- v = Bitcoin.hmac_sha256(k, v)
108
- # 3.2.h
109
- t = ''
110
- 10000.times do
111
- v = Bitcoin.hmac_sha256(k, v)
112
- t = (t + v)
113
- t_num = t.bth.to_i(16)
114
- return t_num if 1 <= t_num && t_num < GROUP.order
115
- k = Bitcoin.hmac_sha256(k, v + '00'.htb)
116
- v = Bitcoin.hmac_sha256(k, v)
117
- end
118
- raise 'A valid nonce was not found.'
119
- end
120
90
  end
121
-
122
91
  end
123
92
  end
@@ -0,0 +1,93 @@
1
+ module Bitcoin
2
+ module SLIP39
3
+
4
+ WORDS = File.readlines("#{__dir__}/slip39/wordlist/english.txt").map(&:strip)
5
+
6
+ module_function
7
+
8
+ def bits_to_bytes(n)
9
+ (n + 7) / 8
10
+ end
11
+
12
+ def bits_to_words(n)
13
+ (n + RADIX_BITS - 1) / RADIX_BITS
14
+ end
15
+
16
+ # The length of the radix in bits.
17
+ RADIX_BITS = 10
18
+ # The number of words in the wordlist.
19
+ RADIX = 2 ** RADIX_BITS
20
+ # The length of the random identifier in bits.
21
+ ID_LENGTH_BITS = 15
22
+ # The length of the iteration exponent in bits.
23
+ ITERATION_EXP_LENGTH_BITS = 5
24
+ # The length of the random identifier and iteration exponent in words.
25
+ ID_EXP_LENGTH_WORDS = bits_to_words(ID_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS)
26
+ # The maximum number of shares that can be created.
27
+ MAX_SHARE_COUNT = 16
28
+ # The length of the RS1024 checksum in words.
29
+ CHECKSUM_LENGTH_WORDS = 3
30
+ # The length of the digest of the shared secret in bytes.
31
+ DIGEST_LENGTH_BYTES = 4
32
+ # The customization string used in the RS1024 checksum and in the PBKDF2 salt.
33
+ CUSTOMIZATION_STRING = 'shamir'.bytes
34
+ # The length of the mnemonic in words without the share value.
35
+ METADATA_LENGTH_WORDS = ID_EXP_LENGTH_WORDS + 2 + CHECKSUM_LENGTH_WORDS
36
+ # The minimum allowed entropy of the master secret.
37
+ MIN_STRENGTH_BITS = 128
38
+ # The minimum allowed length of the mnemonic in words.
39
+ MIN_MNEMONIC_LENGTH_WORDS = METADATA_LENGTH_WORDS + bits_to_words(MIN_STRENGTH_BITS)
40
+ # The minimum number of iterations to use in PBKDF2.
41
+ BASE_ITERATION_COUNT = 10000
42
+ # The number of rounds to use in the Feistel cipher.
43
+ ROUND_COUNT = 4
44
+ # The index of the share containing the shared secret.
45
+ SECRET_INDEX = 255
46
+ # The index of the share containing the digest of the shared secret.
47
+ DIGEST_INDEX = 254
48
+
49
+ EXP_TABLE = [
50
+ 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19,
51
+ 53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34,
52
+ 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144,
53
+ 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184,
54
+ 211, 110, 178, 205, 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241,
55
+ 8, 24, 40, 120, 136, 131, 158, 185, 208, 107, 189, 220, 127, 129, 152,
56
+ 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240, 11,
57
+ 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236,
58
+ 47, 113, 147, 174, 233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183,
59
+ 194, 93, 231, 50, 86, 250, 21, 63, 65, 195, 94, 226, 61, 71, 201,
60
+ 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172,
61
+ 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88,
62
+ 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33,
63
+ 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, 69, 207,
64
+ 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18,
65
+ 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
66
+ 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246
67
+ ]
68
+
69
+ LOG_TABLE = [
70
+ 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
71
+ 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28,
72
+ 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201,
73
+ 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53,
74
+ 147, 218, 142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241,
75
+ 64, 70, 131, 56, 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226,
76
+ 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66, 58, 107,
77
+ 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78,
78
+ 212, 172, 229, 243, 115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116,
79
+ 79, 174, 233, 213, 231, 230, 173, 232, 44, 215, 117, 122, 235, 22, 11,
80
+ 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, 196,
81
+ 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251,
82
+ 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, 151, 178, 135, 144,
83
+ 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, 83, 57, 132,
84
+ 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146,
85
+ 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237,
86
+ 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7
87
+ ]
88
+
89
+ autoload :SSS, 'bitcoin/slip39/sss'
90
+ autoload :Share, 'bitcoin/slip39/share'
91
+
92
+ end
93
+ end