bitcoinrb 0.3.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.rspec_parallel +2 -0
  4. data/.ruby-version +1 -1
  5. data/README.md +17 -6
  6. data/bitcoinrb.gemspec +9 -8
  7. data/exe/bitcoinrbd +5 -0
  8. data/lib/bitcoin.rb +37 -19
  9. data/lib/bitcoin/bip85_entropy.rb +111 -0
  10. data/lib/bitcoin/block_filter.rb +14 -0
  11. data/lib/bitcoin/block_header.rb +2 -0
  12. data/lib/bitcoin/chain_params.rb +9 -8
  13. data/lib/bitcoin/chainparams/regtest.yml +1 -1
  14. data/lib/bitcoin/chainparams/signet.yml +39 -0
  15. data/lib/bitcoin/chainparams/testnet.yml +1 -1
  16. data/lib/bitcoin/constants.rb +44 -10
  17. data/lib/bitcoin/descriptor.rb +1 -1
  18. data/lib/bitcoin/errors.rb +19 -0
  19. data/lib/bitcoin/ext.rb +6 -0
  20. data/lib/bitcoin/ext/array_ext.rb +22 -0
  21. data/lib/bitcoin/ext/ecdsa.rb +36 -0
  22. data/lib/bitcoin/ext/json_parser.rb +46 -0
  23. data/lib/bitcoin/ext_key.rb +51 -20
  24. data/lib/bitcoin/key.rb +89 -30
  25. data/lib/bitcoin/key_path.rb +12 -5
  26. data/lib/bitcoin/message.rb +79 -0
  27. data/lib/bitcoin/message/addr_v2.rb +34 -0
  28. data/lib/bitcoin/message/base.rb +17 -0
  29. data/lib/bitcoin/message/cf_parser.rb +16 -0
  30. data/lib/bitcoin/message/cfcheckpt.rb +36 -0
  31. data/lib/bitcoin/message/cfheaders.rb +40 -0
  32. data/lib/bitcoin/message/cfilter.rb +35 -0
  33. data/lib/bitcoin/message/fee_filter.rb +1 -1
  34. data/lib/bitcoin/message/filter_load.rb +3 -3
  35. data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
  36. data/lib/bitcoin/message/get_cfheaders.rb +24 -0
  37. data/lib/bitcoin/message/get_cfilters.rb +25 -0
  38. data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
  39. data/lib/bitcoin/message/inventory.rb +1 -1
  40. data/lib/bitcoin/message/merkle_block.rb +1 -1
  41. data/lib/bitcoin/message/network_addr.rb +141 -18
  42. data/lib/bitcoin/message/ping.rb +1 -1
  43. data/lib/bitcoin/message/pong.rb +1 -1
  44. data/lib/bitcoin/message/send_addr_v2.rb +13 -0
  45. data/lib/bitcoin/message/send_cmpct.rb +2 -2
  46. data/lib/bitcoin/message/tx.rb +1 -1
  47. data/lib/bitcoin/message/version.rb +7 -0
  48. data/lib/bitcoin/message_sign.rb +47 -0
  49. data/lib/bitcoin/mnemonic.rb +7 -7
  50. data/lib/bitcoin/network/peer.rb +9 -4
  51. data/lib/bitcoin/network/peer_discovery.rb +1 -1
  52. data/lib/bitcoin/node/cli.rb +14 -10
  53. data/lib/bitcoin/node/configuration.rb +3 -1
  54. data/lib/bitcoin/node/spv.rb +9 -1
  55. data/lib/bitcoin/opcodes.rb +14 -1
  56. data/lib/bitcoin/out_point.rb +2 -0
  57. data/lib/bitcoin/payment_code.rb +92 -0
  58. data/lib/bitcoin/payments/payment.pb.rb +1 -1
  59. data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
  60. data/lib/bitcoin/psbt/input.rb +9 -18
  61. data/lib/bitcoin/psbt/output.rb +1 -1
  62. data/lib/bitcoin/psbt/tx.rb +12 -17
  63. data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
  64. data/lib/bitcoin/rpc/request_handler.rb +5 -5
  65. data/lib/bitcoin/script/script.rb +96 -39
  66. data/lib/bitcoin/script/script_error.rb +27 -1
  67. data/lib/bitcoin/script/script_interpreter.rb +166 -66
  68. data/lib/bitcoin/script/tx_checker.rb +62 -14
  69. data/lib/bitcoin/secp256k1.rb +1 -0
  70. data/lib/bitcoin/secp256k1/native.rb +184 -17
  71. data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
  72. data/lib/bitcoin/secp256k1/ruby.rb +112 -56
  73. data/lib/bitcoin/sighash_generator.rb +156 -0
  74. data/lib/bitcoin/store.rb +1 -0
  75. data/lib/bitcoin/store/chain_entry.rb +1 -0
  76. data/lib/bitcoin/store/utxo_db.rb +226 -0
  77. data/lib/bitcoin/taproot.rb +9 -0
  78. data/lib/bitcoin/taproot/leaf_node.rb +23 -0
  79. data/lib/bitcoin/taproot/simple_builder.rb +139 -0
  80. data/lib/bitcoin/tx.rb +34 -104
  81. data/lib/bitcoin/tx_in.rb +4 -5
  82. data/lib/bitcoin/tx_out.rb +2 -3
  83. data/lib/bitcoin/util.rb +22 -6
  84. data/lib/bitcoin/version.rb +1 -1
  85. data/lib/bitcoin/wallet.rb +1 -0
  86. data/lib/bitcoin/wallet/account.rb +2 -1
  87. data/lib/bitcoin/wallet/base.rb +2 -2
  88. data/lib/bitcoin/wallet/master_key.rb +1 -0
  89. data/lib/bitcoin/wallet/utxo.rb +37 -0
  90. metadata +86 -32
  91. data/.travis.yml +0 -11
