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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e60d83b48a9f7189cd07c75ee61749ab50a83895f86dd97c7d7fbbd568234067
4
- data.tar.gz: 04e39f6f592379617273944009f78d224cf181f1877de15a285665e9b25602b3
3
+ metadata.gz: cc2fd227e7a25b6fa3bdd88ef6035e7b7f4a76bb1ef501285ccd0f7f5d961c4c
4
+ data.tar.gz: 0f2557375f3918a5416983dde696cb922be71cf7a7c762c2be2305b6e9edb3d3
5
5
  SHA512:
6
- metadata.gz: 13046ad4ba44662c2689d13dec7d51bc88e2d80cccaedc0048ee7484136e5cf04600161b5b7ae99faf4e1d7081fb845b403ebc3defb9cf92e46847e500befd65
7
- data.tar.gz: 12c68844e79ca382dc774d6d4d6cdad8bbd33064a315098e0a31eecb88ac1369c2bd4ff6b39da49d3d5b2eae9750beff11e5761dbbdbe6221c3ef1100cd8cde2
6
+ metadata.gz: 9cd9ed6bfca773abf5d25228be6b2a88ef7a17c163e3ddd5bb7de9c6365261d65e51fa8c3e9a6f87304a5bfc48036f87e7b0fb99d8b910c368001f7c5aa78d81
7
+ data.tar.gz: bec0d5c4ab556984e2fb9fa4da85008c92fb392d4510e41901701626658f8356f20aa5b74d1837ed90c1a35efe86b52f6ea5713b90f4c2caf2112b47a42a1706
@@ -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 / REKEY_INTERVAL)].pack("VQ<")
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
@@ -65,6 +65,7 @@ module Bitcoin
65
65
  # @return [Array] [header, plaintext]
66
66
  def decrypt(aad, ciphertext)
67
67
  contents = crypt(aad, ciphertext, true)
68
+ raise Bitcoin::BIP324::Error, 'Decryption failed.' unless contents
68
69
  [contents[0], contents[1..-1]]
69
70
  end
70
71
 
@@ -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
- symbols = descsum_expand(s[0...-9]) + s[-8..-1].each_char.map{|c|CHECKSUM_CHARSET.index(c)}
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
- symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
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]
@@ -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
- new_key.ver = version
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
- ver ? ver : Bitcoin.chain_params.extended_privkey_version
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
- new_key.ver = version
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
- ver ? ver : Bitcoin.chain_params.extended_pubkey_version
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('I*').bth.ljust(64, '0').htb
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('CLH*')
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('CLH*')
11
+ [filter_type, start_height, stop_hash].pack('CVH*')
12
12
  end
13
13
 
14
14
  end
@@ -15,11 +15,11 @@ module Bitcoin
15
15
  end
16
16
 
17
17
  def self.parse_from_payload(payload)
18
- new(payload.unpack1('Q'))
18
+ new(payload.unpack1('Q<'))
19
19
  end
20
20
 
21
21
  def to_payload
22
- [fee_rate].pack('Q')
22
+ [fee_rate].pack('Q<')
23
23
  end
24
24
 
25
25
  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*'))[0...16]
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('%12x', id).htb.reverse}.join
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..4].unpack1('V')
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.to_i.to_s(16).htb
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
@@ -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
@@ -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].pack('Q')
21
+ [nonce].pack('Q<')
22
22
  end
23
23
  end
24
24
 
@@ -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('VQQa26a26Qa*')
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('VQQ'),
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)
@@ -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
@@ -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(&:strip)
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
@@ -55,8 +55,9 @@ module Bitcoin
55
55
  found_sep = true
56
56
  break
57
57
  end
