tapyrus 0.2.4 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) 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 +14 -9
  12. data/lib/schnorr/sign_to_contract.rb +51 -0
  13. data/lib/schnorr/signature.rb +3 -6
  14. data/lib/tapyrus.rb +8 -30
  15. data/lib/tapyrus/base58.rb +7 -6
  16. data/lib/tapyrus/bip175.rb +67 -0
  17. data/lib/tapyrus/block.rb +1 -2
  18. data/lib/tapyrus/block_header.rb +15 -9
  19. data/lib/tapyrus/bloom_filter.rb +5 -3
  20. data/lib/tapyrus/chain_params.rb +1 -4
  21. data/lib/tapyrus/chainparams/dev.yml +3 -2
  22. data/lib/tapyrus/chainparams/prod.yml +3 -2
  23. data/lib/tapyrus/constants.rb +29 -23
  24. data/lib/tapyrus/errors.rb +1 -3
  25. data/lib/tapyrus/ext.rb +1 -1
  26. data/lib/tapyrus/ext/ecdsa.rb +4 -4
  27. data/lib/tapyrus/ext/json_parser.rb +1 -4
  28. data/lib/tapyrus/ext_key.rb +44 -32
  29. data/lib/tapyrus/key.rb +31 -35
  30. data/lib/tapyrus/key_path.rb +15 -12
  31. data/lib/tapyrus/logger.rb +20 -16
  32. data/lib/tapyrus/merkle_tree.rb +19 -20
  33. data/lib/tapyrus/message.rb +14 -16
  34. data/lib/tapyrus/message/addr.rb +1 -7
  35. data/lib/tapyrus/message/base.rb +0 -3
  36. data/lib/tapyrus/message/block.rb +2 -9
  37. data/lib/tapyrus/message/block_transaction_request.rb +3 -6
  38. data/lib/tapyrus/message/block_transactions.rb +2 -6
  39. data/lib/tapyrus/message/block_txn.rb +0 -4
  40. data/lib/tapyrus/message/cmpct_block.rb +1 -7
  41. data/lib/tapyrus/message/error.rb +1 -4
  42. data/lib/tapyrus/message/fee_filter.rb +1 -4
  43. data/lib/tapyrus/message/filter_add.rb +0 -4
  44. data/lib/tapyrus/message/filter_clear.rb +0 -4
  45. data/lib/tapyrus/message/filter_load.rb +2 -5
  46. data/lib/tapyrus/message/get_addr.rb +0 -4
  47. data/lib/tapyrus/message/get_block_txn.rb +0 -4
  48. data/lib/tapyrus/message/get_blocks.rb +0 -3
  49. data/lib/tapyrus/message/get_data.rb +1 -4
  50. data/lib/tapyrus/message/get_headers.rb +1 -3
  51. data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
  52. data/lib/tapyrus/message/headers.rb +0 -4
  53. data/lib/tapyrus/message/headers_parser.rb +3 -8
  54. data/lib/tapyrus/message/inv.rb +1 -4
  55. data/lib/tapyrus/message/inventories_parser.rb +2 -7
  56. data/lib/tapyrus/message/inventory.rb +12 -5
  57. data/lib/tapyrus/message/mem_pool.rb +0 -4
  58. data/lib/tapyrus/message/merkle_block.rb +4 -9
  59. data/lib/tapyrus/message/network_addr.rb +7 -6
  60. data/lib/tapyrus/message/not_found.rb +0 -3
  61. data/lib/tapyrus/message/ping.rb +0 -3
  62. data/lib/tapyrus/message/pong.rb +0 -3
  63. data/lib/tapyrus/message/prefilled_tx.rb +0 -4
  64. data/lib/tapyrus/message/reject.rb +0 -3
  65. data/lib/tapyrus/message/send_cmpct.rb +1 -3
  66. data/lib/tapyrus/message/send_headers.rb +0 -3
  67. data/lib/tapyrus/message/tx.rb +0 -4
  68. data/lib/tapyrus/message/ver_ack.rb +1 -5
  69. data/lib/tapyrus/message/version.rb +2 -5
  70. data/lib/tapyrus/mnemonic.rb +17 -15
  71. data/lib/tapyrus/network.rb +0 -2
  72. data/lib/tapyrus/network/connection.rb +0 -3
  73. data/lib/tapyrus/network/message_handler.rb +61 -60
  74. data/lib/tapyrus/network/peer.rb +13 -12
  75. data/lib/tapyrus/network/peer_discovery.rb +3 -5
  76. data/lib/tapyrus/network/pool.rb +12 -12
  77. data/lib/tapyrus/node.rb +1 -1
  78. data/lib/tapyrus/node/cli.rb +12 -14
  79. data/lib/tapyrus/node/configuration.rb +1 -3
  80. data/lib/tapyrus/node/spv.rb +2 -3
  81. data/lib/tapyrus/opcodes.rb +9 -7
  82. data/lib/tapyrus/out_point.rb +5 -5
  83. data/lib/tapyrus/rpc.rb +1 -0
  84. data/lib/tapyrus/rpc/http_server.rb +21 -22
  85. data/lib/tapyrus/rpc/request_handler.rb +42 -44
  86. data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
  87. data/lib/tapyrus/script/color.rb +20 -2
  88. data/lib/tapyrus/script/multisig.rb +13 -12
  89. data/lib/tapyrus/script/script.rb +104 -67
  90. data/lib/tapyrus/script/script_error.rb +1 -4
  91. data/lib/tapyrus/script/script_interpreter.rb +439 -399
  92. data/lib/tapyrus/script/tx_checker.rb +20 -10
  93. data/lib/tapyrus/secp256k1.rb +0 -4
  94. data/lib/tapyrus/secp256k1/native.rb +14 -15
  95. data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
  96. data/lib/tapyrus/secp256k1/ruby.rb +10 -12
  97. data/lib/tapyrus/slip39.rb +20 -5
  98. data/lib/tapyrus/slip39/share.rb +41 -29
  99. data/lib/tapyrus/slip39/sss.rb +101 -57
  100. data/lib/tapyrus/store.rb +1 -3
  101. data/lib/tapyrus/store/chain_entry.rb +0 -4
  102. data/lib/tapyrus/store/db.rb +0 -2
  103. data/lib/tapyrus/store/db/level_db.rb +5 -9
  104. data/lib/tapyrus/store/spv_chain.rb +11 -17
  105. data/lib/tapyrus/tx.rb +45 -37
  106. data/lib/tapyrus/tx_builder.rb +158 -0
  107. data/lib/tapyrus/tx_in.rb +1 -6
  108. data/lib/tapyrus/tx_out.rb +2 -7
  109. data/lib/tapyrus/util.rb +20 -7
  110. data/lib/tapyrus/validation.rb +12 -11
  111. data/lib/tapyrus/version.rb +1 -1
  112. data/lib/tapyrus/wallet/account.rb +22 -18
  113. data/lib/tapyrus/wallet/base.rb +12 -9
  114. data/lib/tapyrus/wallet/db.rb +6 -9
  115. data/lib/tapyrus/wallet/master_key.rb +2 -4
  116. data/tapyrusrb.gemspec +13 -16
  117. metadata +22 -31
  118. data/.travis.yml +0 -12
