bitcoinrb 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +5 -4
  4. data/README.md +10 -0
  5. data/bitcoinrb.gemspec +4 -4
  6. data/lib/bitcoin.rb +29 -16
  7. data/lib/bitcoin/block_filter.rb +14 -0
  8. data/lib/bitcoin/chain_params.rb +9 -0
  9. data/lib/bitcoin/chainparams/signet.yml +39 -0
  10. data/lib/bitcoin/constants.rb +43 -3
  11. data/lib/bitcoin/descriptor.rb +1 -1
  12. data/lib/bitcoin/errors.rb +19 -0
  13. data/lib/bitcoin/ext/ecdsa.rb +31 -0
  14. data/lib/bitcoin/ext_key.rb +35 -19
  15. data/lib/bitcoin/key.rb +43 -26
  16. data/lib/bitcoin/message/cfcheckpt.rb +2 -2
  17. data/lib/bitcoin/message/cfheaders.rb +1 -1
  18. data/lib/bitcoin/message/cfilter.rb +1 -1
  19. data/lib/bitcoin/message/fee_filter.rb +1 -1
  20. data/lib/bitcoin/message/filter_load.rb +3 -3
  21. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  22. data/lib/bitcoin/message/inventory.rb +1 -1
  23. data/lib/bitcoin/message/merkle_block.rb +1 -1
  24. data/lib/bitcoin/message/network_addr.rb +3 -3
  25. data/lib/bitcoin/message/ping.rb +1 -1
  26. data/lib/bitcoin/message/pong.rb +1 -1
  27. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  28. data/lib/bitcoin/mnemonic.rb +2 -2
  29. data/lib/bitcoin/network/peer_discovery.rb +1 -3
  30. data/lib/bitcoin/node/configuration.rb +3 -1
  31. data/lib/bitcoin/node/spv.rb +8 -0
  32. data/lib/bitcoin/opcodes.rb +14 -1
  33. data/lib/bitcoin/payment_code.rb +2 -2
  34. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  35. data/lib/bitcoin/psbt/input.rb +3 -3
  36. data/lib/bitcoin/psbt/output.rb +1 -1
  37. data/lib/bitcoin/psbt/tx.rb +4 -4
  38. data/lib/bitcoin/rpc/bitcoin_core_client.rb +1 -1
  39. data/lib/bitcoin/script/script.rb +52 -19
  40. data/lib/bitcoin/script/script_error.rb +27 -1
  41. data/lib/bitcoin/script/script_interpreter.rb +161 -62
  42. data/lib/bitcoin/script/tx_checker.rb +64 -14
  43. data/lib/bitcoin/secp256k1/native.rb +138 -25
  44. data/lib/bitcoin/secp256k1/ruby.rb +78 -19
  45. data/lib/bitcoin/sighash_generator.rb +156 -0
  46. data/lib/bitcoin/tx.rb +13 -80
  47. data/lib/bitcoin/tx_in.rb +1 -1
  48. data/lib/bitcoin/tx_out.rb +2 -3
  49. data/lib/bitcoin/util.rb +15 -6
  50. data/lib/bitcoin/version.rb +1 -1
  51. data/lib/bitcoin/wallet/account.rb +1 -1
  52. metadata +19 -15
@@ -28,7 +28,7 @@ module Bitcoin
28
28
  # @param [Integer] key_type a key type which determine address type.
29
29
  # @param [Boolean] compressed [Deprecated] whether public key is compressed.
30
30
  # @return [Bitcoin::Key] a key object.
31
- def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true)
31
+ def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
32
32
  puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
33
33
  if key_type
34
34
  @key_type = key_type
@@ -39,13 +39,14 @@ module Bitcoin
39
39
  @secp256k1_module = Bitcoin.secp_impl
40
40
  @priv_key = priv_key
41
41
  if @priv_key
42
- raise ArgumentError, 'private key is not on curve' unless validate_private_key_range(@priv_key)
42
+ raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
43
43
  end
