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