@@ -1,9 +1,7 @@
1
1
  module Tapyrus
2
2
  module Node
3
-
4
3
  # SPV class
5
4
  class SPV
6
-
7
5
  attr_reader :chain
8
6
  attr_reader :pool
9
7
  attr_reader :logger
@@ -20,6 +18,7 @@ module Tapyrus
20
18
  @logger = Tapyrus::Logger.create(:debug)
21
19
  @running = false
22
20
  @wallet = Tapyrus::Wallet::Base.current_wallet
21
+
23
22
  # TODO : optimize bloom filter parameters
24
23
  setup_filter
25
24
  end
@@ -72,7 +71,7 @@ module Tapyrus
72
71
 
73
72
  def setup_filter
74
73
  @bloom = Tapyrus::BloomFilter.create_filter(512, 0.01)
75
- wallet.watch_targets.each{|t|bloom.add(t.htb)} if wallet
74
+ wallet.watch_targets.each { |t| bloom.add(t.htb) } if wallet
76
75
  end
77
76
  end
78
77
  end
@@ -1,8 +1,6 @@
1
1
  module Tapyrus
2
-
3
2
  # https://bitcoin.org/en/developer-reference#opcodes
4
3
  module Opcodes
5
-
6
4
  module_function
7
5
 
8
6
  # https://en.bitcoin.it/wiki/Script#Constants
