tapyrus 0.2.3 → 0.2.8

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.prettierignore +3 -0
  4. data/.prettierrc.yaml +3 -0
  5. data/CODE_OF_CONDUCT.md +7 -7
  6. data/README.md +14 -17
  7. data/Rakefile +3 -3
  8. data/lib/openassets.rb +0 -2
  9. data/lib/openassets/marker_output.rb +0 -4
  10. data/lib/openassets/payload.rb +4 -10
  11. data/lib/schnorr.rb +2 -3
  12. data/lib/schnorr/signature.rb +3 -6
  13. data/lib/tapyrus.rb +7 -30
  14. data/lib/tapyrus/base58.rb +7 -6
  15. data/lib/tapyrus/block.rb +1 -2
  16. data/lib/tapyrus/block_header.rb +15 -9
  17. data/lib/tapyrus/bloom_filter.rb +5 -3
  18. data/lib/tapyrus/chain_params.rb +1 -4
  19. data/lib/tapyrus/chainparams/dev.yml +3 -2
  20. data/lib/tapyrus/chainparams/prod.yml +3 -2
  21. data/lib/tapyrus/constants.rb +29 -23
  22. data/lib/tapyrus/errors.rb +1 -3
  23. data/lib/tapyrus/ext.rb +1 -1
  24. data/lib/tapyrus/ext/ecdsa.rb +4 -4
  25. data/lib/tapyrus/ext/json_parser.rb +1 -4
  26. data/lib/tapyrus/ext_key.rb +38 -34
  27. data/lib/tapyrus/key.rb +31 -35
  28. data/lib/tapyrus/key_path.rb +15 -12
  29. data/lib/tapyrus/logger.rb +20 -16
  30. data/lib/tapyrus/merkle_tree.rb +19 -20
  31. data/lib/tapyrus/message.rb +14 -16
  32. data/lib/tapyrus/message/addr.rb +1 -7
  33. data/lib/tapyrus/message/base.rb +0 -3
  34. data/lib/tapyrus/message/block.rb +2 -9
  35. data/lib/tapyrus/message/block_transaction_request.rb +3 -6
  36. data/lib/tapyrus/message/block_transactions.rb +2 -6
  37. data/lib/tapyrus/message/block_txn.rb +0 -4
  38. data/lib/tapyrus/message/cmpct_block.rb +1 -7
  39. data/lib/tapyrus/message/error.rb +1 -4
  40. data/lib/tapyrus/message/fee_filter.rb +1 -4
  41. data/lib/tapyrus/message/filter_add.rb +0 -4
  42. data/lib/tapyrus/message/filter_clear.rb +0 -4
  43. data/lib/tapyrus/message/filter_load.rb +2 -5
  44. data/lib/tapyrus/message/get_addr.rb +0 -4
  45. data/lib/tapyrus/message/get_block_txn.rb +0 -4
  46. data/lib/tapyrus/message/get_blocks.rb +0 -3
  47. data/lib/tapyrus/message/get_data.rb +1 -4
  48. data/lib/tapyrus/message/get_headers.rb +1 -3
  49. data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
  50. data/lib/tapyrus/message/headers.rb +0 -4
  51. data/lib/tapyrus/message/headers_parser.rb +3 -8
  52. data/lib/tapyrus/message/inv.rb +1 -4
  53. data/lib/tapyrus/message/inventories_parser.rb +2 -7
  54. data/lib/tapyrus/message/inventory.rb +12 -5
  55. data/lib/tapyrus/message/mem_pool.rb +0 -4
  56. data/lib/tapyrus/message/merkle_block.rb +4 -9
  57. data/lib/tapyrus/message/network_addr.rb +7 -6
  58. data/lib/tapyrus/message/not_found.rb +0 -3
  59. data/lib/tapyrus/message/ping.rb +0 -3
  60. data/lib/tapyrus/message/pong.rb +0 -3
  61. data/lib/tapyrus/message/prefilled_tx.rb +0 -4
  62. data/lib/tapyrus/message/reject.rb +0 -3
  63. data/lib/tapyrus/message/send_cmpct.rb +1 -3
  64. data/lib/tapyrus/message/send_headers.rb +0 -3
  65. data/lib/tapyrus/message/tx.rb +0 -4
  66. data/lib/tapyrus/message/ver_ack.rb +1 -5
  67. data/lib/tapyrus/message/version.rb +2 -5
  68. data/lib/tapyrus/mnemonic.rb +17 -15
  69. data/lib/tapyrus/network.rb +0 -2
  70. data/lib/tapyrus/network/connection.rb +0 -3
  71. data/lib/tapyrus/network/message_handler.rb +61 -60
  72. data/lib/tapyrus/network/peer.rb +13 -12
  73. data/lib/tapyrus/network/peer_discovery.rb +3 -5
  74. data/lib/tapyrus/network/pool.rb +12 -12
  75. data/lib/tapyrus/node.rb +1 -1
  76. data/lib/tapyrus/node/cli.rb +12 -14
  77. data/lib/tapyrus/node/configuration.rb +1 -3
  78. data/lib/tapyrus/node/spv.rb +2 -3
  79. data/lib/tapyrus/opcodes.rb +9 -7
  80. data/lib/tapyrus/out_point.rb +5 -5
  81. data/lib/tapyrus/rpc/http_server.rb +21 -22
  82. data/lib/tapyrus/rpc/request_handler.rb +42 -44
  83. data/lib/tapyrus/rpc/tapyrus_core_client.rb +53 -25
  84. data/lib/tapyrus/script/color.rb +20 -2
  85. data/lib/tapyrus/script/multisig.rb +13 -12
  86. data/lib/tapyrus/script/script.rb +104 -67
  87. data/lib/tapyrus/script/script_error.rb +1 -4
  88. data/lib/tapyrus/script/script_interpreter.rb +439 -399
  89. data/lib/tapyrus/script/tx_checker.rb +20 -10
  90. data/lib/tapyrus/secp256k1.rb +0 -4
  91. data/lib/tapyrus/secp256k1/native.rb +17 -18
  92. data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
  93. data/lib/tapyrus/secp256k1/ruby.rb +10 -12
  94. data/lib/tapyrus/slip39.rb +20 -5
  95. data/lib/tapyrus/slip39/share.rb +41 -29
  96. data/lib/tapyrus/slip39/sss.rb +101 -57
  97. data/lib/tapyrus/store.rb +1 -3
  98. data/lib/tapyrus/store/chain_entry.rb +0 -4
  99. data/lib/tapyrus/store/db.rb +0 -2
  100. data/lib/tapyrus/store/db/level_db.rb +5 -9
  101. data/lib/tapyrus/store/spv_chain.rb +11 -17
  102. data/lib/tapyrus/tx.rb +45 -37
  103. data/lib/tapyrus/tx_builder.rb +158 -0
  104. data/lib/tapyrus/tx_in.rb +1 -6
  105. data/lib/tapyrus/tx_out.rb +2 -7
  106. data/lib/tapyrus/util.rb +20 -7
  107. data/lib/tapyrus/validation.rb +12 -11
  108. data/lib/tapyrus/version.rb +1 -1
  109. data/lib/tapyrus/wallet/account.rb +22 -18
  110. data/lib/tapyrus/wallet/base.rb +12 -9
  111. data/lib/tapyrus/wallet/db.rb +6 -9
  112. data/lib/tapyrus/wallet/master_key.rb +2 -4
  113. data/tapyrusrb.gemspec +13 -16
  114. metadata +20 -31
  115. data/.travis.yml +0 -12
