bitcoinrb 0.3.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +6 -3
  4. data/README.md +17 -6
  5. data/bitcoinrb.gemspec +9 -8
  6. data/exe/bitcoinrbd +5 -0
  7. data/lib/bitcoin.rb +35 -19
  8. data/lib/bitcoin/bip85_entropy.rb +111 -0
  9. data/lib/bitcoin/block_filter.rb +14 -0
  10. data/lib/bitcoin/block_header.rb +2 -0
  11. data/lib/bitcoin/chain_params.rb +9 -8
  12. data/lib/bitcoin/chainparams/regtest.yml +1 -1
  13. data/lib/bitcoin/chainparams/signet.yml +39 -0
  14. data/lib/bitcoin/chainparams/testnet.yml +1 -1
  15. data/lib/bitcoin/constants.rb +45 -12
  16. data/lib/bitcoin/descriptor.rb +1 -1
  17. data/lib/bitcoin/errors.rb +19 -0
  18. data/lib/bitcoin/ext.rb +5 -0
  19. data/lib/bitcoin/ext/ecdsa.rb +31 -0
  20. data/lib/bitcoin/ext/json_parser.rb +46 -0
  21. data/lib/bitcoin/ext_key.rb +50 -19
  22. data/lib/bitcoin/key.rb +46 -29
  23. data/lib/bitcoin/key_path.rb +12 -5
  24. data/lib/bitcoin/message.rb +79 -0
  25. data/lib/bitcoin/message/addr_v2.rb +34 -0
  26. data/lib/bitcoin/message/base.rb +17 -0
  27. data/lib/bitcoin/message/cf_parser.rb +16 -0
  28. data/lib/bitcoin/message/cfcheckpt.rb +36 -0
  29. data/lib/bitcoin/message/cfheaders.rb +40 -0
  30. data/lib/bitcoin/message/cfilter.rb +35 -0
  31. data/lib/bitcoin/message/fee_filter.rb +1 -1
  32. data/lib/bitcoin/message/filter_load.rb +3 -3
  33. data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
  34. data/lib/bitcoin/message/get_cfheaders.rb +24 -0
  35. data/lib/bitcoin/message/get_cfilters.rb +25 -0
  36. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  37. data/lib/bitcoin/message/inventory.rb +1 -1
  38. data/lib/bitcoin/message/merkle_block.rb +1 -1
  39. data/lib/bitcoin/message/network_addr.rb +141 -18
  40. data/lib/bitcoin/message/ping.rb +1 -1
  41. data/lib/bitcoin/message/pong.rb +1 -1
  42. data/lib/bitcoin/message/send_addr_v2.rb +13 -0
  43. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  44. data/lib/bitcoin/message/version.rb +7 -0
  45. data/lib/bitcoin/mnemonic.rb +7 -7
  46. data/lib/bitcoin/network/peer.rb +9 -4
  47. data/lib/bitcoin/network/peer_discovery.rb +1 -1
  48. data/lib/bitcoin/node/cli.rb +14 -10
  49. data/lib/bitcoin/node/configuration.rb +3 -1
  50. data/lib/bitcoin/node/spv.rb +9 -1
  51. data/lib/bitcoin/opcodes.rb +14 -1
  52. data/lib/bitcoin/out_point.rb +7 -0
  53. data/lib/bitcoin/payment_code.rb +92 -0
  54. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  55. data/lib/bitcoin/psbt/input.rb +8 -17
  56. data/lib/bitcoin/psbt/output.rb +1 -1
  57. data/lib/bitcoin/psbt/tx.rb +11 -16
  58. data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
  59. data/lib/bitcoin/rpc/request_handler.rb +3 -3
  60. data/lib/bitcoin/script/script.rb +68 -28
  61. data/lib/bitcoin/script/script_error.rb +27 -1
  62. data/lib/bitcoin/script/script_interpreter.rb +164 -67
  63. data/lib/bitcoin/script/tx_checker.rb +64 -14
  64. data/lib/bitcoin/secp256k1.rb +1 -0
  65. data/lib/bitcoin/secp256k1/native.rb +138 -25
  66. data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
  67. data/lib/bitcoin/secp256k1/ruby.rb +82 -54
  68. data/lib/bitcoin/sighash_generator.rb +156 -0
  69. data/lib/bitcoin/store.rb +2 -1
  70. data/lib/bitcoin/store/chain_entry.rb +1 -0
  71. data/lib/bitcoin/store/db/level_db.rb +2 -2
  72. data/lib/bitcoin/store/utxo_db.rb +226 -0
  73. data/lib/bitcoin/tx.rb +17 -88
  74. data/lib/bitcoin/tx_in.rb +4 -5
  75. data/lib/bitcoin/tx_out.rb +2 -3
  76. data/lib/bitcoin/util.rb +34 -6
  77. data/lib/bitcoin/version.rb +1 -1
  78. data/lib/bitcoin/wallet.rb +1 -0
  79. data/lib/bitcoin/wallet/account.rb +2 -1
  80. data/lib/bitcoin/wallet/base.rb +3 -3
  81. data/lib/bitcoin/wallet/db.rb +1 -1
  82. data/lib/bitcoin/wallet/master_key.rb +1 -0
  83. data/lib/bitcoin/wallet/utxo.rb +37 -0
  84. metadata +66 -32
