tapyrus 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -14
  3. data/exe/tapyrusrbd +2 -2
  4. data/lib/openassets/util.rb +2 -4
  5. data/lib/schnorr.rb +83 -0
  6. data/lib/schnorr/signature.rb +38 -0
  7. data/lib/tapyrus.rb +9 -11
  8. data/lib/tapyrus/block.rb +1 -32
  9. data/lib/tapyrus/block_header.rb +7 -6
  10. data/lib/tapyrus/chain_params.rb +13 -26
  11. data/lib/tapyrus/chainparams/{testnet.yml → dev.yml} +7 -9
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -10
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/ext.rb +5 -0
  15. data/lib/tapyrus/ext/json_parser.rb +47 -0
  16. data/lib/tapyrus/ext_key.rb +5 -10
  17. data/lib/tapyrus/key.rb +57 -29
  18. data/lib/tapyrus/message.rb +2 -2
  19. data/lib/tapyrus/message/base.rb +1 -0
  20. data/lib/tapyrus/message/block.rb +3 -3
  21. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  22. data/lib/tapyrus/message/tx.rb +2 -2
  23. data/lib/tapyrus/network/peer.rb +1 -15
  24. data/lib/tapyrus/node/cli.rb +15 -11
  25. data/lib/tapyrus/node/configuration.rb +1 -1
  26. data/lib/tapyrus/node/spv.rb +1 -1
  27. data/lib/tapyrus/opcodes.rb +5 -0
  28. data/lib/tapyrus/out_point.rb +1 -1
  29. data/lib/tapyrus/rpc/request_handler.rb +3 -3
  30. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  31. data/lib/tapyrus/script/color.rb +79 -0
  32. data/lib/tapyrus/script/multisig.rb +0 -27
  33. data/lib/tapyrus/script/script.rb +74 -89
  34. data/lib/tapyrus/script/script_error.rb +8 -14
  35. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  36. data/lib/tapyrus/script/tx_checker.rb +16 -4
  37. data/lib/tapyrus/secp256k1.rb +1 -0
  38. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  39. data/lib/tapyrus/secp256k1/ruby.rb +5 -31
  40. data/lib/tapyrus/store/chain_entry.rb +1 -0
  41. data/lib/tapyrus/tx.rb +18 -160
  42. data/lib/tapyrus/tx_in.rb +4 -11
  43. data/lib/tapyrus/tx_out.rb +2 -1
  44. data/lib/tapyrus/util.rb +8 -0
  45. data/lib/tapyrus/validation.rb +1 -6
  46. data/lib/tapyrus/version.rb +1 -1
  47. data/lib/tapyrus/wallet/account.rb +1 -0
  48. data/lib/tapyrus/wallet/master_key.rb +1 -0
  49. data/tapyrusrb.gemspec +3 -3
  50. metadata +42 -39
  51. data/lib/tapyrus/chainparams/regtest.yml +0 -38
  52. data/lib/tapyrus/descriptor.rb +0 -147
  53. data/lib/tapyrus/script_witness.rb +0 -38
@@ -17,11 +17,11 @@ module Tapyrus
17
17
 
18
18
  def self.parse_from_payload(payload)
19
19
  tx = Tapyrus::Tx.parse_from_payload(payload)
20
- new(tx, tx.witness?)
20
+ new(tx)
21
21
  end
22
22
 
23
23
  def to_payload
24
- use_segwit ? tx.to_payload : tx.serialize_old_format
24
+ tx.to_payload
25
25
  end
26
26
 
27
27
  end
@@ -99,21 +99,7 @@ module Tapyrus
99
99
 
100
100
  # broadcast tx.
101
101
  def broadcast_tx(tx)
102
- conn.send_message(Tapyrus::Message::Tx.new(tx, support_witness?))
103
- end
104
-
105
- # check the remote peer support witness.
106
- def support_witness?
107
- return false unless remote_version
108
- remote_version.services & Tapyrus::Message::SERVICE_FLAGS[:witness] > 0
109
- end
110
-
111
- # check the remote peer supports compact block.
112
- def support_cmpct?
113
- return false if remote_version.version < Tapyrus::Message::VERSION[:compact]
114
- return true unless local_version.services & Tapyrus::Message::SERVICE_FLAGS[:witness] > 0
115
- return false unless support_witness?
116
- remote_version.version >= Tapyrus::Message::VERSION[:compact_witness]
102
+ conn.send_message(Tapyrus::Message::Tx.new(tx))
117
103
  end