44
44
  if pubkey
45
45
  @pubkey = pubkey
46
46
  else
47
47
  @pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key
48
48
  end
49
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless fully_valid_pubkey?(allow_hybrid)
49
50
  end
50
51
 
51
52
  # generate key pair
@@ -63,9 +64,9 @@ module Bitcoin
63
64
  data = hex[2...-8].htb
64
65
  checksum = hex[-8..-1]
65
66
  raise ArgumentError, 'invalid version' unless version == Bitcoin.chain_params.privkey_version
66
- raise ArgumentError, 'invalid checksum' unless Bitcoin.calc_checksum(version + data.bth) == checksum
67
+ raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(version + data.bth) == checksum
67
68
  key_len = data.bytesize
68
- if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack('C').first == 1
69
+ if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack1('C') == 1
69
70
  key_type = TYPES[:compressed]
70
71
  data = data[0..-2]
71
72
  elsif key_len == 32
@@ -88,30 +89,46 @@ module Bitcoin
88
89
  # sign +data+ with private key
89
90
  # @param [String] data a data to be signed with binary format
90
91
  # @param [Boolean] low_r flag to apply low-R.
91
- # @param [String] extra_entropy the extra entropy for rfc6979.
92
+ # @param [String] extra_entropy the extra entropy with binary format for rfc6979.
93
+ # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
92
94
  # @return [String] signature data with binary format
93
- def sign(data, low_r = true, extra_entropy = nil)
94
- sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
95
- if low_r && !sig_has_low_r?(sig)
96
- counter = 1
97
- until sig_has_low_r?(sig)
98
- extra_entropy = [counter].pack('I*').bth.ljust(64, '0').htb
99
- sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
100
- counter += 1
95
+ def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa)
96
+ case algo
97
+ when :ecdsa
98
+ sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
99
+ if low_r && !sig_has_low_r?(sig)
100
+ counter = 1
101
+ until sig_has_low_r?(sig)
102
+ extra_entropy = [counter].pack('I*').bth.ljust(64, '0').htb
103
+ sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
104
+ counter += 1
105
+ end
101
106
  end
107
+ sig
108
+ when :schnorr
109
+ secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :schnorr)
110
+ else
111
+ raise ArgumentError "Unsupported algo specified: #{algo}"
102
112
  end
103
- sig
104
113
  end
105
114
 
106
115
  # verify signature using public key
107
116
  # @param [String] sig signature data with binary format
108
- # @param [String] origin original message
117
+ # @param [String] data original message
118
+ # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
109
119
  # @return [Boolean] verify result
110
- def verify(sig, origin)
120
+ def verify(sig, data, algo: :ecdsa)
111
121
  return false unless valid_pubkey?
112
122
  begin
113
- sig = ecdsa_signature_parse_der_lax(sig)
114
- secp256k1_module.verify_sig(origin, sig, pubkey)
123
+ case algo
124
+ when :ecdsa
125
+ sig = ecdsa_signature_parse_der_lax(sig)
126
+ secp256k1_module.verify_sig(data, sig, pubkey)
127
+ when :schnorr
128
+ secp256k1_module.verify_sig(data, sig, xonly_pubkey, algo: :schnorr)
129
+ else
130
+ false
131
+ end
115
132
  rescue Exception
116
133
  false
117
134
  end
@@ -152,6 +169,12 @@ module Bitcoin
152
169
  ECDSA::Format::PointOctetString.decode(p.htb, Bitcoin::Secp256k1::GROUP)
153
170
  end
154
171
 
172
+ # get xonly public key (32 bytes).
173
+ # @return [String] xonly public key with hex format
174
+ def xonly_pubkey
175
+ pubkey[2..65]
176
+ end
177
+
155
178
  # check +pubkey+ (hex) is compress or uncompress pubkey.
156
179
  def self.compress_or_uncompress_pubkey?(pubkey)
157
180
  p = pubkey.htb
@@ -225,14 +248,8 @@ module Bitcoin
225
248
  end
