tapyrus 0.2.7 → 0.2.12

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.prettierignore +3 -0
  4. data/.prettierrc.yaml +3 -0
  5. data/.ruby-version +1 -1
  6. data/CODE_OF_CONDUCT.md +7 -7
  7. data/README.md +14 -17
  8. data/Rakefile +3 -3
  9. data/lib/openassets/marker_output.rb +0 -4
  10. data/lib/openassets/payload.rb +4 -10
  11. data/lib/openassets.rb +0 -2
  12. data/lib/schnorr/sign_to_contract.rb +51 -0
  13. data/lib/schnorr/signature.rb +3 -6
  14. data/lib/schnorr.rb +14 -9
  15. data/lib/tapyrus/base58.rb +7 -6
  16. data/lib/tapyrus/bip175.rb +78 -0
  17. data/lib/tapyrus/block.rb +1 -2
  18. data/lib/tapyrus/block_header.rb +15 -9
  19. data/lib/tapyrus/bloom_filter.rb +5 -3
  20. data/lib/tapyrus/chain_params.rb +1 -4
  21. data/lib/tapyrus/chainparams/dev.yml +3 -2
  22. data/lib/tapyrus/chainparams/prod.yml +3 -2
  23. data/lib/tapyrus/constants.rb +29 -23
  24. data/lib/tapyrus/errors.rb +1 -3
  25. data/lib/tapyrus/ext/ecdsa.rb +4 -4
  26. data/lib/tapyrus/ext/json_parser.rb +1 -4
  27. data/lib/tapyrus/ext.rb +1 -1
  28. data/lib/tapyrus/ext_key.rb +44 -32
  29. data/lib/tapyrus/key.rb +31 -35
  30. data/lib/tapyrus/key_path.rb +15 -12
  31. data/lib/tapyrus/logger.rb +20 -16
  32. data/lib/tapyrus/merkle_tree.rb +22 -20
  33. data/lib/tapyrus/message/addr.rb +1 -7
  34. data/lib/tapyrus/message/base.rb +0 -3
  35. data/lib/tapyrus/message/block.rb +2 -9
  36. data/lib/tapyrus/message/block_transaction_request.rb +3 -6
  37. data/lib/tapyrus/message/block_transactions.rb +2 -6
  38. data/lib/tapyrus/message/block_txn.rb +0 -4
  39. data/lib/tapyrus/message/cmpct_block.rb +1 -7
  40. data/lib/tapyrus/message/error.rb +1 -4
  41. data/lib/tapyrus/message/fee_filter.rb +1 -4
  42. data/lib/tapyrus/message/filter_add.rb +0 -4
  43. data/lib/tapyrus/message/filter_clear.rb +0 -4
  44. data/lib/tapyrus/message/filter_load.rb +2 -5
  45. data/lib/tapyrus/message/get_addr.rb +0 -4
  46. data/lib/tapyrus/message/get_block_txn.rb +0 -4
  47. data/lib/tapyrus/message/get_blocks.rb +0 -3
  48. data/lib/tapyrus/message/get_data.rb +1 -4
  49. data/lib/tapyrus/message/get_headers.rb +1 -3
  50. data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
  51. data/lib/tapyrus/message/headers.rb +0 -4
  52. data/lib/tapyrus/message/headers_parser.rb +3 -8
  53. data/lib/tapyrus/message/inv.rb +1 -4
  54. data/lib/tapyrus/message/inventories_parser.rb +2 -7
  55. data/lib/tapyrus/message/inventory.rb +12 -5
  56. data/lib/tapyrus/message/mem_pool.rb +0 -4
  57. data/lib/tapyrus/message/merkle_block.rb +4 -9
  58. data/lib/tapyrus/message/network_addr.rb +7 -6
  59. data/lib/tapyrus/message/not_found.rb +0 -3
  60. data/lib/tapyrus/message/ping.rb +0 -3
  61. data/lib/tapyrus/message/pong.rb +0 -3
  62. data/lib/tapyrus/message/prefilled_tx.rb +0 -4
  63. data/lib/tapyrus/message/reject.rb +0 -3
  64. data/lib/tapyrus/message/send_cmpct.rb +1 -3
  65. data/lib/tapyrus/message/send_headers.rb +0 -3
  66. data/lib/tapyrus/message/tx.rb +0 -4
  67. data/lib/tapyrus/message/ver_ack.rb +1 -5
  68. data/lib/tapyrus/message/version.rb +2 -5
  69. data/lib/tapyrus/message.rb +14 -16
  70. data/lib/tapyrus/mnemonic.rb +17 -15
  71. data/lib/tapyrus/network/connection.rb +0 -3
  72. data/lib/tapyrus/network/message_handler.rb +61 -60
  73. data/lib/tapyrus/network/peer.rb +13 -12
  74. data/lib/tapyrus/network/peer_discovery.rb +10 -9
  75. data/lib/tapyrus/network/pool.rb +12 -12
  76. data/lib/tapyrus/network.rb +0 -2
  77. data/lib/tapyrus/node/cli.rb +12 -14
  78. data/lib/tapyrus/node/configuration.rb +1 -3
  79. data/lib/tapyrus/node/spv.rb +2 -3
  80. data/lib/tapyrus/node.rb +1 -1
  81. data/lib/tapyrus/opcodes.rb +9 -7
  82. data/lib/tapyrus/out_point.rb +5 -5
  83. data/lib/tapyrus/rpc/http_server.rb +21 -22
  84. data/lib/tapyrus/rpc/request_handler.rb +16 -21
  85. data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
  86. data/lib/tapyrus/rpc.rb +1 -0
  87. data/lib/tapyrus/script/color.rb +10 -0
  88. data/lib/tapyrus/script/multisig.rb +13 -12
  89. data/lib/tapyrus/script/script.rb +93 -88
  90. data/lib/tapyrus/script/script_error.rb +1 -4
  91. data/lib/tapyrus/script/script_interpreter.rb +439 -399
  92. data/lib/tapyrus/script/tx_checker.rb +20 -10
  93. data/lib/tapyrus/secp256k1/native.rb +14 -15
  94. data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
  95. data/lib/tapyrus/secp256k1/ruby.rb +10 -12
  96. data/lib/tapyrus/secp256k1.rb +0 -4
  97. data/lib/tapyrus/slip39/share.rb +41 -29
  98. data/lib/tapyrus/slip39/sss.rb +92 -49
  99. data/lib/tapyrus/slip39.rb +20 -5
  100. data/lib/tapyrus/store/chain_entry.rb +0 -4
  101. data/lib/tapyrus/store/db/level_db.rb +5 -9
  102. data/lib/tapyrus/store/db.rb +0 -2
  103. data/lib/tapyrus/store/spv_chain.rb +11 -17
  104. data/lib/tapyrus/store.rb +1 -3
  105. data/lib/tapyrus/tx.rb +45 -37
  106. data/lib/tapyrus/tx_builder.rb +160 -0
  107. data/lib/tapyrus/tx_in.rb +1 -6
  108. data/lib/tapyrus/tx_out.rb +2 -7
  109. data/lib/tapyrus/util.rb +7 -9
  110. data/lib/tapyrus/validation.rb +12 -11
  111. data/lib/tapyrus/version.rb +1 -1
  112. data/lib/tapyrus/wallet/account.rb +22 -18
  113. data/lib/tapyrus/wallet/base.rb +12 -9
  114. data/lib/tapyrus/wallet/db.rb +6 -9
  115. data/lib/tapyrus/wallet/master_key.rb +2 -4
  116. data/lib/tapyrus.rb +7 -22
  117. data/tapyrusrb.gemspec +13 -14
  118. metadata +26 -7
  119. data/.travis.yml +0 -14
