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