@@ -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
 
@@ -16,7 +16,7 @@ module Bitcoin
16
16
  end
17
17
 
18
18
  def self.parse_from_payload(payload)
19
- tx = Bitcoin::Tx.parse_from_payload(payload)
19
+ tx = Bitcoin::Tx.parse_from_payload(payload, strict: true)
20
20
  new(tx, tx.witness?)
21
21
  end
22
22
 
@@ -64,6 +64,13 @@ module Bitcoin
64
64
  ( version >= 70001 && payload ) ? unpack_boolean(payload) : [ true, nil ]
65
65
  end
66
66
 
67
+ # Check whether +service_flag+ support this version.
68
+ # @param [Integer] service_flag the service flags.
69
+ # @return [Boolean] whether support +service_flag+
70
+ def support?(service_flag)
71
+ (services & service_flag) != 0
72
+ end
73
+
67
74
  end
68
75
  end
69
76
  end
@@ -0,0 +1,47 @@
1
+ module Bitcoin
2
+
3
+ module MessageSign
4
+
5
+ class Error < StandardError; end
6
+
7
+ module_function
8
+
9
+ # Sign a message.
10
+ # @param [Bitcoin::Key] key Private key to sign with.
11
+ # @param [String] message The message to sign.
12
+ # @return [String] Signature, base64 encoded.
13
+ def sign_message(key, message, prefix: Bitcoin.chain_params.message_magic)
14
+ digest = message_hash(message, prefix: prefix)
15
+ compact_sig = key.sign_compact(digest)
16
+ Base64.strict_encode64(compact_sig)
17
+ end
18
+
19
+ # Verify a signed message.
20
+ # @param [String] address Signer's bitcoin address, it must refer to a public key.
21
+ # @param [String] signature The signature in base64 format.
22
+ # @param [String] message The message that was signed.
23
+ # @return [Boolean] Verification result.
24
+ def verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic)
25
+ validate_address!(address)
26
+ sig = Base64.decode64(signature)
27
+ raise ArgumentError, 'Invalid signature length' unless sig.bytesize == Bitcoin::Key::COMPACT_SIGNATURE_SIZE
28
+ digest = message_hash(message, prefix: prefix)
29
+ pubkey = Bitcoin::Key.recover_compact(digest, sig)
30
+ return false unless pubkey
31
+ pubkey.to_p2pkh == address
32
+ end
33
+
34
+ # Hashes a message for signing and verification.
35
+ def message_hash(message, prefix: Bitcoin.chain_params.message_magic)
36
+ Bitcoin.double_sha256(Bitcoin.pack_var_string(prefix) << Bitcoin.pack_var_string(message))
37
+ end
38
+
39
+ def validate_address!(address)
40
+ raise ArgumentError, 'Invalid address' unless Bitcoin.valid_address?(address)
41
+ script = Bitcoin::Script.parse_from_addr(address)
42
+ raise ArgumentError, 'Address has no key' unless script.p2pkh?
43
+ end
44
+
45
+ private_class_method :validate_address!
46
+ end
47
+ end
@@ -6,11 +6,11 @@ module Bitcoin
6
6
 