@@ -1,6 +1,5 @@
1
1
  module Tapyrus
2
2
  class TxChecker
3
-
4
3
  attr_reader :tx
5
4
  attr_reader :input_index
6
5
  attr_reader :amount
@@ -22,8 +21,8 @@ module Tapyrus
22
21
  script_sig = script_sig.htb
23
22
  hash_type = script_sig[-1].unpack('C').first
24
23
  sig = script_sig[0..-2]
25
- sighash = tx.sighash_for_input(input_index, script_code, hash_type: hash_type,
26
- amount: amount, sig_version: sig_version)
24
+ sighash =
25
+ tx.sighash_for_input(input_index, script_code, hash_type: hash_type, amount: amount, sig_version: sig_version)
27
26
  verify_sig(sig.bth, pubkey, sighash)
28
27
  end
29
28
 
@@ -33,7 +32,8 @@ module Tapyrus
33
32
  # @param [String] digest a message digest with binary format to be verified.
34
33
  # @return [Boolean] if check is passed return true, otherwise false.
35
34
  def verify_sig(sig, pubkey, digest, allow_hybrid: false)
36
- key_type = pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed]
35
+ key_type =
36
+ pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed]
37
37
  sig = sig.htb
38
38
  algo = sig.bytesize == 64 ? :schnorr : :ecdsa
39
39
  begin
@@ -49,8 +49,10 @@ module Tapyrus
49
49
  # distinguished by whether nLockTime < LOCKTIME_THRESHOLD.
50
50
 
51
51
  # We want to compare apples to apples, so fail the script unless the type of nLockTime being tested is the same as the nLockTime in the transaction.