118
104
 
119
105
  # get peer's block type.
@@ -1,4 +1,4 @@
1
- require 'rest-client'
1
+ require 'net/http'
2
2
  require 'thor'
3
3
  require 'json'
4
4
 
@@ -7,7 +7,7 @@ module Tapyrus
7
7
 
8
8
  class CLI < Thor
9
9
 
10
- class_option :network, aliases: '-n', default: :mainnet
10
+ class_option :network, aliases: '-n', default: :prod
11
11
 
12
12
  desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
13
13
  def getblockchaininfo
@@ -92,15 +92,19 @@ module Tapyrus
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
@@ -8,7 +8,7 @@ module Tapyrus
8
8
 
9
9
  def initialize(opts = {})
10
10
  # TODO apply configuration file.
11
- opts[:network] = :mainnet unless opts[:network]
11
+ opts[:network] = :prod unless opts[:network]
12
12
  opts[:relay] = false unless opts[:relay]
13
13
  Tapyrus.chain_params = opts[:network]
14
14
 
@@ -45,7 +45,7 @@ module Tapyrus
45
45
  # broadcast a transaction
46
46
  def broadcast(tx)
47
47
  pool.broadcast(tx)
48
- logger.debug "broadcast tx: #{tx.to_payload.bth}"
48
+ logger.debug "broadcast tx: #{tx.to_hex}"
49
49
  end
50
50
 
51
51
  # add filter element to bloom filter.
@@ -141,6 +141,11 @@ module Tapyrus
141
141
  OP_PUBKEY = 0xfe
142
142
  OP_INVALIDOPCODE = 0xff
143
143
 
144
+ # tapyrus extension
145
+ OP_CHECKDATASIG = 0xba
146
+ OP_CHECKDATASIGVERIFY = 0xbb
147
+ OP_COLOR = 0xbc
148
+
144
149
  DUPLICATE_KEY = [:OP_NOP2, :OP_NOP3]
145
150
  OPCODES_MAP = Hash[*(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map { |c| [const_get(c), c.to_s] }.flatten]
146
151
  NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
@@ -19,7 +19,7 @@ module Tapyrus
19
19
  end
20
20
 
21
21
  def coinbase?
22
- tx_hash == COINBASE_HASH && index == COINBASE_INDEX
22
+ tx_hash == COINBASE_HASH
23
23
  end
24
24
 
25
25
  def to_payload
@@ -31,8 +31,8 @@ module Tapyrus
31
31
  {
32
32
  hash: block_id,
33
33
  height: entry.height,
34
- version: entry.header.version,
35
- versionHex: entry.header.version.to_even_length_hex,
34
+ features: entry.header.features,
35
+ featuresHex: entry.header.features.to_even_length_hex,
36
36
  merkleroot: entry.header.merkle_root.rhex,
37
37
  time: entry.header.time,
38
38
  mediantime: node.chain.mtp(block_hash),
@@ -42,7 +42,7 @@ module Tapyrus
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
 
@@ -1,15 +1,16 @@
1
- require 'rest-client'
1
+ require 'net/http'
2
+ require 'json/pure'
2
3
 
3
4
  module Tapyrus
4
5
  module RPC
5
6
 
6
- # Client implementation for RPC to Bitcoin Core.
7
+ # Client implementation for RPC to Tapyrus Core.
7
8
  #
8
9
  # [Usage]
9
10
  # config = {schema: 'http', host: 'localhost', port: 18332, user: 'xxx', password: 'yyy'}
10
- # client = Tapyrus::RPC::BitcoinCoreClient.new(config)
11
+ # client = Tapyrus::RPC::TapyrusCoreClient.new(config)
11
12
  #
12
- # You can execute the CLI command supported by Bitcoin Core as follows:
13
+ # You can execute the CLI command supported by Tapyrus Core as follows:
13
14
  #
14
15
  # client.listunspent
15
16
  # client.getblockchaininfo
@@ -53,17 +54,18 @@ module Tapyrus
53
54
  :params => params,
54
55
  :id => 'jsonrpc'
55
56
  }
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
62
- 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)
57
+ uri = URI.parse(server_url)
58
+ http = Net::HTTP.new(uri.hostname, uri.port)
59
+ http.use_ssl = uri.scheme === "https"
60
+ request = Net::HTTP::Post.new('/')
61
+ request.basic_auth(uri.user, uri.password)
62
+ request.content_type = 'application/json'
63
+ request.body = data.to_json
64
+ response = http.request(request)
65
+ body = response.body
66
+ response = Tapyrus::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
67
+ raise response['error'].to_s if response['error']
68
+ response['result']
67
69
  end