@@ -3,6 +3,8 @@ module Bitcoin
3
3
  # outpoint class
4
4
  class OutPoint
5
5
 
6
+ include Bitcoin::HexConverter
7
+
6
8
  COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
7
9
  COINBASE_INDEX = 4294967295
8
10
 
@@ -39,6 +41,11 @@ module Bitcoin
39
41
  tx_hash.rhex
40
42
  end
41
43
 
44
+ def to_s
45
+ return "[#{index}]" unless tx_hash
46
+ "#{txid}[#{index}]"
47
+ end
48
+
42
49
  end
43
50
 
44
51
  end
@@ -0,0 +1,92 @@
1
+ module Bitcoin
2
+
3
+ # BIP47 payment code
4
+ class PaymentCode < ExtKey
5
+
6
+ include Bitcoin::HexConverter
7
+
8
+ attr_accessor :x_value
9
+ attr_accessor :sign
10
+
11
+ VERSION_BYTE = '47'
12
+ SUPPORT_VERSIONS = ['01']
13
+ SUPPORT_SIGNS = ['02', '03']
14
+
15
+ def initialize
16
+ @version = '01'
17
+ @features_bits = '00'
18
+ @reserve_field = '0' * 26
19
+ end
20
+
21
+ # generate master key from seed.
22
+ # @params [String] seed a seed data with hex format.
23
+ def self.generate_master(seed)
24
+ master_ext_key = super.derive(47, harden=true).derive(0, harden=true).derive(0, harden=true)
25
+ compressed_pubkey = master_ext_key.pub
26
+
27
+ payment_code = PaymentCode.new
28
+ payment_code.depth = master_ext_key.depth
29
+ payment_code.key = master_ext_key.key
30
+ payment_code.sign = compressed_pubkey[0..1]
31
+ payment_code.x_value = compressed_pubkey[2..-1]
32
+ payment_code.chain_code = master_ext_key.chain_code
33
+ payment_code
34
+ end
35
+
36
+ # Base58 encoded payment code
37
+ def to_base58
38
+ payment_code_with_version_byte = VERSION_BYTE + to_hex
39
+ Bitcoin::Base58.encode(payment_code_with_version_byte + Bitcoin.calc_checksum(payment_code_with_version_byte))
40
+ end
41
+
42
+ # serialize payment code
43
+ def to_payload
44
+ @version.htb << @features_bits.htb << @sign.htb << @x_value.htb << @chain_code << @reserve_field.htb
45
+ end
46
+
47
+ # get notification address
48
+ def notification_address
49
+ ext_pubkey.derive(0).addr
50
+ end
51
+
52
+ # decode base58 encoded payment code
53
+ # @params [String] base58_payment_code base58 encoded payment code
54
+ def self.from_base58(base58_payment_code)
55
+ hex = Bitcoin::Base58.decode(base58_payment_code)
56
+ version = hex[2..3]
57
+ sign = hex[6..7]
58
+ public_key = hex[8..71]
59
+ payment_code = hex[0...-8]
60
+
61
+ raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
62
+ raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
63
+ raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
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
+
67
+ x_value = payment_code[8..71]
68
+ chain_code_hex = payment_code[72..135]
69
+
70
+ payment_code_pubkey = PaymentCode.new
71
+ payment_code_pubkey.depth = 3
72
+ payment_code_pubkey.sign = sign
73
+ payment_code_pubkey.x_value = x_value
74
+ payment_code_pubkey.chain_code = [chain_code_hex].pack('H*')
75
+
76
+ payment_code_pubkey.to_payload
77
+ end
78
+
79
+ # check whether +version+ is supported version bytes.
80
+ def self.support_version?(version)
81
+ SUPPORT_VERSIONS.include?(version)
82
+ end
83
+
84
+ # check whether +sign+ is supported version bytes.
85
+ def self.support_sign?(sign)
86
+ SUPPORT_SIGNS.include?(sign)
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
@@ -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
@@ -93,7 +93,8 @@ module Bitcoin
93
93
 