52
- unless ((tx.lock_time < LOCKTIME_THRESHOLD && locktime < LOCKTIME_THRESHOLD) ||
53
- (tx.lock_time >= LOCKTIME_THRESHOLD && locktime >= LOCKTIME_THRESHOLD))
52
+ unless (
53
+ (tx.lock_time < LOCKTIME_THRESHOLD && locktime < LOCKTIME_THRESHOLD) ||
54
+ (tx.lock_time >= LOCKTIME_THRESHOLD && locktime >= LOCKTIME_THRESHOLD)
55
+ )
54
56
  return false
55
57
  end
56
58
 
@@ -68,6 +70,7 @@ module Tapyrus
68
70
 
69
71
  def check_sequence(sequence)
70
72
  tx_sequence = tx.inputs[input_index].sequence
73
+
71
74
  # Fail if the transaction's version number is not set high enough to trigger BIP 68 rules.
72
75
  return false if tx.features < 2
73
76
 
@@ -84,14 +87,21 @@ module Tapyrus
84
87
  # distinguished by whether sequence_masked < TxIn#SEQUENCE_LOCKTIME_TYPE_FLAG.
85
88
  # We want to compare apples to apples, so fail the script
86
89
  # unless the type of nSequenceMasked being tested is the same as the nSequenceMasked in the transaction.
87
- unless ((tx_sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) ||
88
- (tx_sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG))
90
+ unless (
91
+ (
92
+ tx_sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG &&
93
+ sequence_masked < TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG
94
+ ) ||
95
+ (
96
+ tx_sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG &&
97
+ sequence_masked >= TxIn::SEQUENCE_LOCKTIME_TYPE_FLAG
98
+ )
99
+ )
89
100
  return false
90
101
  end
91
102
 
92
103
  # Now that we know we're comparing apples-to-apples, the comparison is a simple numeric one.
93
104
  sequence_masked <= tx_sequence_masked
94
105
  end
95
-
96
106
  end
97
- end
107
+ end
@@ -3,7 +3,6 @@
3
3
 
4
4
  module Tapyrus
5
5
  module Secp256k1
6
-
7
6
  # binding for secp256k1 (https://github.com/chaintope/tapyrus-core/tree/v0.4.0/src/secp256k1)
8
7
  # tag: v0.4.0
9
8
  # this is not included by default, to enable set shared object path to ENV['SECP256K1_LIB_PATH']
@@ -80,8 +79,8 @@ module Tapyrus
80
79
  priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
81
80
  ret = secp256k1_ec_seckey_verify(context, priv_key)
82
81
  end
83
- private_key = priv_key.read_string(32).bth
84
- [private_key , generate_pubkey_in_context(context, private_key, compressed: compressed) ]
82
+ private_key = priv_key.read_string(32).bth
83
+ [private_key, generate_pubkey_in_context(context, private_key, compressed: compressed)]
85
84
  end
86
85
  end
87
86
 
@@ -92,9 +91,7 @@ module Tapyrus
92
91
  end
93
92
 
94
93
  def generate_pubkey(priv_key, compressed: true)
95
- with_context do |context|
96
- generate_pubkey_in_context(context, priv_key, compressed: compressed)
97
- end
94
+ with_context { |context| generate_pubkey_in_context(context, priv_key, compressed: compressed) }
98
95
  end
99
96
 
100
97
  # sign data.
@@ -152,13 +149,14 @@ module Tapyrus
152
149
 
153
150
  pubkey = FFI::MemoryPointer.new(:uchar, 65)
154
151
  pubkey_len = FFI::MemoryPointer.new(:uint64)
155
- result = if compressed
156
- pubkey_len.put_uint64(0, 33)
157
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
158
- else
159
- pubkey_len.put_uint64(0, 65)
160
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
161
- end
152
+ result =
153
+ if compressed
154
+ pubkey_len.put_uint64(0, 33)
155
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
156
+ else
157
+ pubkey_len.put_uint64(0, 65)
158
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
159
+ end
162
160
  raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
163
161
  pubkey.read_string(pubkey_len.read_uint64).bth
164
162
  end
@@ -196,7 +194,9 @@ module Tapyrus
196
194
 
197
195
  signature = FFI::MemoryPointer.new(:uchar, 64)
198
196
  msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
199
- raise 'Failed to generate schnorr signature.' unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1
197
+ unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1
198
+ raise 'Failed to generate schnorr signature.'
199
+ end
200
200
  signature.read_string(64)
201
201
  end
202
202
  end
@@ -241,7 +241,6 @@ module Tapyrus
241
241
  result == 1
242
242
  end
243
243
  end
244
-
245
244
  end
246
245
  end
247
246
  end
@@ -1,7 +1,6 @@
1
1
  module Tapyrus