68
70
 
69
71
  end
@@ -0,0 +1,79 @@
1
+ module Tapyrus
2
+ module Color
3
+ module TokenTypes
4
+ NONE = 0x00
5
+ REISSUABLE = 0xC1
6
+ NON_REISSUABLE = 0xC2
7
+ NFT = 0xC3
8
+ end
9
+
10
+ class ColorIdentifier
11
+ include Tapyrus::HexConverter
12
+
13
+ attr_reader :type, :payload
14
+
15
+ def self.reissuable(script_pubkey)
16
+ new(TokenTypes::REISSUABLE, Tapyrus.sha256(script_pubkey.to_payload))
17
+ end
18
+
19
+ def self.non_reissuable(out_point)
20
+ new(TokenTypes::NON_REISSUABLE, Tapyrus.sha256(out_point.to_payload))
21
+ end
22
+
23
+ def self.nft(out_point)
24
+ new(TokenTypes::NFT, Tapyrus.sha256(out_point.to_payload))
25
+ end
26
+
27
+ def to_payload
28
+ [type, payload].pack('Ca*')
29
+ end
30
+
31
+ def self.parse_from_payload(payload)
32
+ type, payload = payload.unpack('Ca*')
33
+ new(type, payload)
34
+ end
35
+
36
+ def ==(other)
37
+ other && other.to_payload == to_payload
38
+ end
39
+
40
+ def valid?
41
+ return false unless [TokenTypes::REISSUABLE, TokenTypes::NON_REISSUABLE, TokenTypes::NFT].include?(type)
42
+ return false unless payload.bytesize == 32
43
+ true
44
+ end
45
+
46
+ private
47
+
48
+ def initialize(type, payload)
49
+ @type = type
50
+ @payload = payload
51
+ end
52
+ end
53
+
54
+ module ColoredOutput
55
+ def colored?
56
+ script_pubkey.cp2pkh? || script_pubkey.cp2sh?
57
+ end
58
+
59
+ def color_id
60
+ @color_id ||= ColorIdentifier.parse_from_payload(script_pubkey.chunks[0].pushed_data)
61
+ end
62
+
63
+ def reissuable?
64
+ return false unless colored?
65
+ color_id.type == TokenTypes::REISSUABLE
66
+ end
67
+
68
+ def non_reissuable?
69
+ return false unless colored?
70
+ color_id.type == TokenTypes::NON_REISSUABLE
71
+ end
72
+
73
+ def nft?
74
+ return false unless colored?
75
+ color_id.type == TokenTypes::NFT
76
+ end
77
+ end
78
+ end
79
+ end
@@ -61,32 +61,5 @@ module Tapyrus
61
61
  prefix + pubkeys.map { |k| sigs[k] ? Tapyrus::Script.pack_pushdata(sigs[k]) : nil }.join +
62
62
  Tapyrus::Script.pack_pushdata(script.chunks[-1].pushed_data)
63
63
  end