@@ -1,9 +1,7 @@
1
1
  module Tapyrus
2
2
  module RPC
3
-
4
3
  # RPC server's request handler.
5
4
  module RequestHandler
6
-
7
5
  # Returns an object containing various state info regarding blockchain processing.
8
6
  def getblockchaininfo
9
7
  h = {}
@@ -28,19 +26,19 @@ module Tapyrus
28
26
  raise ArgumentError.new('Block not found') unless entry
29
27
  if verbose
30
28
  {
31
- hash: block_id,
32
- height: entry.height,
33
- features: entry.header.features,
34
- featuresHex: entry.header.features.to_even_length_hex.ljust(8, '0'),
35
- merkleroot: entry.header.merkle_root.rhex,
36
- immutablemerkleroot: entry.header.im_merkle_root.rhex,
37
- time: entry.header.time,
38
- mediantime: node.chain.mtp(block_hash),
39
- xfield_type: entry.header.x_field_type,
40
- xfield: entry.header.x_field,
41
- proof: entry.header.proof,
42
- previousblockhash: entry.prev_hash.rhex,
43
- nextblockhash: node.chain.next_hash(block_hash).rhex
29
+ hash: block_id,
30
+ height: entry.height,
31
+ features: entry.header.features,
32
+ featuresHex: entry.header.features.to_even_length_hex.ljust(8, '0'),
33
+ merkleroot: entry.header.merkle_root.rhex,
34
+ immutablemerkleroot: entry.header.im_merkle_root.rhex,
35
+ time: entry.header.time,
36
+ mediantime: node.chain.mtp(block_hash),
37
+ xfield_type: entry.header.x_field_type,
38
+ xfield: entry.header.x_field,
39
+ proof: entry.header.proof,
40
+ previousblockhash: entry.prev_hash.rhex,
41
+ nextblockhash: node.chain.next_hash(block_hash).rhex
44
42
  }