2
2
  module Secp256k1
3
3
  module RFC6979
4
-
5
4
  INITIAL_V = '0101010101010101010101010101010101010101010101010101010101010101'.htb
6
5
  INITIAL_K = '0000000000000000000000000000000000000000000000000000000000000000'.htb
7
6
  ZERO_B = '00'.htb
@@ -17,17 +16,22 @@ module Tapyrus
17
16
  def generate_rfc6979_nonce(key_data, extra_entropy)
18
17
  v = INITIAL_V # 3.2.b
19
18
  k = INITIAL_K # 3.2.c
19
+
20
20
  # 3.2.d
21
21
  k = Tapyrus.hmac_sha256(k, v + ZERO_B + key_data + extra_entropy)
22
+
22
23
  # 3.2.e
23
24
  v = Tapyrus.hmac_sha256(k, v)
25
+
24
26
  # 3.2.f
25
27
  k = Tapyrus.hmac_sha256(k, v + ONE_B + key_data + extra_entropy)
28
+
26
29
  # 3.2.g
27
30
  v = Tapyrus.hmac_sha256(k, v)
31
+
28
32
  # 3.2.h
29
33
  t = ''
30
- 10000.times do
34
+ 10_000.times do
31
35
  v = Tapyrus.hmac_sha256(k, v)
32
36
  t = (t + v)
33
37
  t_num = t.bth.to_i(16)
@@ -37,7 +41,6 @@ module Tapyrus
37
41
  end
38
42
  raise 'A valid nonce was not found.'
39
43
  end
40
-
41
44
  end
42
45
  end
43
- end
46
+ end
@@ -1,10 +1,8 @@
1
1
  module Tapyrus
2
2
  module Secp256k1
3
-
4
3
  # secp256 module using ecdsa gem
5
4
  # https://github.com/DavidEGrayson/ruby_ecdsa
6
5
  module Ruby
7
-
8
6
  module_function
9
7
 
10
8
  # generate ec private key and public key
@@ -58,7 +56,7 @@ module Tapyrus
58
56
  end
59
57
  end
60
58
 
61
- alias :valid_sig? :verify_sig
59
+ alias valid_sig? verify_sig
62
60
 
63
61
  module_function :valid_sig?
64
62
 
@@ -68,7 +66,8 @@ module Tapyrus
68
66
  # @return [Boolean] If valid public key return true, otherwise false.
69
67
  def parse_ec_pubkey?(pubkey, allow_hybrid = false)
70
68
  begin
71
- point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1, allow_hybrid: allow_hybrid)
69
+ point =
70
+ ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1, allow_hybrid: allow_hybrid)
72
71
  ECDSA::Group::Secp256k1.valid_public_key?(point)
73
72
  rescue ECDSA::Format::DecodeError
74
73
  false
@@ -80,11 +79,11 @@ module Tapyrus
80
79
  def repack_pubkey(pubkey)
81
80
  p = pubkey.htb
82
81
  case p[0]
83
- when "\x06", "\x07"
84
- p[0] = "\x04"
85
- p
86
- else
87
- pubkey.htb
82
+ when "\x06", "\x07"
83
+ p[0] = "\x04"
84
+ p
85
+ else
86
+ pubkey.htb
88
87
  end
89
88
  end
90
89
 
@@ -104,7 +103,8 @@ module Tapyrus
104
103
  e = ECDSA.normalize_digest(data, GROUP.bit_length)
105
104
  s = point_field.mod(point_field.inverse(nonce) * (e + r * private_key))
106
105
 
107
- if s > (GROUP.order / 2) # convert low-s
106
+ if s > (GROUP.order / 2)
107
+ # convert low-s
108
108
  s = GROUP.order - s
109
109
  end
110
110
 
@@ -125,8 +125,6 @@ module Tapyrus
125
125
  false
126
126
  end
127
127
  end
128
-
129
128
  end
130
-
131
129
  end
132
130
  end
@@ -1,13 +1,9 @@
1
1
  module Tapyrus
2
-
3
2
  module Secp256k1
4
-
5
3
  GROUP = ECDSA::Group::Secp256k1
6
4
 
7
5
  autoload :Ruby, 'tapyrus/secp256k1/ruby'
8
6
  autoload :Native, 'tapyrus/secp256k1/native'
9
7
  autoload :RFC6979, 'tapyrus/secp256k1/rfc6979'
10
-
11
8
  end
12
-
13
9
  end
@@ -1,36 +1,35 @@
1
1
  module Tapyrus
2
2
  module SLIP39
3
-
4
3
  # Share of Shamir's Secret Sharing Scheme
5
4
  class Share
