bitcoinrb 0.2.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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