bitcoinrb 0.2.9 → 0.3.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 +2 -2
- data/lib/bitcoin.rb +22 -0
- data/lib/bitcoin/descriptor.rb +147 -0
- data/lib/bitcoin/key.rb +6 -2
- data/lib/bitcoin/message/network_addr.rb +31 -12
- data/lib/bitcoin/message/version.rb +7 -22
- data/lib/bitcoin/network/peer.rb +3 -7
- data/lib/bitcoin/out_point.rb +7 -7
- data/lib/bitcoin/psbt.rb +3 -1
- data/lib/bitcoin/psbt/input.rb +2 -2
- data/lib/bitcoin/psbt/tx.rb +11 -0
- data/lib/bitcoin/rpc/request_handler.rb +1 -1
- data/lib/bitcoin/script/script.rb +2 -1
- data/lib/bitcoin/slip39.rb +93 -0
- data/lib/bitcoin/slip39/share.rb +122 -0
- data/lib/bitcoin/slip39/sss.rb +242 -0
- data/lib/bitcoin/slip39/wordlist/english.txt +1024 -0
- data/lib/bitcoin/tx_in.rb +1 -1
- data/lib/bitcoin/util.rb +1 -1
- data/lib/bitcoin/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52fe21b51d659b4d5a45abbe8207727101813f1f30d7e0cf3438dad43ad3be8d
|
4
|
+
data.tar.gz: b8d94902ad2733c963f23507bdde7f018560cf8cd2ceef17bc349f78373b67ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62f1c0c37657b0f3a1df74d902c42d53a0134cbf254968db70d3814bd1190937c049fc9362bc2f7a6b7570958909bf8ce7fb080ee40d1ff3a6ce410f77305ff7
|
7
|
+
data.tar.gz: 7306ece331df7d9fffd781d0d8bcabcd1f6fc0d6ca64271acbc6138de6ebcb1e7bd843021ef432067136c5ec28bdad4613cb42356daeb6246bf17c3f51ea4526
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.6.
|
1
|
+
2.6.3
|
data/.travis.yml
CHANGED
data/lib/bitcoin.rb
CHANGED
@@ -52,6 +52,8 @@ module Bitcoin
|
|
52
52
|
autoload :BitStreamWriter, 'bitcoin/bit_stream'
|
53
53
|
autoload :BitStreamReader, 'bitcoin/bit_stream'
|
54
54
|
autoload :KeyPath, 'bitcoin/key_path'
|
55
|
+
autoload :Descriptor, 'bitcoin/descriptor'
|
56
|
+
autoload :SLIP39, 'bitcoin/slip39'
|
55
57
|
|
56
58
|
require_relative 'bitcoin/constants'
|
57
59
|
|
@@ -110,6 +112,11 @@ module Bitcoin
|
|
110
112
|
[self].pack('H*')
|
111
113
|
end
|
112
114
|
|
115
|
+
# binary convert to integer
|
116
|
+
def bti
|
117
|
+
bth.to_i(16)
|
118
|
+
end
|
119
|
+
|
113
120
|
# reverse hex string endian
|
114
121
|
def rhex
|
115
122
|
htb.reverse.bth
|
@@ -154,6 +161,12 @@ module Bitcoin
|
|
154
161
|
self[offset..-1]
|
155
162
|
end
|
156
163
|
|
164
|
+
# whether value is hex or not hex
|
165
|
+
# @return [Boolean] return true if data is hex
|
166
|
+
def valid_hex?
|
167
|
+
!self[/\H/]
|
168
|
+
end
|
169
|
+
|
157
170
|
end
|
158
171
|
|
159
172
|
class ::Object
|
@@ -191,6 +204,15 @@ module Bitcoin
|
|
191
204
|
def itb
|
192
205
|
to_even_length_hex.htb
|
193
206
|
end
|
207
|
+
|
208
|
+
# convert bit string
|
209
|
+
def to_bits(length = nil )
|
210
|
+
if length
|
211
|
+
to_s(2).rjust(length, '0')
|
212
|
+
else
|
213
|
+
to_s(2)
|
214
|
+
end
|
215
|
+
end
|
194
216
|
end
|
195
217
|
|
196
218
|
class ::ECDSA::Signature
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
module Descriptor
|
4
|
+
|
5
|
+
include Bitcoin::Opcodes
|
6
|
+
|
7
|
+
# generate P2PK output for the given public key.
|
8
|
+
# @param [String] key private key or public key with hex format
|
9
|
+
# @return [Bitcoin::Script] P2PK script.
|
10
|
+
def pk(key)
|
11
|
+
Bitcoin::Script.new << extract_pubkey(key) << OP_CHECKSIG
|
12
|
+
end
|
13
|
+
|
14
|
+
# generate P2PKH output for the given public key.
|
15
|
+
# @param [String] key private key or public key with hex format.
|
16
|
+
# @return [Bitcoin::Script] P2PKH script.
|
17
|
+
def pkh(key)
|
18
|
+
Bitcoin::Script.to_p2pkh(Bitcoin.hash160(extract_pubkey(key)))
|
19
|
+
end
|
20
|
+
|
21
|
+
# generate P2PKH output for the given public key.
|
22
|
+
# @param [String] key private key or public key with hex format.
|
23
|
+
# @return [Bitcoin::Script] P2WPKH script.
|
24
|
+
def wpkh(key)
|
25
|
+
pubkey = extract_pubkey(key)
|
26
|
+
raise ArgumentError, "Uncompressed key are not allowed." unless compressed_key?(pubkey)
|
27
|
+
Bitcoin::Script.to_p2wpkh(Bitcoin.hash160(pubkey))
|
28
|
+
end
|
29
|
+
|
30
|
+
# generate P2SH embed the argument.
|
31
|
+
# @param [String or Script] script script to be embed.
|
32
|
+
# @return [Bitcoin::Script] P2SH script.
|
33
|
+
def sh(script)
|
34
|
+
script = script.to_hex if script.is_a?(Bitcoin::Script)
|
35
|
+
raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Bitcoin::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.htb.bytesize > Bitcoin::MAX_SCRIPT_ELEMENT_SIZE
|
36
|
+
Bitcoin::Script.to_p2sh(Bitcoin.hash160(script))
|
37
|
+
end
|
38
|
+
|
39
|
+
# generate P2WSH embed the argument.
|
40
|
+
# @param [String or Script] script script to be embed.
|
41
|
+
# @return [Bitcoin::Script] P2WSH script.
|
42
|
+
def wsh(script)
|
43
|
+
script = Bitcoin::Script(script.htb) if script.is_a?(String)
|
44
|
+
raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Bitcoin::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.to_payload.bytesize > Bitcoin::MAX_SCRIPT_ELEMENT_SIZE
|
45
|
+
raise ArgumentError, "Uncompressed key are not allowed." if script.get_pubkeys.any?{|p|!compressed_key?(p)}
|
46
|
+
Bitcoin::Script.to_p2wsh(script)
|
47
|
+
end
|
48
|
+
|
49
|
+
# an alias for the collection of `pk(KEY)` and `pkh(KEY)`.
|
50
|
+
# If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
|
51
|
+
# @param [String] key private key or public key with hex format.
|
52
|
+
# @return [Array[Bitcoin::Script]]
|
53
|
+
def combo(key)
|
54
|
+
result = [pk(key), pkh(key)]
|
55
|
+
pubkey = extract_pubkey(key)
|
56
|
+
if compressed_key?(pubkey)
|
57
|
+
result << wpkh(key)
|
58
|
+
result << sh(result.last)
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# generate multisig output for given keys.
|
64
|
+
# @param [Integer] threshold the threshold of multisig.
|
65
|
+
# @param [Array[String]] keys an array of keys.
|
66
|
+
# @return [Bitcoin::Script] multisig script.
|
67
|
+
def multi(threshold, *keys, sort: false)
|
68
|
+
raise ArgumentError, 'Multisig threshold is not valid.' unless threshold.is_a?(Integer)
|
69
|
+
raise ArgumentError, 'Multisig threshold cannot be 0, must be at least 1.' unless threshold > 0
|
70
|
+
raise ArgumentError, 'Multisig threshold cannot be larger than the number of keys.' if threshold > keys.size
|
71
|
+
raise ArgumentError, 'Multisig must have between 1 and 16 keys, inclusive.' if keys.size > 16
|
72
|
+
pubkeys = keys.map{|key| extract_pubkey(key) }
|
73
|
+
Bitcoin::Script.to_multisig_script(threshold, pubkeys, sort: sort)
|
74
|
+
end
|
75
|
+
|
76
|
+
# generate sorted multisig output for given keys.
|
77
|
+
# @param [Integer] threshold the threshold of multisig.
|
78
|
+
# @param [Array[String]] keys an array of keys.
|
79
|
+
# @return [Bitcoin::Script] multisig script.
|
80
|
+
def sortedmulti(threshold, *keys)
|
81
|
+
multi(threshold, *keys, sort: true)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# extract public key from KEY format.
|
87
|
+
# @param [String] key KEY string.
|
88
|
+
# @return [String] public key.
|
89
|
+
def extract_pubkey(key)
|
90
|
+
if key.start_with?('[') # BIP32 fingerprint
|
91
|
+
raise ArgumentError, 'Invalid key origin.' if key.count('[') > 1 || key.count(']') > 1
|
92
|
+
info = key[1...key.index(']')] # TODO
|
93
|
+
fingerprint, *paths = info.split('/')
|
94
|
+
raise ArgumentError, 'Fingerprint is not hex.' unless fingerprint.valid_hex?
|
95
|
+
raise ArgumentError, 'Fingerprint is not 4 bytes.' unless fingerprint.size == 8
|
96
|
+
key = key[(key.index(']') + 1)..-1]
|
97
|
+
else
|
98
|
+
raise ArgumentError, 'Invalid key origin.' if key.include?(']')
|
99
|
+
end
|
100
|
+
|
101
|
+
# check BIP32 derivation path
|
102
|
+
key, *paths = key.split('/')
|
103
|
+
|
104
|
+
if key.start_with?('xprv')
|
105
|
+
key = Bitcoin::ExtKey.from_base58(key)
|
106
|
+
key = derive_path(key, paths, true) if paths
|
107
|
+
elsif key.start_with?('xpub')
|
108
|
+
key = Bitcoin::ExtPubkey.from_base58(key)
|
109
|
+
key = derive_path(key, paths, false) if paths
|
110
|
+
else
|
111
|
+
begin
|
112
|
+
key = Bitcoin::Key.from_wif(key)
|
113
|
+
rescue ArgumentError
|
114
|
+
key_type = compressed_key?(key) ? Bitcoin::Key::TYPES[:compressed] : Bitcoin::Key::TYPES[:uncompressed]
|
115
|
+
key = Bitcoin::Key.new(pubkey: key, key_type: key_type)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
key = key.is_a?(Bitcoin::Key) ? key : key.key
|
119
|
+
raise ArgumentError, 'Invalid pubkey.' unless key.fully_valid_pubkey?
|
120
|
+
key.pubkey
|
121
|
+
end
|
122
|
+
|
123
|
+
def compressed_key?(key)
|
124
|
+
%w(02 03).include?(key[0..1]) && [key].pack("H*").bytesize == 33
|
125
|
+
end
|
126
|
+
|
127
|
+
def derive_path(key, paths, is_private)
|
128
|
+
paths.each do |path|
|
129
|
+
raise ArgumentError, 'xpub can not derive hardened key.' if !is_private && path.end_with?("'")
|
130
|
+
if is_private
|
131
|
+
hardened = path.end_with?("'")
|
132
|
+
path = hardened ? path[0..-2] : path
|
133
|
+
raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
|
134
|
+
raise ArgumentError, 'Key path value is out of range.' if !hardened && path.to_i >= Bitcoin::HARDENED_THRESHOLD
|
135
|
+
key = key.derive(path.to_i, hardened)
|
136
|
+
else
|
137
|
+
raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
|
138
|
+
raise ArgumentError, 'Key path value is out of range.' if path.to_i >= Bitcoin::HARDENED_THRESHOLD
|
139
|
+
key = key.derive(path.to_i)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
key
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/lib/bitcoin/key.rb
CHANGED
@@ -227,8 +227,12 @@ module Bitcoin
|
|
227
227
|
# fully validate whether this is a valid public key (more expensive than IsValid())
|
228
228
|
def fully_valid_pubkey?
|
229
229
|
return false unless valid_pubkey?
|
230
|
-
|
231
|
-
|
230
|
+
begin
|
231
|
+
point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1)
|
232
|
+
ECDSA::Group::Secp256k1.valid_public_key?(point)
|
233
|
+
rescue ECDSA::Format::DecodeError
|
234
|
+
false
|
235
|
+
end
|
232
236
|
end
|
233
237
|
|
234
238
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
1
3
|
module Bitcoin
|
2
4
|
module Message
|
3
5
|
|
@@ -12,30 +14,47 @@ module Bitcoin
|
|
12
14
|
# The services the node advertised in its version message.
|
13
15
|
attr_accessor :services
|
14
16
|
|
15
|
-
attr_accessor :
|
17
|
+
attr_accessor :ip_addr # IPAddr
|
16
18
|
|
17
19
|
attr_accessor :port
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
attr_reader :skip_time
|
22
|
+
|
23
|
+
def initialize(ip: '127.0.0.1', port: Bitcoin.chain_params.default_port, services: DEFAULT_SERVICE_FLAGS, time: Time.now.to_i)
|
24
|
+
@time = time
|
25
|
+
@ip_addr = IPAddr.new(ip)
|
26
|
+
@port = port
|
27
|
+
@services = services
|
22
28
|
end
|
23
29
|
|
24
30
|
def self.parse_from_payload(payload)
|
25
31
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
26
|
-
|
27
|
-
addr
|
32
|
+
has_time = buf.size > 26
|
33
|
+
addr = new(time: nil)
|
34
|
+
addr.time = buf.read(4).unpack('V').first if has_time
|
28
35
|
addr.services = buf.read(8).unpack('Q').first
|
29
|
-
|
30
|
-
addr.ip = ip.ipv4_mapped? ? ip.native : ip.to_s
|
36
|
+
addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
|
31
37
|
addr.port = buf.read(2).unpack('n').first
|
32
38
|
addr
|
33
39
|
end
|
34
40
|
|
35
|
-
def
|
36
|
-
|
37
|
-
ip_addr =
|
38
|
-
|
41
|
+
def self.local_addr
|
42
|
+
addr = new
|
43
|
+
addr.ip_addr = IPAddr.new('127.0.0.1')
|
44
|
+
addr.port = Bitcoin.chain_params.default_port
|
45
|
+
addr.services = DEFAULT_SERVICE_FLAGS
|
46
|
+
addr
|
47
|
+
end
|
48
|
+
|
49
|
+
def ip
|
50
|
+
ip_addr.ipv4_mapped? ? ip_addr.native : ip_addr.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_payload(skip_time = false)
|
54
|
+
p = ''
|
55
|
+
p << [time].pack('V') unless skip_time
|
56
|
+
addr = ip_addr.ipv4? ? ip_addr.ipv4_mapped : ip_addr
|
57
|
+
p << [services].pack('Q') << addr.hton << [port].pack('n')
|
39
58
|
end
|
40
59
|
|
41
60
|
end
|
@@ -22,8 +22,8 @@ module Bitcoin
|
|
22
22
|
@version = Bitcoin.chain_params.protocol_version
|
23
23
|
@services = DEFAULT_SERVICE_FLAGS
|
24
24
|
@timestamp = Time.now.to_i
|
25
|
-
@local_addr =
|
26
|
-
@remote_addr =
|
25
|
+
@local_addr = NetworkAddr.local_addr
|
26
|
+
@remote_addr = NetworkAddr.local_addr
|
27
27
|
@nonce = SecureRandom.random_number(0xffffffffffffffff)
|
28
28
|
@user_agent = Bitcoin::Message::USER_AGENT
|
29
29
|
@start_height = 0
|
@@ -32,13 +32,13 @@ module Bitcoin
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.parse_from_payload(payload)
|
35
|
-
version, services, timestamp,
|
35
|
+
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('VQQa26a26Qa*')
|
36
36
|
v = new
|
37
37
|
v.version = version
|
38
38
|
v.services = services
|
39
39
|
v.timestamp = timestamp
|
40
|
-
v.
|
41
|
-
v.
|
40
|
+
v.local_addr = NetworkAddr.parse_from_payload(local_addr)
|
41
|
+
v.remote_addr = NetworkAddr.parse_from_payload(remote_addr)
|
42
42
|
v.nonce = nonce
|
43
43
|
user_agent, rest = unpack_var_string(rest)
|
44
44
|
start_height, rest = rest.unpack('Va*')
|
@@ -51,8 +51,8 @@ module Bitcoin
|
|
51
51
|
def to_payload
|
52
52
|
[
|
53
53
|
[version, services, timestamp].pack('VQQ'),
|
54
|
-
|
55
|
-
|
54
|
+
local_addr.to_payload(true),
|
55
|
+
remote_addr.to_payload(true),
|
56
56
|
[nonce].pack('Q'),
|
57
57
|
pack_var_string(user_agent),
|
58
58
|
[start_height].pack('V'),
|
@@ -60,21 +60,6 @@ module Bitcoin
|
|
60
60
|
].join
|
61
61
|
end
|
62
62
|
|
63
|
-
def pack_addr(addr)
|
64
|
-
separator = addr.rindex(':')
|
65
|
-
ip = addr[0...separator]
|
66
|
-
port = addr[separator + 1..-1].to_i
|
67
|
-
ip_addr = IPAddr.new(ip)
|
68
|
-
ip_addr = ip_addr.ipv4_mapped if ip_addr.ipv4?
|
69
|
-
[1].pack('Q') << ip_addr.hton << [port].pack('n')
|
70
|
-
# [[1].pack('Q'), "\x00" * 10, "\xFF\xFF", sockaddr[4...8], sockaddr[2...4]].join
|
71
|
-
end
|
72
|
-
|
73
|
-
def unpack_addr(addr)
|
74
|
-
host, port = addr.unpack('x8x12a4n')
|
75
|
-
"#{host.unpack('C*').join('.')}:#{port}"
|
76
|
-
end
|
77
|
-
|
78
63
|
def unpack_relay_field(payload)
|
79
64
|
( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
|
80
65
|
end
|
data/lib/bitcoin/network/peer.rb
CHANGED
@@ -50,7 +50,8 @@ module Bitcoin
|
|
50
50
|
@bytes_recv = 0
|
51
51
|
@relay = configuration.conf[:relay]
|
52
52
|
current_height = @chain.latest_block.height
|
53
|
-
|
53
|
+
remote_addr = Bitcoin::Message::NetworkAddr.new(ip: host, port: port, time: nil)
|
54
|
+
@local_version = Bitcoin::Message::Version.new(remote_addr: remote_addr, start_height: current_height, relay: @relay)
|
54
55
|
end
|
55
56
|
|
56
57
|
def connect
|
@@ -164,12 +165,7 @@ module Bitcoin
|
|
164
165
|
# @return [Bitcoin::Message::NetworkAddr]
|
165
166
|
def to_network_addr
|
166
167
|
v = remote_version
|
167
|
-
|
168
|
-
addr.time = v.timestamp
|
169
|
-
addr.services = v.services
|
170
|
-
addr.ip = host
|
171
|
-
addr.port = port
|
172
|
-
addr
|
168
|
+
Bitcoin::Message::NetworkAddr.new(ip: host, port: port, services: v.services, time: v.timestamp)
|
173
169
|
end
|
174
170
|
|
175
171
|
# send +addr+ message to remote peer
|
data/lib/bitcoin/out_point.rb
CHANGED
@@ -6,11 +6,11 @@ module Bitcoin
|
|
6
6
|
COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
7
|
COINBASE_INDEX = 4294967295
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :tx_hash
|
10
10
|
attr_reader :index
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
@
|
12
|
+
def initialize(tx_hash, index = -1)
|
13
|
+
@tx_hash = tx_hash
|
14
14
|
@index = index
|
15
15
|
end
|
16
16
|
|
@@ -19,11 +19,11 @@ module Bitcoin
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def coinbase?
|
22
|
-
|
22
|
+
tx_hash == COINBASE_HASH && index == COINBASE_INDEX
|
23
23
|
end
|
24
24
|
|
25
25
|
def to_payload
|
26
|
-
[
|
26
|
+
[tx_hash.htb, index].pack('a32V')
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.create_coinbase_outpoint
|
@@ -31,12 +31,12 @@ module Bitcoin
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def valid?
|
34
|
-
index >= 0 && (!coinbase? &&
|
34
|
+
index >= 0 && (!coinbase? && tx_hash != COINBASE_HASH)
|
35
35
|
end
|
36
36
|
|
37
37
|
# convert hash to txid
|
38
38
|
def txid
|
39
|
-
|
39
|
+
tx_hash.rhex
|
40
40
|
end
|
41
41
|
|
42
42
|
end
|
data/lib/bitcoin/psbt.rb
CHANGED
@@ -12,13 +12,15 @@ module Bitcoin
|
|
12
12
|
|
13
13
|
# constants for PSBT
|
14
14
|
PSBT_MAGIC_BYTES = 0x70736274
|
15
|
-
PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00, xpub: 0x01}
|
15
|
+
PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00, xpub: 0x01, ver: 0xfb}
|
16
16
|
PSBT_IN_TYPES = {non_witness_utxo: 0x00, witness_utxo: 0x01, partial_sig: 0x02,
|
17
17
|
sighash: 0x03, redeem_script: 0x04, witness_script: 0x05,
|
18
18
|
bip32_derivation: 0x06, script_sig: 0x07, script_witness: 0x08}
|
19
19
|
PSBT_OUT_TYPES = {redeem_script: 0x00, witness_script: 0x01, bip32_derivation: 0x02}
|
20
20
|
PSBT_SEPARATOR = 0x00
|
21
21
|
|
22
|
+
SUPPORT_VERSION = 0
|
23
|
+
|
22
24
|
module_function
|
23
25
|
|
24
26
|
def self.serialize_to_vector(key_type, key: nil, value: nil)
|