6
-
7
- attr_accessor :id # 15 bits, Integer
8
- attr_accessor :iteration_exp # 5 bits, Integer
9
- attr_accessor :group_index # 4 bits, Integer
10
- attr_accessor :group_threshold # 4 bits, Integer
11
- attr_accessor :group_count # 4 bits, Integer
12
- attr_accessor :member_index # 4 bits, Integer
5
+ attr_accessor :id # 15 bits, Integer
6
+ attr_accessor :iteration_exp # 5 bits, Integer
7
+ attr_accessor :group_index # 4 bits, Integer
8
+ attr_accessor :group_threshold # 4 bits, Integer
9
+ attr_accessor :group_count # 4 bits, Integer
10
+ attr_accessor :member_index # 4 bits, Integer
13
11
  attr_accessor :member_threshold # 4 bits, Integer
14
- attr_accessor :value # 8n bits, hex string.
15
- attr_accessor :checksum # 30 bits, Integer
12
+ attr_accessor :value # 8n bits, hex string.
13
+ attr_accessor :checksum # 30 bits, Integer
16
14
 
17
15
  # Recover Share from the mnemonic words
18
16
  # @param [Array{String}] words the mnemonic words
19
17
  # @return [Tapyrus::SLIP39::Share] a share
20
18
  def self.from_words(words)
21
19
  raise ArgumentError, 'Mnemonics should be an array of strings' unless words.is_a?(Array)
22
- indices = words.map do |word|
23
- index = Tapyrus::SLIP39::WORDS.index(word.downcase)
24
- raise IndexError, 'word not found in words list.' unless index
25
- index
26
- end
20
+ indices =
21
+ words.map do |word|
22
+ index = Tapyrus::SLIP39::WORDS.index(word.downcase)
23
+ raise IndexError, 'word not found in words list.' unless index
24
+ index
25
+ end
27
26
 
28
27
  raise ArgumentError, 'Invalid mnemonic length.' if indices.size < MIN_MNEMONIC_LENGTH_WORDS
29
28
  raise ArgumentError, 'Invalid mnemonic checksum.' unless verify_rs1024_checksum(indices)
30
29
 
31
30
  padding_length = (RADIX_BITS * (indices.size - METADATA_LENGTH_WORDS)) % 16
32
31
  raise ArgumentError, 'Invalid mnemonic length.' if padding_length > 8
33
- data = indices.map{|i|i.to_s(2).rjust(10, '0')}.join
32
+ data = indices.map { |i| i.to_s(2).rjust(10, '0') }.join
34
33
 
35
34
  s = self.new
36
35
  s.id = data[0...ID_LENGTH_BITS].to_i(2)
@@ -38,14 +37,17 @@ module Tapyrus
38
37
  s.group_index = data[20...24].to_i(2)
39
38
  s.group_threshold = data[24...28].to_i(2) + 1
40
39
  s.group_count = data[28...32].to_i(2) + 1
41
- raise ArgumentError, "Invalid mnemonic. Group threshold(#{s.group_threshold}) cannot be greater than group count(#{s.group_count})." if s.group_threshold > s.group_count
40
+ if s.group_threshold > s.group_count
41
+ raise ArgumentError,
42
+ "Invalid mnemonic. Group threshold(#{s.group_threshold}) cannot be greater than group count(#{s.group_count})."
43
+ end
42
44
  s.member_index = data[32...36].to_i(2)
43
45
  s.member_threshold = data[36...40].to_i(2) + 1
44
46
  value_length = data.length - 70
45
47
  start_index = 40 + padding_length
46
48
  end_index = start_index + value_length - padding_length
47
49
  padding_value = data[40...(40 + padding_length)]
48
- raise ArgumentError, "Invalid mnemonic. padding must only zero." unless padding_value.to_i(2) == 0
50
+ raise ArgumentError, 'Invalid mnemonic. padding must only zero.' unless padding_value.to_i(2) == 0
49
51
  s.value = data[start_index...end_index].to_i(2).to_even_length_hex
50
52
  s.checksum = data[(40 + value_length)..-1].to_i(2)
51
53
  s
@@ -55,25 +57,34 @@ module Tapyrus
55
57
  # @return [Array[String]] array of mnemonic word.
56
58
  def to_words
57
59
  indices = build_word_indices
58
- indices.map{|index| Tapyrus::SLIP39::WORDS[index]}
60
+ indices.map { |index| Tapyrus::SLIP39::WORDS[index] }
59
61
  end
60
62
 
61
63
  # Calculate checksum using current fields
62
64
  # @return [Integer] checksum
63
65
  def calculate_checksum
64
66
  indices = build_word_indices(false)
65
- create_rs1024_checksum(indices).map{|i|i.to_bits(10)}.join.to_i(2)
67
+ create_rs1024_checksum(indices).map { |i| i.to_bits(10) }.join.to_i(2)
66
68
  end