7
7
  WORD_DIR = "#{__dir__}/mnemonic/wordlist"
8
8
 
9
- attr_reader :word_list
9
+ attr_reader :language
10
10
 
11
- def initialize(word_list)
12
- raise ArgumentError, 'specified language is not supported.' unless Mnemonic.word_lists.include?(word_list)
13
- @word_list = word_list
11
+ def initialize(language)
12
+ raise ArgumentError, 'specified language is not supported.' unless Mnemonic.word_lists.include?(language)
13
+ @language = language
14
14
  end
15
15
 
16
16
  # get support language list
@@ -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
 
@@ -69,7 +69,7 @@ module Bitcoin
69
69
 
70
70
  # load word list contents
71
71
  def load_words
72
- File.readlines("#{WORD_DIR}/#{word_list}.txt").map(&:strip)
72
+ File.readlines("#{WORD_DIR}/#{language}.txt").map(&:strip)
73
73
  end
74
74
 
75
75
  end
@@ -83,10 +83,15 @@ module Bitcoin
83
83
 
84
84
  def post_handshake
85
85
  @connected = true
86
- pool.handle_new_peer(self)
87
- # require remote peer to use headers message instead fo inv message.
88
- conn.send_message(Bitcoin::Message::SendHeaders.new)
89
- EM.add_periodic_timer(PING_INTERVAL) {send_ping}
86
+ if remote_version.support?(Bitcoin::Message::SERVICE_FLAGS[:bloom])
87
+ pool.handle_new_peer(self)
88
+ # require remote peer to use headers message instead fo inv message.
89
+ conn.send_message(Bitcoin::Message::SendHeaders.new)
90
+ EM.add_periodic_timer(PING_INTERVAL) {send_ping}
91
+ else
92
+ close("peer does not support NODE_BLOOM.")
93
+ pool.pending_peers.delete(self)
94
+ end
90
95
  end
91
96
 
92
97
  # start block header download
@@ -30,7 +30,7 @@ module Bitcoin
30
30
  logger.debug 'discover peer address from DNS seeds.'
31
31
  dns_seeds.map { |seed|
32
32
  begin
33
- Socket.getaddrinfo(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
34
34
  rescue SocketError => e
35
35
  logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
36
36
  nil
@@ -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
 
@@ -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
+
@@ -17,7 +17,7 @@ module Bitcoin
17
17
  end
18
18
 
19
19
  def transactions
20
- @values[:transactions].map{|raw_tx|Bitcoin::Tx.parse_from_payload(raw_tx)}
20
+ @values[:transactions].map{|raw_tx|Bitcoin::Tx.parse_from_payload(raw_tx, strict: true)}
21
21
  end
22
22
 
23
23
  end
@@ -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
 
@@ -44,7 +44,7 @@ module Bitcoin
44
44
  when PSBT_IN_TYPES[:non_witness_utxo]
45
45
  raise ArgumentError, 'Invalid non-witness utxo typed key.' unless key_len == 1
46
46
  raise ArgumentError, 'Duplicate Key, input non-witness utxo already provided.' if input.non_witness_utxo
47
- input.non_witness_utxo = Bitcoin::Tx.parse_from_payload(value)
47
+ input.non_witness_utxo = Bitcoin::Tx.parse_from_payload(value, strict: true)
48
48
  when PSBT_IN_TYPES[:witness_utxo]
49
49
  raise ArgumentError, 'Invalid input witness utxo typed key.' unless key_len == 1
50
50
  raise ArgumentError, 'Duplicate Key, input witness utxo already provided.' if input.witness_utxo
@@ -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