94
94
  def to_payload
95
95
  payload = ''
96
- payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value: non_witness_utxo.to_payload) if non_witness_utxo
96
+ payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:non_witness_utxo], value:
97
+ (witness_utxo && valid_witness_input?) ? non_witness_utxo.serialize_old_format : non_witness_utxo.to_payload) if non_witness_utxo
97
98
  payload << PSBT.serialize_to_vector(PSBT_IN_TYPES[:witness_utxo], value: witness_utxo.to_payload) if witness_utxo
98
99
  if final_script_sig.nil? && final_script_witness.nil?
99
100
  payload << partial_sigs.map{|k, v|PSBT.serialize_to_vector(PSBT_IN_TYPES[:partial_sig], key: k.htb, value: v)}.join
@@ -109,15 +110,6 @@ module Bitcoin
109
110
  payload
110
111
  end
111
112
 
112
- # Sanity check
113
- # @return [Boolean]
114
- def sane?
115
- return false if non_witness_utxo && witness_utxo
116
- return false if witness_script && witness_utxo.nil?
117
- return false if final_script_witness && witness_utxo.nil?
118
- true
119
- end
120
-
121
113
  # Check whether input's scriptPubkey is correct witness.
122
114
  # @return [Boolean]
123
115
  def valid_witness_input?
@@ -141,7 +133,6 @@ module Bitcoin
141
133
  # @param [Bitcoin::TxOut] utxo utxo object which input refers.
142
134
  # @return [Boolean]
143
135
  def ready_to_sign?(utxo)
144
- return false unless sane?
145
136
  return valid_witness_input? if witness_utxo
146
137
  valid_non_witness_input?(utxo) # non_witness_utxo
147
138
  end
@@ -177,8 +168,8 @@ module Bitcoin
177
168
  combined.witness_script = witness_script
178
169
  combined.sighash_type = sighash_type
179
170
  sigs = Hash[partial_sigs.merge(psbi.partial_sigs)]
180
- redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script && redeem_script.multisig?
181
- witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script && witness_script.multisig?
171
+ redeem_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if redeem_script&.multisig?
172
+ witness_script.get_multisig_pubkeys.each{|pubkey|combined.partial_sigs[pubkey.bth] = sigs[pubkey.bth]} if witness_script&.multisig?
182
173
  combined.hd_key_paths = hd_key_paths.merge(psbi.hd_key_paths)
183
174
  combined
184
175
  end
@@ -190,7 +181,7 @@ module Bitcoin
190
181
  if non_witness_utxo
191
182
  self.final_script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 if redeem_script.multisig?
192
183
  partial_sigs.values.each {|sig|final_script_sig << sig}
