bitcoinrb 0.5.0 → 0.6.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.
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]