226
249
 
227
250
  # fully validate whether this is a valid public key (more expensive than IsValid())
228
- def fully_valid_pubkey?
229
- return false unless valid_pubkey?
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
251
+ def fully_valid_pubkey?(allow_hybrid = false)
252
+ valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid)
236
253
  end
237
254
 
238
255
  private
@@ -19,8 +19,8 @@ module Bitcoin
19
19
 
20
20
  def self.parse_from_payload(payload)
21
21
  buf = StringIO.new(payload)
22
- type = buf.read(1).unpack('C').first
23
- hash = buf.read(32).unpack('H*').first
22
+ type = buf.read(1).unpack1('C')
23
+ hash = buf.read(32).unpack1('H*')
24
24
  count = Bitcoin.unpack_var_int_from_io(buf)
25
25
  headers = count.times.map{buf.read(32).bth}
26
26
  self.new(type, hash, headers)
@@ -21,7 +21,7 @@ module Bitcoin
21
21
 
22
22
  def self.parse_from_payload(payload)
23
23
  buf = StringIO.new(payload)
24
- type = buf.read(1).unpack("C").first
24
+ type = buf.read(1).unpack1("C")
25
25
  hash = buf.read(32).bth
26
26
  header = buf.read(32).bth
27
27
  count = Bitcoin.unpack_var_int_from_io(buf)
@@ -19,7 +19,7 @@ module Bitcoin
19
19
 
20
20
  def self.parse_from_payload(payload)
21
21
  buf = StringIO.new(payload)
22
- type = buf.read(1).unpack("C").first
22
+ type = buf.read(1).unpack1("C")
23
23
  hash = buf.read(32).bth
24
24
  len = Bitcoin.unpack_var_int_from_io(buf)
25
25
  filter = buf.read(len).bth
@@ -15,7 +15,7 @@ module Bitcoin
15
15
  end
16
16
 
17
17
  def self.parse_from_payload(payload)
18
- new(payload.unpack('Q').first)
18
+ new(payload.unpack1('Q'))
19
19
  end
20
20
 
21
21
  def to_payload
@@ -23,9 +23,9 @@ module Bitcoin
23
23
  buf = StringIO.new(payload)
24
24
  filter_count = Bitcoin.unpack_var_int_from_io(buf)
25
25
  filter = buf.read(filter_count).unpack('C*')
26
- func_count = buf.read(4).unpack('V').first
27
- tweak = buf.read(4).unpack('V').first
28
- flag = buf.read(1).unpack('C').first
26
+ func_count = buf.read(4).unpack1('V')
27
+ tweak = buf.read(4).unpack1('V')
28
+ flag = buf.read(1).unpack1('C')
29
29
  FilterLoad.new(Bitcoin::BloomFilter.new(filter, func_count, tweak), flag)
30
30
  end
31
31
 
@@ -22,7 +22,7 @@ module Bitcoin
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).unpack('q*').first
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)
@@ -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].unpack('V').first
29
+ identifier = payload[0..4].unpack1('V')
30
30
  hash = payload[4..-1].bth # internal byte order
31
31
  new(identifier, hash)
32
32
  end
@@ -20,7 +20,7 @@ module Bitcoin
20
20
  m = new
21
21
  buf = StringIO.new(payload)
22
22
  m.header = Bitcoin::BlockHeader.parse_from_payload(buf.read(80))
23
- m.tx_count = buf.read(4).unpack('V').first
23
+ m.tx_count = buf.read(4).unpack1('V')
24
24
  hash_count = Bitcoin.unpack_var_int_from_io(buf)
25
25
  hash_count.times do
26
26
  m.hashes << buf.read(32).bth
@@ -31,10 +31,10 @@ module Bitcoin
31
31
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
32
32
  has_time = buf.size > 26
33
33
  addr = new(time: nil)
