bitcoinrb 1.14.0 → 1.16.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.
- checksums.yaml +4 -4
- data/bitcoinrb.gemspec +5 -4
- data/lib/bitcoin/bip321_uri.rb +95 -32
- data/lib/bitcoin/bip324/ell_swift_pubkey.rb +3 -1
- data/lib/bitcoin/bip324/fs_chacha20.rb +3 -0
- data/lib/bitcoin/bip324.rb +1 -1
- data/lib/bitcoin/bip85_entropy.rb +79 -29
- data/lib/bitcoin/bit_stream.rb +2 -2
- data/lib/bitcoin/block.rb +46 -7
- data/lib/bitcoin/block_filter.rb +3 -2
- data/lib/bitcoin/block_header.rb +31 -8
- data/lib/bitcoin/bloom_filter.rb +5 -2
- data/lib/bitcoin/chainparams/regtest.yml +0 -1
- data/lib/bitcoin/chainparams/signet.yml +1 -1
- data/lib/bitcoin/constants.rb +13 -3
- data/lib/bitcoin/descriptor/combo.rb +13 -1
- data/lib/bitcoin/descriptor/expression.rb +35 -15
- data/lib/bitcoin/descriptor/multi.rb +9 -1
- data/lib/bitcoin/descriptor/multi_a.rb +4 -0
- data/lib/bitcoin/descriptor/musig.rb +1 -0
- data/lib/bitcoin/descriptor/pk.rb +7 -0
- data/lib/bitcoin/descriptor/pkh.rb +5 -0
- data/lib/bitcoin/descriptor/raw.rb +1 -1
- data/lib/bitcoin/descriptor/sh.rb +1 -0
- data/lib/bitcoin/descriptor/sp.rb +2 -2
- data/lib/bitcoin/descriptor/tr.rb +29 -3
- data/lib/bitcoin/descriptor/wpkh.rb +1 -0
- data/lib/bitcoin/descriptor/wsh.rb +1 -0
- data/lib/bitcoin/descriptor.rb +59 -35
- data/lib/bitcoin/ext/array_ext.rb +3 -0
- data/lib/bitcoin/ext/ecdsa.rb +7 -1
- data/lib/bitcoin/ext_key.rb +10 -0
- data/lib/bitcoin/gcs_filter.rb +21 -6
- data/lib/bitcoin/key.rb +76 -17
- data/lib/bitcoin/key_path.rb +8 -5
- data/lib/bitcoin/message/addr.rb +1 -0
- data/lib/bitcoin/message/addr_v2.rb +1 -0
- data/lib/bitcoin/message/base.rb +8 -1
- data/lib/bitcoin/message/block.rb +4 -8
- data/lib/bitcoin/message/block_transaction_request.rb +11 -8
- data/lib/bitcoin/message/block_transactions.rb +2 -1
- data/lib/bitcoin/message/cf_parser.rb +1 -0
- data/lib/bitcoin/message/cfcheckpt.rb +3 -3
- data/lib/bitcoin/message/cfheaders.rb +5 -4
- data/lib/bitcoin/message/cfilter.rb +3 -3
- data/lib/bitcoin/message/error.rb +3 -1
- data/lib/bitcoin/message/fee_filter.rb +1 -0
- data/lib/bitcoin/message/filter_add.rb +3 -1
- data/lib/bitcoin/message/filter_load.rb +4 -4
- data/lib/bitcoin/message/get_cfcheckpt.rb +1 -0
- data/lib/bitcoin/message/header_and_short_ids.rb +26 -11
- data/lib/bitcoin/message/headers.rb +10 -2
- data/lib/bitcoin/message/headers_parser.rb +4 -2
- data/lib/bitcoin/message/inventories_parser.rb +2 -1
- data/lib/bitcoin/message/inventory.rb +15 -6
- data/lib/bitcoin/message/merkle_block.rb +4 -4
- data/lib/bitcoin/message/network_addr.rb +13 -13
- data/lib/bitcoin/message/ping.rb +1 -0
- data/lib/bitcoin/message/pong.rb +1 -0
- data/lib/bitcoin/message/reject.rb +1 -0
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/send_tx_rcncl.rb +1 -1
- data/lib/bitcoin/message/version.rb +23 -8
- data/lib/bitcoin/message.rb +15 -0
- data/lib/bitcoin/message_sign.rb +67 -15
- data/lib/bitcoin/mnemonic.rb +8 -0
- data/lib/bitcoin/out_point.rb +6 -2
- data/lib/bitcoin/partial_tree.rb +41 -28
- data/lib/bitcoin/payment_code.rb +26 -8
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -2
- data/lib/bitcoin/psbt/input.rb +199 -48
- data/lib/bitcoin/psbt/key_origin_info.rb +2 -0
- data/lib/bitcoin/psbt/output.rb +9 -6
- data/lib/bitcoin/psbt/proprietary.rb +6 -3
- data/lib/bitcoin/psbt/tx.rb +9 -4
- data/lib/bitcoin/psbt.rb +46 -0
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +54 -13
- data/lib/bitcoin/rpc/error.rb +43 -0
- data/lib/bitcoin/rpc.rb +1 -0
- data/lib/bitcoin/script/script.rb +114 -33
- data/lib/bitcoin/script/script_interpreter.rb +11 -6
- data/lib/bitcoin/script/tx_checker.rb +7 -2
- data/lib/bitcoin/script_witness.rb +5 -1
- data/lib/bitcoin/secp256k1/ruby.rb +10 -4
- data/lib/bitcoin/silent_payment.rb +6 -3
- data/lib/bitcoin/slip39/share.rb +42 -12
- data/lib/bitcoin/slip39/sss.rb +33 -12
- data/lib/bitcoin/slip39.rb +13 -5
- data/lib/bitcoin/taproot/control_block.rb +6 -1
- data/lib/bitcoin/taproot/custom_depth_builder.rb +12 -0
- data/lib/bitcoin/tx.rb +35 -14
- data/lib/bitcoin/tx_in.rb +2 -2
- data/lib/bitcoin/tx_out.rb +2 -2
- data/lib/bitcoin/util.rb +49 -28
- data/lib/bitcoin/validation.rb +64 -12
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +35 -11
- data/lib/bitcoin/wallet/base.rb +40 -15
- data/lib/bitcoin/wallet/utxo.rb +22 -4
- data/lib/bitcoin.rb +16 -4
- metadata +24 -9
|
@@ -92,15 +92,26 @@ module Bitcoin
|
|
|
92
92
|
new << WITNESS_VERSION_V0 << redeem_script.to_sha256
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
# The range of a decimal number in a script string, as ParseScript in Bitcoin Core: a number
|
|
96
|
+
# outside of it is not valid in a script.
|
|
97
|
+
MAX_SCRIPT_STRING_NUMBER = 0xffffffff
|
|
98
|
+
|
|
95
99
|
# generate script from string.
|
|
100
|
+
# A number is pushed as a script number, as ParseScript in Bitcoin Core does: 0, -1 and 1 to 16
|
|
101
|
+
# become OP_0, OP_1NEGATE and OP_1 to OP_16, and any other number a push of its minimal encoding.
|
|
102
|
+
# @param [String] string script string such as the result of #to_s.
|
|
103
|
+
# @return [Bitcoin::Script]
|
|
104
|
+
# @raise [ArgumentError] If a number is out of -0xffffffff..0xffffffff or a token is not valid.
|
|
96
105
|
def self.from_string(string)
|
|
97
106
|
script = new
|
|
98
107
|
string.split(' ').each do |v|
|
|
99
108
|
opcode = Opcodes.name_to_opcode(v)
|
|
100
109
|
if opcode
|
|
101
110
|
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
|
111
|
+
elsif number_token?(v)
|
|
112
|
+
script.send(:push_script_number, v.to_i)
|
|
102
113
|
else
|
|
103
|
-
script <<
|
|
114
|
+
script << v
|
|
104
115
|
end
|
|
105
116
|
end
|
|
106
117
|
script
|
|
@@ -112,7 +123,8 @@ module Bitcoin
|
|
|
112
123
|
# @param [String] token a token of the script string.
|
|
113
124
|
# @return [Boolean] whether +token+ should be interpreted as a script number.
|
|
114
125
|
def self.number_token?(token)
|
|
115
|
-
return
|
|
126
|
+
return true if token =~ /\A-[0-9]+\z/ # A negative number cannot be pushed data.
|
|
127
|
+
return false unless token =~ /\A[0-9]+\z/
|
|
116
128
|
token.length < 12 || token.length.odd?
|
|
117
129
|
end
|
|
118
130
|
|
|
@@ -153,31 +165,31 @@ module Bitcoin
|
|
|
153
165
|
s.chunks << opcode
|
|
154
166
|
return s
|
|
155
167
|
end
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
size_length, format = case pushcode
|
|
169
|
+
when OP_PUSHDATA1 then [1, 'C']
|
|
170
|
+
when OP_PUSHDATA2 then [2, 'v']
|
|
171
|
+
when OP_PUSHDATA4 then [4, 'V']
|
|
172
|
+
else [0, nil]
|
|
173
|
+
end
|
|
174
|
+
if size_length > 0
|
|
175
|
+
packed_size = buf.read(size_length)
|
|
176
|
+
# A length field cut short. Keep the bytes as they are so that the payload round trips,
|
|
177
|
+
# and leave it to the interpreter to reject the push.
|
|
178
|
+
if packed_size.bytesize < size_length
|
|
179
|
+
s.chunks << (opcode + packed_size)
|
|
180
|
+
return s
|
|
181
|
+
end
|
|
182
|
+
len = packed_size.unpack1(format)
|
|
171
183
|
else
|
|
172
|
-
|
|
173
|
-
s.chunks << chunk
|
|
184
|
+
len = pushcode
|
|
174
185
|
end
|
|
186
|
+
# StringIO#read returns nil at EOF for a positive length, which is a push whose data is missing entirely.
|
|
187
|
+
s.chunks << (opcode + (packed_size || '') + buf.read(len).to_s)
|
|
175
188
|
else
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
end
|
|
189
|
+
# Every non-push byte is an opcode of its own, defined or not, like GetOp in Bitcoin Core.
|
|
190
|
+
# An undefined opcode only fails the script when it is executed, so the bytes after it
|
|
191
|
+
# still have to be parsed for OP_ELSE and OP_ENDIF in an unexecuted branch to be found.
|
|
192
|
+
s.chunks << opcode
|
|
181
193
|
end
|
|
182
194
|
end
|
|
183
195
|
s
|
|
@@ -215,8 +227,23 @@ module Bitcoin
|
|
|
215
227
|
end
|
|
216
228
|
|
|
217
229
|
# check whether standard script.
|
|
230
|
+
# Whether this script is a standard scriptPubKey, as IsStandard in Bitcoin Core judges it.
|
|
231
|
+
# An OP_RETURN script is standard up to MAX_OP_RETURN_RELAY bytes, which a tx shares among all of its
|
|
232
|
+
# OP_RETURN outputs. A bare multisig has at most 3 public keys.
|
|
233
|
+
# @return [Boolean]
|
|
218
234
|
def standard?
|
|
219
|
-
p2pkh?
|
|
235
|
+
p2pkh? || p2sh? || p2wpkh? || p2wsh? || p2tr? || p2a? || witness_unknown? ||
|
|
236
|
+
standard_p2pk? || standard_multisig? || standard_op_return?
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Whether this script is a witness program of a version which is not defined yet, which Bitcoin Core relays
|
|
240
|
+
# so that a future soft fork can use it. A version 0 program is defined only for 20 and 32 bytes.
|
|
241
|
+
# @return [Boolean]
|
|
242
|
+
def witness_unknown?
|
|
243
|
+
return false unless witness_program?
|
|
244
|
+
version, program = witness_data
|
|
245
|
+
return false if version == 0
|
|
246
|
+
!(version == 1 && (program.bytesize == 32 || program == P2A_PROGRAM))
|
|
220
247
|
end
|
|
221
248
|
|
|
222
249
|
# Check whether this script is a P2PKH format script.
|
|
@@ -287,13 +314,46 @@ module Bitcoin
|
|
|
287
314
|
sig_count <= pubkey_count
|
|
288
315
|
end
|
|
289
316
|
|
|
317
|
+
# Whether a public key is of a valid size for its header byte, as CPubKey::ValidSize in Bitcoin Core checks it.
|
|
318
|
+
def self.valid_pubkey_size?(data)
|
|
319
|
+
return false if data.nil? || data.empty?
|
|
320
|
+
case data.getbyte(0)
|
|
321
|
+
when 0x02, 0x03 then data.bytesize == 33
|
|
322
|
+
when 0x04, 0x06, 0x07 then data.bytesize == 65
|
|
323
|
+
else false
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
private_class_method :valid_pubkey_size?
|
|
327
|
+
|
|
328
|
+
# Whether this script is a P2PK script which is standard.
|
|
329
|
+
# @return [Boolean]
|
|
330
|
+
def standard_p2pk?
|
|
331
|
+
chunks.size == 2 && chunks[0].pushdata? && chunks[1].ord == OP_CHECKSIG &&
|
|
332
|
+
Bitcoin::Script.send(:valid_pubkey_size?, chunks[0].pushed_data)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Whether this script is a bare multisig script which is standard, m-of-n with 1 <= m <= n <= 3.
|
|
336
|
+
# @return [Boolean]
|
|
337
|
+
def standard_multisig?
|
|
338
|
+
return false if chunks.size < 4 || chunks.last.ord != OP_CHECKMULTISIG
|
|
339
|
+
m = chunks[0].opcode
|
|
340
|
+
n = chunks[-2].opcode
|
|
341
|
+
return false unless m && n && (OP_1..OP_16).cover?(m) && (OP_1..OP_16).cover?(n)
|
|
342
|
+
m = Opcodes.opcode_to_small_int(m)
|
|
343
|
+
n = Opcodes.opcode_to_small_int(n)
|
|
344
|
+
keys = chunks[1...-2]
|
|
345
|
+
return false unless keys.size == n && keys.all? { |k| k.pushdata? && Bitcoin::Script.send(:valid_pubkey_size?, k.pushed_data) }
|
|
346
|
+
n <= 3 && m <= n
|
|
347
|
+
end
|
|
348
|
+
|
|
290
349
|
def op_return?
|
|
291
350
|
chunks.size >= 1 && chunks[0].ord == OP_RETURN
|
|
292
351
|
end
|
|
293
352
|
|
|
353
|
+
# Whether this script is an OP_RETURN script which is standard, OP_RETURN followed by pushes only.
|
|
354
|
+
# @return [Boolean]
|
|
294
355
|
def standard_op_return?
|
|
295
|
-
op_return? && size <= MAX_OP_RETURN_RELAY &&
|
|
296
|
-
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
|
356
|
+
op_return? && size <= MAX_OP_RETURN_RELAY && chunks[1..-1].all? { |c| c.opcode.nil? || c.opcode <= OP_16 }
|
|
297
357
|
end
|
|
298
358
|
|
|
299
359
|
def op_return_data
|
|
@@ -334,11 +394,14 @@ module Bitcoin
|
|
|
334
394
|
end
|
|
335
395
|
|
|
336
396
|
# get witness commitment
|
|
397
|
+
# The witness commitment this script carries, if it is a commitment output as Bitcoin Core's
|
|
398
|
+
# GetWitnessCommitmentIndex recognizes it: at least 38 bytes which begin with OP_RETURN, a push of
|
|
399
|
+
# 0x24 bytes and the header aa21a9ed. Anything after the 32 byte commitment is ignored.
|
|
400
|
+
# @return [String, nil] The commitment with hex format, or nil if this is not a commitment output.
|
|
337
401
|
def witness_commitment
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
buf.read(32).bth
|
|
402
|
+
payload = to_payload
|
|
403
|
+
return nil if payload.bytesize < 38 || payload[0, 6] != "\x6a\x24".b + WITNESS_COMMITMENT_HEADER.htb
|
|
404
|
+
payload[6, 32].bth
|
|
342
405
|
end
|
|
343
406
|
|
|
344
407
|
# If this script is witness program, return its script code,
|
|
@@ -372,6 +435,24 @@ module Bitcoin
|
|
|
372
435
|
end
|
|
373
436
|
end
|
|
374
437
|
|
|
438
|
+
# Push +n+ as a script number. Unlike #push_int, a number which is also an opcode, e.g. 172
|
|
439
|
+
# (OP_CHECKSIG), is not appended as that opcode.
|
|
440
|
+
# @raise [ArgumentError] If +n+ is out of -0xffffffff..0xffffffff.
|
|
441
|
+
def push_script_number(n)
|
|
442
|
+
unless n.between?(-MAX_SCRIPT_STRING_NUMBER, MAX_SCRIPT_STRING_NUMBER)
|
|
443
|
+
raise ArgumentError, 'decimal numeric value only allowed in the range -0xFFFFFFFF...0xFFFFFFFF.'
|
|
444
|
+
end
|
|
445
|
+
if n == 0
|
|
446
|
+
chunks << Opcodes::OP_0.chr
|
|
447
|
+
elsif n == -1 || n.between?(1, 16)
|
|
448
|
+
chunks << Opcodes.small_int_to_opcode(n).chr
|
|
449
|
+
else
|
|
450
|
+
append_data(Script.encode_number(n))
|
|
451
|
+
end
|
|
452
|
+
self
|
|
453
|
+
end
|
|
454
|
+
private :push_script_number
|
|
455
|
+
|
|
375
456
|
# push integer to stack.
|
|
376
457
|
def push_int(n)
|
|
377
458
|
begin
|
|
@@ -425,8 +506,8 @@ module Bitcoin
|
|
|
425
506
|
if v
|
|
426
507
|
v
|
|
427
508
|
else
|
|
428
|
-
|
|
429
|
-
|
|
509
|
+
if c.valid_pushdata_length?
|
|
510
|
+
data = c.pushed_data
|
|
430
511
|
if data.bytesize <= 4
|
|
431
512
|
Script.decode_number(data.bth) # for scriptnum
|
|
432
513
|
else
|
|
@@ -67,7 +67,7 @@ module Bitcoin
|
|
|
67
67
|
raise 'stack cannot be empty.' if stack.empty?
|
|
68
68
|
redeem_script = Bitcoin::Script.parse_from_payload(stack.pop.htb)
|
|
69
69
|
return false unless eval_script(redeem_script, :base)
|
|
70
|
-
return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last)
|
|
70
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last.htb)
|
|
71
71
|
|
|
72
72
|
# P2SH witness program
|
|
73
73
|
if flag?(SCRIPT_VERIFY_WITNESS) && redeem_script.witness_program?
|
|
@@ -77,6 +77,8 @@ module Bitcoin
|
|
|
77
77
|
|
|
78
78
|
version, program = redeem_script.witness_data
|
|
79
79
|
return false unless verify_witness_program(witness, version, program, true)
|
|
80
|
+
# Bypass the cleanstack check at the end. The actual stack is obviously not clean for witness programs.
|
|
81
|
+
stack.resize!(1, Script.encode_number(0))
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
|
|
@@ -182,7 +184,7 @@ module Bitcoin
|
|
|
182
184
|
return false unless eval_script(script_pubkey, sig_version, opts: opts)
|
|
183
185
|
|
|
184
186
|
return set_error(SCRIPT_ERR_CLEANSTACK) unless stack.size == 1
|
|
185
|
-
return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last)
|
|
187
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last.htb)
|
|
186
188
|
true
|
|
187
189
|
end
|
|
188
190
|
|
|
@@ -324,7 +326,7 @@ module Bitcoin
|
|
|
324
326
|
3.times { stack << stack[-3] }
|
|
325
327
|
when OP_IFDUP
|
|
326
328
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
|
327
|
-
stack << stack.last if cast_to_bool(stack.last)
|
|
329
|
+
stack << stack.last if cast_to_bool(stack.last.htb)
|
|
328
330
|
when OP_RIPEMD160
|
|
329
331
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
|
330
332
|
stack << Digest::RMD160.hexdigest(pop_string.htb)
|
|
@@ -483,6 +485,7 @@ module Bitcoin
|
|
|
483
485
|
|
|
484
486
|
push_int(success ? num + 1 : num)
|
|
485
487
|
when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
|
488
|
+
return set_error(SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG) if sig_version == :tapscript # BIP-342
|
|
486
489
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
|
487
490
|
pubkey_count = pop_int
|
|
488
491
|
unless (0..MAX_PUBKEYS_PER_MULTISIG).include?(pubkey_count)
|
|
@@ -505,7 +508,8 @@ module Bitcoin
|
|
|
505
508
|
|
|
506
509
|
sigs = pop_string(sig_count)
|
|
507
510
|
sigs = [sigs] if sigs.is_a?(String)
|
|
508
|
-
|
|
511
|
+
all_sigs = sigs.dup # the loop below drops a signature once it verifies
|
|
512
|
+
|
|
509
513
|
subscript = script.subscript(opts[:begincodehash]..-1)
|
|
510
514
|
|
|
511
515
|
if sig_version == :base
|
|
@@ -532,8 +536,9 @@ module Bitcoin
|
|
|
532
536
|
end
|
|
533
537
|
|
|
534
538
|
if !success && flag?(SCRIPT_VERIFY_NULLFAIL)
|
|
535
|
-
|
|
536
|
-
|
|
539
|
+
# If the operation failed, we require that all signatures must be empty vector, including
|
|
540
|
+
# the ones which did verify, as Bitcoin Core checks every signature element on the stack.
|
|
541
|
+
all_sigs.each do |sig|
|
|
537
542
|
return set_error(SCRIPT_ERR_SIG_NULLFAIL) if sig.bytesize > 0
|
|
538
543
|
end
|
|
539
544
|
end
|
|
@@ -63,11 +63,16 @@ module Bitcoin
|
|
|
63
63
|
|
|
64
64
|
begin
|
|
65
65
|
sighash = tx.sighash_for_input(input_index, opts: opts, hash_type: hash_type, sig_version: sig_version, prevouts: prevouts)
|
|
66
|
-
key = Key.from_xonly_pubkey(pubkey)
|
|
67
|
-
key.verify(sig, sighash, algo: :schnorr)
|
|
68
66
|
rescue ArgumentError
|
|
69
67
|
return set_error(SCRIPT_ERR_SCHNORR_SIG_HASHTYPE)
|
|
70
68
|
end
|
|
69
|
+
begin
|
|
70
|
+
key = Key.from_xonly_pubkey(pubkey)
|
|
71
|
+
rescue ArgumentError
|
|
72
|
+
# A public key which is not on the curve makes the signature fail, as XOnlyPubKey::VerifySchnorr in Bitcoin Core does.
|
|
73
|
+
return set_error(SCRIPT_ERR_SCHNORR_SIG)
|
|
74
|
+
end
|
|
75
|
+
key.verify(sig, sighash, algo: :schnorr)
|
|
71
76
|
end
|
|
72
77
|
|
|
73
78
|
def check_locktime(locktime)
|
|
@@ -14,11 +14,15 @@ module Bitcoin
|
|
|
14
14
|
return self.new if buf.eof?
|
|
15
15
|
size = Bitcoin.unpack_var_int_from_io(buf)
|
|
16
16
|
stack = size.times.map do
|
|
17
|
-
|
|
17
|
+
Bitcoin.read_exact(buf, Bitcoin.unpack_var_int_from_io(buf))
|
|
18
18
|
end
|
|
19
19
|
self.new(stack)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def ==(other)
|
|
23
|
+
other.is_a?(ScriptWitness) && stack == other.stack
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
def empty?
|
|
23
27
|
stack.empty?
|
|
24
28
|
end
|
|
@@ -102,14 +102,20 @@ module Bitcoin
|
|
|
102
102
|
digest = ECDSA.normalize_digest(data, group.bit_length)
|
|
103
103
|
field = ECDSA::PrimeField.new(group.order)
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
# Bit 1 of the recovery id says the x coordinate of R is r + n rather than r. It only fits in the
|
|
106
|
+
# field when r < p - n, and libsecp256k1 rejects the signature otherwise. r + n must not be
|
|
107
|
+
# reduced modulo n here, which would turn it back into r.
|
|
108
|
+
x = r
|
|
109
|
+
if rec & 2 != 0
|
|
110
|
+
return nil if r >= group.field.prime - group.order
|
|
111
|
+
x = r + group.order
|
|
107
112
|
end
|
|
108
113
|
|
|
109
114
|
is_odd = (rec & 1 == 1)
|
|
110
|
-
y_coordinate = group.solve_for_y(
|
|
115
|
+
y_coordinate = group.solve_for_y(x).find{|y| is_odd ? y.odd? : y.even?}
|
|
116
|
+
return nil if y_coordinate.nil? # x is not on the curve
|
|
111
117
|
|
|
112
|
-
p = group.new_point([
|
|
118
|
+
p = group.new_point([x, y_coordinate])
|
|
113
119
|
|
|
114
120
|
inv_r = field.inverse(r)
|
|
115
121
|
u1 = field.mod(inv_r * digest)
|
|
@@ -134,16 +134,19 @@ module Bitcoin
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
elsif prevout.p2sh?
|
|
137
|
-
|
|
137
|
+
redeem_payload = input.script_sig.chunks.last&.pushed_data
|
|
138
|
+
return nil if redeem_payload.nil? # an input without a scriptSig, or whose last element is not a push
|
|
139
|
+
redeem_script = Bitcoin::Script.parse_from_payload(redeem_payload)
|
|
138
140
|
if redeem_script.p2wpkh?
|
|
139
141
|
pk = input.script_witness.stack.last
|
|
140
|
-
return Bitcoin::Key.new(pubkey: pk.bth) if pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
|
|
142
|
+
return Bitcoin::Key.new(pubkey: pk.bth) if pk && pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
|
|
141
143
|
end
|
|
142
144
|
elsif prevout.p2wpkh?
|
|
143
145
|
pk = input.script_witness.stack.last
|
|
144
|
-
return Bitcoin::Key.new(pubkey: pk.bth) if pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
|
|
146
|
+
return Bitcoin::Key.new(pubkey: pk.bth) if pk && pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
|
|
145
147
|
elsif prevout.p2tr?
|
|
146
148
|
witness_stack = input.script_witness.stack.dup
|
|
149
|
+
return nil if witness_stack.empty?
|
|
147
150
|
witness_stack.pop if witness_stack.last.bth.start_with?("50")
|
|
148
151
|
if witness_stack.length > 1
|
|
149
152
|
# script-path
|
data/lib/bitcoin/slip39/share.rb
CHANGED
|
@@ -5,7 +5,8 @@ module Bitcoin
|
|
|
5
5
|
class Share
|
|
6
6
|
|
|
7
7
|
attr_accessor :id # 15 bits, Integer
|
|
8
|
-
attr_accessor :
|
|
8
|
+
attr_accessor :extendable # 1 bit, Boolean. The extendable backup flag.
|
|
9
|
+
attr_accessor :iteration_exp # 4 bits, Integer
|
|
9
10
|
attr_accessor :group_index # 4 bits, Integer
|
|
10
11
|
attr_accessor :group_threshold # 4 bits, Integer
|
|
11
12
|
attr_accessor :group_count # 4 bits, Integer
|
|
@@ -14,6 +15,24 @@ module Bitcoin
|
|
|
14
15
|
attr_accessor :value # 8n bits, hex string.
|
|
15
16
|
attr_accessor :checksum # 30 bits, Integer
|
|
16
17
|
|
|
18
|
+
def initialize
|
|
19
|
+
@extendable = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Whether the extendable backup flag is set.
|
|
23
|
+
# When it is set, the identifier is not used as the salt of the master secret encryption, so sets of shares
|
|
24
|
+
# with different identifiers can be created for the same master secret and passphrase.
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
def extendable?
|
|
27
|
+
!!extendable
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The customization string of the RS1024 checksum for this share.
|
|
31
|
+
# @return [Array[Integer]]
|
|
32
|
+
def customization_string
|
|
33
|
+
extendable? ? CUSTOMIZATION_STRING_EXTENDABLE : CUSTOMIZATION_STRING_ORIG
|
|
34
|
+
end
|
|
35
|
+
|
|
17
36
|
# Recover Share from the mnemonic words
|
|
18
37
|
# @param [Array{String}] words the mnemonic words
|
|
19
38
|
# @return [Bitcoin::SLIP39::Share] a share
|
|
@@ -26,15 +45,19 @@ module Bitcoin
|
|
|
26
45
|
end
|
|
27
46
|
|
|
28
47
|
raise ArgumentError, 'Invalid mnemonic length.' if indices.size < MIN_MNEMONIC_LENGTH_WORDS
|
|
29
|
-
raise ArgumentError, 'Invalid mnemonic checksum.' unless verify_rs1024_checksum(indices)
|
|
30
|
-
|
|
31
|
-
padding_length = (RADIX_BITS * (indices.size - METADATA_LENGTH_WORDS)) % 16
|
|
32
|
-
raise ArgumentError, 'Invalid mnemonic length.' if padding_length > 8
|
|
33
48
|
data = indices.map{|i|i.to_s(2).rjust(10, '0')}.join
|
|
34
49
|
|
|
35
50
|
s = self.new
|
|
36
51
|
s.id = data[0...ID_LENGTH_BITS].to_i(2)
|
|
37
|
-
|
|
52
|
+
# The flag selects the customization string, so it is read before the checksum is verified.
|
|
53
|
+
s.extendable = data[ID_LENGTH_BITS] == '1'
|
|
54
|
+
raise ArgumentError, 'Invalid mnemonic checksum.' unless verify_rs1024_checksum(indices, s.customization_string)
|
|
55
|
+
|
|
56
|
+
padding_length = (RADIX_BITS * (indices.size - METADATA_LENGTH_WORDS)) % 16
|
|
57
|
+
raise ArgumentError, 'Invalid mnemonic length.' if padding_length > 8
|
|
58
|
+
|
|
59
|
+
exp_start = ID_LENGTH_BITS + EXTENDABLE_FLAG_LENGTH_BITS
|
|
60
|
+
s.iteration_exp = data[exp_start...(exp_start + ITERATION_EXP_LENGTH_BITS)].to_i(2)
|
|
38
61
|
s.group_index = data[20...24].to_i(2)
|
|
39
62
|
s.group_threshold = data[24...28].to_i(2) + 1
|
|
40
63
|
s.group_count = data[28...32].to_i(2) + 1
|
|
@@ -46,7 +69,7 @@ module Bitcoin
|
|
|
46
69
|
end_index = start_index + value_length - padding_length
|
|
47
70
|
padding_value = data[40...(40 + padding_length)]
|
|
48
71
|
raise ArgumentError, "Invalid mnemonic. padding must only zero." unless padding_value.to_i(2) == 0
|
|
49
|
-
s.value = data[start_index...end_index].to_i(2).
|
|
72
|
+
s.value = data[start_index...end_index].to_i(2).to_s(16).rjust((value_length - padding_length) / 4, '0')
|
|
50
73
|
s.checksum = data[(40 + value_length)..-1].to_i(2)
|
|
51
74
|
s
|
|
52
75
|
end
|
|
@@ -84,7 +107,9 @@ module Bitcoin
|
|
|
84
107
|
# @param [Boolean] include_checksum whether include checksum when creating indices.
|
|
85
108
|
# @param [Array[Integer]] the array of index
|
|
86
109
|
def build_word_indices(include_checksum = true)
|
|
110
|
+
raise ArgumentError, "Iteration exponent(#{iteration_exp}) must be between 0 and #{MAX_ITERATION_EXP}." unless (0..MAX_ITERATION_EXP).cover?(iteration_exp)
|
|
87
111
|
s = id.to_bits(ID_LENGTH_BITS)
|
|
112
|
+
s << (extendable? ? '1' : '0')
|
|
88
113
|
s << iteration_exp.to_bits(ITERATION_EXP_LENGTH_BITS)
|
|
89
114
|
s << group_index.to_bits(4)
|
|
90
115
|
s << (group_threshold - 1).to_bits(4)
|
|
@@ -92,8 +117,12 @@ module Bitcoin
|
|
|
92
117
|
raise StandardError, "Group threshold(#{group_threshold}) cannot be greater than group count(#{group_count})." if group_threshold > group_count
|
|
93
118
|
s << member_index.to_bits(4)
|
|
94
119
|
s << (member_threshold - 1).to_bits(4)
|
|
95
|
-
|
|
96
|
-
|
|
120
|
+
# from_words derives the padding length from the number of value words modulo 16, which only
|
|
121
|
+
# matches the padding written here when the value is an even number of bytes. An odd one is
|
|
122
|
+
# read back as a different value instead of being rejected.
|
|
123
|
+
raise ArgumentError, 'The length of the share value in bytes must be an even number.' unless value.htb.bytesize.even?
|
|
124
|
+
value_length = value.htb.bytesize * 8
|
|
125
|
+
padding_length = (RADIX_BITS - (value_length % RADIX_BITS)) % RADIX_BITS
|
|
97
126
|
s << value.to_i(16).to_bits(value_length + padding_length)
|
|
98
127
|
s << checksum.to_bits(30) if include_checksum
|
|
99
128
|
s.chars.each_slice(10).map{|index| index.join.to_i(2)}
|
|
@@ -101,16 +130,17 @@ module Bitcoin
|
|
|
101
130
|
|
|
102
131
|
# Verify RS1024 checksum
|
|
103
132
|
# @param [Array[Integer] data the array of mnemonic word index
|
|
133
|
+
# @param [Array[Integer]] cs the customization string
|
|
104
134
|
# @return [Boolean] verify result
|
|
105
|
-
def self.verify_rs1024_checksum(data)
|
|
106
|
-
rs1024_polymod(
|
|
135
|
+
def self.verify_rs1024_checksum(data, cs)
|
|
136
|
+
rs1024_polymod(cs + data) == 1
|
|
107
137
|
end
|
|
108
138
|
|
|
109
139
|
# Create RS1024 checksum
|
|
110
140
|
# @param [Array[Integer] data the array of mnemonic word index without checksum
|
|
111
141
|
# @return [Array[Integer]] the array of checksum integer
|
|
112
142
|
def create_rs1024_checksum(data)
|
|
113
|
-
values =
|
|
143
|
+
values = customization_string + data + Array.new(CHECKSUM_LENGTH_WORDS, 0)
|
|
114
144
|
polymod = Bitcoin::SLIP39::Share.rs1024_polymod(values) ^ 1
|
|
115
145
|
CHECKSUM_LENGTH_WORDS.times.to_a.reverse.map {|i|(polymod >> (10 * i)) & 1023 }
|
|
116
146
|
end
|
data/lib/bitcoin/slip39/sss.rb
CHANGED
|
@@ -32,9 +32,15 @@ module Bitcoin
|
|
|
32
32
|
# @param [Integer] exp Iteration exponent. default is 0.
|
|
33
33
|
# @param [String] secret master secret with hex format.
|
|
34
34
|
# @param [String] passphrase the passphrase used for encryption/decryption.
|
|
35
|
+
# @param [Boolean] extendable the extendable backup flag. default is true, as SLIP-39 recommends.
|
|
36
|
+
# When it is set, the identifier is not used as the salt of the encryption, so another set of shares with
|
|
37
|
+
# a different identifier can be created later for the same master secret and passphrase.
|
|
38
|
+
# Shares with the flag set cannot be recovered by bitcoinrb 1.15.0 and earlier, nor by other implementations
|
|
39
|
+
# which predate the flag.
|
|
35
40
|
# @return [Array[Array[Bitcoin::SLIP39::Share]]] array of group shares.
|
|
36
|
-
def self.setup_shares(groups: [], group_threshold: nil, exp: 0, secret: nil, passphrase: '')
|
|
41
|
+
def self.setup_shares(groups: [], group_threshold: nil, exp: 0, secret: nil, passphrase: '', extendable: true)
|
|
37
42
|
raise ArgumentError, 'Groups is empty.' if groups.empty?
|
|
43
|
+
raise ArgumentError, "Iteration exponent(#{exp}) must be between 0 and #{MAX_ITERATION_EXP}." unless (0..MAX_ITERATION_EXP).cover?(exp)
|
|
38
44
|
raise ArgumentError, 'Group threshold must be greater than 0.' if group_threshold.nil? || group_threshold < 1
|
|
39
45
|
raise ArgumentError, 'Master secret does not specified.' unless secret
|
|
40
46
|
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
|
|
@@ -48,7 +54,7 @@ module Bitcoin
|
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
id = SecureRandom.random_number(32767) # 32767 is max number for 15 bits.
|
|
51
|
-
ems = encrypt(secret, passphrase, exp, id)
|
|
57
|
+
ems = encrypt(secret, passphrase, exp, id, extendable)
|
|
52
58
|
|
|
53
59
|
group_shares = split_secret(group_threshold, groups.length, ems)
|
|
54
60
|
|
|
@@ -59,6 +65,7 @@ module Bitcoin
|
|
|
59
65
|
shares.map do |member_index, member_share|
|
|
60
66
|
share = Bitcoin::SLIP39::Share.new
|
|
61
67
|
share.id = id
|
|
68
|
+
share.extendable = extendable
|
|
62
69
|
share.iteration_exp = exp
|
|
63
70
|
share.group_index = group_index
|
|
64
71
|
share.group_threshold = group_threshold
|
|
@@ -81,17 +88,22 @@ module Bitcoin
|
|
|
81
88
|
#
|
|
82
89
|
# @param [Array[Bitcoin::SLIP30::Share]] shares an array of shares.
|
|
83
90
|
# @param [String] passphrase the passphrase using decrypt master secret.
|
|
91
|
+
# @param [Boolean] legacy_salt if true, build the PBKDF2 salt with the minimum number of bytes for the
|
|
92
|
+
# identifier. Shares generated by bitcoinrb 1.14.0 and earlier need this to be recovered.
|
|
93
|
+
# It has no effect on shares whose extendable backup flag is set, since their salt does not hold the identifier.
|
|
84
94
|
# @return [String] a master secret.
|
|
85
|
-
def self.recover_secret(shares, passphrase: '')
|
|
95
|
+
def self.recover_secret(shares, passphrase: '', legacy_salt: false)
|
|
86
96
|
raise ArgumentError, 'share is empty.' if shares.nil? || shares.empty?
|
|
87
97
|
groups = {}
|
|
88
98
|
id = shares[0].id
|
|
99
|
+
extendable = shares[0].extendable?
|
|
89
100
|
exp = shares[0].iteration_exp
|
|
90
101
|
group_threshold = shares.first.group_threshold
|
|
91
102
|
group_count = shares.first.group_count
|
|
92
103
|
|
|
93
104
|
shares.each do |share|
|
|
94
105
|
raise ArgumentError, 'Invalid set of shares. All shares must have the same id.' unless id == share.id
|
|
106
|
+
raise ArgumentError, 'Invalid set of shares. All shares must have the same extendable backup flag.' unless extendable == share.extendable?
|
|
95
107
|
raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.' unless group_threshold == share.group_threshold
|
|
96
108
|
raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.' unless group_count == share.group_count
|
|
97
109
|
raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.' unless exp == share.iteration_exp
|
|
@@ -127,7 +139,7 @@ module Bitcoin
|
|
|
127
139
|
end
|
|
128
140
|
end
|
|
129
141
|
|
|
130
|
-
return decrypt(group_shares.values.first, passphrase, exp, id) if group_threshold == 1
|
|
142
|
+
return decrypt(group_shares.values.first, passphrase, exp, id, extendable, legacy_salt) if group_threshold == 1
|
|
131
143
|
|
|
132
144
|
raise ArgumentError, "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}" if group_shares.length < group_threshold
|
|
133
145
|
|
|
@@ -138,7 +150,7 @@ module Bitcoin
|
|
|
138
150
|
recover_digest = create_digest(secret, random_value)
|
|
139
151
|
raise ArgumentError, 'Invalid digest of the shared secret.' unless digest == recover_digest
|
|
140
152
|
|
|
141
|
-
decrypt(secret, passphrase, exp, id)
|
|
153
|
+
decrypt(secret, passphrase, exp, id, extendable, legacy_salt)
|
|
142
154
|
end
|
|
143
155
|
|
|
144
156
|
private
|
|
@@ -168,9 +180,11 @@ module Bitcoin
|
|
|
168
180
|
# @param [String] passphrase the passphrase when using encrypt master secret with binary format.
|
|
169
181
|
# @param [Integer] exp iteration exponent
|
|
170
182
|
# @param [Integer] id identifier
|
|
171
|
-
|
|
183
|
+
# @param [Boolean] extendable the extendable backup flag
|
|
184
|
+
# @param [Boolean] legacy_salt if true, build the salt with the minimum number of bytes for the identifier.
|
|
185
|
+
def self.decrypt(ems, passphrase, exp, id, extendable, legacy_salt = false)
|
|
172
186
|
l, r = ems[0...(ems.length / 2)].htb, ems[(ems.length / 2)..-1].htb
|
|
173
|
-
salt = get_salt(id)
|
|
187
|
+
salt = get_salt(id, extendable: extendable, legacy: legacy_salt)
|
|
174
188
|
e = (Bitcoin::SLIP39::BASE_ITERATION_COUNT << exp) / Bitcoin::SLIP39::ROUND_COUNT
|
|
175
189
|
Bitcoin::SLIP39::ROUND_COUNT.times.to_a.reverse.each do |i|
|
|
176
190
|
f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
|
|
@@ -184,11 +198,12 @@ module Bitcoin
|
|
|
184
198
|
# @param [String] passphrase the passphrase when using encrypt master secret with binary format.
|
|
185
199
|
# @param [Integer] exp iteration exponent
|
|
186
200
|
# @param [Integer] id identifier
|
|
201
|
+
# @param [Boolean] extendable the extendable backup flag
|
|
187
202
|
# @return [String] encrypted master secret with hex format.
|
|
188
|
-
def self.encrypt(secret, passphrase, exp, id)
|
|
203
|
+
def self.encrypt(secret, passphrase, exp, id, extendable)
|
|
189
204
|
s = secret.htb
|
|
190
205
|
l, r = s[0...(s.bytesize / 2)], s[(s.bytesize / 2)..-1]
|
|
191
|
-
salt = get_salt(id)
|
|
206
|
+
salt = get_salt(id, extendable: extendable)
|
|
192
207
|
e = (Bitcoin::SLIP39::BASE_ITERATION_COUNT << exp) / Bitcoin::SLIP39::ROUND_COUNT
|
|
193
208
|
Bitcoin::SLIP39::ROUND_COUNT.times.to_a.each do |i|
|
|
194
209
|
f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
|
|
@@ -206,11 +221,17 @@ module Bitcoin
|
|
|
206
221
|
h[0...4].bth
|
|
207
222
|
end
|
|
208
223
|
|
|
209
|
-
# get salt
|
|
224
|
+
# get salt prefix used in encryption/decryption.
|
|
210
225
|
# @param [Integer] id id
|
|
226
|
+
# @param [Boolean] extendable the extendable backup flag. If true, the salt is empty since the identifier
|
|
227
|
+
# is not part of the encryption.
|
|
228
|
+
# @param [Boolean] legacy if true, encode the identifier with the minimum number of bytes. SLIP-39 requires
|
|
229
|
+
# two bytes, so a salt built this way only matches shares generated by bitcoinrb 1.14.0 and earlier.
|
|
211
230
|
# @return [String] salt with binary format.
|
|
212
|
-
def self.get_salt(id)
|
|
213
|
-
|
|
231
|
+
def self.get_salt(id, extendable: false, legacy: false)
|
|
232
|
+
return '' if extendable
|
|
233
|
+
identifier = legacy ? id.itb : [id].pack('n')
|
|
234
|
+
(Bitcoin::SLIP39::CUSTOMIZATION_STRING_ORIG.pack('c*') + identifier)
|
|
214
235
|
end
|
|
215
236
|
|
|
216
237
|
# Split the share into +count+ with threshold +threshold+.
|
data/lib/bitcoin/slip39.rb
CHANGED
|
@@ -19,18 +19,26 @@ module Bitcoin
|
|
|
19
19
|
RADIX = 2 ** RADIX_BITS
|
|
20
20
|
# The length of the random identifier in bits.
|
|
21
21
|
ID_LENGTH_BITS = 15
|
|
22
|
+
# The length of the extendable backup flag in bits.
|
|
23
|
+
EXTENDABLE_FLAG_LENGTH_BITS = 1
|
|
22
24
|
# The length of the iteration exponent in bits.
|
|
23
|
-
ITERATION_EXP_LENGTH_BITS =
|
|
24
|
-
# The
|
|
25
|
-
|
|
25
|
+
ITERATION_EXP_LENGTH_BITS = 4
|
|
26
|
+
# The maximum iteration exponent.
|
|
27
|
+
MAX_ITERATION_EXP = (1 << ITERATION_EXP_LENGTH_BITS) - 1
|
|
28
|
+
# The length of the random identifier, extendable backup flag and iteration exponent in words.
|
|
29
|
+
ID_EXP_LENGTH_WORDS = bits_to_words(ID_LENGTH_BITS + EXTENDABLE_FLAG_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS)
|
|
26
30
|
# The maximum number of shares that can be created.
|
|
27
31
|
MAX_SHARE_COUNT = 16
|
|
28
32
|
# The length of the RS1024 checksum in words.
|
|
29
33
|
CHECKSUM_LENGTH_WORDS = 3
|
|
30
34
|
# The length of the digest of the shared secret in bytes.
|
|
31
35
|
DIGEST_LENGTH_BYTES = 4
|
|
32
|
-
# The customization string used in the RS1024 checksum and in the PBKDF2 salt.
|
|
33
|
-
|
|
36
|
+
# The customization string used in the RS1024 checksum and in the PBKDF2 salt when the extendable backup flag is not set.
|
|
37
|
+
CUSTOMIZATION_STRING_ORIG = 'shamir'.bytes
|
|
38
|
+
# The customization string used in the RS1024 checksum when the extendable backup flag is set.
|
|
39
|
+
CUSTOMIZATION_STRING_EXTENDABLE = 'shamir_extendable'.bytes
|
|
40
|
+
# Deprecated alias of CUSTOMIZATION_STRING_ORIG.
|
|
41
|
+
CUSTOMIZATION_STRING = CUSTOMIZATION_STRING_ORIG
|
|
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
|
|
36
44
|
# The minimum allowed entropy of the master secret.
|