193
- final_script_sig << redeem_script.to_payload.bth
184
+ final_script_sig << redeem_script.to_hex
194
185
  self.partial_sigs = {}
195
186
  self.hd_key_paths = {}
196
187
  self.redeem_script = nil
@@ -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
@@ -2,6 +2,7 @@ module Bitcoin
2
2
  module PSBT
3
3
 
4
4
  class GlobalXpub
5
+ include Bitcoin::HexConverter
5
6
 
6
7
  attr_reader :xpub # Bitcoin::ExtPubkey
7
8
  attr_reader :info # Bitcoin::PSBT::KeyOriginInfo
@@ -16,7 +17,7 @@ module Bitcoin
16
17
  end
17
18
 
18
19
  def to_h
19
- {xpub: xpub.to_payload.bth}.merge(info.to_h)
20
+ {xpub: xpub.to_hex}.merge(info.to_h)
20
21
  end
21
22
 
22
23
  def to_s
@@ -25,6 +26,8 @@ module Bitcoin
25
26
  end
26
27
 
27
28
  class Tx
29
+ include Bitcoin::HexConverter
30
+
28
31
  attr_accessor :tx
29
32
  attr_accessor :xpubs
30
33
  attr_reader :inputs
@@ -52,7 +55,7 @@ module Bitcoin
52
55
  # @return [Bitcoin::PartiallySignedTx]
53
56
  def self.parse_from_payload(payload)
54
57
  buf = StringIO.new(payload)
55
- 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
56
59
  raise ArgumentError, 'Invalid PSBT separator.' unless buf.read(1).bth.to_i(16) == 0xff
57
60
  partial_tx = self.new
58
61
  found_sep = false
@@ -63,7 +66,7 @@ module Bitcoin
63
66
  found_sep = true
64
67
  break
65
68
  end
66
- key_type = buf.read(1).unpack('C').first
69
+ key_type = buf.read(1).unpack1('C')
67
70
  key = buf.read(key_len - 1)
68
71
  value = buf.read(Bitcoin.unpack_var_int_from_io(buf))
69
72
 
@@ -78,13 +81,13 @@ module Bitcoin
78
81
  when PSBT_GLOBAL_TYPES[:xpub]
79
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
80
83
  xpub = Bitcoin::ExtPubkey.parse_from_payload(key)
81
- raise ArgumentError, 'Invalid pubkey.' unless xpub.key.fully_valid_pubkey?
84
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless xpub.key.fully_valid_pubkey?
82
85
  raise ArgumentError, 'Duplicate key, global xpub already provided' if partial_tx.xpubs.any?{|x|x.xpub == xpub}
83
86
  info = Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)
84
87
  raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
85
88
  partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
86
89
  when PSBT_GLOBAL_TYPES[:ver]
87
- partial_tx.version_number = value.unpack('V').first
90
+ partial_tx.version_number = value.unpack1('V')
88
91
  raise ArgumentError, "An unsupported version was detected." if SUPPORT_VERSION < partial_tx.version_number
89
92
  else
90
93
  raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]
@@ -117,10 +120,6 @@ module Bitcoin
117
120
 
118
121
  raise ArgumentError, 'Outputs provided does not match the number of outputs in transaction.' unless partial_tx.outputs.size == partial_tx.tx.out.size
119
122
 
120
- partial_tx.inputs.each do |input|
121
- raise ArgumentError, 'PSBT is not sane.' unless input.sane?
122
- end
123
-
124
123
  partial_tx
125
124
  end
126
125
 
@@ -149,11 +148,9 @@ module Bitcoin
149
148
  payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:unsigned_tx], value: tx.to_payload)
150
149
  payload << xpubs.map(&:to_payload).join
151
150
  payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:ver], value: [version_number].pack('V')) if version_number
152
-
153
151
  payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join
154
152
 
155
153
  payload << PSBT_SEPARATOR.itb
156
-
157
154
  payload << inputs.map(&:to_payload).join
158
155
  payload << outputs.map(&:to_payload).join
159
156
  payload