45
43
  else
46
44
  entry.header.to_hex
@@ -49,34 +47,38 @@ module Tapyrus
49
47
 
50
48
  # Returns connected peer information.
51
49
  def getpeerinfo
52
- node.pool.peers.map do |peer|
53
- local_addr = "#{peer.remote_version.remote_addr.ip}:18333"
54
- {
55
- id: peer.id,
56
- addr: "#{peer.host}:#{peer.port}",
57
- addrlocal: local_addr,
58
- services: peer.remote_version.services.to_even_length_hex.rjust(16, '0'),
59
- relaytxes: peer.remote_version.relay,
60
- lastsend: peer.last_send,
61
- lastrecv: peer.last_recv,
62
- bytessent: peer.bytes_sent,
63
- bytesrecv: peer.bytes_recv,
64
- conntime: peer.conn_time,
65
- pingtime: peer.ping_time,
66
- minping: peer.min_ping,
67
- version: peer.remote_version.version,
68
- subver: peer.remote_version.user_agent,
69
- inbound: !peer.outbound?,
70
- startingheight: peer.remote_version.start_height,
71
- best_hash: peer.best_hash,
72
- best_height: peer.best_height
73
- }
74
- end
50
+ node
51
+ .pool
52
+ .peers
53
+ .map do |peer|
54
+ local_addr = "#{peer.remote_version.remote_addr.ip}:18333"
55
+ {
56
+ id: peer.id,
57
+ addr: "#{peer.host}:#{peer.port}",
58
+ addrlocal: local_addr,
59
+ services: peer.remote_version.services.to_even_length_hex.rjust(16, '0'),
60
+ relaytxes: peer.remote_version.relay,
61
+ lastsend: peer.last_send,
62
+ lastrecv: peer.last_recv,
63
+ bytessent: peer.bytes_sent,
64
+ bytesrecv: peer.bytes_recv,
65
+ conntime: peer.conn_time,
66
+ pingtime: peer.ping_time,
67
+ minping: peer.min_ping,
68
+ version: peer.remote_version.version,
69
+ subver: peer.remote_version.user_agent,
70
+ inbound: !peer.outbound?,
71
+ startingheight: peer.remote_version.start_height,
72
+ best_hash: peer.best_hash,
73
+ best_height: peer.best_height
74
+ }
75
+ end
75
76
  end
76
77
 
77
78
  # broadcast transaction
78
79
  def sendrawtransaction(hex_tx)
79
80
  tx = Tapyrus::Tx.parse_from_payload(hex_tx.htb)
81
+
80
82
  # TODO check wether tx is valid
81
83
  node.broadcast(tx)
82
84
  tx.txid
@@ -110,7 +112,7 @@ module Tapyrus
110
112
  def createwallet(wallet_id = 1, wallet_path_prefix = Tapyrus::Wallet::Base.default_path_prefix)
111
113
  wallet = Tapyrus::Wallet::Base.create(wallet_id, wallet_path_prefix)
112
114
  node.wallet = wallet unless node.wallet
113
- {wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic}
115
+ { wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic }
114
116
  end
115
117
 
116
118
  # get wallet list.
@@ -127,9 +129,7 @@ module Tapyrus
127
129
  def listaccounts
128
130
  return {} unless node.wallet
129
131
  accounts = {}
130
- node.wallet.accounts.each do |a|
131
- accounts[a.name] = node.wallet.get_balance(a)
132
- end
132
+ node.wallet.accounts.each { |a| accounts[a.name] = node.wallet.get_balance(a) }
133
133
  accounts
