tapyrus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/tapyrusrb-cli +5 -0
- data/exe/tapyrusrbd +41 -0
- data/lib/openassets/marker_output.rb +20 -0
- data/lib/openassets/payload.rb +54 -0
- data/lib/openassets/util.rb +28 -0
- data/lib/openassets.rb +9 -0
- data/lib/tapyrus/base58.rb +38 -0
- data/lib/tapyrus/block.rb +77 -0
- data/lib/tapyrus/block_header.rb +88 -0
- data/lib/tapyrus/bloom_filter.rb +78 -0
- data/lib/tapyrus/chain_params.rb +90 -0
- data/lib/tapyrus/chainparams/mainnet.yml +41 -0
- data/lib/tapyrus/chainparams/regtest.yml +38 -0
- data/lib/tapyrus/chainparams/testnet.yml +41 -0
- data/lib/tapyrus/constants.rb +195 -0
- data/lib/tapyrus/descriptor.rb +147 -0
- data/lib/tapyrus/ext_key.rb +337 -0
- data/lib/tapyrus/key.rb +296 -0
- data/lib/tapyrus/key_path.rb +26 -0
- data/lib/tapyrus/logger.rb +42 -0
- data/lib/tapyrus/merkle_tree.rb +149 -0
- data/lib/tapyrus/message/addr.rb +35 -0
- data/lib/tapyrus/message/base.rb +28 -0
- data/lib/tapyrus/message/block.rb +46 -0
- data/lib/tapyrus/message/block_transaction_request.rb +45 -0
- data/lib/tapyrus/message/block_transactions.rb +31 -0
- data/lib/tapyrus/message/block_txn.rb +27 -0
- data/lib/tapyrus/message/cmpct_block.rb +42 -0
- data/lib/tapyrus/message/error.rb +10 -0
- data/lib/tapyrus/message/fee_filter.rb +27 -0
- data/lib/tapyrus/message/filter_add.rb +28 -0
- data/lib/tapyrus/message/filter_clear.rb +17 -0
- data/lib/tapyrus/message/filter_load.rb +39 -0
- data/lib/tapyrus/message/get_addr.rb +17 -0
- data/lib/tapyrus/message/get_block_txn.rb +27 -0
- data/lib/tapyrus/message/get_blocks.rb +29 -0
- data/lib/tapyrus/message/get_data.rb +21 -0
- data/lib/tapyrus/message/get_headers.rb +28 -0
- data/lib/tapyrus/message/header_and_short_ids.rb +57 -0
- data/lib/tapyrus/message/headers.rb +35 -0
- data/lib/tapyrus/message/headers_parser.rb +24 -0
- data/lib/tapyrus/message/inv.rb +21 -0
- data/lib/tapyrus/message/inventories_parser.rb +23 -0
- data/lib/tapyrus/message/inventory.rb +51 -0
- data/lib/tapyrus/message/mem_pool.rb +17 -0
- data/lib/tapyrus/message/merkle_block.rb +42 -0
- data/lib/tapyrus/message/network_addr.rb +63 -0
- data/lib/tapyrus/message/not_found.rb +21 -0
- data/lib/tapyrus/message/ping.rb +30 -0
- data/lib/tapyrus/message/pong.rb +26 -0
- data/lib/tapyrus/message/prefilled_tx.rb +29 -0
- data/lib/tapyrus/message/reject.rb +46 -0
- data/lib/tapyrus/message/send_cmpct.rb +43 -0
- data/lib/tapyrus/message/send_headers.rb +16 -0
- data/lib/tapyrus/message/tx.rb +30 -0
- data/lib/tapyrus/message/ver_ack.rb +17 -0
- data/lib/tapyrus/message/version.rb +69 -0
- data/lib/tapyrus/message.rb +70 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/english.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/french.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/tapyrus/mnemonic.rb +77 -0
- data/lib/tapyrus/network/connection.rb +73 -0
- data/lib/tapyrus/network/message_handler.rb +241 -0
- data/lib/tapyrus/network/peer.rb +223 -0
- data/lib/tapyrus/network/peer_discovery.rb +42 -0
- data/lib/tapyrus/network/pool.rb +135 -0
- data/lib/tapyrus/network.rb +13 -0
- data/lib/tapyrus/node/cli.rb +112 -0
- data/lib/tapyrus/node/configuration.rb +38 -0
- data/lib/tapyrus/node/spv.rb +79 -0
- data/lib/tapyrus/node.rb +7 -0
- data/lib/tapyrus/opcodes.rb +178 -0
- data/lib/tapyrus/out_point.rb +44 -0
- data/lib/tapyrus/rpc/http_server.rb +65 -0
- data/lib/tapyrus/rpc/request_handler.rb +150 -0
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +72 -0
- data/lib/tapyrus/rpc.rb +7 -0
- data/lib/tapyrus/script/multisig.rb +92 -0
- data/lib/tapyrus/script/script.rb +551 -0
- data/lib/tapyrus/script/script_error.rb +111 -0
- data/lib/tapyrus/script/script_interpreter.rb +668 -0
- data/lib/tapyrus/script/tx_checker.rb +81 -0
- data/lib/tapyrus/script_witness.rb +38 -0
- data/lib/tapyrus/secp256k1/native.rb +174 -0
- data/lib/tapyrus/secp256k1/ruby.rb +123 -0
- data/lib/tapyrus/secp256k1.rb +12 -0
- data/lib/tapyrus/slip39/share.rb +122 -0
- data/lib/tapyrus/slip39/sss.rb +245 -0
- data/lib/tapyrus/slip39/wordlist/english.txt +1024 -0
- data/lib/tapyrus/slip39.rb +93 -0
- data/lib/tapyrus/store/chain_entry.rb +67 -0
- data/lib/tapyrus/store/db/level_db.rb +98 -0
- data/lib/tapyrus/store/db.rb +9 -0
- data/lib/tapyrus/store/spv_chain.rb +101 -0
- data/lib/tapyrus/store.rb +9 -0
- data/lib/tapyrus/tx.rb +347 -0
- data/lib/tapyrus/tx_in.rb +89 -0
- data/lib/tapyrus/tx_out.rb +74 -0
- data/lib/tapyrus/util.rb +133 -0
- data/lib/tapyrus/validation.rb +115 -0
- data/lib/tapyrus/version.rb +3 -0
- data/lib/tapyrus/wallet/account.rb +151 -0
- data/lib/tapyrus/wallet/base.rb +162 -0
- data/lib/tapyrus/wallet/db.rb +81 -0
- data/lib/tapyrus/wallet/master_key.rb +110 -0
- data/lib/tapyrus/wallet.rb +8 -0
- data/lib/tapyrus.rb +219 -0
- data/tapyrusrb.conf.sample +0 -0
- data/tapyrusrb.gemspec +47 -0
- metadata +451 -0
@@ -0,0 +1,668 @@
|
|
1
|
+
module Tapyrus
|
2
|
+
|
3
|
+
class ScriptInterpreter
|
4
|
+
|
5
|
+
include Tapyrus::Opcodes
|
6
|
+
|
7
|
+
attr_reader :stack
|
8
|
+
attr_reader :debug
|
9
|
+
attr_reader :flags
|
10
|
+
attr_accessor :error
|
11
|
+
attr_reader :checker
|
12
|
+
attr_reader :require_minimal
|
13
|
+
|
14
|
+
DISABLE_OPCODES = [OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, OP_DIV, OP_MUL, OP_MOD, OP_LSHIFT, OP_RSHIFT]
|
15
|
+
|
16
|
+
|
17
|
+
# syntax sugar for simple evaluation for script.
|
18
|
+
# @param [Tapyrus::Script] script_sig a scriptSig.
|
19
|
+
# @param [Tapyrus::Script] script_pubkey a scriptPubkey.
|
20
|
+
def self.eval(script_sig, script_pubkey)
|
21
|
+
self.new.verify_script(script_sig, script_pubkey)
|
22
|
+
end
|
23
|
+
|
24
|
+
# initialize runner
|
25
|
+
def initialize(flags: SCRIPT_VERIFY_NONE, checker: TxChecker.new)
|
26
|
+
@stack, @debug = [], []
|
27
|
+
@flags = flags
|
28
|
+
@checker = checker
|
29
|
+
@require_minimal = flag?(SCRIPT_VERIFY_MINIMALDATA)
|
30
|
+
end
|
31
|
+
|
32
|
+
# eval script
|
33
|
+
# @param [Tapyrus::Script] script_sig a signature script (unlock script which data push only)
|
34
|
+
# @param [Tapyrus::Script] script_pubkey a script pubkey (locking script)
|
35
|
+
# @param [Tapyrus::ScriptWitness] witness a witness script
|
36
|
+
# @return [Boolean] result
|
37
|
+
def verify_script(script_sig, script_pubkey, witness = ScriptWitness.new)
|
38
|
+
|
39
|
+
return set_error(SCRIPT_ERR_SIG_PUSHONLY) if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !script_sig.push_only?
|
40
|
+
|
41
|
+
stack_copy = nil
|
42
|
+
had_witness = false
|
43
|
+
|
44
|
+
return false unless eval_script(script_sig, :base)
|
45
|
+
|
46
|
+
stack_copy = stack.dup if flag?(SCRIPT_VERIFY_P2SH)
|
47
|
+
|
48
|
+
return false unless eval_script(script_pubkey, :base)
|
49
|
+
|
50
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last.htb)
|
51
|
+
|
52
|
+
# Bare witness programs
|
53
|
+
if flag?(SCRIPT_VERIFY_WITNESS) && script_pubkey.witness_program?
|
54
|
+
had_witness = true
|
55
|
+
return set_error(SCRIPT_ERR_WITNESS_MALLEATED) unless script_sig.size == 0
|
56
|
+
version, program = script_pubkey.witness_data
|
57
|
+
stack_copy = stack.dup
|
58
|
+
return false unless verify_witness_program(witness, version, program)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Additional validation for spend-to-script-hash transactions
|
62
|
+
if flag?(SCRIPT_VERIFY_P2SH) && script_pubkey.p2sh?
|
63
|
+
return set_error(SCRIPT_ERR_SIG_PUSHONLY) unless script_sig.push_only?
|
64
|
+
tmp = stack
|
65
|
+
@stack = stack_copy
|
66
|
+
raise 'stack cannot be empty.' if stack.empty?
|
67
|
+
begin
|
68
|
+
redeem_script = Tapyrus::Script.parse_from_payload(stack.pop.htb)
|
69
|
+
rescue Exception => e
|
70
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
|
71
|
+
end
|
72
|
+
return false unless eval_script(redeem_script, :base)
|
73
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last)
|
74
|
+
|
75
|
+
# P2SH witness program
|
76
|
+
if flag?(SCRIPT_VERIFY_WITNESS) && redeem_script.witness_program?
|
77
|
+
had_witness = true
|
78
|
+
# The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we reintroduce malleability.
|
79
|
+
return set_error(SCRIPT_ERR_WITNESS_MALLEATED_P2SH) unless script_sig == (Tapyrus::Script.new << redeem_script.to_payload.bth)
|
80
|
+
|
81
|
+
version, program = redeem_script.witness_data
|
82
|
+
return false unless verify_witness_program(witness, version, program)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The CLEANSTACK check is only performed after potential P2SH evaluation,
|
87
|
+
# as the non-P2SH evaluation of a P2SH script will obviously not result in a clean stack (the P2SH inputs remain).
|
88
|
+
# The same holds for witness evaluation.
|
89
|
+
if flag?(SCRIPT_VERIFY_CLEANSTACK)
|
90
|
+
# Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK would be possible,
|
91
|
+
# which is not a softfork (and P2SH should be one).
|
92
|
+
raise 'assert' unless flag?(SCRIPT_VERIFY_P2SH)
|
93
|
+
return set_error(SCRIPT_ERR_CLEANSTACK) unless stack.size == 1
|
94
|
+
end
|
95
|
+
|
96
|
+
if flag?(SCRIPT_VERIFY_WITNESS)
|
97
|
+
raise 'assert' unless flag?(SCRIPT_VERIFY_P2SH)
|
98
|
+
return set_error(SCRIPT_ERR_WITNESS_UNEXPECTED) if !had_witness && !witness.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def set_error(err_code, extra_message = nil)
|
105
|
+
@error = ScriptError.new(err_code, extra_message)
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
109
|
+
def verify_witness_program(witness, version, program)
|
110
|
+
if version == 0
|
111
|
+
if program.bytesize == 32
|
112
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY) if witness.stack.size == 0
|
113
|
+
script_pubkey = Tapyrus::Script.parse_from_payload(witness.stack.last)
|
114
|
+
@stack = witness.stack[0..-2].map{|w|w.bth}
|
115
|
+
script_hash = Tapyrus.sha256(script_pubkey.to_payload)
|
116
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless script_hash == program
|
117
|
+
elsif program.bytesize == 20
|
118
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless witness.stack.size == 2
|
119
|
+
script_pubkey = Tapyrus::Script.to_p2pkh(program.bth)
|
120
|
+
@stack = witness.stack.map{|w|w.bth}
|
121
|
+
else
|
122
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH)
|
123
|
+
end
|
124
|
+
elsif flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
|
125
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
|
126
|
+
else
|
127
|
+
return true # Higher version witness scripts return true for future softfork compatibility
|
128
|
+
end
|
129
|
+
|
130
|
+
stack.each do |s| # Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
|
131
|
+
return set_error(SCRIPT_ERR_PUSH_SIZE) if s.htb.bytesize > MAX_SCRIPT_ELEMENT_SIZE
|
132
|
+
end
|
133
|
+
|
134
|
+
return false unless eval_script(script_pubkey, :witness_v0)
|
135
|
+
|
136
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) unless stack.size == 1
|
137
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last)
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def eval_script(script, sig_version)
|
142
|
+
return set_error(SCRIPT_ERR_SCRIPT_SIZE) if script.size > MAX_SCRIPT_SIZE
|
143
|
+
begin
|
144
|
+
flow_stack = []
|
145
|
+
alt_stack = []
|
146
|
+
last_code_separator_index = 0
|
147
|
+
op_count = 0
|
148
|
+
|
149
|
+
script.chunks.each_with_index do |c, index|
|
150
|
+
need_exec = !flow_stack.include?(false)
|
151
|
+
|
152
|
+
return set_error(SCRIPT_ERR_PUSH_SIZE) if c.pushdata? && c.pushed_data.bytesize > MAX_SCRIPT_ELEMENT_SIZE
|
153
|
+
|
154
|
+
opcode = c.opcode
|
155
|
+
|
156
|
+
if need_exec && c.pushdata?
|
157
|
+
if require_minimal && !minimal_push?(c.pushed_data, opcode)
|
158
|
+
return set_error(SCRIPT_ERR_MINIMALDATA)
|
159
|
+
end
|
160
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE) unless verify_pushdata_length(c)
|
161
|
+
stack << c.pushed_data.bth
|
162
|
+
else
|
163
|
+
if opcode > OP_16 && (op_count += 1) > MAX_OPS_PER_SCRIPT
|
164
|
+
return set_error(SCRIPT_ERR_OP_COUNT)
|
165
|
+
end
|
166
|
+
return set_error(SCRIPT_ERR_DISABLED_OPCODE) if DISABLE_OPCODES.include?(opcode)
|
167
|
+
return set_error(SCRIPT_ERR_OP_CODESEPARATOR) if opcode == OP_CODESEPARATOR && sig_version == :base && flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE)
|
168
|
+
next unless (need_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
|
169
|
+
small_int = Opcodes.opcode_to_small_int(opcode)
|
170
|
+
if small_int && opcode != OP_0
|
171
|
+
push_int(small_int)
|
172
|
+
else
|
173
|
+
case opcode
|
174
|
+
when OP_0
|
175
|
+
stack << ''
|
176
|
+
when OP_DEPTH
|
177
|
+
push_int(stack.size)
|
178
|
+
when OP_EQUAL, OP_EQUALVERIFY
|
179
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
180
|
+
a, b = pop_string(2)
|
181
|
+
result = a == b
|
182
|
+
push_int(result ? 1 : 0)
|
183
|
+
if opcode == OP_EQUALVERIFY
|
184
|
+
if result
|
185
|
+
stack.pop
|
186
|
+
else
|
187
|
+
return set_error(SCRIPT_ERR_EQUALVERIFY)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
when OP_0NOTEQUAL
|
191
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
192
|
+
push_int(pop_int == 0 ? 0 : 1)
|
193
|
+
when OP_ADD
|
194
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
195
|
+
a, b = pop_int(2)
|
196
|
+
push_int(a + b)
|
197
|
+
when OP_1ADD
|
198
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
199
|
+
push_int(pop_int + 1)
|
200
|
+
when OP_SUB
|
201
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
202
|
+
a, b = pop_int(2)
|
203
|
+
push_int(a - b)
|
204
|
+
when OP_1SUB
|
205
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
206
|
+
push_int(pop_int - 1)
|
207
|
+
when OP_IF, OP_NOTIF
|
208
|
+
result = false
|
209
|
+
if need_exec
|
210
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if stack.size < 1
|
211
|
+
value = pop_string.htb
|
212
|
+
if sig_version == :witness_v0 && flag?(SCRIPT_VERIFY_MINIMALIF)
|
213
|
+
if value.bytesize > 1 || (value.bytesize == 1 && value[0].unpack('C').first != 1)
|
214
|
+
return set_error(SCRIPT_ERR_MINIMALIF)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
result = cast_to_bool(value)
|
218
|
+
result = !result if opcode == OP_NOTIF
|
219
|
+
end
|
220
|
+
flow_stack << result
|
221
|
+
when OP_ELSE
|
222
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if flow_stack.size < 1
|
223
|
+
flow_stack << !flow_stack.pop
|
224
|
+
when OP_ENDIF
|
225
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if flow_stack.empty?
|
226
|
+
flow_stack.pop
|
227
|
+
when OP_NOP
|
228
|
+
when OP_NOP1, OP_NOP4..OP_NOP10
|
229
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
230
|
+
when OP_CHECKLOCKTIMEVERIFY
|
231
|
+
next unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
|
232
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
233
|
+
# Note that elsewhere numeric opcodes are limited to operands in the range -2**31+1 to 2**31-1,
|
234
|
+
# however it is legal for opcodes to produce results exceeding that range.
|
235
|
+
# This limitation is implemented by CScriptNum's default 4-byte limit.
|
236
|
+
# If we kept to that limit we'd have a year 2038 problem,
|
237
|
+
# even though the nLockTime field in transactions themselves is uint32 which only becomes meaningless after the year 2106.
|
238
|
+
# Thus as a special case we tell CScriptNum to accept up to 5-byte bignums,
|
239
|
+
# which are good until 2**39-1, well beyond the 2**32-1 limit of the nLockTime field itself.
|
240
|
+
locktime = cast_to_int(stack.last, 5)
|
241
|
+
return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if locktime < 0
|
242
|
+
return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_locktime(locktime)
|
243
|
+
when OP_CHECKSEQUENCEVERIFY
|
244
|
+
next unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
|
245
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
246
|
+
|
247
|
+
# nSequence, like nLockTime, is a 32-bit unsigned integer field.
|
248
|
+
# See the comment in CHECKLOCKTIMEVERIFY regarding 5-byte numeric operands.
|
249
|
+
sequence = cast_to_int(stack.last, 5)
|
250
|
+
|
251
|
+
# In the rare event that the argument may be < 0 due to some arithmetic being done first,
|
252
|
+
# you can always use 0 MAX CHECKSEQUENCEVERIFY.
|
253
|
+
return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if sequence < 0
|
254
|
+
|
255
|
+
# To provide for future soft-fork extensibility,
|
256
|
+
# if the operand has the disabled lock-time flag set, CHECKSEQUENCEVERIFY behaves as a NOP.
|
257
|
+
next if (sequence & Tapyrus::TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0
|
258
|
+
|
259
|
+
# Compare the specified sequence number with the input.
|
260
|
+
unless checker.check_sequence(sequence)
|
261
|
+
return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
|
262
|
+
end
|
263
|
+
when OP_DUP
|
264
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
265
|
+
stack << stack.last
|
266
|
+
when OP_2DUP
|
267
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
268
|
+
2.times { stack << stack[-2] }
|
269
|
+
when OP_3DUP
|
270
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
271
|
+
3.times { stack << stack[-3] }
|
272
|
+
when OP_IFDUP
|
273
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
274
|
+
stack << stack.last if cast_to_bool(stack.last)
|
275
|
+
when OP_RIPEMD160
|
276
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
277
|
+
stack << Digest::RMD160.hexdigest(pop_string.htb)
|
278
|
+
when OP_SHA1
|
279
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
280
|
+
stack << Digest::SHA1.hexdigest(pop_string.htb)
|
281
|
+
when OP_SHA256
|
282
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
283
|
+
stack << Digest::SHA256.hexdigest(pop_string.htb)
|
284
|
+
when OP_HASH160
|
285
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
286
|
+
stack << Tapyrus.hash160(pop_string)
|
287
|
+
when OP_HASH256
|
288
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
289
|
+
stack << Tapyrus.double_sha256(pop_string.htb).bth
|
290
|
+
when OP_VERIFY
|
291
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
292
|
+
return set_error(SCRIPT_ERR_VERIFY) unless pop_bool
|
293
|
+
when OP_TOALTSTACK
|
294
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
295
|
+
alt_stack << stack.pop
|
296
|
+
when OP_FROMALTSTACK
|
297
|
+
return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION) if alt_stack.size < 1
|
298
|
+
stack << alt_stack.pop
|
299
|
+
when OP_DROP
|
300
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
301
|
+
stack.pop
|
302
|
+
when OP_2DROP
|
303
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
304
|
+
2.times { stack.pop }
|
305
|
+
when OP_NIP
|
306
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
307
|
+
stack.delete_at(-2)
|
308
|
+
when OP_OVER
|
309
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
310
|
+
stack << stack[-2]
|
311
|
+
when OP_2OVER
|
312
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
313
|
+
2.times { stack << stack[-4]}
|
314
|
+
when OP_PICK, OP_ROLL
|
315
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
316
|
+
pos = pop_int
|
317
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if pos < 0 || pos >= stack.size
|
318
|
+
stack << stack[-pos - 1]
|
319
|
+
stack.delete_at(-pos - 2) if opcode == OP_ROLL
|
320
|
+
when OP_ROT
|
321
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
322
|
+
stack << stack[-3]
|
323
|
+
stack.delete_at(-4)
|
324
|
+
when OP_2ROT
|
325
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 6
|
326
|
+
2.times { stack << stack[-6] }
|
327
|
+
2.times { stack.delete_at(-7) }
|
328
|
+
when OP_SWAP
|
329
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
330
|
+
tmp = stack.last
|
331
|
+
stack[-1] = stack[-2]
|
332
|
+
stack[-2] = tmp
|
333
|
+
when OP_2SWAP
|
334
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
335
|
+
2.times {stack << stack[-4]}
|
336
|
+
2.times {stack.delete_at(-5)}
|
337
|
+
when OP_TUCK
|
338
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
339
|
+
stack.insert(-3, stack.last)
|
340
|
+
when OP_ABS
|
341
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
342
|
+
v = pop_int
|
343
|
+
push_int(v.abs)
|
344
|
+
when OP_BOOLAND
|
345
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
346
|
+
a, b = pop_int(2)
|
347
|
+
push_int((!a.zero? && !b.zero?) ? 1 : 0)
|
348
|
+
when OP_BOOLOR
|
349
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
350
|
+
a, b = pop_int(2)
|
351
|
+
push_int((!a.zero? || !b.zero?) ? 1 : 0)
|
352
|
+
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
353
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
354
|
+
a, b = pop_int(2)
|
355
|
+
result = a == b
|
356
|
+
push_int(result ? 1 : 0)
|
357
|
+
if opcode == OP_NUMEQUALVERIFY
|
358
|
+
if result
|
359
|
+
stack.pop
|
360
|
+
else
|
361
|
+
return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
when OP_LESSTHAN, OP_LESSTHANOREQUAL
|
365
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
366
|
+
a, b = pop_int(2)
|
367
|
+
push_int(a < b ? 1 : 0) if opcode == OP_LESSTHAN
|
368
|
+
push_int(a <= b ? 1 : 0) if opcode == OP_LESSTHANOREQUAL
|
369
|
+
when OP_GREATERTHAN, OP_GREATERTHANOREQUAL
|
370
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
371
|
+
a, b = pop_int(2)
|
372
|
+
push_int(a > b ? 1 : 0) if opcode == OP_GREATERTHAN
|
373
|
+
push_int(a >= b ? 1 : 0) if opcode == OP_GREATERTHANOREQUAL
|
374
|
+
when OP_MIN
|
375
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
376
|
+
push_int(pop_int(2).min)
|
377
|
+
when OP_MAX
|
378
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
379
|
+
push_int(pop_int(2).max)
|
380
|
+
when OP_WITHIN
|
381
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
382
|
+
x, a, b = pop_int(3)
|
383
|
+
push_int((a <= x && x < b) ? 1 : 0)
|
384
|
+
when OP_NOT
|
385
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
386
|
+
push_int(pop_int == 0 ? 1 : 0)
|
387
|
+
when OP_SIZE
|
388
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
389
|
+
item = stack.last
|
390
|
+
item = Tapyrus::Script.encode_number(item) if item.is_a?(Numeric)
|
391
|
+
size = item.htb.bytesize
|
392
|
+
push_int(size)
|
393
|
+
when OP_NEGATE
|
394
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
395
|
+
push_int(-pop_int)
|
396
|
+
when OP_NUMNOTEQUAL
|
397
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
398
|
+
a, b = pop_int(2)
|
399
|
+
push_int(a == b ? 0 : 1)
|
400
|
+
when OP_CODESEPARATOR
|
401
|
+
last_code_separator_index = index + 1
|
402
|
+
when OP_CHECKSIG, OP_CHECKSIGVERIFY
|
403
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
404
|
+
sig, pubkey = pop_string(2)
|
405
|
+
|
406
|
+
subscript = script.subscript(last_code_separator_index..-1)
|
407
|
+
if sig_version == :base
|
408
|
+
tmp = subscript.find_and_delete(Script.new << sig)
|
409
|
+
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
410
|
+
subscript = tmp
|
411
|
+
end
|
412
|
+
|
413
|
+
return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
|
414
|
+
|
415
|
+
success = checker.check_sig(sig, pubkey, subscript, sig_version)
|
416
|
+
|
417
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL
|
418
|
+
if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
|
419
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL)
|
420
|
+
end
|
421
|
+
|
422
|
+
push_int(success ? 1 : 0)
|
423
|
+
|
424
|
+
if opcode == OP_CHECKSIGVERIFY
|
425
|
+
if success
|
426
|
+
stack.pop
|
427
|
+
else
|
428
|
+
return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
432
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
433
|
+
pubkey_count = pop_int
|
434
|
+
unless (0..MAX_PUBKEYS_PER_MULTISIG).include?(pubkey_count)
|
435
|
+
return set_error(SCRIPT_ERR_PUBKEY_COUNT)
|
436
|
+
end
|
437
|
+
|
438
|
+
op_count += pubkey_count
|
439
|
+
return set_error(SCRIPT_ERR_OP_COUNT) if op_count > MAX_OPS_PER_SCRIPT
|
440
|
+
|
441
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < pubkey_count
|
442
|
+
|
443
|
+
pubkeys = pop_string(pubkey_count)
|
444
|
+
pubkeys = [pubkeys] if pubkeys.is_a?(String)
|
445
|
+
|
446
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
447
|
+
|
448
|
+
sig_count = pop_int
|
449
|
+
return set_error(SCRIPT_ERR_SIG_COUNT) if sig_count < 0 || sig_count > pubkey_count
|
450
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < (sig_count)
|
451
|
+
|
452
|
+
sigs = pop_string(sig_count)
|
453
|
+
sigs = [sigs] if sigs.is_a?(String)
|
454
|
+
|
455
|
+
subscript = script.subscript(last_code_separator_index..-1)
|
456
|
+
|
457
|
+
if sig_version == :base
|
458
|
+
sigs.each do |sig|
|
459
|
+
tmp = subscript.find_and_delete(Script.new << sig)
|
460
|
+
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
461
|
+
subscript = tmp
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
success = true
|
466
|
+
while success && sig_count > 0
|
467
|
+
sig = sigs.pop
|
468
|
+
pubkey = pubkeys.pop
|
469
|
+
return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
|
470
|
+
ok = checker.check_sig(sig, pubkey, subscript, sig_version)
|
471
|
+
if ok
|
472
|
+
sig_count -= 1
|
473
|
+
else
|
474
|
+
sigs << sig
|
475
|
+
end
|
476
|
+
pubkey_count -= 1
|
477
|
+
success = false if sig_count > pubkey_count
|
478
|
+
end
|
479
|
+
|
480
|
+
if !success && flag?(SCRIPT_VERIFY_NULLFAIL)
|
481
|
+
sigs.each do |sig|
|
482
|
+
# If the operation failed, we require that all signatures must be empty vector
|
483
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL) if sig.bytesize > 0
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# A bug causes CHECKMULTISIG to consume one extra argument whose contents were not checked in any way.
|
488
|
+
# Unfortunately this is a potential source of mutability,
|
489
|
+
# so optionally verify it is exactly equal to zero prior to removing it from the stack.
|
490
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
491
|
+
if flag?(SCRIPT_VERIFY_NULLDUMMY) && stack[-1].size > 0
|
492
|
+
return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
|
493
|
+
end
|
494
|
+
stack.pop
|
495
|
+
|
496
|
+
push_int(success ? 1 : 0)
|
497
|
+
if opcode == OP_CHECKMULTISIGVERIFY
|
498
|
+
if success
|
499
|
+
stack.pop
|
500
|
+
else
|
501
|
+
return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
when OP_RETURN
|
505
|
+
return set_error(SCRIPT_ERR_OP_RETURN)
|
506
|
+
else
|
507
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE)
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# max stack size check
|
513
|
+
return set_error(SCRIPT_ERR_STACK_SIZE) if stack.size + alt_stack.size > MAX_STACK_SIZE
|
514
|
+
end
|
515
|
+
rescue Exception => e
|
516
|
+
puts e
|
517
|
+
puts e.backtrace
|
518
|
+
return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
|
519
|
+
end
|
520
|
+
|
521
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) unless flow_stack.empty?
|
522
|
+
|
523
|
+
set_error(SCRIPT_ERR_OK)
|
524
|
+
true
|
525
|
+
end
|
526
|
+
|
527
|
+
private
|
528
|
+
|
529
|
+
def flag?(flag)
|
530
|
+
(flags & flag) != 0
|
531
|
+
end
|
532
|
+
|
533
|
+
# pop the item with the int value for the number specified by +count+ from the stack.
|
534
|
+
def pop_int(count = 1)
|
535
|
+
i = stack.pop(count).map{ |s| cast_to_int(s) }
|
536
|
+
count == 1 ? i.first : i
|
537
|
+
end
|
538
|
+
|
539
|
+
# cast item to int value.
|
540
|
+
def cast_to_int(s, max_num_size = DEFAULT_MAX_NUM_SIZE)
|
541
|
+
data = s.htb
|
542
|
+
raise '"script number overflow"' if data.bytesize > max_num_size
|
543
|
+
if require_minimal && data.bytesize > 0
|
544
|
+
if data.bytes[-1] & 0x7f == 0 && (data.bytesize <= 1 || data.bytes[data.bytesize - 2] & 0x80 == 0)
|
545
|
+
raise 'non-minimally encoded script number'
|
546
|
+
end
|
547
|
+
end
|
548
|
+
Script.decode_number(s)
|
549
|
+
end
|
550
|
+
|
551
|
+
# push +i+ into stack as encoded by Script#encode_number
|
552
|
+
def push_int(i)
|
553
|
+
stack << Script.encode_number(i)
|
554
|
+
end
|
555
|
+
|
556
|
+
# pop the item with the string(hex) value for the number specified by +count+ from the stack.
|
557
|
+
def pop_string(count = 1)
|
558
|
+
s = stack.pop(count).map do |s|
|
559
|
+
case s
|
560
|
+
when Numeric
|
561
|
+
Script.encode_number(s)
|
562
|
+
else
|
563
|
+
s
|
564
|
+
end
|
565
|
+
end
|
566
|
+
count == 1 ? s.first : s
|
567
|
+
end
|
568
|
+
|
569
|
+
# pop the item with the boolean value from the stack.
|
570
|
+
def pop_bool
|
571
|
+
cast_to_bool(pop_string.htb)
|
572
|
+
end
|
573
|
+
|
574
|
+
# see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L36-L49
|
575
|
+
def cast_to_bool(v)
|
576
|
+
case v
|
577
|
+
when Numeric
|
578
|
+
return v != 0
|
579
|
+
when String
|
580
|
+
v.each_byte.with_index do |b, i|
|
581
|
+
return !(i == (v.bytesize - 1) && b == 0x80) unless b == 0
|
582
|
+
end
|
583
|
+
false
|
584
|
+
else
|
585
|
+
false
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def check_signature_encoding(sig)
|
590
|
+
return true if sig.size.zero?
|
591
|
+
if (flag?(SCRIPT_VERIFY_DERSIG) || flag?(SCRIPT_VERIFY_LOW_S) || flag?(SCRIPT_VERIFY_STRICTENC)) && !Key.valid_signature_encoding?(sig.htb)
|
592
|
+
return set_error(SCRIPT_ERR_SIG_DER)
|
593
|
+
elsif flag?(SCRIPT_VERIFY_LOW_S) && !low_der_signature?(sig)
|
594
|
+
return false
|
595
|
+
elsif flag?(SCRIPT_VERIFY_STRICTENC) && !defined_hashtype_signature?(sig)
|
596
|
+
return set_error(SCRIPT_ERR_SIG_HASHTYPE)
|
597
|
+
end
|
598
|
+
true
|
599
|
+
end
|
600
|
+
|
601
|
+
def low_der_signature?(sig)
|
602
|
+
return set_error(SCRIPT_ERR_SIG_DER) unless Key.valid_signature_encoding?(sig.htb)
|
603
|
+
return set_error(SCRIPT_ERR_SIG_HIGH_S) unless Key.low_signature?(sig.htb)
|
604
|
+
true
|
605
|
+
end
|
606
|
+
|
607
|
+
def defined_hashtype_signature?(signature)
|
608
|
+
sig = signature.htb
|
609
|
+
return false if sig.empty?
|
610
|
+
s = sig.unpack('C*')
|
611
|
+
hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
|
612
|
+
hash_type &= (~(Tapyrus::SIGHASH_FORK_ID)) if Tapyrus.chain_params.fork_chain? # for fork coin.
|
613
|
+
return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single]
|
614
|
+
true
|
615
|
+
end
|
616
|
+
|
617
|
+
def check_pubkey_encoding(pubkey, sig_version)
|
618
|
+
if flag?(SCRIPT_VERIFY_STRICTENC) && !Key.compress_or_uncompress_pubkey?(pubkey)
|
619
|
+
return set_error(SCRIPT_ERR_PUBKEYTYPE)
|
620
|
+
end
|
621
|
+
# Only compressed keys are accepted in segwit
|
622
|
+
if flag?(SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) &&
|
623
|
+
sig_version == :witness_v0 && !Key.compress_pubkey?(pubkey)
|
624
|
+
return set_error(SCRIPT_ERR_WITNESS_PUBKEYTYPE)
|
625
|
+
end
|
626
|
+
true
|
627
|
+
end
|
628
|
+
|
629
|
+
def minimal_push?(data, opcode)
|
630
|
+
if data.bytesize.zero?
|
631
|
+
return opcode == OP_0
|
632
|
+
elsif data.bytesize == 1 && data.bytes[0] >= 1 && data.bytes[0] <= 16
|
633
|
+
return opcode == OP_1 + (data.bytes[0] - 1)
|
634
|
+
elsif data.bytesize == 1 && data.bytes[0] == 0x81
|
635
|
+
return opcode == OP_1NEGATE
|
636
|
+
elsif data.bytesize <= 75
|
637
|
+
return opcode == data.bytesize
|
638
|
+
elsif data.bytesize <= 255
|
639
|
+
return opcode == OP_PUSHDATA1
|
640
|
+
elsif data.bytesize <= 65535
|
641
|
+
return opcode == OP_PUSHDATA2
|
642
|
+
end
|
643
|
+
true
|
644
|
+
end
|
645
|
+
|
646
|
+
def verify_pushdata_length(chunk)
|
647
|
+
buf = StringIO.new(chunk)
|
648
|
+
opcode = buf.read(1).ord
|
649
|
+
offset = 1
|
650
|
+
len = case opcode
|
651
|
+
when OP_PUSHDATA1
|
652
|
+
offset += 1
|
653
|
+
buf.read(1).unpack('C').first
|
654
|
+
when OP_PUSHDATA2
|
655
|
+
offset += 2
|
656
|
+
buf.read(2).unpack('v').first
|
657
|
+
when OP_PUSHDATA4
|
658
|
+
offset += 4
|
659
|
+
buf.read(4).unpack('V').first
|
660
|
+
else
|
661
|
+
opcode
|
662
|
+
end
|
663
|
+
chunk.bytesize == len + offset
|
664
|
+
end
|
665
|
+
|
666
|
+
end
|
667
|
+
|
668
|
+
end
|