@@ -177,14 +174,12 @@ module Bitcoin
177
174
  utxo = prev_tx.out[tx_in.out_point.index]
178
175
  raise ArgumentError, 'redeem script does not match utxo.' if redeem_script && !utxo.script_pubkey.include?(redeem_script.to_hash160)
179
176
  raise ArgumentError, 'witness script does not match redeem script.' if redeem_script && witness_script && !redeem_script.include?(witness_script.to_sha256)
180
- if utxo.script_pubkey.witness_program? || (redeem_script && redeem_script.witness_program?)
181
- inputs[i].witness_utxo = utxo
182
- else
183
- inputs[i].non_witness_utxo = prev_tx
184
- end
177
+ inputs[i].witness_utxo = utxo if utxo.script_pubkey.witness_program? || redeem_script&.witness_program?
178
+ inputs[i].non_witness_utxo = prev_tx
185
179
  inputs[i].redeem_script = redeem_script if redeem_script
186
180
  inputs[i].witness_script = witness_script if witness_script
187
181
  inputs[i].hd_key_paths = hd_key_paths.map(&:pubkey).zip(hd_key_paths).to_h
182
+ break
188
183
  end
189
184
  end
190
185
  end
@@ -1,4 +1,4 @@
1
- require 'rest-client'
1
+ require 'net/http'
2
2
 
3
3
  module Bitcoin
4
4
  module RPC
@@ -53,20 +53,30 @@ module Bitcoin
53
53
  :params => params,
54
54
  :id => 'jsonrpc'
55
55
  }
56
- post(server_url, @config[:timeout], @config[:open_timeout], data.to_json, content_type: :json) do |respdata, request, result|
57
- raise result.message if !result.kind_of?(Net::HTTPSuccess) && respdata.empty?
58
- response = JSON.parse(respdata.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') })
59
- raise response['error'] if response['error']
60
- response['result']
61
- end
56
+ uri = URI.parse(server_url)
57
+ http = Net::HTTP.new(uri.hostname, uri.port)
58
+ http.use_ssl = uri.scheme === "https"
59
+ request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
60
+ request.basic_auth(uri.user, uri.password)
61
+ request.content_type = 'application/json'
62
+ request.body = data.to_json
63
+ response = http.request(request)
64
+ body = response.body
65
+ response = Bitcoin::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
66
+ raise response['error'].to_json if response['error']
67
+ response['result']
62
68
  end
63
-
64
- def post(url, timeout, open_timeout, payload, headers={}, &block)
65
- RestClient::Request.execute(method: :post, url: url, timeout: timeout,
66
- open_timeout: open_timeout, payload: payload, headers: headers, &block)
69
+
70
+ # Call CLI command on Ruby-like method names.
71
+ # e.g. generate_to_address, send_to_address, get_wallet_info
72
+ def method_missing(name, *args)
73
+ if name.to_s.include?('_')
74
+ send(name.to_s.gsub('_', '').to_sym, args)
75
+ else
76
+ super
77
+ end
67
78
  end
68
79
 
69
80
  end
70
-
71
81
  end
72
82
  end
@@ -42,14 +42,14 @@ module Bitcoin
42
42
  nextblockhash: node.chain.next_hash(block_hash).rhex
43
43
  }
44
44
  else
45
- entry.header.to_payload.bth
45
+ entry.header.to_hex
46
46
  end
47
47
  end
48
48
 
49
49
  # Returns connected peer information.
50
50
  def getpeerinfo
51
51
  node.pool.peers.map do |peer|