134
134
  end
135
135
 
@@ -144,8 +144,6 @@ module Tapyrus
144
144
  def getnewaddress(account_name)
145
145
  node.wallet.generate_new_address(account_name)
146
146
  end
147
-
148
147
  end
149
-
150
148
  end
151
149
  end
@@ -3,6 +3,37 @@ require 'json/pure'
3
3
 
4
4
  module Tapyrus
5
5
  module RPC
6
+ # Throw when happened anything http's error with connect to server.
7
+ #
8
+ # Almost case this exception happened from 401 Unauthorized or 500 Internal Server Error.
9
+ # And also, throw by cause of other http's errors.
10
+ #
11
+ # You can pull RPC error message when happened 500 Internal Server Error, like below:
12
+ #
13
+ # rescue Tapyrus::RPC::Error => ex
14
+ # if ex.message.response_code == 500
15
+ # puts ex.message[:rpc_error]
16
+ # end
17
+ # end
18
+ class Error < StandardError
19
+ attr_reader :message
20
+
21
+ def initialize(response)
22
+ raise ArgumentError, 'Must set response as cause.' unless response
23
+
24
+ # set message from response body or status code.
25
+ @message = { response_code: response&.code, response_msg: response&.msg }
26
+ begin
27
+ @message.merge!({ rpc_error: Tapyrus::RPC.response_body2json(response.body)['error'] })
28
+ rescue JSON::ParserError => _
29
+ # if RPC server don't send error message.
30
+ end
31
+ end
32
+
33
+ def to_s
34
+ @message.to_s
35
+ end
36
+ end
6
37
 
7
38
  # Client implementation for RPC to Tapyrus Core.
8
39
  #
@@ -16,25 +47,21 @@ module Tapyrus
16
47
  # client.getblockchaininfo
17
48
  #
18
49
  class TapyrusCoreClient
19
-
20
50
  attr_reader :config
21
51
 
22
52
  # @param [Hash] config a configuration required to connect to Bitcoin Core.
23
53
  def initialize(config)
24
54
  @config = config
25
55
 
26
- commands = request(:help).split("\n").inject([]) do |memo_ary, line|
27
- if !line.empty? && !line.start_with?('==')
28
- memo_ary << line.split(' ').first.to_sym
29
- end
30
- memo_ary
31
- end
32
- TapyrusCoreClient.class_eval do
33
- commands.each do |command|
34
- define_method(command) do |*params|
35
- request(command, *params)
56
+ commands =
57
+ request(:help)
58
+ .split("\n")
59
+ .inject([]) do |memo_ary, line|
60
+ memo_ary << line.split(' ').first.to_sym if !line.empty? && !line.start_with?('==')
61
+ memo_ary
36
62
  end
37
- end
63
+ TapyrusCoreClient.class_eval do
64
+ commands.each { |command| define_method(command) { |*params| request(command, *params) } }
38
65
  end
39
66
  end
40
67
 
@@ -42,33 +69,34 @@ module Tapyrus
42
69
 
43
70
  def server_url
44
71
  url = "#{config[:schema]}://#{config[:user]}:#{config[:password]}@#{config[:host]}:#{config[:port]}"
45
- if !config[:wallet].nil? && !config[:wallet].empty?
46
- url += "/wallet/#{config[:wallet]}"
47
- end
72
+ url += "/wallet/#{config[:wallet]}" if !config[:wallet].nil? && !config[:wallet].empty?
48
73
  url
49
74
  end
50
75
 
51
76
  def request(command, *params)
52
- data = {
53
- :method => command,
54
- :params => params,
55
- :id => 'jsonrpc'
56
- }
77
+ data = { method: command, params: params, id: 'jsonrpc' }
57
78
  uri = URI.parse(server_url)
58
79
  http = Net::HTTP.new(uri.hostname, uri.port)
59
- http.use_ssl = uri.scheme === "https"
80
+ http.use_ssl = uri.scheme === 'https'
60
81
  request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
61
82
  request.basic_auth(uri.user, uri.password)
62
83
  request.content_type = 'application/json'
63
84
  request.body = data.to_json
64
85
  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_json if response['error']