64
-
65
- def self.add_sig_to_multisig_script_witness(sig_to_add, script_witness, hash_type = SIGHASH_TYPE[:all])
66
- signature = sig_to_add + [hash_type].pack("C*")
67
- script_witness.stack << signature
68
- end
69
-
70
- # Sort signatures in the given +script_witness+ according to the order of pubkeys in
71
- # the redeem script. Also needs the +sig_hash+ to match signatures to pubkeys.
72
- # @param [ScriptWitness] script_witness for multisig.
73
- # @param [String] sig_hash to be signed.
74
- def self.sort_witness_multisig_signatures(script_witness, sig_hash)
75
- redeem_script = Tapyrus::Script.parse_from_payload(script_witness.stack[-1])
76
- pubkeys = redeem_script.get_multisig_pubkeys
77
- sigs = Hash[script_witness.stack[1...-1].map.with_index do |sig, idx|
78
- pubkey = pubkeys.map do |key|
79
- Tapyrus::Key.new(pubkey: key.bth).verify(sig, sig_hash) ? key : nil
80
- end.compact.first
81
- raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
82
- [pubkey, sig]
83
- end]
84
- script_witness.stack.clear
85
- script_witness.stack << ''
86
- pubkeys.each do |pubkey|
87
- script_witness.stack << sigs[pubkey] if sigs[pubkey]
88
- end
89
- script_witness.stack << redeem_script.to_payload
90
- end
91
64
  end
92
65
  end
@@ -5,6 +5,7 @@ module Tapyrus
5
5
 
6
6
  # tapyrus script
7
7
  class Script
8
+ include Tapyrus::HexConverter
8
9
  include Tapyrus::Opcodes
9
10
 
10
11
  attr_accessor :chunks
@@ -18,11 +19,6 @@ module Tapyrus
18
19
  new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
19
20
  end
20
21
 
21
- # generate P2WPKH script
22
- def self.to_p2wpkh(pubkey_hash)
23
- new << WITNESS_VERSION << pubkey_hash
24
- end
25
-
26
22
  # generate m of n multisig p2sh script
27
23
  # @param [String] m the number of signatures required for multisig
28
24
  # @param [Array] pubkeys array of public keys that compose multisig
@@ -45,6 +41,40 @@ module Tapyrus
45
41
  Script.to_p2sh(to_hash160)
46
42
  end
47
43
 
44
+ # generate cp2pkh script
45
+ # @param [ColorIdentifier] color identifier
46
+ # @param [String] hash160 of pubkey
47
+ # @return [Script] CP2PKH script
48
+ # @raise [ArgumentError] if color_id is nil or invalid
49
+ def self.to_cp2pkh(color_id, pubkey_hash)
50
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
51
+ new << color_id.to_payload << OP_COLOR << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
52
+ end
53
+
54
+ # generate cp2sh script
55
+ # @param [ColorIdentifier] color identifier
56
+ # @param [String] hash160 of script
57
+ # @return [Script] CP2SH script
58
+ # @raise [ArgumentError] if color_id is nil or invalid
59
+ def self.to_cp2sh(color_id, script_hash)
60
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
61
+ new << color_id.to_payload << OP_COLOR << OP_HASH160 << script_hash << OP_EQUAL
62
+ end
63
+
64
+ # Add color identifier to existing p2pkh or p2sh
65
+ # @param [ColorIdentifier] color identifier
66
+ # @return [Script] CP2PKH or CP2SH script
67
+ # @raise [ArgumentError] if color_id is nil or invalid
68
+ # @raise [RuntimeError] if script is neither p2pkh nor p2sh
69
+ def add_color(color_id)
70
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
71
+ raise RuntimeError, 'Only p2pkh and p2sh can add color' unless p2pkh? or p2sh?
72
+ Tapyrus::Script.new.tap do |s|
73
+ s << color_id.to_payload << OP_COLOR
74
+ s.chunks += self.chunks
75
+ end
76
+ end
77
+
48
78
  def get_multisig_pubkeys
49
79
  num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
50
80
  (1..num).map{ |i| chunks[i].pushed_data }
@@ -59,13 +89,6 @@ module Tapyrus
59
89
  new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
60
90
  end
61
91
 