58
- key_type = buf.read(1).unpack1('C')
59
- key = buf.read(key_len - 1)
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.sighash_type = value.unpack1('I')
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
- raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if input.proprietaries.any?{|p| p.key == key}
121
- input.proprietaries << Proprietary.new(key, value)
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 = ([key_type].pack('C') + key).bth
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
- if final_script_sig.nil? && final_script_witness.nil?
167
- payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join
168
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:sighash], value: [sighash_type].pack('I')) if sighash_type
169
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:redeem_script], value: redeem_script.to_payload) if redeem_script
170
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_script], value: witness_script.to_payload) if witness_script
171
- payload << hd_key_paths.values.map(&:to_payload).join
172
- payload << ripemd160_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:ripemd160], key: k.htb, value: v.htb)}.join
173
- payload << sha256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:sha256], key: k.htb, value: v.htb)}.join
174
- payload << hash160_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash160], key: k.htb, value: v.htb)}.join
175
- payload << hash256_preimages.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:hash256], key: k.htb, value: v.htb)}.join
176
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_key_sig], value: tap_key_sig.htb) if tap_key_sig
177
- payload << tap_script_sigs.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_script_sig], key: k.htb, value: v.htb)}.join
178
- 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
179
- payload << tap_bip32_derivations.map{|k, v| PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_bip32_derivation], key: k.htb, value: v.htb)}.join
180
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:tap_internal_key], value: tap_internal_key.htb) if tap_internal_key
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 true if witness_utxo&.script_pubkey.p2wpkh? # P2WPKH
195
- return true if witness_utxo&.script_pubkey.p2wsh? && witness_utxo&.script_pubkey == redeem_script.to_p2wsh # P2WSH
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&.script_pubkey.p2sh? && redeem_script&.witness_program? && redeem_script.to_p2sh == witness_utxo&.script_pubkey
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 == psbi.non_witness_utxo
239
- raise ArgumentError, 'The Partially Signed Input\'s witness_utxo are different.' unless witness_utxo == psbi.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 == psbi.redeem_script
242
- raise ArgumentError, 'The Partially Signed Input\'s witness_script are different.' unless witness_script == psbi.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 redeem_script
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('I*'))
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('I*')
22
+ fingerprint.htb + key_paths.pack('V*')
23
23
  end
24
24
 
25
25
  def to_h
@@ -32,8 +32,9 @@ module Bitcoin
32
32
  found_sep = true
33
33
  break
34
34
  end
35
- key_type = buf.read(1).unpack1('C')
36
- key = buf.read(key_len - 1)
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
- raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if output.proprietaries.any?{|p| p.key == key}
52
- output.proprietaries << Proprietary.new(key, value)
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 = ([key_type].pack('C') + key).bth
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
@@ -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 - 1)
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
- raise ArgumentError, 'Duplicate Key, key for proprietary value already provided.' if partial_tx.proprietaries.any?{|p| p.key == key}
96
- partial_tx.proprietaries << Proprietary.new(key, value)
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
- raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]
99
- partial_tx.unknowns[([key_type].pack('C') + key).bth] = value
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.dup
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
- tx.in.each_with_index do |tx_in, index|
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 tx.verify_input_sig(index, utxo.script_pubkey)
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}]'s signature is invalid.'" unless tx.verify_input_sig(index, utxo.script_pubkey, amount: input.witness_utxo.value)
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.itb.bytesize
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 =~ /^[0-9]+$/ ? v.to_i : 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 < 0xff
504
+ elsif size <= 0xff
495
505
  [OP_PUSHDATA1, size].pack('CC')
496
- elsif size < 0xffff
506
+ elsif size <= 0xffff
497
507
  [OP_PUSHDATA2, size].pack('Cv')
498
- elsif size < 0xffffffff
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
- v = Bitcoin.hmac_sha256(k, v)
32
- t = (t + v)
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*'))# sha_amounts
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 [Array] flags the flags used when execute script interpreter.
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
- if script_pubkey.p2sh?
240
- flags << SCRIPT_VERIFY_P2SH
241
- redeem_script = Script.parse_from_payload(script_sig.chunks.last)
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)
@@ -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('q')
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('xQa*')
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
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "1.12.1"
2
+ VERSION = "1.13.0"
3
3
  end
@@ -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('I*')
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('I*')
47
+ payload << [purpose, index, receive_depth, change_depth, lookahead].pack('V*')
48
48
  payload
49
49
  end
50
50
 
@@ -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('I*').bth
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('I*').bth
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('I*').bth
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
@@ -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('H64VVQa*')
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
@@ -4,6 +4,7 @@
4
4
  require 'bitcoin/version'
5
5
  require 'schnorr'
6
6
  require 'securerandom'
7
+ require 'stringio'
7
8
  require 'json'
8
9
  require 'bech32'
9
10
  require 'base64'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.1
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi