tapyrus 0.2.7 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets.rb +0 -2
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/schnorr.rb +2 -3
- data/lib/schnorr/signature.rb +3 -6
- data/lib/tapyrus.rb +6 -22
- data/lib/tapyrus/base58.rb +7 -6
- 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.rb +1 -1
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext_key.rb +38 -34
- 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 +19 -20
- data/lib/tapyrus/message.rb +14 -16
- 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/mnemonic.rb +17 -15
- data/lib/tapyrus/network.rb +0 -2
- 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 +3 -5
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/node.rb +1 -1
- 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/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 +53 -25
- data/lib/tapyrus/script/color.rb +10 -0
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +72 -71
- 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.rb +0 -4
- 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/slip39.rb +20 -5
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +101 -57
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- 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 +7 -9
- 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/tapyrusrb.gemspec +13 -14
- metadata +21 -4
- data/.travis.yml +0 -14
@@ -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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
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 =
|
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)
|
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
|
-
|
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 ===
|
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
|
-
|
66
|
-
response = Tapyrus::
|
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
|
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
|
|
@@ -150,19 +149,20 @@ module Tapyrus
|
|
150
149
|
if opcode.pushdata?
|
151
150
|
pushcode = opcode.ord
|
152
151
|
packed_size = nil
|
153
|
-
len =
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
166
166
|
if len
|
167
167
|
s.chunks << [len].pack('C') if buf.eof?
|
168
168
|
unless buf.eof?
|
@@ -194,7 +194,7 @@ module Tapyrus
|
|
194
194
|
return [p2sh_addr] if p2sh?
|
195
195
|
return [cp2pkh_addr] if cp2pkh?
|
196
196
|
return [cp2sh_addr] if cp2sh?
|
197
|
-
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?
|
198
198
|
[]
|
199
199
|
end
|
200
200
|
|
@@ -206,8 +206,8 @@ module Tapyrus
|
|
206
206
|
# whether this script is a P2PKH format script.
|
207
207
|
def p2pkh?
|
208
208
|
return false unless chunks.size == 5
|
209
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
210
|
-
|
209
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[0..1] + chunks[3..4]).map(&:ord) &&
|
210
|
+
chunks[2].bytesize == 21
|
211
211
|
end
|
212
212
|
|
213
213
|
def p2sh?
|
@@ -228,8 +228,7 @@ module Tapyrus
|
|
228
228
|
end
|
229
229
|
|
230
230
|
def standard_op_return?
|
231
|
-
op_return? && size <= MAX_OP_RETURN_RELAY &&
|
232
|
-
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
231
|
+
op_return? && size <= MAX_OP_RETURN_RELAY && (chunks.size == 1 || chunks[1].opcode <= OP_16)
|
233
232
|
end
|
234
233
|
|
235
234
|
# Return whether this script is a CP2PKH format script or not.
|
@@ -239,8 +238,8 @@ module Tapyrus
|
|
239
238
|
return false unless chunks[0].bytesize == 34
|
240
239
|
return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
|
241
240
|
return false unless chunks[1].ord == OP_COLOR
|
242
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
243
|
-
|
241
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[2..3] + chunks[5..6]).map(&:ord) &&
|
242
|
+
chunks[4].bytesize == 21
|
244
243
|
end
|
245
244
|
|
246
245
|
# Return whether this script is a CP2SH format script or not.
|
@@ -252,7 +251,7 @@ module Tapyrus
|
|
252
251
|
return false unless chunks[1].ord == OP_COLOR
|
253
252
|
OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
|
254
253
|
end
|
255
|
-
|
254
|
+
|
256
255
|
# Return whether this script represents colored coin.
|
257
256
|
# @return [Boolean] true if this script is colored, otherwise false.
|
258
257
|
def colored?
|
@@ -275,24 +274,23 @@ module Tapyrus
|
|
275
274
|
|
276
275
|
# whether data push only script which dose not include other opcode
|
277
276
|
def push_only?
|
278
|
-
chunks.each
|
279
|
-
return false if !c.opcode.nil? && c.opcode > OP_16
|
280
|
-
end
|
277
|
+
chunks.each { |c| return false if !c.opcode.nil? && c.opcode > OP_16 }
|
281
278
|
true
|
282
279
|
end
|
283
280
|
|
284
281
|
# get public keys in the stack.
|
285
282
|
# @return[Array[String]] an array of the pubkeys with hex format.
|
286
283
|
def get_pubkeys
|
287
|
-
chunks.select
|
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 }
|
288
288
|
end
|
289
289
|
|
290
290
|
# returns the self payload. ScriptInterpreter does not use this.
|
291
291
|
def to_script_code(skip_separator_index = 0)
|
292
292
|
payload = to_payload
|
293
|
-
if skip_separator_index > 0
|
294
|
-
payload = subscript_codeseparator(skip_separator_index)
|
295
|
-
end
|
293
|
+
payload = subscript_codeseparator(skip_separator_index) if skip_separator_index > 0
|
296
294
|
Tapyrus.pack_var_string(payload)
|
297
295
|
end
|
298
296
|
|
@@ -303,7 +301,7 @@ module Tapyrus
|
|
303
301
|
elsif obj.is_a?(String)
|
304
302
|
append_data(obj)
|
305
303
|
elsif obj.is_a?(Array)
|
306
|
-
obj.each { |o| self.<< o}
|
304
|
+
obj.each { |o| self.<< o }
|
307
305
|
self
|
308
306
|
end
|
309
307
|
end
|
@@ -339,18 +337,19 @@ module Tapyrus
|
|
339
337
|
|
340
338
|
# Check the item is in the chunk of the script.
|
341
339
|
def include?(item)
|
342
|
-
chunk_item =
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
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
|
348
347
|
return false unless chunk_item
|
349
348
|
chunks.include?(chunk_item)
|
350
349
|
end
|
351
350
|
|
352
351
|
def to_s
|
353
|
-
chunks.map
|
352
|
+
chunks.map do |c|
|
354
353
|
case c
|
355
354
|
when Integer
|
356
355
|
opcode_to_name(c)
|
@@ -373,7 +372,7 @@ module Tapyrus
|
|
373
372
|
opcode ? opcode : 'OP_UNKNOWN [error]'
|
374
373
|
end
|
375
374
|
end
|
376
|
-
|
375
|
+
end.join(' ')
|
377
376
|
end
|
378
377
|
|
379
378
|
# generate sha-256 hash for payload
|
@@ -431,17 +430,18 @@ module Tapyrus
|
|
431
430
|
# binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
|
432
431
|
def self.pack_pushdata(data)
|
433
432
|
size = data.bytesize
|
434
|
-
header =
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
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
|
445
445
|
header + data
|
446
446
|
end
|
447
447
|
|
@@ -465,8 +465,12 @@ module Tapyrus
|
|
465
465
|
if chunk == sub_chunk
|
466
466
|
buf << chunk
|
467
467
|
i += 1
|
468
|
-
(
|
469
|
-
|
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
|
470
474
|
i = 0
|
471
475
|
tmp = chunk.dup
|
472
476
|
tmp.slice!(sub_chunk)
|
@@ -488,7 +492,7 @@ module Tapyrus
|
|
488
492
|
|
489
493
|
# remove all occurences of opcode. Typically it's OP_CODESEPARATOR.
|
490
494
|
def delete_opcode(opcode)
|
491
|
-
@chunks = chunks.select{|chunk| chunk.ord != opcode}
|
495
|
+
@chunks = chunks.select { |chunk| chunk.ord != opcode }
|
492
496
|
self
|
493
497
|
end
|
494
498
|
|
@@ -496,12 +500,10 @@ module Tapyrus
|
|
496
500
|
def subscript_codeseparator(separator_index)
|
497
501
|
buf = []
|
498
502
|
process_separator_index = 0
|
499
|
-
chunks.each
|
503
|
+
chunks.each do |chunk|
|
500
504
|
buf << chunk if process_separator_index == separator_index
|
501
|
-
if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
502
|
-
|
503
|
-
end
|
504
|
-
}
|
505
|
+
process_separator_index += 1 if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
506
|
+
end
|
505
507
|
buf.join
|
506
508
|
end
|
507
509
|
|
@@ -518,10 +520,10 @@ module Tapyrus
|
|
518
520
|
end
|
519
521
|
|
520
522
|
def to_h
|
521
|
-
h = {asm: to_s, hex: to_hex, type: type}
|
523
|
+
h = { asm: to_s, hex: to_hex, type: type }
|
522
524
|
addrs = addresses
|
523
525
|
unless addrs.empty?
|
524
|
-
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
|
525
527
|
h[:addresses] = addrs
|
526
528
|
end
|
527
529
|
h
|
@@ -568,5 +570,4 @@ module Tapyrus
|
|
568
570
|
Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
|
569
571
|
end
|
570
572
|
end
|
571
|
-
|
572
573
|
end
|