67
69
 
68
70
  def self.rs1024_polymod(values)
69
- gen = [0xe0e040, 0x1c1c080, 0x3838100, 0x7070200, 0xe0e0009, 0x1c0c2412, 0x38086c24, 0x3090fc48, 0x21b1f890, 0x3f3f120]
71
+ gen = [
72
+ 0xe0e040,
73
+ 0x1c1c080,
74
+ 0x3838100,
75
+ 0x7070200,
76
+ 0xe0e0009,
77
+ 0x1c0c2412,
78
+ 0x38086c24,
79
+ 0x3090fc48,
80
+ 0x21b1f890,
81
+ 0x3f3f120
82
+ ]
70
83
  chk = 1
71
84
  values.each do |v|
72
85
  b = (chk >> 20)
73
86
  chk = (chk & 0xfffff) << 10 ^ v
74
- 10.times do |i|
75
- chk ^= (((b >> i) & 1 == 1) ? gen[i] : 0)
76
- end
87
+ 10.times { |i| chk ^= (((b >> i) & 1 == 1) ? gen[i] : 0) }
77
88
  end
78
89
  chk
79
90
  end
@@ -89,14 +100,16 @@ module Tapyrus
89
100
  s << group_index.to_bits(4)
90
101
  s << (group_threshold - 1).to_bits(4)
91
102
  s << (group_count - 1).to_bits(4)
92
- raise StandardError, "Group threshold(#{group_threshold}) cannot be greater than group count(#{group_count})." if group_threshold > group_count
103
+ if group_threshold > group_count
104
+ raise StandardError, "Group threshold(#{group_threshold}) cannot be greater than group count(#{group_count})."
105
+ end
93
106
  s << member_index.to_bits(4)
94
107
  s << (member_threshold - 1).to_bits(4)
95
108
  value_length = value.to_i(16).bit_length
96
109
  padding_length = RADIX_BITS - (value_length % RADIX_BITS)
97
110
  s << value.to_i(16).to_bits(value_length + padding_length)
98
111
  s << checksum.to_bits(30) if include_checksum
99
- s.chars.each_slice(10).map{|index| index.join.to_i(2)}
112
+ s.chars.each_slice(10).map { |index| index.join.to_i(2) }
100
113
  end
101
114
 
102
115
  # Verify RS1024 checksum
@@ -112,11 +125,10 @@ module Tapyrus
112
125
  def create_rs1024_checksum(data)
113
126
  values = CUSTOMIZATION_STRING + data + Array.new(CHECKSUM_LENGTH_WORDS, 0)
114
127
  polymod = Tapyrus::SLIP39::Share.rs1024_polymod(values) ^ 1
115
- CHECKSUM_LENGTH_WORDS.times.to_a.reverse.map {|i|(polymod >> (10 * i)) & 1023 }
128
+ CHECKSUM_LENGTH_WORDS.times.to_a.reverse.map { |i| (polymod >> (10 * i)) & 1023 }
116
129
  end
117
130
 
118
131
  private_class_method :verify_rs1024_checksum
119
-
120
132
  end
121
133
  end
122
- end
134
+ end
@@ -2,10 +2,8 @@ require 'securerandom'
2
2
 
3
3
  module Tapyrus
4
4
  module SLIP39
5
-
6
5
  # Shamir's Secret Sharing
7
6
  class SSS
8
-
9
7
  include Tapyrus::Util
10
8
  extend Tapyrus::Util
11
9
 
@@ -37,39 +35,56 @@ module Tapyrus
37
35
  raise ArgumentError, 'Groups is empty.' if groups.empty?
38
36
  raise ArgumentError, 'Group threshold must be greater than 0.' if group_threshold.nil? || group_threshold < 1
39
37
  raise ArgumentError, 'Master secret does not specified.' unless secret
40
- raise ArgumentError, "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes." if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS
41
- raise ArgumentError, 'The length of the master secret in bytes must be an even number.' unless secret.bytesize.even?
42
- raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).' unless passphrase.ascii_only?
43
- raise ArgumentError, "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})." if group_threshold > groups.length
38
+ if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS
39
+ raise ArgumentError,
40
+ "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes."
41
+ end
42
+ unless secret.bytesize.even?
43
+ raise ArgumentError, 'The length of the master secret in bytes must be an even number.'
44
+ end
45
+ unless passphrase.ascii_only?
46
+ raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).'
47
+ end
48
+ if group_threshold > groups.length
49
+ raise ArgumentError,
50
+ "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})."
51
+ end
44
52
  groups.each do |threshold, count|
45
53
  raise ArgumentError, 'Group threshold must be greater than 0.' if threshold.nil? || threshold < 1