86
+ raise Error.new(response) unless response.is_a? Net::HTTPOK
87
+ response = Tapyrus::RPC.response_body2json(response.body)
68
88
  response['result']
69
89
  end
90
+ end
70
91
 
92
+ def response_body2json(body)
93
+ Tapyrus::Ext::JsonParser.new(
94
+ body.gsub(/\\u([\da-fA-F]{4})/) do
95
+ [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8')
96
+ end
97
+ ).parse
71
98
  end
72
99
 
100
+ module_function :response_body2json
73
101
  end
74
- end
102
+ end
@@ -24,6 +24,11 @@ module Tapyrus
24
24
  new(TokenTypes::NFT, Tapyrus.sha256(out_point.to_payload))
25
25
  end
26
26
 
27
+ # Return ColorIdentifier for native token(i.e TPC)
28
+ def self.default
29
+ new(TokenTypes::NONE, '0000000000000000000000000000000000000000000000000000000000000000'.htb)
30
+ end
31
+
27
32
  def to_payload
28
33
  [type, payload].pack('Ca*')
29
34
  end
@@ -43,6 +48,19 @@ module Tapyrus
43
48
  true
44
49
  end
45
50
 
51
+ def hash
52
+ to_payload.hash
53
+ end
54
+
55
+ def eql?(other)
56
+ to_payload.eql?(other.to_payload)
57
+ end
58
+
59
+ # Return true if the coin is native token(i.e TPC), otherwise false
60
+ def default?
61
+ self == ColorIdentifier.default
62
+ end
63
+
46
64
  private
47
65
 
48
66
  def initialize(type, payload)
@@ -53,11 +71,11 @@ module Tapyrus
53
71
 
54
72
  module ColoredOutput
55
73
  def colored?
56
- script_pubkey.cp2pkh? || script_pubkey.cp2sh?
74
+ script_pubkey.colored?
57
75
  end
58
76
 
59
77
  def color_id
60
- @color_id ||= ColorIdentifier.parse_from_payload(script_pubkey.chunks[0].pushed_data)
78
+ @color_id ||= script_pubkey.color_id
61
79
  end
62
80
 
63
81
  def reissuable?
@@ -1,11 +1,10 @@
1
1
  module Tapyrus
2
-
3
2
  # utility for multisig
4
3
  module Multisig
5
4
  include Tapyrus::Opcodes
6
5
 
7
6
  def self.prefix
8
- [OP_0].pack("C*")
7
+ [OP_0].pack('C*')
9
8
  end
10
9
 
11
10
  # generate input script sig spending a multisig output script.
@@ -23,7 +22,7 @@ module Tapyrus
23
22
  # multiple parties. Signatures must be in the same order as the
24
23
  # pubkeys in the output script being redeemed.
25
24
  def self.add_sig_to_multisig_script_sig(sig_to_add, script_sig, hash_type = SIGHASH_TYPE[:all])
26
- signature = sig_to_add + [hash_type].pack("C*")
25
+ signature = sig_to_add + [hash_type].pack('C*')
27
26
  offset = script_sig.empty? ? 0 : 1
28
27
  script_sig.insert(offset, Tapyrus::Script.pack_pushdata(signature))
29
28
  end
@@ -49,17 +48,19 @@ module Tapyrus
49
48
  pubkeys = redeem_script.get_multisig_pubkeys
50
49
 
51
50
  # find the pubkey for each signature by trying to verify it
52
- sigs = Hash[script.chunks[1...-1].map.with_index do |sig, idx|
53
- sig = sig.pushed_data
54
- pubkey = pubkeys.map do |key|
55
- Tapyrus::Key.new(pubkey: key.bth).verify(sig, sig_hash) ? key : nil
56
- end.compact.first
57
- raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
58
- [pubkey, sig]
59
- end]
51
+ sigs =
52
+ Hash[
53
+ script.chunks[1...-1].map.with_index do |sig, idx|
54
+ sig = sig.pushed_data
55
+ pubkey =
56
+ pubkeys.map { |key| Tapyrus::Key.new(pubkey: key.bth).verify(sig, sig_hash) ? key : nil }.compact.first
57
+ raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
58
+ [pubkey, sig]
59
+ end
60
+ ]
60
61
 
61
62
  prefix + pubkeys.map { |k| sigs[k] ? Tapyrus::Script.pack_pushdata(sigs[k]) : nil }.join +
62
63
  Tapyrus::Script.pack_pushdata(script.chunks[-1].pushed_data)
63
64
  end
64
65
  end
65
- end
66
+ end
@@ -2,7 +2,6 @@
2
2
  # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
3
 
4
4
  module Tapyrus
5
-
6
5
  # tapyrus script
7
6
  class Script
8
7
  include Tapyrus::HexConverter
@@ -75,9 +74,19 @@ module Tapyrus
75
74
  end
76
75
  end
77
76
 
77
+ # Remove color identifier from cp2pkh or cp2sh
78
+ # @param [ColorIdentifier] color identifier
79
+ # @return [Script] P2PKH or P2SH script
80
+ # @raise [RuntimeError] if script is neither cp2pkh nor cp2sh
81
+ def remove_color
82
+ raise RuntimeError, 'Only cp2pkh and cp2sh can remove color' unless cp2pkh? or cp2sh?
83
+
84
+ Tapyrus::Script.new.tap { |s| s.chunks = self.chunks[2..-1] }
85
+ end
86
+
78
87
  def get_multisig_pubkeys
79
88
  num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
80
- (1..num).map{ |i| chunks[i].pushed_data }
89
+ (1..num).map { |i| chunks[i].pushed_data }
81
90
  end
82
91
 
83
92
  # generate m of n multisig script
@@ -92,14 +101,16 @@ module Tapyrus
92
101
  # generate script from string.
93
102
  def self.from_string(string)
94
103
  script = new
95
- string.split(' ').each do |v|
96
- opcode = Opcodes.name_to_opcode(v)
97
- if opcode
98
- script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
99
- else
100
- script << (v =~ /^[0-9]+$/ ? v.to_i : v)
104
+ string
105
+ .split(' ')
106
+ .each do |v|
107
+ opcode = Opcodes.name_to_opcode(v)
108
+ if opcode
109
+ script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
110
+ else
111
+ script << (v =~ /^[0-9]+$/ ? v.to_i : v)
112
+ end
101
113
  end
102
- end
103
114
  script
104
115
  end
105
116
 
@@ -118,6 +129,12 @@ module Tapyrus
118
129
  Tapyrus::Script.to_p2pkh(hex)
119
130
  when Tapyrus.chain_params.p2sh_version
120
131
  Tapyrus::Script.to_p2sh(hex)
132
+ when Tapyrus.chain_params.cp2pkh_version
133
+ color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
134
+ Tapyrus::Script.to_cp2pkh(color, hex[66..-1])
135
+ when Tapyrus.chain_params.cp2sh_version
136
+ color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
137
+ Tapyrus::Script.to_cp2sh(color, hex[66..-1])
121
138
  else
122
139
  throw e
123
140
  end
@@ -132,19 +149,20 @@ module Tapyrus
132
149
  if opcode.pushdata?
133
150
  pushcode = opcode.ord
134
151
  packed_size = nil
135
- len = case pushcode
136
- when OP_PUSHDATA1
137
- packed_size = buf.read(1)
138
- packed_size.unpack('C').first
139
- when OP_PUSHDATA2
140
- packed_size = buf.read(2)
141
- packed_size.unpack('v').first
142
- when OP_PUSHDATA4
143
- packed_size = buf.read(4)
144
- packed_size.unpack('V').first
145
- else
146
- pushcode if pushcode < OP_PUSHDATA1
147
- end
152
+ len =
153
+ case pushcode
154
+ when OP_PUSHDATA1
155
+ packed_size = buf.read(1)
156
+ packed_size.unpack('C').first
157
+ when OP_PUSHDATA2
158
+ packed_size = buf.read(2)
159
+ packed_size.unpack('v').first
160
+ when OP_PUSHDATA4
161
+ packed_size = buf.read(4)
162
+ packed_size.unpack('V').first
163
+ else
164
+ pushcode if pushcode < OP_PUSHDATA1
165
+ end
148
166
  if len
149
167
  s.chunks << [len].pack('C') if buf.eof?
150
168
  unless buf.eof?
@@ -176,7 +194,7 @@ module Tapyrus
176
194
  return [p2sh_addr] if p2sh?
177
195
  return [cp2pkh_addr] if cp2pkh?
178
196
  return [cp2sh_addr] if cp2sh?
179
- return get_multisig_pubkeys.map{|pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh} if multisig?
197
+ return get_multisig_pubkeys.map { |pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh } if multisig?
180
198
  []
181
199
  end
182
200
 
@@ -188,8 +206,8 @@ module Tapyrus
188
206
  # whether this script is a P2PKH format script.
189
207
  def p2pkh?
190
208
  return false unless chunks.size == 5
191
- [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
192
- (chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
209
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[0..1] + chunks[3..4]).map(&:ord) &&
210
+ chunks[2].bytesize == 21
193
211
  end
194
212
 
195
213
  def p2sh?
@@ -210,19 +228,22 @@ module Tapyrus
210
228
  end
211
229
 
212
230
  def standard_op_return?
213
- op_return? && size <= MAX_OP_RETURN_RELAY &&
214
- (chunks.size == 1 || chunks[1].opcode <= OP_16)
231
+ op_return? && size <= MAX_OP_RETURN_RELAY && (chunks.size == 1 || chunks[1].opcode <= OP_16)
215
232
  end
216
233
 
234
+ # Return whether this script is a CP2PKH format script or not.
235
+ # @return [Boolean] true if this script is cp2pkh, otherwise false.
217
236
  def cp2pkh?
218
237
  return false unless chunks.size == 7
219
238
  return false unless chunks[0].bytesize == 34
220
239
  return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
221
240
  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
241
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[2..3] + chunks[5..6]).map(&:ord) &&
242
+ chunks[4].bytesize == 21
224
243
  end
225
244
 
245
+ # Return whether this script is a CP2SH format script or not.
246
+ # @return [Boolean] true if this script is cp2pkh, otherwise false.
226
247
  def cp2sh?
227
248
  return false unless chunks.size == 5
228
249
  return false unless chunks[0].bytesize == 34
@@ -231,6 +252,20 @@ module Tapyrus
231
252
  OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
232
253
  end
233
254
 
255
+ # Return whether this script represents colored coin.
256
+ # @return [Boolean] true if this script is colored, otherwise false.
257
+ def colored?
258
+ cp2pkh? || cp2sh?
259
+ end
260
+
261
+ # Return color identifier for this script.
262
+ # @return [ColorIdentifer] color identifier for this script if this script is colored. return nil if this script is not colored.
263
+ def color_id
264
+ return nil unless colored?
265
+
266
+ Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)
267
+ end
268
+
234
269
  def op_return_data
