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
@@ -36,13 +36,13 @@ module Bitcoin
36
36
  def self.parse_from_payload(payload, non_witness: false)
37
37
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
38
38
  tx = new
39
- tx.version = buf.read(4).unpack('V').first
39
+ tx.version = buf.read(4).unpack1('V')
40
40
 
41
41
  in_count = Bitcoin.unpack_var_int_from_io(buf)
42
42
  witness = false
43
43
  if in_count.zero? && !non_witness
44
44
  tx.marker = 0
45
- tx.flag = buf.read(1).unpack('c').first
45
+ tx.flag = buf.read(1).unpack1('c')
46
46
  if tx.flag.zero?
47
47
  buf.pos -= 1
48
48
  else
@@ -66,7 +66,7 @@ module Bitcoin
66
66
  end
67
67
  end
68
68
 
69
- tx.lock_time = buf.read(4).unpack('V').first
69
+ tx.lock_time = buf.read(4).unpack1('V')
70
70
 
71
71
  tx
72
72
  end
@@ -188,22 +188,22 @@ module Bitcoin
188
188
  # @param [Integer] input_index input index.
189
189
  # @param [Integer] hash_type signature hash type
190
190
  # @param [Bitcoin::Script] output_script script pubkey or script code. if script pubkey is P2WSH, set witness script to this.
191
+ # @param [Hash] opts Data required for each sig version (amount and skip_separator_index params can also be set to this parameter)
191
192
  # @param [Integer] amount bitcoin amount locked in input. required for witness input only.
192
193
  # @param [Integer] skip_separator_index If output_script is P2WSH and output_script contains any OP_CODESEPARATOR,
193
194
  # the script code needs is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed.
194
- def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all],
195
+ def sighash_for_input(input_index, output_script = nil, opts: {}, hash_type: SIGHASH_TYPE[:all],
195
196
  sig_version: :base, amount: nil, skip_separator_index: 0)
196
197
  raise ArgumentError, 'input_index must be specified.' unless input_index
197
198
  raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
198
- raise ArgumentError, 'script_pubkey must be specified.' unless output_script
199
- raise ArgumentError, 'unsupported sig version specified.' unless SIG_VERSION.include?(sig_version)
200
-
201
- if sig_version == :witness_v0
202
- raise ArgumentError, 'amount must be specified.' unless amount
203
- sighash_for_witness(input_index, output_script, hash_type, amount, skip_separator_index)
204
- else
205
- sighash_for_legacy(input_index, output_script, hash_type)
206
- end
199
+ raise ArgumentError, 'script_pubkey must be specified.' if [:base, :witness_v0].include?(sig_version) && output_script.nil?
200
+
201
+ opts[:amount] = amount if amount
202
+ opts[:skip_separator_index] = skip_separator_index
203
+ opts[:sig_version] = sig_version
204
+ opts[:script_code] = output_script
205
+ sig_hash_gen = SigHashGenerator.load(sig_version)
206
+ sig_hash_gen.generate(self, input_index, hash_type, opts)
207
207
  end
208
208
 
209
209
  # verify input signature.
@@ -245,73 +245,6 @@ module Bitcoin
245
245
 
246
246
  private
247
247
 