34
- addr.time = buf.read(4).unpack('V').first if has_time
35
- addr.services = buf.read(8).unpack('Q').first
34
+ addr.time = buf.read(4).unpack1('V') if has_time
35
+ addr.services = buf.read(8).unpack1('Q')
36
36
  addr.ip_addr = IPAddr::new_ntoh(buf.read(16))
37
- addr.port = buf.read(2).unpack('n').first
37
+ addr.port = buf.read(2).unpack1('n')
38
38
  addr
39
39
  end
40
40
 
@@ -14,7 +14,7 @@ module Bitcoin
14
14
  end
15
15
 
16
16
  def self.parse_from_payload(payload)
17
- new(payload.unpack('Q').first)
17
+ new(payload.unpack1('Q'))
18
18
  end
19
19
 
20
20
  def to_payload
@@ -14,7 +14,7 @@ module Bitcoin
14
14
  end
15
15
 
16
16
  def self.parse_from_payload(payload)
17
- new(payload.unpack('Q').first)
17
+ new(payload.unpack1('Q'))
18
18
  end
19
19
 
20
20
  def to_payload
@@ -21,8 +21,8 @@ module Bitcoin
21
21
 
22
22
  def self.parse_from_payload(payload)
23
23
  buf = StringIO.new(payload)
24
- mode = buf.read(1).unpack('c').first
25
- version = buf.read(8).unpack('Q').first
24
+ mode = buf.read(1).unpack1('c')
25
+ version = buf.read(8).unpack1('Q')
26
26
  new(mode, version)
27
27
  end
28
28
 
@@ -39,7 +39,7 @@ module Bitcoin
39
39
  # @return [Array] the array of mnemonic word.
40
40
  def to_mnemonic(entropy)
41
41
  raise ArgumentError, 'entropy is empty.' if entropy.nil? || entropy.empty?
42
- e = entropy.htb.unpack('B*').first
42
+ e = entropy.htb.unpack1('B*')
43
43
  seed = e + checksum(e)
44
44
  mnemonic_index = seed.chars.each_slice(11).map{|i|i.join.to_i(2)}
45
45
  word_master = load_words
@@ -61,7 +61,7 @@ module Bitcoin
61
61
  # @param [String] entropy an entropy with bit string format
62
62
  # @return [String] an entropy checksum with bit string format
63
63
  def checksum(entropy)
64
- b = Bitcoin.sha256([entropy].pack('B*')).unpack('B*').first
64
+ b = Bitcoin.sha256([entropy].pack('B*')).unpack1('B*')
65
65
  b.slice(0, (entropy.length/32))
66
66
  end
67
67
 
@@ -30,9 +30,7 @@ module Bitcoin
30
30
  logger.debug 'discover peer address from DNS seeds.'