62
- # generate p2wsh script for +redeem_script+
63
- # @param [Script] redeem_script target redeem script
64
- # @param [Script] p2wsh script
65
- def self.to_p2wsh(redeem_script)
66
- new << WITNESS_VERSION << redeem_script.to_sha256
67
- end
68
-
69
92
  # generate script from string.
70
93
  def self.from_string(string)
71
94
  script = new
@@ -151,14 +174,15 @@ module Tapyrus
151
174
  def addresses
152
175
  return [p2pkh_addr] if p2pkh?
153
176
  return [p2sh_addr] if p2sh?
154
- return [bech32_addr] if witness_program?
177
+ return [cp2pkh_addr] if cp2pkh?
178
+ return [cp2sh_addr] if cp2sh?
155
179
  return get_multisig_pubkeys.map{|pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh} if multisig?
156
180
  []
157
181
  end
158
182
 
159
183
  # check whether standard script.
160
184
  def standard?
161
- p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
185
+ p2pkh? | p2sh? | multisig? | standard_op_return?
162
186
  end
163
187
 
164
188
  # whether this script is a P2PKH format script.
@@ -168,17 +192,6 @@ module Tapyrus
168
192
  (chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
169
193
  end
170
194
 
171
- # whether this script is a P2WPKH format script.
172
- def p2wpkh?
173
- return false unless chunks.size == 2
174
- chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 21
175
- end
176
-
177
- def p2wsh?
178
- return false unless chunks.size == 2
179
- chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 33
180
- end
181
-
182
195
  def p2sh?
183
196
  return false unless chunks.size == 3
184
197
  OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
@@ -201,6 +214,23 @@ module Tapyrus
201
214
  (chunks.size == 1 || chunks[1].opcode <= OP_16)
202
215
  end
203
216
 
217
+ def cp2pkh?
218
+ return false unless chunks.size == 7
219
+ return false unless chunks[0].bytesize == 34
220
+ return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
221
+ return false unless chunks[1].ord == OP_COLOR
222
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
223
+ (chunks[2..3]+ chunks[5..6]).map(&:ord) && chunks[4].bytesize == 21
224
+ end
225
+
226
+ def cp2sh?
227
+ return false unless chunks.size == 5
228
+ return false unless chunks[0].bytesize == 34
229
+ return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
230
+ return false unless chunks[1].ord == OP_COLOR
231
+ OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
232
+ end
233
+
204
234
  def op_return_data
205
235
  return nil unless op_return?
206
236
  return nil if chunks.size == 1
@@ -221,50 +251,15 @@ module Tapyrus
221
251
  chunks.select{|c|c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) && [2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))}.map{|c|c.pushed_data.bth}
222
252
  end
223
253
 
224
- # A witness program is any valid Script that consists of a 1-byte push opcode followed by a data push between 2 and 40 bytes.
225
- def witness_program?
226
- return false if size < 4 || size > 42 || chunks.size < 2
227
-
228
- opcode = chunks[0].opcode
229
-
230
- return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
231
- return false unless chunks[1].pushdata?
232
-
233
- if size == (chunks[1][0].unpack('C').first + 2)
234
- program_size = chunks[1].pushed_data.bytesize
235
- return program_size >= 2 && program_size <= 40
236
- end
237
-
238
- false
239
- end
240
-
241
- # get witness commitment
242
- def witness_commitment
243
- return nil if !op_return? || op_return_data.bytesize < 36
244
- buf = StringIO.new(op_return_data)
245
- return nil unless buf.read(4).bth == WITNESS_COMMITMENT_HEADER
246
- buf.read(32).bth
247
- end
248
-
249
- # If this script is witness program, return its script code,
250
- # otherwise returns the self payload. ScriptInterpreter does not use this.
254
+ # returns the self payload. ScriptInterpreter does not use this.
251
255
  def to_script_code(skip_separator_index = 0)
252
256
  payload = to_payload
253
- if p2wpkh?
254
- payload = Script.to_p2pkh(chunks[1].pushed_data.bth).to_payload
255
- elsif skip_separator_index > 0
257
+ if skip_separator_index > 0
256
258
  payload = subscript_codeseparator(skip_separator_index)
257
259
  end
258
260
  Tapyrus.pack_var_string(payload)