52
- local_addr = "#{peer.remote_version.remote_addr.ip}:18333"
52
+ local_addr = "#{peer.remote_version.remote_addr.addr_string}:18333"
53
53
  {
54
54
  id: peer.id,
55
55
  addr: "#{peer.host}:#{peer.port}",
@@ -96,7 +96,7 @@ module Bitcoin
96
96
  script = Bitcoin::Script.parse_from_payload(hex_script.htb)
97
97
  h = script.to_h
98
98
  h.delete(:hex)
99
- h[:p2sh] = script.to_p2sh.addresses.first unless script.p2sh?
99
+ h[:p2sh] = script.to_p2sh.to_addr unless script.p2sh?
100
100
  h
101
101
  rescue Exception
102
102
  raise ArgumentError.new('Script decode failed')
@@ -6,6 +6,7 @@ module Bitcoin
6
6
  # bitcoin script
7
7
  class Script
8
8
  include Bitcoin::Opcodes
9
+ include Bitcoin::HexConverter
9
10
 
10
11
  attr_accessor :chunks
11
12
 
@@ -109,25 +110,28 @@ module Bitcoin
109
110
  if opcode.pushdata?
110
111
  pushcode = opcode.ord
111
112
  packed_size = nil
113
+ if buf.eof?
114
+ s.chunks << opcode
115
+ return s
116
+ end
112
117
  len = case pushcode
113
118
  when OP_PUSHDATA1
114
119
  packed_size = buf.read(1)
115
- packed_size.unpack('C').first
120
+ packed_size.unpack1('C')
116
121
  when OP_PUSHDATA2
117
122
  packed_size = buf.read(2)
118
- packed_size.unpack('v').first
123
+ packed_size.unpack1('v')
119
124
  when OP_PUSHDATA4
120
125
  packed_size = buf.read(4)
121
- packed_size.unpack('V').first
126
+ packed_size.unpack1('V')
122
127
  else
123
- pushcode if pushcode < OP_PUSHDATA1
128
+ pushcode < OP_PUSHDATA1 ? pushcode : 0
124
129
  end
125
- if len
126
- s.chunks << [len].pack('C') if buf.eof?
127
- unless buf.eof?
128
- chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
129
- s.chunks << chunk
130
- end
130
+ if buf.eof?
131
+ s.chunks << [len].pack('C')
132
+ else buf.eof?
133
+ chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
134
+ s.chunks << chunk
131
135
  end
132
136
  else
133
137
  if Opcodes.defined?(opcode.ord)
@@ -140,15 +144,21 @@ module Bitcoin
140
144
  s
141
145
  end
142
146
 
143
- def to_payload
144
- chunks.join
147
+ # Output script payload.
148
+ # @param [Boolean] length_prefixed Flag whether the length of the pyrode should be given at the beginning.(default: false)
149
+ # @return [String] payload
150
+ def to_payload(length_prefixed = false)
151
+ p = chunks.join
152
+ length_prefixed ? (Bitcoin.pack_var_int(p.length) << p) : p
145
153
  end
146
154
 
147
155
  def empty?
148
156
  chunks.size == 0
149
157
  end
150
158
 
159
+ # @deprecated
151
160
  def addresses
161
+ puts "WARNING: Bitcoin::Script#addresses is deprecated. Use Bitcoin::Script#to_addr instead."
152
162
  return [p2pkh_addr] if p2pkh?
153
163
  return [p2sh_addr] if p2sh?
154
164
  return [bech32_addr] if witness_program?
@@ -156,6 +166,15 @@ module Bitcoin
156
166
  []
157
167
  end
158
168
 
169
+ # convert to address
170
+ # @return [String] if script type is p2pkh or p2sh or witness program, return address, otherwise nil.
171
+ def to_addr
172
+ return p2pkh_addr if p2pkh?
173
+ return p2sh_addr if p2sh?
174
+ return bech32_addr if witness_program?
175
+ nil
176
+ end
177
+
159
178
  # check whether standard script.
160
179
  def standard?
161
180
  p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
@@ -230,7 +249,7 @@ module Bitcoin
230
249
  return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
231
250
  return false unless chunks[1].pushdata?
232
251
 
233
- if size == (chunks[1][0].unpack('C').first + 2)
252
+ if size == (chunks[1][0].unpack1('C') + 2)
234
253
  program_size = chunks[1].pushed_data.bytesize
235
254
  return program_size >= 2 && program_size <= 40
236
255
  end
@@ -324,16 +343,21 @@ module Bitcoin
324
343
  when Integer
325
344
  opcode_to_name(c)
326
345
  when String
346
+ return c if c.empty?
327
347
  if c.pushdata?
328
348
  v = Opcodes.opcode_to_small_int(c.ord)
329
349
  if v
330
350
  v
331
351
  else
332
352
  data = c.pushed_data
333
- if data.bytesize <= 4
334
- Script.decode_number(data.bth) # for scriptnum
353
+ if data
354
+ if data.bytesize <= 4
355
+ Script.decode_number(data.bth) # for scriptnum
356
+ else
357
+ data.bth
358
+ end
335
359
  else
336
- data.bth
360
+ c.bth
337
361
  end
338
362
  end
339
363
  else
@@ -351,7 +375,7 @@ module Bitcoin
351
375
 
352
376
  # generate hash160 hash for payload
353
377
  def to_hash160
354
- Bitcoin.hash160(to_payload.bth)
378
+ Bitcoin.hash160(to_hex)
355
379
  end
356
380
 
357
381
  # script size
@@ -380,8 +404,8 @@ module Bitcoin
380
404
  hex = '0' + hex unless (hex.length % 2).zero?
381
405
  v = hex.htb.reverse # change endian
382
406
 
383
- v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack('C').first & 0x80) == 0
384
- v[-1] = [v[-1].unpack('C').first | 0x80].pack('C') if negative
407
+ v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack1('C') & 0x80) == 0
408
+ v[-1] = [v[-1].unpack1('C') | 0x80].pack('C') if negative
385
409
  v.bth