31
31
  dns_seeds.map { |seed|
32
32
  begin
33
- # x5 is a prefix for finding nodes that support NODE_BLOOM.
34
- # https://github.com/bitcoin/bitcoin/pull/8083#issuecomment-221552835
35
- Socket.getaddrinfo("x5.#{seed}", Bitcoin.chain_params.default_port).map{|a|a[2]}.uniq
33
+ Socket.getaddrinfo("#{seed}", Bitcoin.chain_params.default_port).map{|a|a[2]}.uniq
36
34
  rescue SocketError => e
37
35
  logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
38
36
  nil
@@ -4,8 +4,10 @@ module Bitcoin
4
4
  module Node
5
5
  class Configuration
6
6
 
7
- attr_reader :conf
7
+ attr_reader :conf # Hash
8
8
 
9
+ # initialize configuration
10
+ # @param [Hash] opts parameter for node.
9
11
  def initialize(opts = {})
10
12
  # TODO apply configuration file.
11
13
  opts[:network] = :mainnet unless opts[:network]
@@ -13,6 +13,14 @@ module Bitcoin
13
13
  attr_accessor :wallet
14
14
  attr_accessor :bloom
15
15
 
16
+ # Initialize spv settings
17
+ # @param [Bitcoin::Node::Configuration] configuration configuration for spv.
18
+ #
19
+ # ```ruby
20
+ # config = Bitcoin::Node::Configuration.new(network: :mainnet)
21
+ # spv = Bitcoin::Node::SPV.new(config)
22
+ # spv.run
23
+ # ````
16
24
  def initialize(configuration)
17
25
  @chain = Bitcoin::Store::SPVChain.new
18
26
  @configuration = configuration
@@ -136,6 +136,8 @@ module Bitcoin
136
136
  OP_NOP9 = 0xb8
137
137
  OP_NOP10 = 0xb9
138
138
 
139
+ OP_CHECKSIGADD = 0xba # BIP 342 opcodes (Tapscript)
140
+
139
141
  # https://en.bitcoin.it/wiki/Script#Pseudo-words
140
142
  OP_PUBKEYHASH = 0xfd
141
143
  OP_PUBKEY = 0xfe
@@ -145,6 +147,9 @@ module Bitcoin
145
147
  OPCODES_MAP = Hash[*(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map { |c| [const_get(c), c.to_s] }.flatten]
146
148
  NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
147
149
 
150
+ OP_SUCCESSES = [0x50, 0x62, 0x89, 0x8a, 0x8d, 0x8e, (0x7e..0x81).to_a,
151
+ (0x83..0x86).to_a, (0x95..0x99).to_a, (0xbb..0xfe).to_a].flatten
152
+
148
153
  def opcode_to_name(opcode)
149
154
  return OPCODES_MAP[opcode].delete('OP_') if opcode == OP_0 || (opcode <= OP_16 && opcode >= OP_1)
150
155
  OPCODES_MAP[opcode]
@@ -156,7 +161,8 @@ module Bitcoin
156
161
  end
157
162
 
158
163
  # whether opcode is predefined opcode
159
- def defined?(opcode)
164
+ def defined?(opcode, allow_success = false)
165
+ return true if allow_success && op_success?(opcode)
160
166
  !opcode_to_name(opcode).nil?
161
167
  end
162
168
 
@@ -174,5 +180,12 @@ module Bitcoin
174
180
  nil
175
181
  end
176
182
 
183
+ # Check whether +opcode+ is OP_SUCCESSx or not?
184
+ # @param [Integer] opcode an opcode.
185
+ # @return [Boolean] if +opcode+ is OP_SUCCESSx return true, otherwise false.
186
+ def op_success?(opcode)
187
+ OP_SUCCESSES.include?(opcode)
188
+ end
189
+
177
190
  end
178
191
  end
@@ -61,8 +61,8 @@ module Bitcoin
61
61
  raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
62
62
  raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
63
63
  raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
64
- raise ArgumentError, 'invalid public key' unless Bitcoin::Key.new(priv_key: nil, pubkey: sign + public_key).fully_valid_pubkey?
65
- raise ArgumentError, 'invalid checksum' unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
64
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(priv_key: nil, pubkey: sign + public_key).fully_valid_pubkey?
65
+ raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
66
66
 
67
67
  x_value = payment_code[8..71]
68
68
  chain_code_hex = payment_code[72..135]
@@ -12,7 +12,7 @@ module Bitcoin
12
12
  pubkey = pubkey.encoding == Encoding::ASCII_8BIT ? pubkey : pubkey.htb
13
13
  raise ArgumentError, 'Size of key was not the expected size for the type BIP32 keypath.' unless [Bitcoin::Key::PUBLIC_KEY_SIZE, Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE].include?(pubkey.bytesize)
14
14
  pubkey = Bitcoin::Key.new(pubkey: pubkey.bth)
15
- raise ArgumentError, 'Invalid pubkey' unless pubkey.fully_valid_pubkey?
15
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless pubkey.fully_valid_pubkey?
16
16
  @pubkey = pubkey.pubkey
17
17
  @info = info
18
18
  end
@@ -36,7 +36,7 @@ module Bitcoin
36
36
  found_sep = true
37
37
  break
38
38
  end
39
- key_type = buf.read(1).unpack('C').first
39
+ key_type = buf.read(1).unpack1('C')
40
40
  key = buf.read(key_len - 1)
41
41
  value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
42
42
 
@@ -54,13 +54,13 @@ module Bitcoin
54
54
  raise ArgumentError, 'Size of key was not the expected size for the type partial signature pubkey.'
55
55
  end
56
56
  pubkey = Bitcoin::Key.new(pubkey: key.bth)
57
- raise ArgumentError, 'Invalid pubkey.' unless pubkey.fully_valid_pubkey?
57
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless pubkey.fully_valid_pubkey?
58
58
  raise ArgumentError, 'Duplicate Key, input partial signature for pubkey already provided.' if input.partial_sigs[pubkey.pubkey]
59
59
  input.partial_sigs[pubkey.pubkey] = value
60
60
  when PSBT_IN_TYPES[:sighash]
61
61
  raise ArgumentError, 'Invalid input sighash type typed key.' unless key_len == 1
62
62
  raise ArgumentError 'Duplicate Key, input sighash type already provided.' if input.sighash_type
63
- input.sighash_type = value.unpack('I').first
63
+ input.sighash_type = value.unpack1('I')
64
64
  when PSBT_IN_TYPES[:redeem_script]
65
65
  raise ArgumentError, 'Invalid redeemscript typed key.' unless key_len == 1
66
66
  raise ArgumentError, 'Duplicate Key, input redeemScript already provided.' if input.redeem_script
@@ -26,7 +26,7 @@ module Bitcoin
26
26
  found_sep = true
27
27
  break
28
28
  end
29
- key_type = buf.read(1).unpack('C').first
29
+ key_type = buf.read(1).unpack1('C')
30
30
  key = buf.read(key_len - 1)
31
31
  value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
32
32
  case key_type
@@ -55,7 +55,7 @@ module Bitcoin
55
55
  # @return [Bitcoin::PartiallySignedTx]
56
56
  def self.parse_from_payload(payload)
57
57
  buf = StringIO.new(payload)
58
- raise ArgumentError, 'Invalid PSBT magic bytes.' unless buf.read(4).unpack('N').first == PSBT_MAGIC_BYTES
58
+ raise ArgumentError, 'Invalid PSBT magic bytes.' unless buf.read(4).unpack1('N') == PSBT_MAGIC_BYTES
59
59
  raise ArgumentError, 'Invalid PSBT separator.' unless buf.read(1).bth.to_i(16) == 0xff
60
60
  partial_tx = self.new
61
61
  found_sep = false
@@ -66,7 +66,7 @@ module Bitcoin
66
66
  found_sep = true
67
67
  break
68
68
  end
69
- key_type = buf.read(1).unpack('C').first
69
+ key_type = buf.read(1).unpack1('C')
70
70
  key = buf.read(key_len - 1)
71
71
  value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
72
72
 
@@ -81,13 +81,13 @@ module Bitcoin
81
81
  when PSBT_GLOBAL_TYPES[:xpub]
82
82
  raise ArgumentError, 'Size of key was not the expected size for the type global xpub.' unless key.size == Bitcoin::BIP32_EXTKEY_WITH_VERSION_SIZE
83
83
  xpub = Bitcoin::ExtPubkey.parse_from_payload(key)
84
- raise ArgumentError, 'Invalid pubkey.' unless xpub.key.fully_valid_pubkey?
84
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless xpub.key.fully_valid_pubkey?
85
85
  raise ArgumentError, 'Duplicate key, global xpub already provided' if partial_tx.xpubs.any?{|x|x.xpub == xpub}
86
86
  info = Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)
87
87
  raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
88
88
  partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
89
89
  when PSBT_GLOBAL_TYPES[:ver]
90
- partial_tx.version_number = value.unpack('V').first
90
+ partial_tx.version_number = value.unpack1('V')
91
91
  raise ArgumentError, "An unsupported version was detected." if SUPPORT_VERSION < partial_tx.version_number
92
92
  else
93
93
  raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]