@@ -111,7 +109,7 @@ module Tapyrus
111
109
  OP_HASH256 = 0xaa
112
110
  OP_CODESEPARATOR = 0xab
113
111
  OP_CHECKSIG = 0xac
114
- OP_CHECKSIGVERIFY= 0xad
112
+ OP_CHECKSIGVERIFY = 0xad
115
113
  OP_CHECKMULTISIG = 0xae
116
114
  OP_CHECKMULTISIGVERIFY = 0xaf
117
115
 
@@ -124,7 +122,7 @@ module Tapyrus
124
122
  OP_VER = 0x62
125
123
  OP_VERIF = 0x65
126
124
  OP_VERNOTIF = 0x66
127
- OP_RESERVED1= 0x89
125
+ OP_RESERVED1 = 0x89
128
126
  OP_RESERVED2 = 0x8a
129
127
 
130
128
  OP_NOP1 = 0xb0
@@ -147,7 +145,12 @@ module Tapyrus
147
145
  OP_COLOR = 0xbc
148
146
 
149
147
  DUPLICATE_KEY = [:OP_NOP2, :OP_NOP3]
150
- OPCODES_MAP = Hash[*(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map { |c| [const_get(c), c.to_s] }.flatten]
148
+ OPCODES_MAP =
149
+ Hash[
150
+ *(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map do |c|
151
+ [const_get(c), c.to_s]
152
+ end.flatten
153
+ ]
151
154
  NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
152
155
 
153
156
  def opcode_to_name(opcode)
@@ -178,6 +181,5 @@ module Tapyrus
178
181
  return opcode - (OP_1 - 1) if opcode >= OP_1 && opcode <= OP_16
179
182
  nil
180
183
  end
181
-
182
184
  end
183
- end
185
+ end
@@ -1,10 +1,8 @@
1
1
  module Tapyrus
2
-
3
2
  # outpoint class
4
3
  class OutPoint
5
-
6
4
  COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
7
- COINBASE_INDEX = 4294967295
5
+ COINBASE_INDEX = 4_294_967_295
8
6
 
9
7
  attr_reader :tx_hash
10
8
  attr_reader :index
@@ -34,11 +32,13 @@ module Tapyrus
34
32
  index >= 0 && (!coinbase? && tx_hash != COINBASE_HASH)
35
33
  end
36
34
 
35
+ def ==(other)
36
+ to_payload == other&.to_payload
37
+ end
38
+
37
39
  # convert hash to txid
38
40
  def txid
39
41
  tx_hash.rhex
40
42
  end
41
-
42
43
  end
43
-
44
44
  end
data/lib/tapyrus/rpc.rb CHANGED
@@ -3,5 +3,6 @@ module Tapyrus
3
3
  autoload :HttpServer, 'tapyrus/rpc/http_server'
4
4
  autoload :RequestHandler, 'tapyrus/rpc/request_handler'
5
5
  autoload :TapyrusCoreClient, 'tapyrus/rpc/tapyrus_core_client'
6
+ autoload :Error, 'tapyrus/rpc/tapyrus_core_client'
6
7
  end
7
8
  end
@@ -3,7 +3,6 @@ require 'json'
3
3
 
4
4
  module Tapyrus
5
5
  module RPC
6
-
7
6
  # Tapyrusrb RPC server.
8
7
  class HttpServer < EM::Connection
9
8
  include EM::HttpServer
@@ -28,27 +27,29 @@ module Tapyrus
28
27
 
29
28
  # process http request.
30
29
  def process_http_request
31
- operation = proc {
32
- command, args = parse_json_params
33
- logger.debug("process http request. command = #{command}")
34
- begin
35
- send(command, *args).to_json
36
- rescue Exception => e
37
- e
30
+ operation =
31
+ proc do
32
+ command, args = parse_json_params
33
+ logger.debug("process http request. command = #{command}")
34
+ begin
35
+ send(command, *args).to_json
36
+ rescue Exception => e
37
+ e
38
+ end
38
39
  end
39
- }
40
- callback = proc{ |result|
41
- response = EM::DelegatedHttpResponse.new(self)
42
- if result.is_a?(Exception)
43
- response.status = 500
44
- response.content = result.message
45
- else
46
- response.status = 200
47
- response.content = result
40
+ callback =
41
+ proc do |result|
42
+ response = EM::DelegatedHttpResponse.new(self)
43
+ if result.is_a?(Exception)
44
+ response.status = 500
45
+ response.content = result.message
46
+ else
47
+ response.status = 200
48
+ response.content = result
49
+ end
50
+ response.content_type 'application/json'
51
+ response.send_response
48
52
  end
49
- response.content_type 'application/json'
50
- response.send_response
51
- }
52
53
  EM.defer(operation, callback)
53
54
  end
54
55
 
@@ -58,8 +59,6 @@ module Tapyrus
58
59
  params = JSON.parse(@http_post_content)
59
60
  [params['method'], params['params']]
60
61
  end
61
-
62
62
  end
63
-
64
63
  end
65
64
  end
@@ -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,40 @@ 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 :response_code, :response_msg, :rpc_error
20
+
21
+ def initialize(response_code, response_msg, rpc_error)
22
+ @response_code = response_code
23
+ @response_msg = response_msg
24
+ @rpc_error = rpc_error
25
+ end
26
+
27
+ def message
28
+ @message ||=
29
+ begin
30
+ m = { response_code: response_code, response_msg: response_msg }
31
+ m.merge!(rpc_error: rpc_error) if rpc_error
32
+ m
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ message.to_s
38
+ end
39
+ end
6
40
 
7
41
  # Client implementation for RPC to Tapyrus Core.
8
42
  #
@@ -16,25 +50,21 @@ module Tapyrus
16
50
  # client.getblockchaininfo
17
51
  #
18
52
  class TapyrusCoreClient
19
-
20
53
  attr_reader :config
21
54
 
22
55
  # @param [Hash] config a configuration required to connect to Bitcoin Core.
23
56
  def initialize(config)
24
57
  @config = config
25
58
 
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)
59
+ commands =
60
+ request(:help)
61
+ .split("\n")
62
+ .inject([]) do |memo_ary, line|
63
+ memo_ary << line.split(' ').first.to_sym if !line.empty? && !line.start_with?('==')
64
+ memo_ary
36
65
  end
37
- end
66
+ TapyrusCoreClient.class_eval do
67
+ commands.each { |command| define_method(command) { |*params| request(command, *params) } }
38
68
  end
39
69
  end
40
70
 
@@ -42,33 +72,45 @@ module Tapyrus
42
72
 
43
73
  def server_url
44
74
  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
75
+ url += "/wallet/#{config[:wallet]}" if !config[:wallet].nil? && !config[:wallet].empty?
48
76
  url
49
77
  end
50
78
 
51
79
  def request(command, *params)
52
- data = {
53
- :method => command,
54
- :params => params,
55
- :id => 'jsonrpc'
56
- }
80
+ data = { method: command, params: params, id: 'jsonrpc' }
57
81
  uri = URI.parse(server_url)
58
82
  http = Net::HTTP.new(uri.hostname, uri.port)
59
- http.use_ssl = uri.scheme === "https"
83
+ http.use_ssl = uri.scheme === 'https'
60
84
  request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
61
85
  request.basic_auth(uri.user, uri.password)
62
86
  request.content_type = 'application/json'
63
87
  request.body = data.to_json
64
88
  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']
89
+ raise error!(response) unless response.is_a? Net::HTTPOK
90
+ response = Tapyrus::RPC.response_body2json(response.body)
68
91
  response['result']
69
92
  end
70
93
 
94
+ def error!(response)
95
+ rpc_error =
96
+ begin
97
+ Tapyrus::RPC.response_body2json(response.body)['error']
98
+ rescue JSON::ParserError => _
99
+ # if RPC server don't send error message.
100
+ end
101
+
102
+ raise Error.new(response.code, response.msg, rpc_error)
103
+ end
104
+ end
105
+
106
+ def response_body2json(body)
107
+ Tapyrus::Ext::JsonParser.new(
108
+ body.gsub(/\\u([\da-fA-F]{4})/) do
109
+ [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8')
110
+ end
111
+ ).parse
71
112
  end
72
113
 
114
+ module_function :response_body2json
73
115
  end
74
- end
116
+ 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?