tapyrus 0.2.6 → 0.2.10
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/.ruby-version +1 -1
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/openassets.rb +0 -2
- data/lib/schnorr/sign_to_contract.rb +51 -0
- data/lib/schnorr/signature.rb +3 -6
- data/lib/schnorr.rb +14 -9
- data/lib/tapyrus/base58.rb +7 -6
- data/lib/tapyrus/bip175.rb +67 -0
- data/lib/tapyrus/block.rb +1 -2
- data/lib/tapyrus/block_header.rb +15 -9
- data/lib/tapyrus/bloom_filter.rb +5 -3
- data/lib/tapyrus/chain_params.rb +1 -4
- data/lib/tapyrus/chainparams/dev.yml +3 -2
- data/lib/tapyrus/chainparams/prod.yml +3 -2
- data/lib/tapyrus/constants.rb +29 -23
- data/lib/tapyrus/errors.rb +1 -3
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext.rb +1 -1
- data/lib/tapyrus/ext_key.rb +44 -32
- data/lib/tapyrus/key.rb +31 -35
- data/lib/tapyrus/key_path.rb +15 -12
- data/lib/tapyrus/logger.rb +20 -16
- data/lib/tapyrus/merkle_tree.rb +22 -20
- data/lib/tapyrus/message/addr.rb +1 -7
- data/lib/tapyrus/message/base.rb +0 -3
- data/lib/tapyrus/message/block.rb +2 -9
- data/lib/tapyrus/message/block_transaction_request.rb +3 -6
- data/lib/tapyrus/message/block_transactions.rb +2 -6
- data/lib/tapyrus/message/block_txn.rb +0 -4
- data/lib/tapyrus/message/cmpct_block.rb +1 -7
- data/lib/tapyrus/message/error.rb +1 -4
- data/lib/tapyrus/message/fee_filter.rb +1 -4
- data/lib/tapyrus/message/filter_add.rb +0 -4
- data/lib/tapyrus/message/filter_clear.rb +0 -4
- data/lib/tapyrus/message/filter_load.rb +2 -5
- data/lib/tapyrus/message/get_addr.rb +0 -4
- data/lib/tapyrus/message/get_block_txn.rb +0 -4
- data/lib/tapyrus/message/get_blocks.rb +0 -3
- data/lib/tapyrus/message/get_data.rb +1 -4
- data/lib/tapyrus/message/get_headers.rb +1 -3
- data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
- data/lib/tapyrus/message/headers.rb +0 -4
- data/lib/tapyrus/message/headers_parser.rb +3 -8
- data/lib/tapyrus/message/inv.rb +1 -4
- data/lib/tapyrus/message/inventories_parser.rb +2 -7
- data/lib/tapyrus/message/inventory.rb +12 -5
- data/lib/tapyrus/message/mem_pool.rb +0 -4
- data/lib/tapyrus/message/merkle_block.rb +4 -9
- data/lib/tapyrus/message/network_addr.rb +7 -6
- data/lib/tapyrus/message/not_found.rb +0 -3
- data/lib/tapyrus/message/ping.rb +0 -3
- data/lib/tapyrus/message/pong.rb +0 -3
- data/lib/tapyrus/message/prefilled_tx.rb +0 -4
- data/lib/tapyrus/message/reject.rb +0 -3
- data/lib/tapyrus/message/send_cmpct.rb +1 -3
- data/lib/tapyrus/message/send_headers.rb +0 -3
- data/lib/tapyrus/message/tx.rb +0 -4
- data/lib/tapyrus/message/ver_ack.rb +1 -5
- data/lib/tapyrus/message/version.rb +2 -5
- data/lib/tapyrus/message.rb +14 -16
- data/lib/tapyrus/mnemonic.rb +17 -15
- data/lib/tapyrus/network/connection.rb +0 -3
- data/lib/tapyrus/network/message_handler.rb +61 -60
- data/lib/tapyrus/network/peer.rb +13 -12
- data/lib/tapyrus/network/peer_discovery.rb +10 -9
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/network.rb +0 -2
- data/lib/tapyrus/node/cli.rb +12 -14
- data/lib/tapyrus/node/configuration.rb +1 -3
- data/lib/tapyrus/node/spv.rb +2 -3
- data/lib/tapyrus/node.rb +1 -1
- data/lib/tapyrus/opcodes.rb +9 -7
- data/lib/tapyrus/out_point.rb +5 -5
- data/lib/tapyrus/rpc/http_server.rb +21 -22
- data/lib/tapyrus/rpc/request_handler.rb +42 -44
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
- data/lib/tapyrus/rpc.rb +1 -0
- data/lib/tapyrus/script/color.rb +10 -0
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +99 -88
- data/lib/tapyrus/script/script_error.rb +1 -4
- data/lib/tapyrus/script/script_interpreter.rb +439 -399
- data/lib/tapyrus/script/tx_checker.rb +20 -10
- data/lib/tapyrus/secp256k1/native.rb +14 -15
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
- data/lib/tapyrus/secp256k1/ruby.rb +10 -12
- data/lib/tapyrus/secp256k1.rb +0 -4
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +107 -57
- data/lib/tapyrus/slip39.rb +20 -5
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/tx.rb +45 -37
- data/lib/tapyrus/tx_builder.rb +158 -0
- data/lib/tapyrus/tx_in.rb +1 -6
- data/lib/tapyrus/tx_out.rb +2 -7
- data/lib/tapyrus/util.rb +20 -7
- data/lib/tapyrus/validation.rb +12 -11
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +22 -18
- data/lib/tapyrus/wallet/base.rb +12 -9
- data/lib/tapyrus/wallet/db.rb +6 -9
- data/lib/tapyrus/wallet/master_key.rb +2 -4
- data/lib/tapyrus.rb +8 -30
- data/tapyrusrb.gemspec +13 -14
- metadata +26 -7
- data/.travis.yml +0 -14
|
@@ -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 =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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 ===
|
|
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
|
-
|
|
66
|
-
response = Tapyrus::
|
|
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
|
data/lib/tapyrus/rpc.rb
CHANGED
data/lib/tapyrus/script/color.rb
CHANGED
|
@@ -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
|
|
@@ -51,6 +56,11 @@ module Tapyrus
|
|
|
51
56
|
to_payload.eql?(other.to_payload)
|
|
52
57
|
end
|
|
53
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
|
+
|
|
54
64
|
private
|
|
55
65
|
|
|
56
66
|
def initialize(type, payload)
|
|
@@ -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(
|
|
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(
|
|
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 =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
@@ -82,14 +81,12 @@ module Tapyrus
|
|
|
82
81
|
def remove_color
|
|
83
82
|
raise RuntimeError, 'Only cp2pkh and cp2sh can remove color' unless cp2pkh? or cp2sh?
|
|
84
83
|
|
|
85
|
-
Tapyrus::Script.new.tap
|
|
86
|
-
s.chunks = self.chunks[2..-1]
|
|
87
|
-
end
|
|
84
|
+
Tapyrus::Script.new.tap { |s| s.chunks = self.chunks[2..-1] }
|
|
88
85
|
end
|
|
89
86
|
|
|
90
87
|
def get_multisig_pubkeys
|
|
91
88
|
num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
|
|
92
|
-
(1..num).map{ |i| chunks[i].pushed_data }
|
|
89
|
+
(1..num).map { |i| chunks[i].pushed_data }
|
|
93
90
|
end
|
|
94
91
|
|
|
95
92
|
# generate m of n multisig script
|
|
@@ -104,14 +101,16 @@ module Tapyrus
|
|
|
104
101
|
# generate script from string.
|
|
105
102
|
def self.from_string(string)
|
|
106
103
|
script = new
|
|
107
|
-
string
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
113
113
|
end
|
|
114
|
-
end
|
|
115
114
|
script
|
|
116
115
|
end
|
|
117
116
|
|
|
@@ -130,6 +129,12 @@ module Tapyrus
|
|
|
130
129
|
Tapyrus::Script.to_p2pkh(hex)
|
|
131
130
|
when Tapyrus.chain_params.p2sh_version
|
|
132
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])
|
|
133
138
|
else
|
|
134
139
|
throw e
|
|
135
140
|
end
|
|
@@ -144,19 +149,20 @@ module Tapyrus
|
|
|
144
149
|
if opcode.pushdata?
|
|
145
150
|
pushcode = opcode.ord
|
|
146
151
|
packed_size = nil
|
|
147
|
-
len =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
160
166
|
if len
|
|
161
167
|
s.chunks << [len].pack('C') if buf.eof?
|
|
162
168
|
unless buf.eof?
|
|
@@ -188,7 +194,7 @@ module Tapyrus
|
|
|
188
194
|
return [p2sh_addr] if p2sh?
|
|
189
195
|
return [cp2pkh_addr] if cp2pkh?
|
|
190
196
|
return [cp2sh_addr] if cp2sh?
|
|
191
|
-
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?
|
|
192
198
|
[]
|
|
193
199
|
end
|
|
194
200
|
|
|
@@ -200,8 +206,8 @@ module Tapyrus
|
|
|
200
206
|
# whether this script is a P2PKH format script.
|
|
201
207
|
def p2pkh?
|
|
202
208
|
return false unless chunks.size == 5
|
|
203
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
|
204
|
-
|
|
209
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[0..1] + chunks[3..4]).map(&:ord) &&
|
|
210
|
+
chunks[2].bytesize == 21
|
|
205
211
|
end
|
|
206
212
|
|
|
207
213
|
def p2sh?
|
|
@@ -222,8 +228,7 @@ module Tapyrus
|
|
|
222
228
|
end
|
|
223
229
|
|
|
224
230
|
def standard_op_return?
|
|
225
|
-
op_return? && size <= MAX_OP_RETURN_RELAY &&
|
|
226
|
-
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
|
231
|
+
op_return? && size <= MAX_OP_RETURN_RELAY && (chunks.size == 1 || chunks[1].opcode <= OP_16)
|
|
227
232
|
end
|
|
228
233
|
|
|
229
234
|
# Return whether this script is a CP2PKH format script or not.
|
|
@@ -233,8 +238,8 @@ module Tapyrus
|
|
|
233
238
|
return false unless chunks[0].bytesize == 34
|
|
234
239
|
return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
|
|
235
240
|
return false unless chunks[1].ord == OP_COLOR
|
|
236
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
|
237
|
-
|
|
241
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[2..3] + chunks[5..6]).map(&:ord) &&
|
|
242
|
+
chunks[4].bytesize == 21
|
|
238
243
|
end
|
|
239
244
|
|
|
240
245
|
# Return whether this script is a CP2SH format script or not.
|
|
@@ -246,7 +251,7 @@ module Tapyrus
|
|
|
246
251
|
return false unless chunks[1].ord == OP_COLOR
|
|
247
252
|
OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
|
|
248
253
|
end
|
|
249
|
-
|
|
254
|
+
|
|
250
255
|
# Return whether this script represents colored coin.
|
|
251
256
|
# @return [Boolean] true if this script is colored, otherwise false.
|
|
252
257
|
def colored?
|
|
@@ -269,24 +274,25 @@ module Tapyrus
|
|
|
269
274
|
|
|
270
275
|
# whether data push only script which dose not include other opcode
|
|
271
276
|
def push_only?
|
|
272
|
-
chunks.each
|
|
273
|
-
return false if !c.opcode.nil? && c.opcode > OP_16
|
|
274
|
-
end
|
|
277
|
+
chunks.each { |c| return false if !c.opcode.nil? && c.opcode > OP_16 }
|
|
275
278
|
true
|
|
276
279
|
end
|
|
277
280
|
|
|
278
281
|
# get public keys in the stack.
|
|
279
282
|
# @return[Array[String]] an array of the pubkeys with hex format.
|
|
280
283
|
def get_pubkeys
|
|
281
|
-
chunks
|
|
284
|
+
chunks
|
|
285
|
+
.select do |c|
|
|
286
|
+
c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) &&
|
|
287
|
+
[2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))
|
|
288
|
+
end
|
|
289
|
+
.map { |c| c.pushed_data.bth }
|
|
282
290
|
end
|
|
283
291
|
|
|
284
292
|
# returns the self payload. ScriptInterpreter does not use this.
|
|
285
293
|
def to_script_code(skip_separator_index = 0)
|
|
286
294
|
payload = to_payload
|
|
287
|
-
if skip_separator_index > 0
|
|
288
|
-
payload = subscript_codeseparator(skip_separator_index)
|
|
289
|
-
end
|
|
295
|
+
payload = subscript_codeseparator(skip_separator_index) if skip_separator_index > 0
|
|
290
296
|
Tapyrus.pack_var_string(payload)
|
|
291
297
|
end
|
|
292
298
|
|
|
@@ -297,7 +303,7 @@ module Tapyrus
|
|
|
297
303
|
elsif obj.is_a?(String)
|
|
298
304
|
append_data(obj)
|
|
299
305
|
elsif obj.is_a?(Array)
|
|
300
|
-
obj.each { |o| self.<< o}
|
|
306
|
+
obj.each { |o| self.<< o }
|
|
301
307
|
self
|
|
302
308
|
end
|
|
303
309
|
end
|
|
@@ -333,41 +339,44 @@ module Tapyrus
|
|
|
333
339
|
|
|
334
340
|
# Check the item is in the chunk of the script.
|
|
335
341
|
def include?(item)
|
|
336
|
-
chunk_item =
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
+
chunk_item =
|
|
343
|
+
if item.is_a?(Integer)
|
|
344
|
+
item.chr
|
|
345
|
+
elsif item.is_a?(String)
|
|
346
|
+
data = Encoding::ASCII_8BIT == item.encoding ? item : item.htb
|
|
347
|
+
Tapyrus::Script.pack_pushdata(data)
|
|
348
|
+
end
|
|
342
349
|
return false unless chunk_item
|
|
343
350
|
chunks.include?(chunk_item)
|
|
344
351
|
end
|
|
345
352
|
|
|
346
353
|
def to_s
|
|
347
|
-
chunks
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
v
|
|
357
|
-
|
|
358
|
-
data = c.pushed_data
|
|
359
|
-
if data.bytesize <= 4
|
|
360
|
-
Script.decode_number(data.bth) # for scriptnum
|
|
354
|
+
chunks
|
|
355
|
+
.map do |c|
|
|
356
|
+
case c
|
|
357
|
+
when Integer
|
|
358
|
+
opcode_to_name(c)
|
|
359
|
+
when String
|
|
360
|
+
return c if c.empty?
|
|
361
|
+
if c.pushdata?
|
|
362
|
+
v = Opcodes.opcode_to_small_int(c.ord)
|
|
363
|
+
if v
|
|
364
|
+
v
|
|
361
365
|
else
|
|
362
|
-
data.
|
|
366
|
+
data = c.pushed_data
|
|
367
|
+
if data.bytesize <= 4
|
|
368
|
+
Script.decode_number(data.bth) # for scriptnum
|
|
369
|
+
else
|
|
370
|
+
data.bth
|
|
371
|
+
end
|
|
363
372
|
end
|
|
373
|
+
else
|
|
374
|
+
opcode = Opcodes.opcode_to_name(c.ord)
|
|
375
|
+
opcode ? opcode : 'OP_UNKNOWN [error]'
|
|
364
376
|
end
|
|
365
|
-
else
|
|
366
|
-
opcode = Opcodes.opcode_to_name(c.ord)
|
|
367
|
-
opcode ? opcode : 'OP_UNKNOWN [error]'
|
|
368
377
|
end
|
|
369
378
|
end
|
|
370
|
-
|
|
379
|
+
.join(' ')
|
|
371
380
|
end
|
|
372
381
|
|
|
373
382
|
# generate sha-256 hash for payload
|
|
@@ -425,17 +434,18 @@ module Tapyrus
|
|
|
425
434
|
# binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
|
|
426
435
|
def self.pack_pushdata(data)
|
|
427
436
|
size = data.bytesize
|
|
428
|
-
header =
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
437
|
+
header =
|
|
438
|
+
if size < OP_PUSHDATA1
|
|
439
|
+
[size].pack('C')
|
|
440
|
+
elsif size < 0xff
|
|
441
|
+
[OP_PUSHDATA1, size].pack('CC')
|
|
442
|
+
elsif size < 0xffff
|
|
443
|
+
[OP_PUSHDATA2, size].pack('Cv')
|
|
444
|
+
elsif size < 0xffffffff
|
|
445
|
+
[OP_PUSHDATA4, size].pack('CV')
|
|
446
|
+
else
|
|
447
|
+
raise ArgumentError, 'data size is too big.'
|
|
448
|
+
end
|
|
439
449
|
header + data
|
|
440
450
|
end
|
|
441
451
|
|
|
@@ -459,8 +469,12 @@ module Tapyrus
|
|
|
459
469
|
if chunk == sub_chunk
|
|
460
470
|
buf << chunk
|
|
461
471
|
i += 1
|
|
462
|
-
(
|
|
463
|
-
|
|
472
|
+
(
|
|
473
|
+
i = 0
|
|
474
|
+
buf.clear
|
|
475
|
+
) if i == subscript.chunks.size # matched the whole subscript
|
|
476
|
+
else
|
|
477
|
+
# matched the part of head
|
|
464
478
|
i = 0
|
|
465
479
|
tmp = chunk.dup
|
|
466
480
|
tmp.slice!(sub_chunk)
|
|
@@ -482,7 +496,7 @@ module Tapyrus
|
|
|
482
496
|
|
|
483
497
|
# remove all occurences of opcode. Typically it's OP_CODESEPARATOR.
|
|
484
498
|
def delete_opcode(opcode)
|
|
485
|
-
@chunks = chunks.select{|chunk| chunk.ord != opcode}
|
|
499
|
+
@chunks = chunks.select { |chunk| chunk.ord != opcode }
|
|
486
500
|
self
|
|
487
501
|
end
|
|
488
502
|
|
|
@@ -490,12 +504,10 @@ module Tapyrus
|
|
|
490
504
|
def subscript_codeseparator(separator_index)
|
|
491
505
|
buf = []
|
|
492
506
|
process_separator_index = 0
|
|
493
|
-
chunks.each
|
|
507
|
+
chunks.each do |chunk|
|
|
494
508
|
buf << chunk if process_separator_index == separator_index
|
|
495
|
-
if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
|
496
|
-
|
|
497
|
-
end
|
|
498
|
-
}
|
|
509
|
+
process_separator_index += 1 if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
|
510
|
+
end
|
|
499
511
|
buf.join
|
|
500
512
|
end
|
|
501
513
|
|
|
@@ -512,10 +524,10 @@ module Tapyrus
|
|
|
512
524
|
end
|
|
513
525
|
|
|
514
526
|
def to_h
|
|
515
|
-
h = {asm: to_s, hex: to_hex, type: type}
|
|
527
|
+
h = { asm: to_s, hex: to_hex, type: type }
|
|
516
528
|
addrs = addresses
|
|
517
529
|
unless addrs.empty?
|
|
518
|
-
h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
|
|
530
|
+
h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) : addrs.size
|
|
519
531
|
h[:addresses] = addrs
|
|
520
532
|
end
|
|
521
533
|
h
|
|
@@ -562,5 +574,4 @@ module Tapyrus
|
|
|
562
574
|
Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
|
|
563
575
|
end
|
|
564
576
|
end
|
|
565
|
-
|
|
566
577
|
end
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module Tapyrus
|
|
2
|
-
|
|
3
2
|
# tapyrus script error
|
|
4
3
|
class ScriptError < Exception
|
|
5
|
-
|
|
6
4
|
attr_accessor :code
|
|
7
5
|
attr_accessor :extra_msg
|
|
8
6
|
|
|
@@ -100,6 +98,5 @@ module Tapyrus
|
|
|
100
98
|
def self.name_to_code(name)
|
|
101
99
|
NAME_MAP[name]
|
|
102
100
|
end
|
|
103
|
-
|
|
104
101
|
end
|
|
105
|
-
end
|
|
102
|
+
end
|