46
- raise ArgumentError, "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})." if threshold > count
47
- raise ArgumentError, "Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead." if threshold == 1 && count > 1
54
+ if threshold > count
55
+ raise ArgumentError,
56
+ "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})."
57
+ end
58
+ if threshold == 1 && count > 1
59
+ raise ArgumentError,
60
+ 'Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead.'
61
+ end
48
62
  end
49
63
 
50
- id = SecureRandom.random_number(32767) # 32767 is max number for 15 bits.
64
+ id = SecureRandom.random_number(32_767) # 32767 is max number for 15 bits.
51
65
  ems = encrypt(secret, passphrase, exp, id)
52
66
 
53
67
  group_shares = split_secret(group_threshold, groups.length, ems)
54
68
 
55
- shares = group_shares.map.with_index do |s, i|
56
- group_index, group_share = s[0], s[1]
57
- member_threshold, member_count = groups[i][0], groups[i][1]
58
- shares = split_secret(member_threshold, member_count, group_share)
59
- shares.map do |member_index, member_share|
60
- share = Tapyrus::SLIP39::Share.new
61
- share.id = id
62
- share.iteration_exp = exp
63
- share.group_index = group_index
64
- share.group_threshold = group_threshold
65
- share.group_count = groups.length
66
- share.member_index = member_index
67
- share.member_threshold = member_threshold
68
- share.value = member_share
69
- share.checksum = share.calculate_checksum
70
- share
69
+ shares =
70
+ group_shares.map.with_index do |s, i|
71
+ group_index, group_share = s[0], s[1]
72
+ member_threshold, member_count = groups[i][0], groups[i][1]
73
+ shares = split_secret(member_threshold, member_count, group_share)
74
+ shares.map do |member_index, member_share|
75
+ share = Tapyrus::SLIP39::Share.new
76
+ share.id = id
77
+ share.iteration_exp = exp
78
+ share.group_index = group_index
79
+ share.group_threshold = group_threshold
80
+ share.group_count = groups.length
81
+ share.member_index = member_index
82
+ share.member_threshold = member_threshold
83
+ share.value = member_share
84
+ share.checksum = share.calculate_checksum
85
+ share
86
+ end
71
87
  end
72
- end
73
88
  shares
74
89
  end
75
90
 
@@ -92,9 +107,15 @@ module Tapyrus
92
107
 
93
108
  shares.each do |share|
94
109
  raise ArgumentError, 'Invalid set of shares. All shares must have the same id.' unless id == share.id
95
- raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.' unless group_threshold == share.group_threshold
96
- raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.' unless group_count == share.group_count
97
- raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.' unless exp == share.iteration_exp
110
+ unless group_threshold == share.group_threshold
111
+ raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.'
112
+ end
113
+ unless group_count == share.group_count
114
+ raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.'
115
+ end
116
+ unless exp == share.iteration_exp
117
+ raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.'
118
+ end
98
119
  groups[share.group_index] ||= []
99
120
  groups[share.group_index] << share
100
121
  end
@@ -102,20 +123,29 @@ module Tapyrus
102
123
  group_shares = {}
103
124
  groups.each do |group_index, shares|
104
125
  member_threshold = shares.first.member_threshold
105
- raise ArgumentError, "Wrong number of mnemonics. Threshold is #{member_threshold}, but share count is #{shares.length}" if shares.length < member_threshold
126
+ if shares.length < member_threshold
127
+ raise ArgumentError,
128
+ "Wrong number of mnemonics. Threshold is #{member_threshold}, but share count is #{shares.length}"
129
+ end
106
130
  if shares.length == 1 && member_threshold == 1
107
131
  group_shares[group_index] = shares.first.value
108
132
  else
109
133
  value_length = shares.first.value.length
110
134
  x_coordinates = []
111
135
  shares.each do |share|
112
- raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.' unless member_threshold == share.member_threshold
113
- raise ArgumentError, 'Invalid set of shares. All share values must have the same length.' unless value_length == share.value.length
136
+ unless member_threshold == share.member_threshold
137
+ raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.'
138
+ end
139
+ unless value_length == share.value.length
140
+ raise ArgumentError, 'Invalid set of shares. All share values must have the same length.'
141
+ end
114
142
  x_coordinates << share.member_index
115
143
  end
116
144
  x_coordinates.uniq!
117
- raise ArgumentError, 'Invalid set of shares. Share indices must be unique.' unless x_coordinates.size == shares.size
118
- interpolate_shares = shares.map{|s|[s.member_index, s.value]}
145
+ unless x_coordinates.size == shares.size
146
+ raise ArgumentError, 'Invalid set of shares. Share indices must be unique.'
147
+ end
148
+ interpolate_shares = shares.map { |s| [s.member_index, s.value] }
119
149
 
