bitcoinrb 1.12.1 → 1.13.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/lib/bitcoin/bip321_uri.rb +3 -6
- data/lib/bitcoin/bip324/fs_chacha20.rb +1 -1
- data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +1 -0
- data/lib/bitcoin/bip324.rb +2 -0
- data/lib/bitcoin/descriptor/checksum.rb +6 -2
- data/lib/bitcoin/ext_key.rb +8 -4
- data/lib/bitcoin/key.rb +1 -1
- data/lib/bitcoin/message/cf_parser.rb +2 -2
- data/lib/bitcoin/message/fee_filter.rb +2 -2
- data/lib/bitcoin/message/header_and_short_ids.rb +4 -4
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +7 -3
- data/lib/bitcoin/message/ping.rb +2 -2
- data/lib/bitcoin/message/pong.rb +2 -2
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/message/version.rb +3 -3
- data/lib/bitcoin/message.rb +12 -2
- data/lib/bitcoin/mnemonic.rb +9 -4
- data/lib/bitcoin/psbt/input.rb +51 -33
- data/lib/bitcoin/psbt/key_origin_info.rb +2 -2
- data/lib/bitcoin/psbt/output.rb +9 -5
- data/lib/bitcoin/psbt/tx.rb +16 -10
- data/lib/bitcoin/psbt.rb +1 -1
- data/lib/bitcoin/script/script.rb +14 -4
- data/lib/bitcoin/secp256k1/rfc6979.rb +6 -3
- data/lib/bitcoin/sighash_generator.rb +2 -2
- data/lib/bitcoin/taproot/control_block.rb +1 -0
- data/lib/bitcoin/tx.rb +8 -6
- data/lib/bitcoin/tx_out.rb +2 -2
- data/lib/bitcoin/util.rb +3 -3
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +2 -2
- data/lib/bitcoin/wallet/db.rb +3 -3
- data/lib/bitcoin/wallet/master_key.rb +2 -2
- data/lib/bitcoin/wallet/utxo.rb +2 -2
- data/lib/bitcoin.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc2fd227e7a25b6fa3bdd88ef6035e7b7f4a76bb1ef501285ccd0f7f5d961c4c
|
|
4
|
+
data.tar.gz: 0f2557375f3918a5416983dde696cb922be71cf7a7c762c2be2305b6e9edb3d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9cd9ed6bfca773abf5d25228be6b2a88ef7a17c163e3ddd5bb7de9c6365261d65e51fa8c3e9a6f87304a5bfc48036f87e7b0fb99d8b910c368001f7c5aa78d81
|
|
7
|
+
data.tar.gz: bec0d5c4ab556984e2fb9fa4da85008c92fb392d4510e41901701626658f8356f20aa5b74d1837ed90c1a35efe86b52f6ea5713b90f4c2caf2112b47a42a1706
|
data/lib/bitcoin/bip321_uri.rb
CHANGED
|
@@ -105,7 +105,7 @@ module Bitcoin
|
|
|
105
105
|
req_pop = true
|
|
106
106
|
k = 'pop'
|
|
107
107
|
end
|
|
108
|
-
if %w[bc tb].include?(k.downcase)
|
|
108
|
+
if %w[bc tb bcrt].include?(k.downcase)
|
|
109
109
|
raise ArgumentError, "#{k} not allowed in current network." unless Bitcoin.chain_params.bech32_hrp == k.downcase
|
|
110
110
|
query_addrs << v
|
|
111
111
|
nil
|
|
@@ -160,12 +160,9 @@ module Bitcoin
|
|
|
160
160
|
all_params = base_params.merge other_params
|
|
161
161
|
params = all_params.map do |k, v|
|
|
162
162
|
"#{k}=#{CGI.escape(v).gsub('+', '%20')}"
|
|
163
|
-
end.join('&')
|
|
164
|
-
uri << "?#{params}" unless params.empty?
|
|
165
|
-
unless query_addrs.empty?
|
|
166
|
-
uri << '?' unless uri.include?('?')
|
|
167
|
-
uri << query_addrs.map {|addr| "#{Bitcoin.chain_params.bech32_hrp}=#{addr}"}.join('&')
|
|
168
163
|
end
|
|
164
|
+
params += query_addrs.map {|addr| "#{Bitcoin.chain_params.bech32_hrp}=#{addr}"}
|
|
165
|
+
uri << "?#{params.join('&')}" unless params.empty?
|
|
169
166
|
uri
|
|
170
167
|
end
|
|
171
168
|
end
|
|
@@ -101,7 +101,7 @@ module Bitcoin
|
|
|
101
101
|
|
|
102
102
|
def key_stream_bytes(n_bytes)
|
|
103
103
|
while key_stream.bytesize < n_bytes
|
|
104
|
-
nonce = [0, (chunk_counter /
|
|
104
|
+
nonce = [0, (chunk_counter / rekey_interval)].pack("VQ<")
|
|
105
105
|
self.key_stream << ChaCha20.block(key, nonce, block_counter)
|
|
106
106
|
self.block_counter += 1
|
|
107
107
|
end
|
data/lib/bitcoin/bip324.rb
CHANGED
|
@@ -10,7 +10,9 @@ module Bitcoin
|
|
|
10
10
|
|
|
11
11
|
autoload :EllSwiftPubkey, 'bitcoin/bip324/ell_swift_pubkey'
|
|
12
12
|
autoload :Cipher, 'bitcoin/bip324/cipher'
|
|
13
|
+
autoload :ChaCha20, 'bitcoin/bip324/fs_chacha20'
|
|
13
14
|
autoload :FSChaCha20, 'bitcoin/bip324/fs_chacha20'
|
|
15
|
+
autoload :Poly1305, 'bitcoin/bip324/fs_chacha_poly1305'
|
|
14
16
|
autoload :FSChaCha20Poly1305, 'bitcoin/bip324/fs_chacha_poly1305'
|
|
15
17
|
|
|
16
18
|
FIELD_SIZE = 2**256 - 2**32 - 977
|
|
@@ -18,7 +18,9 @@ module Bitcoin
|
|
|
18
18
|
s[-8..-1].each_char do |c|
|
|
19
19
|
return false unless CHECKSUM_CHARSET.include?(c)
|
|
20
20
|
end
|
|
21
|
-
|
|
21
|
+
expanded = descsum_expand(s[0...-9])
|
|
22
|
+
return false unless expanded
|
|
23
|
+
symbols = expanded + s[-8..-1].each_char.map{|c|CHECKSUM_CHARSET.index(c)}
|
|
22
24
|
descsum_polymod(symbols) == 1
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -26,7 +28,9 @@ module Bitcoin
|
|
|
26
28
|
# @param [String] s Descriptor string without checksum.
|
|
27
29
|
# @return [String] Descriptor string with checksum.
|
|
28
30
|
def descsum_create(s)
|
|
29
|
-
|
|
31
|
+
expanded = descsum_expand(s)
|
|
32
|
+
raise ArgumentError, 'Descriptor contains a character which is not in the input charset.' unless expanded
|
|
33
|
+
symbols = expanded + [0, 0, 0, 0, 0, 0, 0, 0]
|
|
30
34
|
checksum = descsum_polymod(symbols) ^ 1
|
|
31
35
|
result = 8.times.map do |i|
|
|
32
36
|
CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31]
|
data/lib/bitcoin/ext_key.rb
CHANGED
|
@@ -114,14 +114,16 @@ module Bitcoin
|
|
|
114
114
|
child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
|
|
115
115
|
new_key.key = Bitcoin::Key.new(priv_key: child_priv.bth, key_type: key_type)
|
|
116
116
|
new_key.chain_code = l[32..-1]
|
|
117
|
-
|
|
117
|
+
# A depth 1 key derives its version bytes from the purpose, see #version.
|
|
118
|
+
new_key.ver = new_key.depth == 1 ? nil : version
|
|
118
119
|
new_key
|
|
119
120
|
end
|
|
120
121
|
|
|
121
122
|
# get version bytes using serialization format
|
|
122
123
|
def version
|
|
124
|
+
return ver if ver
|
|
123
125
|
return ExtKey.version_from_purpose(number) if depth == 1
|
|
124
|
-
|
|
126
|
+
Bitcoin.chain_params.extended_privkey_version
|
|
125
127
|
end
|
|
126
128
|
|
|
127
129
|
# get key type defined by BIP-178 using version.
|
|
@@ -286,14 +288,16 @@ module Bitcoin
|
|
|
286
288
|
p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
|
287
289
|
new_key.pubkey = (p1 + p2).to_hex
|
|
288
290
|
new_key.chain_code = l[32..-1]
|
|
289
|
-
|
|
291
|
+
# A depth 1 key derives its version bytes from the purpose, see #version.
|
|
292
|
+
new_key.ver = new_key.depth == 1 ? nil : version
|
|
290
293
|
new_key
|
|
291
294
|
end
|
|
292
295
|
|
|
293
296
|
# get version bytes using serialization format
|
|
294
297
|
def version
|
|
298
|
+
return ver if ver
|
|
295
299
|
return ExtPubkey.version_from_purpose(number) if depth == 1
|
|
296
|
-
|
|
300
|
+
Bitcoin.chain_params.extended_pubkey_version
|
|
297
301
|
end
|
|
298
302
|
|
|
299
303
|
# get key type defined by BIP-178 using version.
|
data/lib/bitcoin/key.rb
CHANGED
|
@@ -120,7 +120,7 @@ module Bitcoin
|
|
|
120
120
|
if low_r && !sig_has_low_r?(sig)
|
|
121
121
|
counter = 1
|
|
122
122
|
until sig_has_low_r?(sig)
|
|
123
|
-
extra_entropy = [counter].pack('
|
|
123
|
+
extra_entropy = [counter].pack('V').bth.ljust(64, '0').htb
|
|
124
124
|
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
|
|
125
125
|
counter += 1
|
|
126
126
|
end
|
|
@@ -3,12 +3,12 @@ module Bitcoin
|
|
|
3
3
|
module CFParser
|
|
4
4
|
|
|
5
5
|
def parse_from_payload(payload)
|
|
6
|
-
type, start, hash = payload.unpack('
|
|
6
|
+
type, start, hash = payload.unpack('CVH*')
|
|
7
7
|
self.new(type, start, hash)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def to_payload
|
|
11
|
-
[filter_type, start_height, stop_hash].pack('
|
|
11
|
+
[filter_type, start_height, stop_hash].pack('CVH*')
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
end
|
|
@@ -16,13 +16,13 @@ module Bitcoin
|
|
|
16
16
|
@nonce = nonce
|
|
17
17
|
@short_ids = short_ids
|
|
18
18
|
@prefilled_txn = prefilled_txn
|
|
19
|
-
@siphash_key = Bitcoin.sha256(header.to_payload << [nonce].pack('q
|
|
19
|
+
@siphash_key = Bitcoin.sha256(header.to_payload << [nonce].pack('q<*'))[0...16]
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.parse_from_payload(payload)
|
|
23
23
|
buf = StringIO.new(payload)
|
|
24
24
|
header = Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
|
|
25
|
-
nonce = buf.read(8).unpack1('q
|
|
25
|
+
nonce = buf.read(8).unpack1('q<*')
|
|
26
26
|
short_ids_len = Bitcoin.unpack_var_int_from_io(buf)
|
|
27
27
|
short_ids = short_ids_len.times.map do
|
|
28
28
|
buf.read(6).reverse.bth.to_i(16)
|
|
@@ -36,9 +36,9 @@ module Bitcoin
|
|
|
36
36
|
|
|
37
37
|
def to_payload
|
|
38
38
|
p = header.to_payload
|
|
39
|
-
p << [nonce].pack('q
|
|
39
|
+
p << [nonce].pack('q<*')
|
|
40
40
|
p << Bitcoin.pack_var_int(short_ids.size)
|
|
41
|
-
p << short_ids.map{|id|sprintf('%
|
|
41
|
+
p << short_ids.map{|id|sprintf('%012x', id).htb.reverse}.join
|
|
42
42
|
p << Bitcoin.pack_var_int(prefilled_txn.size)
|
|
43
43
|
p << prefilled_txn.map(&:to_payload).join
|
|
44
44
|
p
|
|
@@ -26,7 +26,7 @@ module Bitcoin
|
|
|
26
26
|
# parse inventory payload
|
|
27
27
|
def self.parse_from_payload(payload)
|
|
28
28
|
raise Error, 'invalid inventory size.' if payload.bytesize != 36
|
|
29
|
-
identifier = payload[0..
|
|
29
|
+
identifier = payload[0..3].unpack1('V')
|
|
30
30
|
hash = payload[4..-1].bth # internal byte order
|
|
31
31
|
new(identifier, hash)
|
|
32
32
|
end
|
|
@@ -107,7 +107,7 @@ module Bitcoin
|
|
|
107
107
|
has_time = buf.size > 26
|
|
108
108
|
addr = NetworkAddr.new(time: nil)
|
|
109
109
|
addr.time = buf.read(4).unpack1('V') if has_time
|
|
110
|
-
addr.services = buf.read(8).unpack1('Q')
|
|
110
|
+
addr.services = buf.read(8).unpack1('Q<')
|
|
111
111
|
addr.addr = IPAddr::new_ntoh(buf.read(16))
|
|
112
112
|
addr.port = buf.read(2).unpack1('n')
|
|
113
113
|
addr
|
|
@@ -153,7 +153,7 @@ module Bitcoin
|
|
|
153
153
|
p = ''
|
|
154
154
|
p << [time].pack('V') unless skip_time
|
|
155
155
|
ip = addr.ipv4? ? addr.ipv4_mapped : addr
|
|
156
|
-
p << [services].pack('Q') << ip.hton << [port].pack('n')
|
|
156
|
+
p << [services].pack('Q<') << ip.hton << [port].pack('n')
|
|
157
157
|
end
|
|
158
158
|
|
|
159
159
|
def v2_payload
|
|
@@ -163,18 +163,22 @@ module Bitcoin
|
|
|
163
163
|
case net
|
|
164
164
|
when NETWORK_ID[:ipv4]
|
|
165
165
|
p << Bitcoin.pack_var_int(4)
|
|
166
|
-
p << addr.
|
|
166
|
+
p << addr.hton
|
|
167
167
|
when NETWORK_ID[:ipv6]
|
|
168
168
|
p << Bitcoin.pack_var_int(16)
|
|
169
169
|
p << addr.hton
|
|
170
170
|
when NETWORK_ID[:tor_v2]
|
|
171
171
|
p << Bitcoin.pack_var_int(10)
|
|
172
|
+
p << addr.htb
|
|
172
173
|
when NETWORK_ID[:tor_v3]
|
|
173
174
|
p << Bitcoin.pack_var_int(32)
|
|
175
|
+
p << addr.htb
|
|
174
176
|
when NETWORK_ID[:i2p]
|
|
175
177
|
p << Bitcoin.pack_var_int(32)
|
|
178
|
+
p << addr.htb
|
|
176
179
|
when NETWORK_ID[:cjdns]
|
|
177
180
|
p << Bitcoin.pack_var_int(16)
|
|
181
|
+
p << addr.hton
|
|
178
182
|
end
|
|
179
183
|
p << [port].pack('n')
|
|
180
184
|
p
|
data/lib/bitcoin/message/ping.rb
CHANGED
|
@@ -14,11 +14,11 @@ module Bitcoin
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.parse_from_payload(payload)
|
|
17
|
-
new(payload.unpack1('Q'))
|
|
17
|
+
new(payload.unpack1('Q<'))
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def to_payload
|
|
21
|
-
nonce ? [nonce].pack('Q') : ''
|
|
21
|
+
nonce ? [nonce].pack('Q<') : ''
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# Generate pong message as a response. Return nil if the ping has no nonce
|
data/lib/bitcoin/message/pong.rb
CHANGED
|
@@ -22,12 +22,12 @@ module Bitcoin
|
|
|
22
22
|
def self.parse_from_payload(payload)
|
|
23
23
|
buf = StringIO.new(payload)
|
|
24
24
|
mode = buf.read(1).unpack1('c')
|
|
25
|
-
version = buf.read(8).unpack1('Q')
|
|
25
|
+
version = buf.read(8).unpack1('Q<')
|
|
26
26
|
new(mode, version)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def to_payload
|
|
30
|
-
[mode, version].pack('cQ')
|
|
30
|
+
[mode, version].pack('cQ<')
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def high?
|
|
@@ -32,7 +32,7 @@ module Bitcoin
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def self.parse_from_payload(payload)
|
|
35
|
-
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('
|
|
35
|
+
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack('VQ<Q<a26a26Q<a*')
|
|
36
36
|
v = new
|
|
37
37
|
v.version = version
|
|
38
38
|
v.services = services
|
|
@@ -50,10 +50,10 @@ module Bitcoin
|
|
|
50
50
|
|
|
51
51
|
def to_payload
|
|
52
52
|
[
|
|
53
|
-
[version, services, timestamp].pack('
|
|
53
|
+
[version, services, timestamp].pack('VQ<Q<'),
|
|
54
54
|
local_addr.to_payload(true),
|
|
55
55
|
remote_addr.to_payload(true),
|
|
56
|
-
[nonce].pack('Q'),
|
|
56
|
+
[nonce].pack('Q<'),
|
|
57
57
|
pack_var_string(user_agent),
|
|
58
58
|
[start_height].pack('V'),
|
|
59
59
|
pack_boolean(relay)
|
data/lib/bitcoin/message.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module Bitcoin
|
|
2
2
|
module Message
|
|
3
3
|
|
|
4
|
-
class Error < StandardError; end
|
|
5
|
-
|
|
6
4
|
autoload :Base, 'bitcoin/message/base'
|
|
7
5
|
autoload :Inventory, 'bitcoin/message/inventory'
|
|
8
6
|
autoload :InventoriesParser, 'bitcoin/message/inventories_parser'
|
|
@@ -107,6 +105,8 @@ module Bitcoin
|
|
|
107
105
|
Pong.parse_from_payload(payload)
|
|
108
106
|
when GetHeaders::COMMAND
|
|
109
107
|
GetHeaders.parse_from_payload(payload)
|
|
108
|
+
when GetBlocks::COMMAND
|
|
109
|
+
GetBlocks.parse_from_payload(payload)
|
|
110
110
|
when Headers::COMMAND
|
|
111
111
|
Headers.parse_from_payload(payload)
|
|
112
112
|
when Block::COMMAND
|
|
@@ -125,8 +125,18 @@ module Bitcoin
|
|
|
125
125
|
Inv.parse_from_payload(payload)
|
|
126
126
|
when MerkleBlock::COMMAND
|
|
127
127
|
MerkleBlock.parse_from_payload(payload)
|
|
128
|
+
when FilterLoad::COMMAND
|
|
129
|
+
FilterLoad.parse_from_payload(payload)
|
|
130
|
+
when FilterAdd::COMMAND
|
|
131
|
+
FilterAdd.parse_from_payload(payload)
|
|
132
|
+
when FilterClear::COMMAND
|
|
133
|
+
FilterClear.new
|
|
128
134
|
when CmpctBlock::COMMAND
|
|
129
135
|
CmpctBlock.parse_from_payload(payload)
|
|
136
|
+
when GetBlockTxn::COMMAND
|
|
137
|
+
GetBlockTxn.parse_from_payload(payload)
|
|
138
|
+
when BlockTxn::COMMAND
|
|
139
|
+
BlockTxn.parse_from_payload(payload)
|
|
130
140
|
when GetData::COMMAND
|
|
131
141
|
GetData.parse_from_payload(payload)
|
|
132
142
|
when GetCFHeaders::COMMAND
|
data/lib/bitcoin/mnemonic.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Bitcoin
|
|
|
24
24
|
def to_entropy(words)
|
|
25
25
|
word_master = load_words
|
|
26
26
|
mnemonic = words.map do |w|
|
|
27
|
-
index = word_master.index(w.downcase)
|
|
27
|
+
index = word_master.index(normalize(w).downcase)
|
|
28
28
|
raise IndexError, 'word not found in words list.' unless index
|
|
29
29
|
index.to_s(2).rjust(11, '0')
|
|
30
30
|
end.join
|
|
@@ -53,8 +53,8 @@ module Bitcoin
|
|
|
53
53
|
# @return [String] seed
|
|
54
54
|
def to_seed(mnemonic, passphrase: '')
|
|
55
55
|
to_entropy(mnemonic)
|
|
56
|
-
OpenSSL::PKCS5.pbkdf2_hmac(mnemonic.join(' ').downcase,
|
|
57
|
-
'mnemonic' + passphrase, 2048, 64, OpenSSL::Digest::SHA512.new).bth
|
|
56
|
+
OpenSSL::PKCS5.pbkdf2_hmac(normalize(mnemonic.join(' ')).downcase,
|
|
57
|
+
'mnemonic' + normalize(passphrase), 2048, 64, OpenSSL::Digest::SHA512.new).bth
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# calculate entropy checksum
|
|
@@ -69,7 +69,12 @@ module Bitcoin
|
|
|
69
69
|
|
|
70
70
|
# load word list contents
|
|
71
71
|
def load_words
|
|
72
|
-
File.readlines("#{WORD_DIR}/#{language}.txt").map(
|
|
72
|
+
File.readlines("#{WORD_DIR}/#{language}.txt", encoding: Encoding::UTF_8).map{|w| normalize(w.strip)}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# BIP-39 requires that mnemonic sentences and passphrases are normalized using NFKD.
|
|
76
|
+
def normalize(str)
|
|
77
|
+
str.unicode_normalize(:nfkd)
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
end
|
data/lib/bitcoin/psbt/input.rb
CHANGED
|
@@ -55,8 +55,9 @@ module Bitcoin
|
|
|
55
55
|
found_sep = true
|
|
56
56
|
break
|
|
57
57
|
end
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
key_start = buf.pos
|
|
59
|
+
key_type = Bitcoin.unpack_var_int_from_io(buf)
|
|
60
|
+
key = buf.read(key_len - (buf.pos - key_start))
|
|
60
61
|
value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
|
|
61
62
|
|
|
62
63
|
case key_type
|
|
@@ -79,7 +80,8 @@ module Bitcoin
|
|
|
79
80
|
when PSBT_IN_TYPES[:sighash]
|
|
80
81
|
raise ArgumentError, 'Invalid input sighash type typed key.' unless key_len == 1
|
|
81
82
|
raise ArgumentError, 'Duplicate Key, input sighash type already provided.' if input.sighash_type
|
|
82
|
-
input.
|
|
83
|
+
raise ArgumentError, 'Invalid input sighash type value.' unless value.bytesize == 4
|
|
84
|
+
input.sighash_type = value.unpack1('V')
|
|
83
85
|
when PSBT_IN_TYPES[:redeem_script]
|
|
84
86
|
raise ArgumentError, 'Invalid redeemscript typed key.' unless key_len == 1
|
|
85
87
|
raise ArgumentError, 'Duplicate Key, input redeemScript already provided.' if input.redeem_script
|
|
@@ -117,10 +119,12 @@ module Bitcoin
|
|
|
117
119
|
raise ArgumentError, 'Duplicate Key, input hash256 preimage already provided' if input.hash256_preimages[key.bth]
|
|
118
120
|
input.hash256_preimages[key.bth] = value.bth
|
|
119
121
|
when PSBT_IN_TYPES[:proprietary]
|
|
120
|
-
|
|
121
|
-
input.proprietaries
|
|
122
|
+
proprietary = Proprietary.new(key, value)
|
|
123
|
+
raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if input.proprietaries.any?{|p| p.key == proprietary.key}
|
|
124
|
+
input.proprietaries << proprietary
|
|
122
125
|
when PSBT_IN_TYPES[:tap_key_sig]
|
|
123
126
|
raise ArgumentError, 'Size of key was not the expected size for the type tap key sig' unless key_len == 1
|
|
127
|
+
raise ArgumentError, 'Duplicate Key, input tap key sig already provided.' if input.tap_key_sig
|
|
124
128
|
raise ArgumentError, 'Invalid schnorr signature size for the type tap key sig' unless [64, 65].include?(value.bytesize)
|
|
125
129
|
input.tap_key_sig = value.bth
|
|
126
130
|
when PSBT_IN_TYPES[:tap_script_sig]
|
|
@@ -142,14 +146,16 @@ module Bitcoin
|
|
|
142
146
|
input.tap_bip32_derivations[key.bth] = value.bth
|
|
143
147
|
when PSBT_IN_TYPES[:tap_internal_key]
|
|
144
148
|
raise ArgumentError, 'Size of key was not the expected size for the type tap internal key' unless key_len == 1
|
|
149
|
+
raise ArgumentError, 'Duplicate Key, input tap internal key already provided.' if input.tap_internal_key
|
|
145
150
|
raise ArgumentError, 'Invalid x-only public key size for the type tap internal key' unless value.bytesize == X_ONLY_PUBKEY_SIZE
|
|
146
151
|
input.tap_internal_key = value.bth
|
|
147
152
|
when PSBT_IN_TYPES[:tap_merkle_root]
|
|
148
153
|
raise ArgumentError, 'Size of key was not the expected size for the type tap merkle root' unless key_len == 1
|
|
154
|
+
raise ArgumentError, 'Duplicate Key, input tap merkle root already provided.' if input.tap_merkle_root
|
|
149
155
|
raise ArgumentError, 'Invalid merkle root hash size for the type tap merkle root' unless value.bytesize == 32
|
|
150
156
|
input.tap_merkle_root = value.bth
|
|
151
157
|
else
|
|
152
|
-
unknown_key = (
|
|
158
|
+
unknown_key = (Bitcoin.pack_var_int(key_type) + key).bth
|
|
153
159
|
raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if input.unknowns[unknown_key]
|
|
154
160
|
input.unknowns[unknown_key] = value
|
|
155
161
|
end
|
|
@@ -163,23 +169,21 @@ module Bitcoin
|
|
|
163
169
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
|
|
164
170
|
(witness_utxo && valid_witness_input?) ? non_witness_utxo.serialize_old_format : non_witness_utxo.to_payload) if non_witness_utxo
|
|
165
171
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_utxo], value: witness_utxo.to_payload) if witness_utxo
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_merkle_root], value: tap_merkle_root.htb) if tap_merkle_root
|
|
182
|
-
end
|
|
172
|
+
payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join
|
|
173
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:sighash], value: [sighash_type].pack('V')) if sighash_type
|
|
174
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:redeem_script], value: redeem_script.to_payload) if redeem_script
|
|
175
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_script], value: witness_script.to_payload) if witness_script
|
|
176
|
+
payload << hd_key_paths.values.map(&:to_payload).join
|
|
177
|
+
payload << ripemd160_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:ripemd160], key: k.htb, value: v.htb)}.join
|
|
178
|
+
payload << sha256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:sha256], key: k.htb, value: v.htb)}.join
|
|
179
|
+
payload << hash160_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash160], key: k.htb, value: v.htb)}.join
|
|
180
|
+
payload << hash256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash256], key: k.htb, value: v.htb)}.join
|
|
181
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_key_sig], value: tap_key_sig.htb) if tap_key_sig
|
|
182
|
+
payload << tap_script_sigs.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_script_sig], key: k.htb, value: v.htb)}.join
|
|
183
|
+
payload << tap_leaf_scripts.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_leaf_script], key: k.to_payload, value: v.htb)}.join
|
|
184
|
+
payload << tap_bip32_derivations.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_bip32_derivation], key: k.htb, value: v.htb)}.join
|
|
185
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_internal_key], value: tap_internal_key.htb) if tap_internal_key
|
|
186
|
+
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_merkle_root], value: tap_merkle_root.htb) if tap_merkle_root
|
|
183
187
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_sig], value: final_script_sig.to_payload) if final_script_sig
|
|
184
188
|
payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:script_witness], value: final_script_witness.to_payload) if final_script_witness
|
|
185
189
|
payload << proprietaries.map(&:to_payload).join
|
|
@@ -191,10 +195,13 @@ module Bitcoin
|
|
|
191
195
|
# Check whether input's scriptPubkey is correct witness.
|
|
192
196
|
# @return [Boolean]
|
|
193
197
|
def valid_witness_input?
|
|
194
|
-
return
|
|
195
|
-
return true if witness_utxo
|
|
198
|
+
return false unless witness_utxo
|
|
199
|
+
return true if witness_utxo.script_pubkey.p2wpkh? # P2WPKH
|
|
200
|
+
# P2WSH. The scriptPubkey commits to the witness script directly, so a redeem script is not used.
|
|
201
|
+
return true if witness_utxo.script_pubkey.p2wsh? && witness_script &&
|
|
202
|
+
Bitcoin::Script.to_p2wsh(witness_script) == witness_utxo.script_pubkey
|
|
196
203
|
# segwit nested in P2SH
|
|
197
|
-
if witness_utxo
|
|
204
|
+
if witness_utxo.script_pubkey.p2sh? && redeem_script&.witness_program? && redeem_script.to_p2sh == witness_utxo.script_pubkey
|
|
198
205
|
return true if redeem_script.p2wpkh?# nested p2wpkh
|
|
199
206
|
return true if witness_script&.to_sha256 == redeem_script.witness_data[1].bth # nested p2wsh
|
|
200
207
|
end
|
|
@@ -235,19 +242,19 @@ module Bitcoin
|
|
|
235
242
|
# @return [Bitcoin::PSBT::Input] combined object.
|
|
236
243
|
def merge(psbi)
|
|
237
244
|
raise ArgumentError, 'The argument psbt must be an instance of Bitcoin::PSBT::Input.' unless psbi.is_a?(Bitcoin::PSBT::Input)
|
|
238
|
-
raise ArgumentError, 'The Partially Signed Input\'s non_witness_utxo are different.' unless non_witness_utxo
|
|
239
|
-
raise ArgumentError, 'The Partially Signed Input\'s witness_utxo are different.' unless witness_utxo
|
|
245
|
+
raise ArgumentError, 'The Partially Signed Input\'s non_witness_utxo are different.' unless same_field?(non_witness_utxo, psbi.non_witness_utxo)
|
|
246
|
+
raise ArgumentError, 'The Partially Signed Input\'s witness_utxo are different.' unless same_field?(witness_utxo, psbi.witness_utxo)
|
|
240
247
|
raise ArgumentError, 'The Partially Signed Input\'s sighash_type are different.' if sighash_type && psbi.sighash_type && sighash_type != psbi.sighash_type
|
|
241
|
-
raise ArgumentError, 'The Partially Signed Input\'s redeem_script are different.' unless redeem_script
|
|
242
|
-
raise ArgumentError, 'The Partially Signed Input\'s witness_script are different.' unless witness_script
|
|
248
|
+
raise ArgumentError, 'The Partially Signed Input\'s redeem_script are different.' unless same_field?(redeem_script, psbi.redeem_script)
|
|
249
|
+
raise ArgumentError, 'The Partially Signed Input\'s witness_script are different.' unless same_field?(witness_script, psbi.witness_script)
|
|
243
250
|
combined = Bitcoin::PSBT::Input.new(non_witness_utxo: non_witness_utxo, witness_utxo: witness_utxo)
|
|
244
251
|
combined.unknowns = Hash[unknowns.merge(psbi.unknowns).sort]
|
|
245
252
|
combined.redeem_script = redeem_script
|
|
246
253
|
combined.witness_script = witness_script
|
|
247
254
|
combined.sighash_type = sighash_type
|
|
248
255
|
sigs = Hash[partial_sigs.merge(psbi.partial_sigs)]
|
|
249
|
-
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script&.multisig?
|
|
250
|
-
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script&.multisig?
|
|
256
|
+
redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth] if sigs[pubkey.bth]} if redeem_script&.multisig?
|
|
257
|
+
witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth] if sigs[pubkey.bth]} if witness_script&.multisig?
|
|
251
258
|
combined.hd_key_paths = hd_key_paths.merge(psbi.hd_key_paths)
|
|
252
259
|
combined
|
|
253
260
|
end
|
|
@@ -288,7 +295,7 @@ module Bitcoin
|
|
|
288
295
|
h[:non_witness_utxo] = non_witness_utxo.to_h if non_witness_utxo
|
|
289
296
|
h[:witness_utxo] = witness_utxo.to_h if witness_utxo
|
|
290
297
|
h[:redeem_script] = redeem_script.to_h if redeem_script
|
|
291
|
-
h[:witness_script] = witness_script.to_h if
|
|
298
|
+
h[:witness_script] = witness_script.to_h if witness_script
|
|
292
299
|
h[:final_script_sig] = final_script_sig.to_h if final_script_sig
|
|
293
300
|
h[:final_script_witness] = final_script_witness.to_h if final_script_witness
|
|
294
301
|
h[:bip32_derivs] = hd_key_paths.values.map(&:to_h) unless hd_key_paths.empty?
|
|
@@ -309,6 +316,17 @@ module Bitcoin
|
|
|
309
316
|
h
|
|
310
317
|
end
|
|
311
318
|
|
|
319
|
+
private
|
|
320
|
+
|
|
321
|
+
# Whether +a+ and +b+ are the same field value. Unlike ==, this does not raise
|
|
322
|
+
# NoMethodError when only one of them is nil.
|
|
323
|
+
# @return [Boolean]
|
|
324
|
+
def same_field?(a, b)
|
|
325
|
+
return true if a.nil? && b.nil?
|
|
326
|
+
return false if a.nil? || b.nil?
|
|
327
|
+
a == b
|
|
328
|
+
end
|
|
329
|
+
|
|
312
330
|
end
|
|
313
331
|
|
|
314
332
|
end
|
|
@@ -15,11 +15,11 @@ module Bitcoin
|
|
|
15
15
|
|
|
16
16
|
def self.parse_from_payload(payload)
|
|
17
17
|
buf = StringIO.new(payload)
|
|
18
|
-
self.new(fingerprint: buf.read(4).bth, key_paths: buf.read.unpack('
|
|
18
|
+
self.new(fingerprint: buf.read(4).bth, key_paths: buf.read.unpack('V*'))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def to_payload
|
|
22
|
-
fingerprint.htb + key_paths.pack('
|
|
22
|
+
fingerprint.htb + key_paths.pack('V*')
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def to_h
|
data/lib/bitcoin/psbt/output.rb
CHANGED
|
@@ -32,8 +32,9 @@ module Bitcoin
|
|
|
32
32
|
found_sep = true
|
|
33
33
|
break
|
|
34
34
|
end
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
key_start = buf.pos
|
|
36
|
+
key_type = Bitcoin.unpack_var_int_from_io(buf)
|
|
37
|
+
key = buf.read(key_len - (buf.pos - key_start))
|
|
37
38
|
value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
|
|
38
39
|
case key_type
|
|
39
40
|
when PSBT_OUT_TYPES[:redeem_script]
|
|
@@ -48,21 +49,24 @@ module Bitcoin
|
|
|
48
49
|
raise ArgumentError, 'Duplicate Key, pubkey derivation path already provided' if output.hd_key_paths[key.bth]
|
|
49
50
|
output.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.new(key, Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value))
|
|
50
51
|
when PSBT_OUT_TYPES[:proprietary]
|
|
51
|
-
|
|
52
|
-
output.proprietaries
|
|
52
|
+
proprietary = Proprietary.new(key, value)
|
|
53
|
+
raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if output.proprietaries.any?{|p| p.key == proprietary.key}
|
|
54
|
+
output.proprietaries << proprietary
|
|
53
55
|
when PSBT_OUT_TYPES[:tap_internal_key]
|
|
54
56
|
raise ArgumentError, 'Invalid output tap internal key typed key.' unless key_len == 1
|
|
57
|
+
raise ArgumentError, 'Duplicate Key, output tap internal key already provided.' if output.tap_internal_key
|
|
55
58
|
raise ArgumentError, 'Invalid x-only public key size for the type tap internal key' unless value.bytesize == X_ONLY_PUBKEY_SIZE
|
|
56
59
|
output.tap_internal_key = value.bth
|
|
57
60
|
when PSBT_OUT_TYPES[:tap_tree]
|
|
58
61
|
raise ArgumentError, 'Invalid output tap tree typed key.' unless key_len == 1
|
|
62
|
+
raise ArgumentError, 'Duplicate Key, output tap tree already provided.' if output.tap_tree
|
|
59
63
|
output.tap_tree = value.bth # TODO implement builder.
|
|
60
64
|
when PSBT_OUT_TYPES[:tap_bip32_derivation]
|
|
61
65
|
raise ArgumentError, 'Duplicate Key, key for tap bip32 derivation value already provided.' if output.tap_bip32_derivations[key.bth]
|
|
62
66
|
raise ArgumentError, 'Size of key was not the expected size for the type tap bip32 derivation' unless key.bytesize == X_ONLY_PUBKEY_SIZE
|
|
63
67
|
output.tap_bip32_derivations[key.bth] = value.bth
|
|
64
68
|
else
|
|
65
|
-
unknown_key = (
|
|
69
|
+
unknown_key = (Bitcoin.pack_var_int(key_type) + key).bth
|
|
66
70
|
raise ArgumentError, 'Duplicate Key, key for unknown value already provided' if output.unknowns[unknown_key]
|
|
67
71
|
output.unknowns[unknown_key] = value
|
|
68
72
|
end
|
data/lib/bitcoin/psbt/tx.rb
CHANGED
|
@@ -68,8 +68,9 @@ module Bitcoin
|
|
|
68
68
|
found_sep = true
|
|
69
69
|
break
|
|
70
70
|
end
|
|
71
|
+
key_start = buf.pos
|
|
71
72
|
key_type = Bitcoin.unpack_var_int_from_io(buf)
|
|
72
|
-
key = buf.read(key_len -
|
|
73
|
+
key = buf.read(key_len - (buf.pos - key_start))
|
|
73
74
|
value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
|
|
74
75
|
|
|
75
76
|
case key_type
|
|
@@ -89,14 +90,19 @@ module Bitcoin
|
|
|
89
90
|
raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
|
|
90
91
|
partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
|
|
91
92
|
when PSBT_GLOBAL_TYPES[:ver]
|
|
93
|
+
raise ArgumentError, 'Invalid global version typed key.' unless key_len == 1
|
|
94
|
+
raise ArgumentError, 'Duplicate Key, global version already provided.' if partial_tx.version_number
|
|
95
|
+
raise ArgumentError, 'Invalid global version value.' unless value.bytesize == 4
|
|
92
96
|
partial_tx.version_number = value.unpack1('V')
|
|
93
97
|
raise ArgumentError, "An unsupported version was detected." if SUPPORT_VERSION < partial_tx.version_number
|
|
94
98
|
when PSBT_GLOBAL_TYPES[:proprietary]
|
|
95
|
-
|
|
96
|
-
partial_tx.proprietaries
|
|
99
|
+
proprietary = Proprietary.new(key, value)
|
|
100
|
+
raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if partial_tx.proprietaries.any?{|p| p.key == proprietary.key}
|
|
101
|
+
partial_tx.proprietaries << proprietary
|
|
97
102
|
else
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
unknown_key = (Bitcoin.pack_var_int(key_type) + key).bth
|
|
104
|
+
raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[unknown_key]
|
|
105
|
+
partial_tx.unknowns[unknown_key] = value
|
|
100
106
|
end
|
|
101
107
|
end
|
|
102
108
|
|
|
@@ -206,7 +212,6 @@ module Bitcoin
|
|
|
206
212
|
inputs[i].redeem_script = redeem_script if redeem_script
|
|
207
213
|
inputs[i].witness_script = witness_script if witness_script
|
|
208
214
|
inputs[i].hd_key_paths = hd_key_paths.map(&:pubkey).zip(hd_key_paths).to_h
|
|
209
|
-
break
|
|
210
215
|
end
|
|
211
216
|
end
|
|
212
217
|
end
|
|
@@ -267,20 +272,21 @@ module Bitcoin
|
|
|
267
272
|
# extract final tx.
|
|
268
273
|
# @return [Bitcoin::Tx] final tx.
|
|
269
274
|
def extract_tx
|
|
270
|
-
extract_tx = tx.
|
|
275
|
+
extract_tx = Bitcoin::Tx.parse_from_payload(tx.to_payload)
|
|
271
276
|
inputs.each_with_index do |input, index|
|
|
272
277
|
extract_tx.in[index].script_sig = input.final_script_sig if input.final_script_sig
|
|
273
278
|
extract_tx.in[index].script_witness = input.final_script_witness if input.final_script_witness
|
|
274
279
|
end
|
|
275
280
|
# validate signature
|
|
276
|
-
|
|
281
|
+
extract_tx.in.each_with_index do |tx_in, index|
|
|
277
282
|
input = inputs[index]
|
|
278
283
|
if input.non_witness_utxo
|
|
279
284
|
utxo = input.non_witness_utxo.out[tx_in.out_point.index]
|
|
280
|
-
raise "input[#{index}]'s signature is invalid.'" unless
|
|
285
|
+
raise "input[#{index}]'s signature is invalid.'" unless extract_tx.verify_input_sig(index, utxo.script_pubkey)
|
|
281
286
|
else
|
|
282
287
|
utxo = input.witness_utxo
|
|
283
|
-
raise "input[#{index}]
|
|
288
|
+
raise ArgumentError, "input[#{index}] does not have utxo." unless utxo
|
|
289
|
+
raise "input[#{index}]'s signature is invalid.'" unless extract_tx.verify_input_sig(index, utxo.script_pubkey, amount: utxo.value)
|
|
284
290
|
end
|
|
285
291
|
end
|
|
286
292
|
extract_tx
|
data/lib/bitcoin/psbt.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Bitcoin
|
|
|
55
55
|
module_function
|
|
56
56
|
|
|
57
57
|
def self.serialize_to_vector(key_type, key: nil, value: nil)
|
|
58
|
-
key_len = key_type.
|
|
58
|
+
key_len = Bitcoin.pack_var_int(key_type).bytesize
|
|
59
59
|
key_len += key.bytesize if key
|
|
60
60
|
s = Bitcoin.pack_var_int(key_len) << Bitcoin.pack_var_int(key_type)
|
|
61
61
|
s << key if key
|
|
@@ -100,12 +100,22 @@ module Bitcoin
|
|
|
100
100
|
if opcode
|
|
101
101
|
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
|
102
102
|
else
|
|
103
|
-
script << (v
|
|
103
|
+
script << (number_token?(v) ? v.to_i : v)
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
script
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Whether +token+ is a script number rather than pushed data with hex format.
|
|
110
|
+
# A digit only token is ambiguous since #to_s emits pushed data as hex. #to_s never emits
|
|
111
|
+
# a number with more than 10 digits, so a longer even-length token must be pushed data.
|
|
112
|
+
# @param [String] token a token of the script string.
|
|
113
|
+
# @return [Boolean] whether +token+ should be interpreted as a script number.
|
|
114
|
+
def self.number_token?(token)
|
|
115
|
+
return false unless token =~ /^[0-9]+$/
|
|
116
|
+
token.length < 12 || token.length.odd?
|
|
117
|
+
end
|
|
118
|
+
|
|
109
119
|
# generate script from addr.
|
|
110
120
|
# @param [String] addr address.
|
|
111
121
|
# @return [Bitcoin::Script] parsed script.
|
|
@@ -491,11 +501,11 @@ module Bitcoin
|
|
|
491
501
|
size = data.bytesize
|
|
492
502
|
header = if size < OP_PUSHDATA1
|
|
493
503
|
[size].pack('C')
|
|
494
|
-
elsif size
|
|
504
|
+
elsif size <= 0xff
|
|
495
505
|
[OP_PUSHDATA1, size].pack('CC')
|
|
496
|
-
elsif size
|
|
506
|
+
elsif size <= 0xffff
|
|
497
507
|
[OP_PUSHDATA2, size].pack('Cv')
|
|
498
|
-
elsif size
|
|
508
|
+
elsif size <= 0xffffffff
|
|
499
509
|
[OP_PUSHDATA4, size].pack('CV')
|
|
500
510
|
else
|
|
501
511
|
raise ArgumentError, 'data size is too big.'
|
|
@@ -26,10 +26,13 @@ module Bitcoin
|
|
|
26
26
|
# 3.2.g
|
|
27
27
|
v = Bitcoin.hmac_sha256(k, v)
|
|
28
28
|
# 3.2.h
|
|
29
|
-
t = ''
|
|
30
29
|
10000.times do
|
|
31
|
-
|
|
32
|
-
t =
|
|
30
|
+
# T is reset for each candidate, otherwise a retry can never produce a value below the order.
|
|
31
|
+
t = ''
|
|
32
|
+
while t.bytesize < 32
|
|
33
|
+
v = Bitcoin.hmac_sha256(k, v)
|
|
34
|
+
t = (t + v)
|
|
35
|
+
end
|
|
33
36
|
t_num = t.bth.to_i(16)
|
|
34
37
|
return t_num if 1 <= t_num && t_num < Bitcoin::Secp256k1::GROUP.order
|
|
35
38
|
k = Bitcoin.hmac_sha256(k, v + '00'.htb)
|
|
@@ -68,7 +68,7 @@ module Bitcoin
|
|
|
68
68
|
hash_prevouts = Bitcoin.double_sha256(tx.inputs.map{|i|i.out_point.to_payload}.join)
|
|
69
69
|
hash_sequence = Bitcoin.double_sha256(tx.inputs.map{|i|[i.sequence].pack('V')}.join)
|
|
70
70
|
outpoint = tx.inputs[input_index].out_point.to_payload
|
|
71
|
-
amount = [amount].pack('Q')
|
|
71
|
+
amount = [amount].pack('Q<')
|
|
72
72
|
nsequence = [tx.inputs[input_index].sequence].pack('V')
|
|
73
73
|
hash_outputs = Bitcoin.double_sha256(tx.outputs.map{|o|o.to_payload}.join)
|
|
74
74
|
if output_script.p2wsh?
|
|
@@ -120,7 +120,7 @@ module Bitcoin
|
|
|
120
120
|
buf << [hash_type, tx.version, tx.lock_time].pack('CVV')
|
|
121
121
|
unless input_type == SIGHASH_TYPE[:anyonecanpay]
|
|
122
122
|
buf << Bitcoin.sha256(tx.in.map{|i|i.out_point.to_payload}.join) # sha_prevouts
|
|
123
|
-
buf << Bitcoin.sha256(opts[:prevouts].map(&:value).pack('Q
|
|
123
|
+
buf << Bitcoin.sha256(opts[:prevouts].map(&:value).pack('Q<*'))# sha_amounts
|
|
124
124
|
buf << Bitcoin.sha256(opts[:prevouts].map{|o|o.script_pubkey.to_payload(true)}.join) # sha_scriptpubkeys
|
|
125
125
|
buf << Bitcoin.sha256(tx.in.map(&:sequence).pack('V*')) # sha_sequences
|
|
126
126
|
end
|
|
@@ -24,6 +24,7 @@ module Bitcoin
|
|
|
24
24
|
# @return [Bitcoin::Taproot::ControlBlock]
|
|
25
25
|
def self.parse_from_payload(payload)
|
|
26
26
|
raise Bitcoin::Taproot::Error, 'Invalid data length for Control Block' if payload.bytesize > TAPROOT_CONTROL_MAX_SIZE
|
|
27
|
+
raise Bitcoin::Taproot::Error, 'Invalid data length for Control Block' if payload.bytesize < TAPROOT_CONTROL_BASE_SIZE
|
|
27
28
|
raise Bitcoin::Taproot::Error, 'Invalid data length for path in Control Block' unless (payload.bytesize - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE == 0
|
|
28
29
|
control, internal_key, paths = payload.unpack('Ca32a*')
|
|
29
30
|
parity = control & 1
|
data/lib/bitcoin/tx.rb
CHANGED
|
@@ -89,6 +89,10 @@ module Bitcoin
|
|
|
89
89
|
to_hex.to_i(16)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
def eql?(other)
|
|
93
|
+
other.is_a?(Tx) && self == other
|
|
94
|
+
end
|
|
95
|
+
|
|
92
96
|
def tx_hash
|
|
93
97
|
Bitcoin.double_sha256(serialize_old_format).bth
|
|
94
98
|
end
|
|
@@ -228,7 +232,7 @@ module Bitcoin
|
|
|
228
232
|
# @param [Integer] input_index
|
|
229
233
|
# @param [Bitcoin::Script] script_pubkey the script pubkey for target input.
|
|
230
234
|
# @param [Integer] amount the amount of bitcoin, require for witness program only.
|
|
231
|
-
# @param [
|
|
235
|
+
# @param [Integer] flags the flags used when execute script interpreter.
|
|
232
236
|
# @param [Array[Bitcoin::TxOut]] prevouts Previous outputs referenced by all Tx inputs, required for taproot.
|
|
233
237
|
# @return [Boolean] result
|
|
234
238
|
def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS, prevouts: [])
|
|
@@ -236,11 +240,9 @@ module Bitcoin
|
|
|
236
240
|
has_witness = inputs[input_index].has_witness?
|
|
237
241
|
has_witness = true if script_pubkey.witness_program?
|
|
238
242
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
script_pubkey = redeem_script if redeem_script.p2wpkh?
|
|
243
|
-
end
|
|
243
|
+
# The script interpreter resolves the redeem script of P2SH, including a nested witness
|
|
244
|
+
# program, from the scriptSig, so the scriptPubkey is passed through as it is.
|
|
245
|
+
flags |= SCRIPT_VERIFY_P2SH if script_pubkey.p2sh?
|
|
244
246
|
|
|
245
247
|
if has_witness
|
|
246
248
|
verify_input_sig_for_witness(input_index, script_pubkey, amount, flags, prevouts)
|
data/lib/bitcoin/tx_out.rb
CHANGED
|
@@ -16,13 +16,13 @@ module Bitcoin
|
|
|
16
16
|
|
|
17
17
|
def self.parse_from_payload(payload)
|
|
18
18
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
|
19
|
-
value = buf.read(8).unpack1('
|
|
19
|
+
value = buf.read(8).unpack1('Q<')
|
|
20
20
|
script_size = Bitcoin.unpack_var_int_from_io(buf)
|
|
21
21
|
new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def to_payload
|
|
25
|
-
[value].pack('Q') << script_pubkey.to_payload(true)
|
|
25
|
+
[value].pack('Q<') << script_pubkey.to_payload(true)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_empty_payload
|
data/lib/bitcoin/util.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Bitcoin
|
|
|
25
25
|
elsif i <= 0xffffffff
|
|
26
26
|
[0xfe, i].pack('CV')
|
|
27
27
|
elsif i <= 0xffffffffffffffff
|
|
28
|
-
[0xff, i].pack('CQ')
|
|
28
|
+
[0xff, i].pack('CQ<')
|
|
29
29
|
else
|
|
30
30
|
raise "int(#{i}) too large!"
|
|
31
31
|
end
|
|
@@ -39,7 +39,7 @@ module Bitcoin
|
|
|
39
39
|
when 0xfe
|
|
40
40
|
payload.unpack('xVa*')
|
|
41
41
|
when 0xff
|
|
42
|
-
payload.unpack('
|
|
42
|
+
payload.unpack('xQ<a*')
|
|
43
43
|
else
|
|
44
44
|
payload.unpack('Ca*')
|
|
45
45
|
end
|
|
@@ -54,7 +54,7 @@ module Bitcoin
|
|
|
54
54
|
when 0xfe
|
|
55
55
|
buf.read(4)&.unpack1('V')
|
|
56
56
|
when 0xff
|
|
57
|
-
buf.read(8)&.unpack1('Q')
|
|
57
|
+
buf.read(8)&.unpack1('Q<')
|
|
58
58
|
else
|
|
59
59
|
uchar
|
|
60
60
|
end
|
data/lib/bitcoin/version.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Bitcoin
|
|
|
33
33
|
payload = buf.read
|
|
34
34
|
name, payload = Bitcoin.unpack_var_string(payload)
|
|
35
35
|
name = name.force_encoding('utf-8')
|
|
36
|
-
purpose, index, receive_depth, change_depth, lookahead = payload.unpack('
|
|
36
|
+
purpose, index, receive_depth, change_depth, lookahead = payload.unpack('V*')
|
|
37
37
|
a = Account.new(account_key, purpose, index, name)
|
|
38
38
|
a.receive_depth = receive_depth
|
|
39
39
|
a.change_depth = change_depth
|
|
@@ -44,7 +44,7 @@ module Bitcoin
|
|
|
44
44
|
def to_payload
|
|
45
45
|
payload = account_key.to_payload
|
|
46
46
|
payload << Bitcoin.pack_var_string(name.unpack1('H*').htb)
|
|
47
|
-
payload << [purpose, index, receive_depth, change_depth, lookahead].pack('
|
|
47
|
+
payload << [purpose, index, receive_depth, change_depth, lookahead].pack('V*')
|
|
48
48
|
payload
|
|
49
49
|
end
|
|
50
50
|
|
data/lib/bitcoin/wallet/db.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Bitcoin
|
|
|
32
32
|
|
|
33
33
|
def save_account(account)
|
|
34
34
|
level_db.batch do
|
|
35
|
-
id = [account.purpose, account.index].pack('
|
|
35
|
+
id = [account.purpose, account.index].pack('V*').bth
|
|
36
36
|
key = KEY_PREFIX[:account] + id
|
|
37
37
|
level_db.put(key, account.to_payload)
|
|
38
38
|
end
|
|
@@ -40,14 +40,14 @@ module Bitcoin
|
|
|
40
40
|
|
|
41
41
|
def save_key(account, purpose, index, key)
|
|
42
42
|
pubkey = key.pub
|
|
43
|
-
id = [account.purpose, account.index, purpose, index].pack('
|
|
43
|
+
id = [account.purpose, account.index, purpose, index].pack('V*').bth
|
|
44
44
|
k = KEY_PREFIX[:key] + id
|
|
45
45
|
level_db.put(k, pubkey)
|
|
46
46
|
key
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def get_keys(account)
|
|
50
|
-
id = [account.purpose, account.index].pack('
|
|
50
|
+
id = [account.purpose, account.index].pack('V*').bth
|
|
51
51
|
from = KEY_PREFIX[:key] + id + '00000000'
|
|
52
52
|
to = KEY_PREFIX[:key] + id + 'ffffffff'
|
|
53
53
|
level_db.each(from: from, to: to).map { |k, v| v}
|
|
@@ -81,7 +81,7 @@ module Bitcoin
|
|
|
81
81
|
encrypted_data = ''
|
|
82
82
|
encrypted_data << enc.update(seed)
|
|
83
83
|
encrypted_data << enc.final
|
|
84
|
-
@seed = encrypted_data
|
|
84
|
+
@seed = encrypted_data.bth
|
|
85
85
|
@encrypted = true
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -92,7 +92,7 @@ module Bitcoin
|
|
|
92
92
|
dec.decrypt
|
|
93
93
|
dec.key, dec.iv = key_iv(dec, passphrase)
|
|
94
94
|
decrypted_data = ''
|
|
95
|
-
decrypted_data << dec.update(seed)
|
|
95
|
+
decrypted_data << dec.update(seed.htb)
|
|
96
96
|
decrypted_data << dec.final
|
|
97
97
|
@seed = decrypted_data
|
|
98
98
|
@encrypted = false
|
data/lib/bitcoin/wallet/utxo.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Bitcoin
|
|
|
18
18
|
def self.parse_from_payload(payload)
|
|
19
19
|
return nil if payload.nil?
|
|
20
20
|
|
|
21
|
-
tx_hash, index, block_height, value, payload = payload.unpack('
|
|
21
|
+
tx_hash, index, block_height, value, payload = payload.unpack('H64VVQ<a*')
|
|
22
22
|
|
|
23
23
|
buf = StringIO.new(payload)
|
|
24
24
|
script_size = Bitcoin.unpack_var_int_from_io(buf)
|
|
@@ -27,7 +27,7 @@ module Bitcoin
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def to_payload
|
|
30
|
-
payload = [tx_hash, index, block_height.nil? ? 0 : block_height, value].pack('H64VVQ')
|
|
30
|
+
payload = [tx_hash, index, block_height.nil? ? 0 : block_height, value].pack('H64VVQ<')
|
|
31
31
|
s = script_pubkey.to_payload
|
|
32
32
|
payload << Bitcoin.pack_var_int(s.length) << s
|
|
33
33
|
payload
|
data/lib/bitcoin.rb
CHANGED