tapyrus 0.2.7 → 0.2.8
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets.rb +0 -2
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/schnorr.rb +2 -3
- data/lib/schnorr/signature.rb +3 -6
- data/lib/tapyrus.rb +6 -22
- data/lib/tapyrus/base58.rb +7 -6
- data/lib/tapyrus/block.rb +1 -2
- data/lib/tapyrus/block_header.rb +15 -9
- data/lib/tapyrus/bloom_filter.rb +5 -3
- data/lib/tapyrus/chain_params.rb +1 -4
- data/lib/tapyrus/chainparams/dev.yml +3 -2
- data/lib/tapyrus/chainparams/prod.yml +3 -2
- data/lib/tapyrus/constants.rb +29 -23
- data/lib/tapyrus/errors.rb +1 -3
- data/lib/tapyrus/ext.rb +1 -1
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext_key.rb +38 -34
- data/lib/tapyrus/key.rb +31 -35
- data/lib/tapyrus/key_path.rb +15 -12
- data/lib/tapyrus/logger.rb +20 -16
- data/lib/tapyrus/merkle_tree.rb +19 -20
- data/lib/tapyrus/message.rb +14 -16
- data/lib/tapyrus/message/addr.rb +1 -7
- data/lib/tapyrus/message/base.rb +0 -3
- data/lib/tapyrus/message/block.rb +2 -9
- data/lib/tapyrus/message/block_transaction_request.rb +3 -6
- data/lib/tapyrus/message/block_transactions.rb +2 -6
- data/lib/tapyrus/message/block_txn.rb +0 -4
- data/lib/tapyrus/message/cmpct_block.rb +1 -7
- data/lib/tapyrus/message/error.rb +1 -4
- data/lib/tapyrus/message/fee_filter.rb +1 -4
- data/lib/tapyrus/message/filter_add.rb +0 -4
- data/lib/tapyrus/message/filter_clear.rb +0 -4
- data/lib/tapyrus/message/filter_load.rb +2 -5
- data/lib/tapyrus/message/get_addr.rb +0 -4
- data/lib/tapyrus/message/get_block_txn.rb +0 -4
- data/lib/tapyrus/message/get_blocks.rb +0 -3
- data/lib/tapyrus/message/get_data.rb +1 -4
- data/lib/tapyrus/message/get_headers.rb +1 -3
- data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
- data/lib/tapyrus/message/headers.rb +0 -4
- data/lib/tapyrus/message/headers_parser.rb +3 -8
- data/lib/tapyrus/message/inv.rb +1 -4
- data/lib/tapyrus/message/inventories_parser.rb +2 -7
- data/lib/tapyrus/message/inventory.rb +12 -5
- data/lib/tapyrus/message/mem_pool.rb +0 -4
- data/lib/tapyrus/message/merkle_block.rb +4 -9
- data/lib/tapyrus/message/network_addr.rb +7 -6
- data/lib/tapyrus/message/not_found.rb +0 -3
- data/lib/tapyrus/message/ping.rb +0 -3
- data/lib/tapyrus/message/pong.rb +0 -3
- data/lib/tapyrus/message/prefilled_tx.rb +0 -4
- data/lib/tapyrus/message/reject.rb +0 -3
- data/lib/tapyrus/message/send_cmpct.rb +1 -3
- data/lib/tapyrus/message/send_headers.rb +0 -3
- data/lib/tapyrus/message/tx.rb +0 -4
- data/lib/tapyrus/message/ver_ack.rb +1 -5
- data/lib/tapyrus/message/version.rb +2 -5
- data/lib/tapyrus/mnemonic.rb +17 -15
- data/lib/tapyrus/network.rb +0 -2
- data/lib/tapyrus/network/connection.rb +0 -3
- data/lib/tapyrus/network/message_handler.rb +61 -60
- data/lib/tapyrus/network/peer.rb +13 -12
- data/lib/tapyrus/network/peer_discovery.rb +3 -5
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/node.rb +1 -1
- data/lib/tapyrus/node/cli.rb +12 -14
- data/lib/tapyrus/node/configuration.rb +1 -3
- data/lib/tapyrus/node/spv.rb +2 -3
- data/lib/tapyrus/opcodes.rb +9 -7
- data/lib/tapyrus/out_point.rb +5 -5
- data/lib/tapyrus/rpc/http_server.rb +21 -22
- data/lib/tapyrus/rpc/request_handler.rb +42 -44
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +53 -25
- data/lib/tapyrus/script/color.rb +10 -0
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +72 -71
- data/lib/tapyrus/script/script_error.rb +1 -4
- data/lib/tapyrus/script/script_interpreter.rb +439 -399
- data/lib/tapyrus/script/tx_checker.rb +20 -10
- data/lib/tapyrus/secp256k1.rb +0 -4
- data/lib/tapyrus/secp256k1/native.rb +14 -15
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
- data/lib/tapyrus/secp256k1/ruby.rb +10 -12
- data/lib/tapyrus/slip39.rb +20 -5
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +101 -57
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- data/lib/tapyrus/tx.rb +45 -37
- data/lib/tapyrus/tx_builder.rb +158 -0
- data/lib/tapyrus/tx_in.rb +1 -6
- data/lib/tapyrus/tx_out.rb +2 -7
- data/lib/tapyrus/util.rb +7 -9
- data/lib/tapyrus/validation.rb +12 -11
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +22 -18
- data/lib/tapyrus/wallet/base.rb +12 -9
- data/lib/tapyrus/wallet/db.rb +6 -9
- data/lib/tapyrus/wallet/master_key.rb +2 -4
- data/tapyrusrb.gemspec +13 -14
- metadata +21 -4
- 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 =
|
26
|
-
|
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 =
|
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 (
|
53
|
-
|
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 (
|
88
|
-
|
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
|
data/lib/tapyrus/secp256k1.rb
CHANGED
@@ -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 =
|
84
|
-
[private_key
|
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
|
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 =
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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)
|
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
|
data/lib/tapyrus/slip39.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module SLIP39
|
3
|
-
|
4
3
|
WORDS = File.readlines("#{__dir__}/slip39/wordlist/english.txt").map(&:strip)
|
5
4
|
|
6
5
|
module_function
|
@@ -15,37 +14,53 @@ module Tapyrus
|
|
15
14
|
|
16
15
|
# The length of the radix in bits.
|
17
16
|
RADIX_BITS = 10
|
17
|
+
|
18
18
|
# The number of words in the wordlist.
|
19
|
-
RADIX = 2
|
19
|
+
RADIX = 2**RADIX_BITS
|
20
|
+
|
20
21
|
# The length of the random identifier in bits.
|
21
22
|
ID_LENGTH_BITS = 15
|
23
|
+
|
22
24
|
# The length of the iteration exponent in bits.
|
23
25
|
ITERATION_EXP_LENGTH_BITS = 5
|
26
|
+
|
24
27
|
# The length of the random identifier and iteration exponent in words.
|
25
28
|
ID_EXP_LENGTH_WORDS = bits_to_words(ID_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS)
|
29
|
+
|
26
30
|
# The maximum number of shares that can be created.
|
27
31
|
MAX_SHARE_COUNT = 16
|
32
|
+
|
28
33
|
# The length of the RS1024 checksum in words.
|
29
34
|
CHECKSUM_LENGTH_WORDS = 3
|
35
|
+
|
30
36
|
# The length of the digest of the shared secret in bytes.
|
31
37
|
DIGEST_LENGTH_BYTES = 4
|
38
|
+
|
32
39
|
# The customization string used in the RS1024 checksum and in the PBKDF2 salt.
|
33
40
|
CUSTOMIZATION_STRING = 'shamir'.bytes
|
41
|
+
|
34
42
|
# The length of the mnemonic in words without the share value.
|
35
43
|
METADATA_LENGTH_WORDS = ID_EXP_LENGTH_WORDS + 2 + CHECKSUM_LENGTH_WORDS
|
44
|
+
|
36
45
|
# The minimum allowed entropy of the master secret.
|
37
46
|
MIN_STRENGTH_BITS = 128
|
47
|
+
|
38
48
|
# The minimum allowed length of the mnemonic in words.
|
39
49
|
MIN_MNEMONIC_LENGTH_WORDS = METADATA_LENGTH_WORDS + bits_to_words(MIN_STRENGTH_BITS)
|
50
|
+
|
40
51
|
# The minimum number of iterations to use in PBKDF2.
|
41
|
-
BASE_ITERATION_COUNT =
|
52
|
+
BASE_ITERATION_COUNT = 10_000
|
53
|
+
|
42
54
|
# The number of rounds to use in the Feistel cipher.
|
43
55
|
ROUND_COUNT = 4
|
56
|
+
|
44
57
|
# The index of the share containing the shared secret.
|
45
58
|
SECRET_INDEX = 255
|
59
|
+
|
46
60
|
# The index of the share containing the digest of the shared secret.
|
47
61
|
DIGEST_INDEX = 254
|
48
62
|
|
63
|
+
# prettier-ignore
|
49
64
|
EXP_TABLE = [
|
50
65
|
1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19,
|
51
66
|
53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34,
|
@@ -66,6 +81,7 @@ module Tapyrus
|
|
66
81
|
57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246
|
67
82
|
]
|
68
83
|
|
84
|
+
# prettier-ignore
|
69
85
|
LOG_TABLE = [
|
70
86
|
0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
|
71
87
|
100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28,
|
@@ -88,6 +104,5 @@ module Tapyrus
|
|
88
104
|
|
89
105
|
autoload :SSS, 'tapyrus/slip39/sss'
|
90
106
|
autoload :Share, 'tapyrus/slip39/share'
|
91
|
-
|
92
107
|
end
|
93
|
-
end
|
108
|
+
end
|
data/lib/tapyrus/slip39/share.rb
CHANGED
@@ -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 :
|
8
|
-
attr_accessor :
|
9
|
-
attr_accessor :
|
10
|
-
attr_accessor :
|
11
|
-
attr_accessor :
|
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
|
15
|
-
attr_accessor :checksum
|
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 =
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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,
|
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 = [
|
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
|
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
|
-
|
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
|