bitcoinrb 0.3.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +5 -3
  4. data/README.md +17 -6
  5. data/bitcoinrb.gemspec +7 -7
  6. data/exe/bitcoinrbd +5 -0
  7. data/lib/bitcoin.rb +34 -11
  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 +7 -0
  25. data/lib/bitcoin/message/base.rb +1 -0
  26. data/lib/bitcoin/message/cf_parser.rb +16 -0
  27. data/lib/bitcoin/message/cfcheckpt.rb +36 -0
  28. data/lib/bitcoin/message/cfheaders.rb +40 -0
  29. data/lib/bitcoin/message/cfilter.rb +35 -0
  30. data/lib/bitcoin/message/fee_filter.rb +1 -1
  31. data/lib/bitcoin/message/filter_load.rb +3 -3
  32. data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
  33. data/lib/bitcoin/message/get_cfheaders.rb +24 -0
  34. data/lib/bitcoin/message/get_cfilters.rb +25 -0
  35. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  36. data/lib/bitcoin/message/inventory.rb +1 -1
  37. data/lib/bitcoin/message/merkle_block.rb +1 -1
  38. data/lib/bitcoin/message/network_addr.rb +3 -3
  39. data/lib/bitcoin/message/ping.rb +1 -1
  40. data/lib/bitcoin/message/pong.rb +1 -1
  41. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  42. data/lib/bitcoin/message/version.rb +7 -0
  43. data/lib/bitcoin/mnemonic.rb +7 -7
  44. data/lib/bitcoin/network/peer.rb +9 -4
  45. data/lib/bitcoin/network/peer_discovery.rb +1 -1
  46. data/lib/bitcoin/node/cli.rb +14 -10
  47. data/lib/bitcoin/node/configuration.rb +3 -1
  48. data/lib/bitcoin/node/spv.rb +9 -1
  49. data/lib/bitcoin/opcodes.rb +14 -1
  50. data/lib/bitcoin/out_point.rb +7 -0
  51. data/lib/bitcoin/payment_code.rb +92 -0
  52. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  53. data/lib/bitcoin/psbt/input.rb +8 -17
  54. data/lib/bitcoin/psbt/output.rb +1 -1
  55. data/lib/bitcoin/psbt/tx.rb +11 -16
  56. data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
  57. data/lib/bitcoin/rpc/request_handler.rb +2 -2
  58. data/lib/bitcoin/script/script.rb +68 -28
  59. data/lib/bitcoin/script/script_error.rb +27 -1
  60. data/lib/bitcoin/script/script_interpreter.rb +164 -67
  61. data/lib/bitcoin/script/tx_checker.rb +64 -14
  62. data/lib/bitcoin/secp256k1.rb +1 -0
  63. data/lib/bitcoin/secp256k1/native.rb +138 -25
  64. data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
  65. data/lib/bitcoin/secp256k1/ruby.rb +82 -54
  66. data/lib/bitcoin/sighash_generator.rb +156 -0
  67. data/lib/bitcoin/slip39/sss.rb +5 -2
  68. data/lib/bitcoin/store.rb +2 -1
  69. data/lib/bitcoin/store/chain_entry.rb +1 -0
  70. data/lib/bitcoin/store/db/level_db.rb +2 -2
  71. data/lib/bitcoin/store/utxo_db.rb +226 -0
  72. data/lib/bitcoin/tx.rb +17 -88
  73. data/lib/bitcoin/tx_in.rb +4 -5
  74. data/lib/bitcoin/tx_out.rb +2 -3
  75. data/lib/bitcoin/util.rb +43 -6
  76. data/lib/bitcoin/version.rb +1 -1
  77. data/lib/bitcoin/wallet.rb +1 -0
  78. data/lib/bitcoin/wallet/account.rb +2 -1
  79. data/lib/bitcoin/wallet/base.rb +3 -3
  80. data/lib/bitcoin/wallet/db.rb +1 -1
  81. data/lib/bitcoin/wallet/master_key.rb +1 -0
  82. data/lib/bitcoin/wallet/utxo.rb +37 -0
  83. metadata +50 -32
@@ -1,4 +1,4 @@
1
- require 'rest-client'
1
+ require 'net/http'
2
2
  require 'thor'
3
3
  require 'json'
4
4
 
@@ -92,15 +92,19 @@ module Bitcoin
92
92
  :id => 'jsonrpc'
93
93
  }
94
94
  begin
95
- RestClient::Request.execute(method: :post, url: config.server_url, payload: data.to_json,
96
- headers: {content_type: :json}) do |response, request, result|
97
- return false if !result.kind_of?(Net::HTTPSuccess) && response.empty?
98
- begin
99
- json = JSON.parse(response.to_str)
100
- puts JSON.pretty_generate(json)
101
- rescue Exception
102
- puts response.to_str
103
- end
95
+ uri = URI.parse(config.server_url)
96
+ http = Net::HTTP.new(uri.hostname, uri.port)
97
+ http.use_ssl = uri.scheme === "https"
98
+ request = Net::HTTP::Post.new('/')
99
+ request.content_type = 'application/json'
100
+ request.body = data.to_json
101
+ response = http.request(request)
102
+ body = response.body
103
+ begin
104
+ json = JSON.parse(body.to_str)
105
+ puts JSON.pretty_generate(json)
106
+ rescue Exception
107
+ puts body.to_str
104
108
  end
105
109
  rescue Exception => e
106
110
  puts e.message
@@ -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
@@ -45,7 +53,7 @@ module Bitcoin
45
53
  # broadcast a transaction
46
54
  def broadcast(tx)
47
55
  pool.broadcast(tx)
48
- logger.debug "broadcast tx: #{tx.to_payload.bth}"
56
+ logger.debug "broadcast tx: #{tx.to_hex}"
49
57
  end
50
58
 
51
59
  # add filter element to bloom filter.
@@ -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
@@ -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