235
270
  return nil unless op_return?
236
271
  return nil if chunks.size == 1
@@ -239,24 +274,23 @@ module Tapyrus
239
274
 
240
275
  # whether data push only script which dose not include other opcode
241
276
  def push_only?
242
- chunks.each do |c|
243
- return false if !c.opcode.nil? && c.opcode > OP_16
244
- end
277
+ chunks.each { |c| return false if !c.opcode.nil? && c.opcode > OP_16 }
245
278
  true
246
279
  end
247
280
 
248
281
  # get public keys in the stack.
249
282
  # @return[Array[String]] an array of the pubkeys with hex format.
250
283
  def get_pubkeys
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}
284
+ chunks.select do |c|
285
+ c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) &&
286
+ [2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))
287
+ end.map { |c| c.pushed_data.bth }
252
288
  end
253
289
 
254
290
  # returns the self payload. ScriptInterpreter does not use this.
255
291
  def to_script_code(skip_separator_index = 0)
256
292
  payload = to_payload
257
- if skip_separator_index > 0
258
- payload = subscript_codeseparator(skip_separator_index)
259
- end
293
+ payload = subscript_codeseparator(skip_separator_index) if skip_separator_index > 0
260
294
  Tapyrus.pack_var_string(payload)
261
295
  end
262
296
 