248
- # generate sighash with legacy format
249
- def sighash_for_legacy(index, script_code, hash_type)
250
- ins = inputs.map.with_index do |i, idx|
251
- if idx == index
252
- i.to_payload(script_code.delete_opcode(Bitcoin::Opcodes::OP_CODESEPARATOR))
253
- else
254
- case hash_type & 0x1f
255
- when SIGHASH_TYPE[:none], SIGHASH_TYPE[:single]
256
- i.to_payload(Bitcoin::Script.new, 0)
257
- else
258
- i.to_payload(Bitcoin::Script.new)
259
- end
260
- end
261
- end
262
-
263
- outs = outputs.map(&:to_payload)
264
- out_size = Bitcoin.pack_var_int(outputs.size)
265
-
266
- case hash_type & 0x1f
267
- when SIGHASH_TYPE[:none]
268
- outs = ''
269
- out_size = Bitcoin.pack_var_int(0)
270
- when SIGHASH_TYPE[:single]
271
- return "\x01".ljust(32, "\x00") if index >= outputs.size
272
- outs = outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
273
- out_size = Bitcoin.pack_var_int(index + 1)
274
- end
275
-
276
- if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
277
- ins = [ins[index]]
278
- end
279
-
280
- buf = [[version].pack('V'), Bitcoin.pack_var_int(ins.size),
281
- ins, out_size, outs, [lock_time, hash_type].pack('VV')].join
282
-
283
- Bitcoin.double_sha256(buf)
284
- end
285
-
286
- # generate sighash with BIP-143 format
287
- # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
288
- def sighash_for_witness(index, script_pubkey_or_script_code, hash_type, amount, skip_separator_index)
289
- hash_prevouts = Bitcoin.double_sha256(inputs.map{|i|i.out_point.to_payload}.join)
290
- hash_sequence = Bitcoin.double_sha256(inputs.map{|i|[i.sequence].pack('V')}.join)
291
- outpoint = inputs[index].out_point.to_payload
292
- amount = [amount].pack('Q')
293
- nsequence = [inputs[index].sequence].pack('V')
294
- hash_outputs = Bitcoin.double_sha256(outputs.map{|o|o.to_payload}.join)
295
-
296
- script_code = script_pubkey_or_script_code.to_script_code(skip_separator_index)
297
-
298
- case (hash_type & 0x1f)
299
- when SIGHASH_TYPE[:single]
300
- hash_outputs = index >= outputs.size ? "\x00".ljust(32, "\x00") : Bitcoin.double_sha256(outputs[index].to_payload)
301
- hash_sequence = "\x00".ljust(32, "\x00")
302
- when SIGHASH_TYPE[:none]
303
- hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
304
- end
305
-
306
- if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
307
- hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
308
- end
309
-
310
- buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint,
311
- script_code ,amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
312
- Bitcoin.double_sha256(buf)
313
- end
314
-
315
248
  # verify input signature for legacy tx.
316
249
  def verify_input_sig_for_legacy(input_index, script_pubkey, flags)
317
250
  script_sig = inputs[input_index].script_sig
@@ -42,7 +42,7 @@ module Bitcoin
42
42
  else
43
43
  i.script_sig = Script.parse_from_payload(buf.read(sig_length))
44
44
  end
45
- i.sequence = buf.read(4).unpack('V').first
45
+ i.sequence = buf.read(4).unpack1('V')
46
46
  i
47
47
  end
48
48
 
@@ -18,14 +18,13 @@ module Bitcoin
18
18
 
19
19
  def self.parse_from_payload(payload)
20
20
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
21
- value = buf.read(8).unpack('q').first
21
+ value = buf.read(8).unpack1('q')
22
22
  script_size = Bitcoin.unpack_var_int_from_io(buf)
23
23
  new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
24
24
  end
25
25
 
26
26
  def to_payload
27
- s = script_pubkey.to_payload
28
- [value].pack('Q') << Bitcoin.pack_var_int(s.length) << s
27
+ [value].pack('Q') << script_pubkey.to_payload(true)
29
28
  end
30
29
 
31
30
  def to_empty_payload
@@ -33,7 +33,7 @@ module Bitcoin
33
33
 
34
34
  # @return an integer for a valid payload, otherwise nil
35
35
  def unpack_var_int(payload)
36
- case payload.unpack('C').first
36
+ case payload.unpack1('C')
37
37
  when 0xfd
38
38
  payload.unpack('xva*')
39
39
  when 0xfe
@@ -47,14 +47,14 @@ module Bitcoin
47
47
 
48
48
  # @return an integer for a valid payload, otherwise nil
49
49
  def unpack_var_int_from_io(buf)
50
- uchar = buf.read(1)&.unpack('C')&.first
50
+ uchar = buf.read(1)&.unpack1('C')
51
51
  case uchar
52
52
  when 0xfd
53
- buf.read(2)&.unpack('v')&.first
53
+ buf.read(2)&.unpack1('v')
54
54
  when 0xfe
55
- buf.read(4)&.unpack('V')&.first
55
+ buf.read(4)&.unpack1('V')
56
56
  when 0xff
57
- buf.read(8)&.unpack('Q')&.first
57
+ buf.read(8)&.unpack1('Q')
58
58
  else
59
59
  uchar
60
60
  end
@@ -79,7 +79,7 @@ module Bitcoin
79
79
 
80
80
  # byte convert to the sequence of bits packed eight in a byte with the least significant bit first.
81
81
  def byte_to_bit(byte)
82
- byte.unpack('b*').first
82
+ byte.unpack1('b*')
83
83
  end
84
84
 
85
85
  # padding zero to the left of binary string until bytesize.
@@ -96,6 +96,15 @@ module Bitcoin
96
96
  Digest::RMD160.hexdigest(Digest::SHA256.digest(hex.htb))
97
97
  end
98
98
 