386
410
  end
387
411
 
@@ -389,7 +413,7 @@ module Bitcoin
389
413
  def self.decode_number(s)
390
414
  v = s.htb.reverse
391
415
  return 0 if v.length.zero?
392
- mbs = v[0].unpack('C').first
416
+ mbs = v[0].unpack1('C')
393
417
  v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
394
418
  result = v.bth.to_i(16)
395
419
  result = -result unless (mbs & 0x80) == 0
@@ -488,7 +512,7 @@ module Bitcoin
488
512
  end
489
513
 
490
514
  def to_h
491
- h = {asm: to_s, hex: to_payload.bth, type: type}
515
+ h = {asm: to_s, hex: to_hex, type: type}
492
516
  addrs = addresses
493
517
  unless addrs.empty?
494
518
  h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
@@ -504,12 +528,6 @@ module Bitcoin
504
528
  (size > 0 && op_return?) || size > Bitcoin::MAX_SCRIPT_SIZE
505
529
  end
506
530
 
507
- # convert payload to hex data.
508
- # @return [String] script with hex format.
509
- def to_hex
510
- to_payload.bth
511
- end
512
-
513
531
  private
514
532
 
515
533
  # generate p2pkh address. if script dose not p2pkh, return nil.
@@ -542,10 +560,32 @@ module Bitcoin
542
560
  def bech32_addr
543
561
  segwit_addr = Bech32::SegwitAddr.new
544
562
  segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
545
- segwit_addr.script_pubkey = to_payload.bth
563
+ segwit_addr.script_pubkey = to_hex
546
564
  segwit_addr.addr
547
565
  end
548
566
 
567
+ # Check whether push data length is valid.
568
+ # @return [Boolean] if valid return true, otherwise false.
569
+ def valid_pushdata_length?(chunk)
570
+ buf = StringIO.new(chunk)
571
+ opcode = buf.read(1).ord
572
+ offset = 1
573
+ len = case opcode
574
+ when OP_PUSHDATA1
575
+ offset += 1
576
+ buf.read(1).unpack1('C')
577
+ when OP_PUSHDATA2
578
+ offset += 2
579
+ buf.read(2).unpack1('v')
580
+ when OP_PUSHDATA4
581
+ offset += 4
582
+ buf.read(4).unpack1('V')
583
+ else
584
+ opcode
585
+ end
586
+ chunk.bytesize == len + offset
587
+ end
588
+
549
589
  end
550
590
 
551
591
  end