@@ -267,7 +301,7 @@ module Tapyrus
267
301
  elsif obj.is_a?(String)
268
302
  append_data(obj)
269
303
  elsif obj.is_a?(Array)
270
- obj.each { |o| self.<< o}
304
+ obj.each { |o| self.<< o }
271
305
  self
272
306
  end
273
307
  end
@@ -303,18 +337,19 @@ module Tapyrus
303
337
 
304
338
  # Check the item is in the chunk of the script.
305
339
  def include?(item)
306
- chunk_item = if item.is_a?(Integer)
307
- item.chr
308
- elsif item.is_a?(String)
309
- data = Encoding::ASCII_8BIT == item.encoding ? item : item.htb
310
- Tapyrus::Script.pack_pushdata(data)
311
- end
340
+ chunk_item =
341
+ if item.is_a?(Integer)
342
+ item.chr
343
+ elsif item.is_a?(String)
344
+ data = Encoding::ASCII_8BIT == item.encoding ? item : item.htb
345
+ Tapyrus::Script.pack_pushdata(data)
346
+ end
312
347
  return false unless chunk_item
313
348
  chunks.include?(chunk_item)
314
349
  end
315
350
 
316
351
  def to_s
317
- chunks.map { |c|
352
+ chunks.map do |c|
318
353
  case c
319
354
  when Integer
320
355
  opcode_to_name(c)
@@ -337,7 +372,7 @@ module Tapyrus
337
372
  opcode ? opcode : 'OP_UNKNOWN [error]'
338
373
  end
339
374
  end
340
- }.join(' ')
375
+ end.join(' ')
341
376
  end