120
150
  secret = interpolate(interpolate_shares, SECRET_INDEX)
121
151
  digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
@@ -129,9 +159,12 @@ module Tapyrus
129
159
 
130
160
  return decrypt(group_shares.values.first, passphrase, exp, id) if group_threshold == 1
131
161
 
132
- raise ArgumentError, "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}" if group_shares.length < group_threshold
162
+ if group_shares.length < group_threshold
163
+ raise ArgumentError,
164
+ "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}"
165
+ end
133
166
 
134
- interpolate_shares = group_shares.map{|k, v|[k, v]}
167
+ interpolate_shares = group_shares.map { |k, v| [k, v] }
135
168
  secret = interpolate(interpolate_shares, SECRET_INDEX)
136
169
  digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
137
170
  digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth
@@ -148,17 +181,24 @@ module Tapyrus
148
181
  # @param [Integer] x the x coordinate of the result.
149
182
  # @return [String] f(x) value with hex format.
150
183
  def self.interpolate(shares, x)
151
- s = shares.find{|s|s[0] == x}
184
+ s = shares.find { |s| s[0] == x }
152
185
  return s[1] if s
153
186
 
154
- log_prod = shares.sum{|s|LOG_TABLE[s[0] ^ x]}
187
+ log_prod = shares.sum { |s| LOG_TABLE[s[0] ^ x] }
155
188
 
156
189
  result = ('00' * shares.first[1].length).htb
157
190
  shares.each do |share|
158
- log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum{|s|LOG_TABLE[share[0] ^ s[0]]}) % 255
159
- result = share[1].htb.bytes.each.map.with_index do |v, i|
160
- (result[i].bti ^ (v == 0 ? 0 : (EXP_TABLE[(LOG_TABLE[v] + log_basis_eval) % 255]))).itb
161
- end.join
191
+ log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum { |s| LOG_TABLE[share[0] ^ s[0]] }) % 255
192
+ result =
193
+ share[1]
194
+ .htb
195
+ .bytes
196
+ .each
197
+ .map
198
+ .with_index do |v, i|
199
+ (result[i].bti ^ (v == 0 ? 0 : (EXP_TABLE[(LOG_TABLE[v] + log_basis_eval) % 255]))).itb
200
+ end
201
+ .join
162
202
  end
163
203
  result.bth
164
204
  end
@@ -220,26 +260,29 @@ module Tapyrus
220
260
  # @return [Array[Integer, String]] the array of split secret.
221
261
  def self.split_secret(threshold, count, secret)
222
262
  raise ArgumentError, "The requested threshold (#{threshold}) must be a positive integer." if threshold < 1
223
- raise ArgumentError, "The requested threshold (#{threshold}) must not exceed the number of shares (#{count})." if threshold > count
224
- raise ArgumentError, "The requested number of shares (#{count}) must not exceed #{MAX_SHARE_COUNT}." if count > MAX_SHARE_COUNT
263
+ if threshold > count
264
+ raise ArgumentError, "The requested threshold (#{threshold}) must not exceed the number of shares (#{count})."
265
+ end
266
+ if count > MAX_SHARE_COUNT
267
+ raise ArgumentError, "The requested number of shares (#{count}) must not exceed #{MAX_SHARE_COUNT}."
268
+ end
225
269
 
226
- return count.times.map{|i|[i, secret]} if threshold == 1 # if the threshold is 1, digest of the share is not used.
270
+ return count.times.map { |i| [i, secret] } if threshold == 1 # if the threshold is 1, digest of the share is not used.
227
271
 
228
272
  random_share_count = threshold - 2
229
273
 
230
- shares = random_share_count.times.map{|i|[i, SecureRandom.hex(secret.htb.bytesize)]}
274
+ shares = random_share_count.times.map { |i| [i, SecureRandom.hex(secret.htb.bytesize)] }
231
275
  random_part = SecureRandom.hex(secret.htb.bytesize - DIGEST_LENGTH_BYTES)
232
276
  digest = create_digest(secret, random_part)
233
277
 
234
278
  base_shares = shares + [[DIGEST_INDEX, digest + random_part], [SECRET_INDEX, secret]]
235
279
 
236
- (random_share_count...count).each { |i| shares << [i, interpolate(base_shares, i)]}
280
+ (random_share_count...count).each { |i| shares << [i, interpolate(base_shares, i)] }
237
281
 
238
282
  shares
239
283
  end
240
284
 
241
285
  private_class_method :split_secret, :get_salt, :interpolate, :encrypt, :decrypt, :create_digest
242
-
243
286
  end
244
287
  end
245
- end
288
+ end