259
261
  end
260
262
 
261
- # get witness version and witness program
262
- def witness_data
263
- version = opcode_to_small_int(chunks[0].opcode)
264
- program = chunks[1].pushed_data
265
- [version, program]
266
- end
267
-
268
263
  # append object to payload
269
264
  def <<(obj)
270
265
  if obj.is_a?(Integer)
@@ -324,6 +319,7 @@ module Tapyrus
324
319
  when Integer
325
320
  opcode_to_name(c)
326
321
  when String
322
+ return c if c.empty?
327
323
  if c.pushdata?
328
324
  v = Opcodes.opcode_to_small_int(c.ord)
329
325
  if v
@@ -351,7 +347,7 @@ module Tapyrus
351
347
 
352
348
  # generate hash160 hash for payload
353
349
  def to_hash160
354
- Tapyrus.hash160(to_payload.bth)
350
+ Tapyrus.hash160(to_hex)
355
351
  end
356
352
 
357
353
  # script size
@@ -482,13 +478,11 @@ module Tapyrus
482
478
  return 'pubkeyhash' if p2pkh?
483
479
  return 'scripthash' if p2sh?
484
480
  return 'multisig' if multisig?
485
- return 'witness_v0_keyhash' if p2wpkh?
486
- return 'witness_v0_scripthash' if p2wsh?
487
481
  'nonstandard'
488
482
  end
489
483
 
490
484
  def to_h
491
- h = {asm: to_s, hex: to_payload.bth, type: type}
485
+ h = {asm: to_s, hex: to_hex, type: type}
492
486
  addrs = addresses
493
487
  unless addrs.empty?
494
488
  h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
@@ -504,48 +498,39 @@ module Tapyrus
504
498
  (size > 0 && op_return?) || size > Tapyrus::MAX_SCRIPT_SIZE
505
499
  end
506
500
 
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
501
  private
514
502
 
515
503
  # generate p2pkh address. if script dose not p2pkh, return nil.
516
504
  def p2pkh_addr
517
505
  return nil unless p2pkh?
518
506
  hash160 = chunks[2].pushed_data.bth
519
- return nil unless hash160.htb.bytesize == 20
520
507
  Tapyrus.encode_base58_address(hash160, Tapyrus.chain_params.address_version)
521
508
  end
522
509
 
523
- # generate p2wpkh address. if script dose not p2wpkh, return nil.
524
- def p2wpkh_addr
525
- p2wpkh? ? bech32_addr : nil
526
- end
527
-
528
510
  # generate p2sh address. if script dose not p2sh, return nil.
529
511
  def p2sh_addr
530
512
  return nil unless p2sh?
531
513
  hash160 = chunks[1].pushed_data.bth
532
- return nil unless hash160.htb.bytesize == 20
533
514
  Tapyrus.encode_base58_address(hash160, Tapyrus.chain_params.p2sh_version)
534
515
  end
535
516
 
536
- # generate p2wsh address. if script dose not p2wsh, return nil.
537
- def p2wsh_addr
538
- p2wsh? ? bech32_addr : nil
539
- end
517
+ # generate cp2pkh address. if script dose not cp2pkh, return nil.
518
+ def cp2pkh_addr
519
+ return nil unless cp2pkh?
540
520
 
541
- # return bech32 address for payload
542
- def bech32_addr
543
- segwit_addr = Bech32::SegwitAddr.new
544
- segwit_addr.hrp = Tapyrus.chain_params.bech32_hrp
545
- segwit_addr.script_pubkey = to_payload.bth
546
- segwit_addr.addr
521
+ color_id = chunks[0].pushed_data.bth
522
+ hash160 = chunks[4].pushed_data.bth
523
+ Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2pkh_version)
547
524
  end
548
525
 
526
+ # generate cp2sh address. if script dose not cp2sh, return nil.
527
+ def cp2sh_addr
528
+ return nil unless cp2sh?
529
+
530
+ color_id = chunks[0].pushed_data.bth
531
+ hash160 = chunks[3].pushed_data.bth
532
+ Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
533
+ end
549
534
  end
550
535
 
551
536
  end