342
377
 
343
378
  # generate sha-256 hash for payload
@@ -395,17 +430,18 @@ module Tapyrus
395
430
  # binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
396
431
  def self.pack_pushdata(data)
397
432
  size = data.bytesize
398
- header = if size < OP_PUSHDATA1
399
- [size].pack('C')
400
- elsif size < 0xff
401
- [OP_PUSHDATA1, size].pack('CC')
402
- elsif size < 0xffff
403
- [OP_PUSHDATA2, size].pack('Cv')
404
- elsif size < 0xffffffff
405
- [OP_PUSHDATA4, size].pack('CV')
406
- else
407
- raise ArgumentError, 'data size is too big.'
408
- end
433
+ header =
434
+ if size < OP_PUSHDATA1
435
+ [size].pack('C')
436
+ elsif size < 0xff
437
+ [OP_PUSHDATA1, size].pack('CC')
438
+ elsif size < 0xffff
439
+ [OP_PUSHDATA2, size].pack('Cv')
440
+ elsif size < 0xffffffff
441
+ [OP_PUSHDATA4, size].pack('CV')
442
+ else
443
+ raise ArgumentError, 'data size is too big.'
444
+ end
409
445
  header + data
410
446
  end
411
447
 
@@ -429,8 +465,12 @@ module Tapyrus
429
465
  if chunk == sub_chunk
430
466
  buf << chunk
431
467
  i += 1
432
- (i = 0; buf.clear) if i == subscript.chunks.size # matched the whole subscript
433
- else # matched the part of head
468
+ (
469
+ i = 0
470
+ buf.clear
471
+ ) if i == subscript.chunks.size # matched the whole subscript
472
+ else
473
+ # matched the part of head
434
474
  i = 0
435
475
  tmp = chunk.dup
436
476
  tmp.slice!(sub_chunk)
@@ -452,7 +492,7 @@ module Tapyrus
452
492
 
453
493
  # remove all occurences of opcode. Typically it's OP_CODESEPARATOR.
454
494
  def delete_opcode(opcode)
455
- @chunks = chunks.select{|chunk| chunk.ord != opcode}
495
+ @chunks = chunks.select { |chunk| chunk.ord != opcode }
456
496
  self
457
497
  end
458
498
 
@@ -460,12 +500,10 @@ module Tapyrus
460
500
  def subscript_codeseparator(separator_index)
461
501
  buf = []
462
502
  process_separator_index = 0
463
- chunks.each{|chunk|
503
+ chunks.each do |chunk|
464
504
  buf << chunk if process_separator_index == separator_index
465
- if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
466
- process_separator_index += 1
467
- end
468
- }
505
+ process_separator_index += 1 if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
506
+ end
469
507
  buf.join
470
508
  end
471
509
 
@@ -482,10 +520,10 @@ module Tapyrus
482
520
  end
483
521
 
484
522
  def to_h
485
- h = {asm: to_s, hex: to_hex, type: type}
523
+ h = { asm: to_s, hex: to_hex, type: type }
486
524
  addrs = addresses
487
525
  unless addrs.empty?
488
- h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
526
+ h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) : addrs.size
489
527
  h[:addresses] = addrs
490
528
  end
491
529
  h
@@ -532,5 +570,4 @@ module Tapyrus
532
570
  Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
533
571
  end
534
572
  end
535
-
536
573
  end