99
+ # Generate tagged hash value.
100
+ # @param [String] tag tag value.
101
+ # @param [String] msg the message to be hashed.
102
+ # @return [String] the hash value with binary format.
103
+ def tagged_hash(tag, msg)
104
+ tag_hash = Digest::SHA256.digest(tag)
105
+ Digest::SHA256.digest(tag_hash + tag_hash + msg)
106
+ end
107
+
99
108
  # encode Base58 check address.
100
109
  # @param [String] hex the address payload.
101
110
  # @param [String] addr_version the address version for P2PKH and P2SH.
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -43,7 +43,7 @@ module Bitcoin
43
43
 
44
44
  def to_payload
45
45
  payload = account_key.to_payload
46
- payload << Bitcoin.pack_var_string(name.unpack('H*').first.htb)
46
+ payload << Bitcoin.pack_var_string(name.unpack1('H*').htb)
47
47
  payload << [purpose, index, receive_depth, change_depth, lookahead].pack('I*')
48
48
  payload
49
49
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-19 00:00:00.000000000 Z
11
+ date: 2021-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -179,33 +179,33 @@ dependencies:
179
179
  - !ruby/object:Gem::Version
180
180
  version: 3.8.5
181
181
  - !ruby/object:Gem::Dependency
182
- name: scrypt
182
+ name: json_pure
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - ">="
186
186
  - !ruby/object:Gem::Version
187
- version: '0'
187
+ version: 2.3.1
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
- version: '0'
194
+ version: 2.3.1
195
195
  - !ruby/object:Gem::Dependency
196
- name: json_pure
196
+ name: bip-schnorr
197
197
  requirement: !ruby/object:Gem::Requirement
198
198
  requirements:
199
199
  - - ">="
200
200
  - !ruby/object:Gem::Version
201
- version: 2.3.1
201
+ version: 0.3.2
202
202
  type: :runtime
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
- version: 2.3.1
208
+ version: 0.3.2
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: leveldb-native
211
211
  requirement: !ruby/object:Gem::Requirement
@@ -280,17 +280,17 @@ dependencies:
280
280
  name: webmock
281
281
  requirement: !ruby/object:Gem::Requirement
282
282
  requirements:
283
- - - "~>"
283
+ - - ">="
284
284
  - !ruby/object:Gem::Version
285
- version: '3.0'
285
+ version: 3.11.1
286
286
  type: :development
287
287
  prerelease: false
288
288
  version_requirements: !ruby/object:Gem::Requirement
289
289
  requirements:
290
- - - "~>"
290
+ - - ">="
291
291
  - !ruby/object:Gem::Version
292
- version: '3.0'
293
- description: "[WIP]The implementation of Bitcoin Protocol for Ruby."
292
+ version: 3.11.1
293
+ description: The implementation of Bitcoin Protocol for Ruby.
294
294
  email:
295
295
  - azuchi@chaintope.com
296
296
  executables:
@@ -326,10 +326,13 @@ files:
326
326
  - lib/bitcoin/chain_params.rb
327
327
  - lib/bitcoin/chainparams/mainnet.yml
328
328
  - lib/bitcoin/chainparams/regtest.yml
329
+ - lib/bitcoin/chainparams/signet.yml
329
330
  - lib/bitcoin/chainparams/testnet.yml
330
331
  - lib/bitcoin/constants.rb
331
332
  - lib/bitcoin/descriptor.rb
333
+ - lib/bitcoin/errors.rb
332
334
  - lib/bitcoin/ext.rb
335
+ - lib/bitcoin/ext/ecdsa.rb
333
336
  - lib/bitcoin/ext/json_parser.rb
334
337
  - lib/bitcoin/ext_key.rb
335
338
  - lib/bitcoin/gcs_filter.rb
@@ -429,6 +432,7 @@ files:
429
432
  - lib/bitcoin/secp256k1/native.rb
430
433
  - lib/bitcoin/secp256k1/rfc6979.rb
431
434
  - lib/bitcoin/secp256k1/ruby.rb
435
+ - lib/bitcoin/sighash_generator.rb
432
436
  - lib/bitcoin/slip39.rb
433
437
  - lib/bitcoin/slip39/share.rb
434
438
  - lib/bitcoin/slip39/sss.rb
@@ -474,8 +478,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
474
478
  - !ruby/object:Gem::Version
475
479
  version: '0'
476
480
  requirements: []
477
- rubygems_version: 3.0.3
481
+ rubygems_version: 3.2.3
478
482
  signing_key:
479
483
  specification_version: 4
480
- summary: "[WIP]The implementation of Bitcoin Protocol for Ruby."
484
+ summary: The implementation of Bitcoin Protocol for Ruby.
481
485
  test_files: []