bitcoinrb 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +5 -4
- data/README.md +10 -0
- data/bitcoinrb.gemspec +4 -4
- data/lib/bitcoin.rb +29 -16
- data/lib/bitcoin/block_filter.rb +14 -0
- data/lib/bitcoin/chain_params.rb +9 -0
- data/lib/bitcoin/chainparams/signet.yml +39 -0
- data/lib/bitcoin/constants.rb +43 -3
- data/lib/bitcoin/descriptor.rb +1 -1
- data/lib/bitcoin/errors.rb +19 -0
- data/lib/bitcoin/ext/ecdsa.rb +31 -0
- data/lib/bitcoin/ext_key.rb +35 -19
- data/lib/bitcoin/key.rb +43 -26
- data/lib/bitcoin/message/cfcheckpt.rb +2 -2
- data/lib/bitcoin/message/cfheaders.rb +1 -1
- data/lib/bitcoin/message/cfilter.rb +1 -1
- data/lib/bitcoin/message/fee_filter.rb +1 -1
- data/lib/bitcoin/message/filter_load.rb +3 -3
- data/lib/bitcoin/message/header_and_short_ids.rb +1 -1
- data/lib/bitcoin/message/inventory.rb +1 -1
- data/lib/bitcoin/message/merkle_block.rb +1 -1
- data/lib/bitcoin/message/network_addr.rb +3 -3
- data/lib/bitcoin/message/ping.rb +1 -1
- data/lib/bitcoin/message/pong.rb +1 -1
- data/lib/bitcoin/message/send_cmpct.rb +2 -2
- data/lib/bitcoin/mnemonic.rb +2 -2
- data/lib/bitcoin/network/peer_discovery.rb +1 -3
- data/lib/bitcoin/node/configuration.rb +3 -1
- data/lib/bitcoin/node/spv.rb +8 -0
- data/lib/bitcoin/opcodes.rb +14 -1
- data/lib/bitcoin/payment_code.rb +2 -2
- data/lib/bitcoin/psbt/hd_key_path.rb +1 -1
- data/lib/bitcoin/psbt/input.rb +3 -3
- data/lib/bitcoin/psbt/output.rb +1 -1
- data/lib/bitcoin/psbt/tx.rb +4 -4
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +1 -1
- data/lib/bitcoin/script/script.rb +52 -19
- data/lib/bitcoin/script/script_error.rb +27 -1
- data/lib/bitcoin/script/script_interpreter.rb +161 -62
- data/lib/bitcoin/script/tx_checker.rb +64 -14
- data/lib/bitcoin/secp256k1/native.rb +138 -25
- data/lib/bitcoin/secp256k1/ruby.rb +78 -19
- data/lib/bitcoin/sighash_generator.rb +156 -0
- data/lib/bitcoin/tx.rb +13 -80
- data/lib/bitcoin/tx_in.rb +1 -1
- data/lib/bitcoin/tx_out.rb +2 -3
- data/lib/bitcoin/util.rb +15 -6
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/account.rb +1 -1
- metadata +19 -15
@@ -63,7 +63,7 @@ module Bitcoin
|
|
63
63
|
response = http.request(request)
|
64
64
|
body = response.body
|
65
65
|
response = Bitcoin::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
|
66
|
-
raise response['error'].
|
66
|
+
raise response['error'].to_json if response['error']
|
67
67
|
response['result']
|
68
68
|
end
|
69
69
|
|
@@ -110,25 +110,28 @@ module Bitcoin
|
|
110
110
|
if opcode.pushdata?
|
111
111
|
pushcode = opcode.ord
|
112
112
|
packed_size = nil
|
113
|
+
if buf.eof?
|
114
|
+
s.chunks << opcode
|
115
|
+
return s
|
116
|
+
end
|
113
117
|
len = case pushcode
|
114
118
|
when OP_PUSHDATA1
|
115
119
|
packed_size = buf.read(1)
|
116
|
-
packed_size.
|
120
|
+
packed_size.unpack1('C')
|
117
121
|
when OP_PUSHDATA2
|
118
122
|
packed_size = buf.read(2)
|
119
|
-
packed_size.
|
123
|
+
packed_size.unpack1('v')
|
120
124
|
when OP_PUSHDATA4
|
121
125
|
packed_size = buf.read(4)
|
122
|
-
packed_size.
|
126
|
+
packed_size.unpack1('V')
|
123
127
|
else
|
124
|
-
pushcode
|
128
|
+
pushcode < OP_PUSHDATA1 ? pushcode : 0
|
125
129
|
end
|
126
|
-
if
|
127
|
-
s.chunks << [len].pack('C')
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
130
|
+
if buf.eof?
|
131
|
+
s.chunks << [len].pack('C')
|
132
|
+
else buf.eof?
|
133
|
+
chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
|
134
|
+
s.chunks << chunk
|
132
135
|
end
|
133
136
|
else
|
134
137
|
if Opcodes.defined?(opcode.ord)
|
@@ -141,8 +144,12 @@ module Bitcoin
|
|
141
144
|
s
|
142
145
|
end
|
143
146
|
|
144
|
-
|
145
|
-
|
147
|
+
# Output script payload.
|
148
|
+
# @param [Boolean] length_prefixed Flag whether the length of the pyrode should be given at the beginning.(default: false)
|
149
|
+
# @return [String] payload
|
150
|
+
def to_payload(length_prefixed = false)
|
151
|
+
p = chunks.join
|
152
|
+
length_prefixed ? (Bitcoin.pack_var_int(p.length) << p) : p
|
146
153
|
end
|
147
154
|
|
148
155
|
def empty?
|
@@ -242,7 +249,7 @@ module Bitcoin
|
|
242
249
|
return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
|
243
250
|
return false unless chunks[1].pushdata?
|
244
251
|
|
245
|
-
if size == (chunks[1][0].
|
252
|
+
if size == (chunks[1][0].unpack1('C') + 2)
|
246
253
|
program_size = chunks[1].pushed_data.bytesize
|
247
254
|
return program_size >= 2 && program_size <= 40
|
248
255
|
end
|
@@ -343,10 +350,14 @@ module Bitcoin
|
|
343
350
|
v
|
344
351
|
else
|
345
352
|
data = c.pushed_data
|
346
|
-
if data
|
347
|
-
|
353
|
+
if data
|
354
|
+
if data.bytesize <= 4
|
355
|
+
Script.decode_number(data.bth) # for scriptnum
|
356
|
+
else
|
357
|
+
data.bth
|
358
|
+
end
|
348
359
|
else
|
349
|
-
|
360
|
+
c.bth
|
350
361
|
end
|
351
362
|
end
|
352
363
|
else
|
@@ -393,8 +404,8 @@ module Bitcoin
|
|
393
404
|
hex = '0' + hex unless (hex.length % 2).zero?
|
394
405
|
v = hex.htb.reverse # change endian
|
395
406
|
|
396
|
-
v = v << (negative ? 0x80 : 0x00) unless (v[-1].
|
397
|
-
v[-1] = [v[-1].
|
407
|
+
v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack1('C') & 0x80) == 0
|
408
|
+
v[-1] = [v[-1].unpack1('C') | 0x80].pack('C') if negative
|
398
409
|
v.bth
|
399
410
|
end
|
400
411
|
|
@@ -402,7 +413,7 @@ module Bitcoin
|
|
402
413
|
def self.decode_number(s)
|
403
414
|
v = s.htb.reverse
|
404
415
|
return 0 if v.length.zero?
|
405
|
-
mbs = v[0].
|
416
|
+
mbs = v[0].unpack1('C')
|
406
417
|
v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
|
407
418
|
result = v.bth.to_i(16)
|
408
419
|
result = -result unless (mbs & 0x80) == 0
|
@@ -553,6 +564,28 @@ module Bitcoin
|
|
553
564
|
segwit_addr.addr
|
554
565
|
end
|
555
566
|
|
567
|
+
# Check whether push data length is valid.
|
568
|
+
# @return [Boolean] if valid return true, otherwise false.
|
569
|
+
def valid_pushdata_length?(chunk)
|
570
|
+
buf = StringIO.new(chunk)
|
571
|
+
opcode = buf.read(1).ord
|
572
|
+
offset = 1
|
573
|
+
len = case opcode
|
574
|
+
when OP_PUSHDATA1
|
575
|
+
offset += 1
|
576
|
+
buf.read(1).unpack1('C')
|
577
|
+
when OP_PUSHDATA2
|
578
|
+
offset += 2
|
579
|
+
buf.read(2).unpack1('v')
|
580
|
+
when OP_PUSHDATA4
|
581
|
+
offset += 4
|
582
|
+
buf.read(4).unpack1('V')
|
583
|
+
else
|
584
|
+
opcode
|
585
|
+
end
|
586
|
+
chunk.bytesize == len + offset
|
587
|
+
end
|
588
|
+
|
556
589
|
end
|
557
590
|
|
558
591
|
end
|
@@ -76,6 +76,14 @@ module Bitcoin
|
|
76
76
|
'NOPx reserved for soft-fork upgrades'
|
77
77
|
when SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
|
78
78
|
'Witness version reserved for soft-fork upgrades'
|
79
|
+
when SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION
|
80
|
+
'Taproot version reserved for soft-fork upgrades'
|
81
|
+
when SCRIPT_ERR_DISCOURAGE_UNKNOWN_ANNEX
|
82
|
+
'Unknown input annex reserved for soft-fork upgrades'
|
83
|
+
when SCRIPT_ERR_DISCOURAGE_OP_SUCCESS
|
84
|
+
'SUCCESSx reserved for soft-fork upgrades'
|
85
|
+
when SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE
|
86
|
+
'Public key version reserved for soft-fork upgrades'
|
79
87
|
when SCRIPT_ERR_PUBKEYTYPE
|
80
88
|
'Public key is neither compressed or uncompressed'
|
81
89
|
when SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH
|
@@ -92,8 +100,22 @@ module Bitcoin
|
|
92
100
|
'Witness provided for non-witness script'
|
93
101
|
when SCRIPT_ERR_WITNESS_PUBKEYTYPE
|
94
102
|
'Using non-compressed keys in segwit'
|
103
|
+
when SCRIPT_ERR_SCHNORR_SIG_SIZE
|
104
|
+
'Invalid Schnorr signature size'
|
105
|
+
when SCRIPT_ERR_SCHNORR_SIG_HASHTYPE
|
106
|
+
'Invalid Schnorr signature hash type'
|
107
|
+
when SCRIPT_ERR_SCHNORR_SIG
|
108
|
+
'Invalid Schnorr signature'
|
109
|
+
when SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE
|
110
|
+
'Invalid Taproot control block size'
|
111
|
+
when SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT
|
112
|
+
'Too much signature validation relative to witness weight'
|
113
|
+
when SCRIPT_ERR_TAPSCRIPT_CHECKMULTISIG
|
114
|
+
'OP_CHECKMULTISIG(VERIFY) is not available in tapscript'
|
115
|
+
when SCRIPT_ERR_TAPSCRIPT_MINIMALIF
|
116
|
+
'OP_IF/NOTIF argument must be minimal in tapscript'
|
95
117
|
when SCRIPT_ERR_OP_CODESEPARATOR
|
96
|
-
'Using OP_CODESEPARATOR in non-witness
|
118
|
+
'Using OP_CODESEPARATOR in non-witness script'
|
97
119
|
when SCRIPT_ERR_SIG_FINDANDDELETE
|
98
120
|
'Signature is found in scriptCode'
|
99
121
|
when SCRIPT_ERR_UNKNOWN_ERROR, SCRIPT_ERR_ERROR_COUNT
|
@@ -103,6 +125,10 @@ module Bitcoin
|
|
103
125
|
end
|
104
126
|
end
|
105
127
|
|
128
|
+
def ok?
|
129
|
+
code == Bitcoin::SCRIPT_ERR_OK
|
130
|
+
end
|
131
|
+
|
106
132
|
def self.name_to_code(name)
|
107
133
|
NAME_MAP[name]
|
108
134
|
end
|
@@ -13,7 +13,6 @@ module Bitcoin
|
|
13
13
|
|
14
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
15
|
|
16
|
-
|
17
16
|
# syntax sugar for simple evaluation for script.
|
18
17
|
# @param [Bitcoin::Script] script_sig a scriptSig.
|
19
18
|
# @param [Bitcoin::Script] script_pubkey a scriptPubkey.
|
@@ -55,7 +54,7 @@ module Bitcoin
|
|
55
54
|
return set_error(SCRIPT_ERR_WITNESS_MALLEATED) unless script_sig.size == 0
|
56
55
|
version, program = script_pubkey.witness_data
|
57
56
|
stack_copy = stack.dup
|
58
|
-
return false unless verify_witness_program(witness, version, program)
|
57
|
+
return false unless verify_witness_program(witness, version, program, false)
|
59
58
|
end
|
60
59
|
|
61
60
|
# Additional validation for spend-to-script-hash transactions
|
@@ -63,11 +62,7 @@ module Bitcoin
|
|
63
62
|
return set_error(SCRIPT_ERR_SIG_PUSHONLY) unless script_sig.push_only?
|
64
63
|
@stack = stack_copy
|
65
64
|
raise 'stack cannot be empty.' if stack.empty?
|
66
|
-
|
67
|
-
redeem_script = Bitcoin::Script.parse_from_payload(stack.pop.htb)
|
68
|
-
rescue Exception => e
|
69
|
-
return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
|
70
|
-
end
|
65
|
+
redeem_script = Bitcoin::Script.parse_from_payload(stack.pop.htb)
|
71
66
|
return false unless eval_script(redeem_script, :base)
|
72
67
|
return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last)
|
73
68
|
|
@@ -78,7 +73,7 @@ module Bitcoin
|
|
78
73
|
return set_error(SCRIPT_ERR_WITNESS_MALLEATED_P2SH) unless script_sig == (Bitcoin::Script.new << redeem_script.to_hex)
|
79
74
|
|
80
75
|
version, program = redeem_script.witness_data
|
81
|
-
return false unless verify_witness_program(witness, version, program)
|
76
|
+
return false unless verify_witness_program(witness, version, program, true)
|
82
77
|
end
|
83
78
|
end
|
84
79
|
|
@@ -105,21 +100,71 @@ module Bitcoin
|
|
105
100
|
false
|
106
101
|
end
|
107
102
|
|
108
|
-
def verify_witness_program(witness, version, program)
|
103
|
+
def verify_witness_program(witness, version, program, is_p2sh)
|
104
|
+
@stack = witness.stack.map(&:bth)
|
105
|
+
need_evaluate = false
|
106
|
+
sig_version = nil
|
107
|
+
opts = {}
|
109
108
|
if version == 0
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
109
|
+
need_evaluate = true
|
110
|
+
sig_version = :witness_v0
|
111
|
+
if program.bytesize == WITNESS_V0_SCRIPTHASH_SIZE # BIP141 P2WSH: 32-byte witness v0 program (which encodes SHA256(script))
|
112
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY) if stack.size == 0
|
113
|
+
script_pubkey = Bitcoin::Script.parse_from_payload(stack.pop.htb)
|
114
114
|
script_hash = Bitcoin.sha256(script_pubkey.to_payload)
|
115
115
|
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless script_hash == program
|
116
|
-
elsif program.bytesize == 20
|
117
|
-
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless
|
116
|
+
elsif program.bytesize == WITNESS_V0_KEYHASH_SIZE # BIP141 P2WPKH: 20-byte witness v0 program (which encodes Hash160(pubkey))
|
117
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless stack.size == 2
|
118
118
|
script_pubkey = Bitcoin::Script.to_p2pkh(program.bth)
|
119
|
-
@stack = witness.stack.map{|w|w.bth}
|
120
119
|
else
|
121
120
|
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH)
|
122
121
|
end
|
122
|
+
elsif version == 1 && program.bytesize == WITNESS_V1_TAPROOT_SIZE && !is_p2sh
|
123
|
+
# BIP341 Taproot: 32-byte non-P2SH witness v1 program (which encodes a P2C-tweaked pubkey)
|
124
|
+
return true unless flag?(SCRIPT_VERIFY_TAPROOT)
|
125
|
+
if stack.size == 0
|
126
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY)
|
127
|
+
elsif stack.size >= 2 && !stack.last.empty? && stack.last[0..1].to_i(16) == ANNEX_TAG
|
128
|
+
opts[:annex] = stack.pop.htb
|
129
|
+
end
|
130
|
+
if stack.size == 1 # Key path spending (stack size is 1 after removing optional annex)
|
131
|
+
result = checker.check_schnorr_sig(stack.last, program.bth, :taproot, opts)
|
132
|
+
return checker.has_error? ? set_error(checker.error_code) : result
|
133
|
+
else
|
134
|
+
sig_version = :tapscript
|
135
|
+
# Script path spending (stack size is >1 after removing optional annex)
|
136
|
+
control = stack.pop.htb
|
137
|
+
script_payload = stack.pop.htb
|
138
|
+
if control.bytesize < TAPROOT_CONTROL_BASE_SIZE || control.bytesize > TAPROOT_CONTROL_MAX_SIZE ||
|
139
|
+
(control.bytesize - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0
|
140
|
+
return set_error(SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE)
|
141
|
+
end
|
142
|
+
leaf_ver = control[0].bti & TAPROOT_LEAF_MASK
|
143
|
+
opts[:leaf_hash] = Bitcoin.tagged_hash('TapLeaf', [leaf_ver].pack('C') + Bitcoin.pack_var_string(script_payload))
|
144
|
+
return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless valid_taproot_commitment?(control, program, opts[:leaf_hash])
|
145
|
+
if (control[0].bti & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT
|
146
|
+
opts[:weight_left] = witness.to_payload.bytesize + VALIDATION_WEIGHT_OFFSET
|
147
|
+
script_pubkey = Bitcoin::Script.parse_from_payload(script_payload)
|
148
|
+
script_pubkey.chunks.each do |c|
|
149
|
+
next if c.empty?
|
150
|
+
# Note how this condition would not be reached if an unknown OP_SUCCESSx was found
|
151
|
+
if c.pushdata?
|
152
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE) unless c.valid_pushdata_length?
|
153
|
+
elsif Opcodes.op_success?(c.ord)
|
154
|
+
# OP_SUCCESSx processing overrides everything, including stack element size limits
|
155
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_OP_SUCCESS) if flag?(SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS)
|
156
|
+
return true
|
157
|
+
else
|
158
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE) unless Opcodes.defined?(c.ord, true)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
return set_error(SCRIPT_ERR_STACK_SIZE) if stack.size > MAX_STACK_SIZE
|
162
|
+
need_evaluate = true
|
163
|
+
end
|
164
|
+
|
165
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION)
|
166
|
+
return true unless need_evaluate
|
167
|
+
end
|
123
168
|
elsif flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
|
124
169
|
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
|
125
170
|
else
|
@@ -130,19 +175,22 @@ module Bitcoin
|
|
130
175
|
return set_error(SCRIPT_ERR_PUSH_SIZE) if s.htb.bytesize > MAX_SCRIPT_ELEMENT_SIZE
|
131
176
|
end
|
132
177
|
|
133
|
-
return false unless eval_script(script_pubkey, :
|
178
|
+
return false unless eval_script(script_pubkey, sig_version, opts: opts)
|
134
179
|
|
135
180
|
return set_error(SCRIPT_ERR_CLEANSTACK) unless stack.size == 1
|
136
181
|
return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last)
|
137
182
|
true
|
138
183
|
end
|
139
184
|
|
140
|
-
def eval_script(script, sig_version)
|
141
|
-
|
185
|
+
def eval_script(script, sig_version, opts: {})
|
186
|
+
# sig_version cannot be TAPROOT here, as it admits no script execution.
|
187
|
+
raise ArgumentError, "Invalid sig version was specified: #{sig_version}" unless [:base, :witness_v0, :tapscript].include?(sig_version)
|
188
|
+
return set_error(SCRIPT_ERR_SCRIPT_SIZE) if script.size > MAX_SCRIPT_SIZE && [:base, :witness_v0].include?(sig_version)
|
142
189
|
begin
|
143
190
|
flow_stack = []
|
144
191
|
alt_stack = []
|
145
|
-
|
192
|
+
opts[:last_code_separator_pos] = 0xffffffff
|
193
|
+
opts[:begincodehash] = 0
|
146
194
|
op_count = 0
|
147
195
|
|
148
196
|
script.chunks.each_with_index do |c, index|
|
@@ -156,10 +204,10 @@ module Bitcoin
|
|
156
204
|
if require_minimal && !minimal_push?(c.pushed_data, opcode)
|
157
205
|
return set_error(SCRIPT_ERR_MINIMALDATA)
|
158
206
|
end
|
159
|
-
return set_error(SCRIPT_ERR_BAD_OPCODE) unless
|
207
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE) unless c.valid_pushdata_length?
|
160
208
|
stack << c.pushed_data.bth
|
161
209
|
else
|
162
|
-
if opcode > OP_16 && (op_count += 1) > MAX_OPS_PER_SCRIPT
|
210
|
+
if [:base, :witness_v0].include?(sig_version) && opcode > OP_16 && (op_count += 1) > MAX_OPS_PER_SCRIPT
|
163
211
|
return set_error(SCRIPT_ERR_OP_COUNT)
|
164
212
|
end
|
165
213
|
return set_error(SCRIPT_ERR_DISABLED_OPCODE) if DISABLE_OPCODES.include?(opcode)
|
@@ -208,9 +256,11 @@ module Bitcoin
|
|
208
256
|
if need_exec
|
209
257
|
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if stack.size < 1
|
210
258
|
value = pop_string.htb
|
211
|
-
if sig_version == :witness_v0 && flag?(SCRIPT_VERIFY_MINIMALIF)
|
212
|
-
|
213
|
-
|
259
|
+
if sig_version == :witness_v0 && flag?(SCRIPT_VERIFY_MINIMALIF) || sig_version == :tapscript
|
260
|
+
# Under witness v0 rules it is only a policy rule, enabled through SCRIPT_VERIFY_MINIMALIF.
|
261
|
+
# Tapscript requires minimal IF/NOTIF inputs as a consensus rule.
|
262
|
+
if value.bytesize > 1 || (value.bytesize == 1 && value[0].unpack1('C') != 1)
|
263
|
+
return set_error(sig_version == :witness_v0 ? SCRIPT_ERR_MINIMALIF : SCRIPT_ERR_TAPSCRIPT_MINIMALIF)
|
214
264
|
end
|
215
265
|
end
|
216
266
|
result = cast_to_bool(value)
|
@@ -397,26 +447,14 @@ module Bitcoin
|
|
397
447
|
a, b = pop_int(2)
|
398
448
|
push_int(a == b ? 0 : 1)
|
399
449
|
when OP_CODESEPARATOR
|
400
|
-
|
450
|
+
opts[:begincodehash] = index + 1
|
451
|
+
opts[:last_code_separator_pos] = index
|
401
452
|
when OP_CHECKSIG, OP_CHECKSIGVERIFY
|
402
453
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
403
454
|
sig, pubkey = pop_string(2)
|
404
455
|
|
405
|
-
|
406
|
-
if
|
407
|
-
tmp = subscript.find_and_delete(Script.new << sig)
|
408
|
-
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
409
|
-
subscript = tmp
|
410
|
-
end
|
411
|
-
|
412
|
-
return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
|
413
|
-
|
414
|
-
success = checker.check_sig(sig, pubkey, subscript, sig_version)
|
415
|
-
|
416
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL
|
417
|
-
if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
|
418
|
-
return set_error(SCRIPT_ERR_SIG_NULLFAIL)
|
419
|
-
end
|
456
|
+
success = valid_sig?(sig, pubkey, sig_version, script, opts)
|
457
|
+
return false if error && !error.ok?
|
420
458
|
|
421
459
|
push_int(success ? 1 : 0)
|
422
460
|
|
@@ -427,6 +465,19 @@ module Bitcoin
|
|
427
465
|
return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
|
428
466
|
end
|
429
467
|
end
|
468
|
+
when OP_CHECKSIGADD
|
469
|
+
# OP_CHECKSIGADD is only available in Tapscript
|
470
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE) if [:base, :witness_v0].include?(sig_version)
|
471
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
472
|
+
|
473
|
+
pubkey = pop_string
|
474
|
+
num = pop_int
|
475
|
+
sig = pop_string
|
476
|
+
|
477
|
+
success = valid_sig?(sig, pubkey, sig_version, script, opts) && !sig.empty?
|
478
|
+
return false if error && !error.ok?
|
479
|
+
|
480
|
+
push_int(success ? num + 1 : num)
|
430
481
|
when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
431
482
|
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
432
483
|
pubkey_count = pop_int
|
@@ -451,7 +502,7 @@ module Bitcoin
|
|
451
502
|
sigs = pop_string(sig_count)
|
452
503
|
sigs = [sigs] if sigs.is_a?(String)
|
453
504
|
|
454
|
-
subscript = script.subscript(
|
505
|
+
subscript = script.subscript(opts[:begincodehash]..-1)
|
455
506
|
|
456
507
|
if sig_version == :base
|
457
508
|
sigs.each do |sig|
|
@@ -466,7 +517,7 @@ module Bitcoin
|
|
466
517
|
sig = sigs.pop
|
467
518
|
pubkey = pubkeys.pop
|
468
519
|
return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
|
469
|
-
ok = checker.check_sig(sig, pubkey, subscript, sig_version)
|
520
|
+
ok = checker.check_sig(sig, pubkey, subscript, sig_version, allow_hybrid: !flag?(SCRIPT_VERIFY_STRICTENC))
|
470
521
|
if ok
|
471
522
|
sig_count -= 1
|
472
523
|
else
|
@@ -513,7 +564,6 @@ module Bitcoin
|
|
513
564
|
end
|
514
565
|
rescue Exception => e
|
515
566
|
puts e
|
516
|
-
puts e.backtrace
|
517
567
|
return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
|
518
568
|
end
|
519
569
|
|
@@ -572,6 +622,7 @@ module Bitcoin
|
|
572
622
|
|
573
623
|
# see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L36-L49
|
574
624
|
def cast_to_bool(v)
|
625
|
+
return false if v.empty?
|
575
626
|
case v
|
576
627
|
when Numeric
|
577
628
|
return v != 0
|
@@ -641,24 +692,72 @@ module Bitcoin
|
|
641
692
|
true
|
642
693
|
end
|
643
694
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
695
|
+
# check whether valid taproot commitment.
|
696
|
+
def valid_taproot_commitment?(control, program, leaf_hash)
|
697
|
+
begin
|
698
|
+
path_len = (control.bytesize - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE
|
699
|
+
xonly_pubkey = control[1...TAPROOT_CONTROL_BASE_SIZE]
|
700
|
+
p = Bitcoin::Key.new(pubkey: "02#{xonly_pubkey.bth}", key_type: Key::TYPES[:compressed])
|
701
|
+
k = leaf_hash
|
702
|
+
path_len.times do |i|
|
703
|
+
pos = (TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i)
|
704
|
+
e = control[pos...(pos + TAPROOT_CONTROL_NODE_SIZE)]
|
705
|
+
k = Bitcoin.tagged_hash('TapBranch', k.bth < e.bth ? k + e : e + k)
|
706
|
+
end
|
707
|
+
t = Bitcoin.tagged_hash('TapTweak', xonly_pubkey + k)
|
708
|
+
key = Bitcoin::Key.new(priv_key: t.bth, key_type: Key::TYPES[:compressed])
|
709
|
+
q = key.to_point + p.to_point
|
710
|
+
return q.x == program.bti && (control[0].bti & 1) == (q.y % 2)
|
711
|
+
rescue ArgumentError
|
712
|
+
return false
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
def valid_sig?(sig, pubkey, sig_version, script, opts)
|
717
|
+
case sig_version
|
718
|
+
when :base, :witness_v0
|
719
|
+
return eval_checksig_pre_tapscript(sig_version, sig, pubkey, script, opts)
|
720
|
+
when :tapscript
|
721
|
+
return eval_checksig_tapscript(sig, pubkey, opts)
|
722
|
+
end
|
723
|
+
false
|
724
|
+
end
|
725
|
+
|
726
|
+
def eval_checksig_pre_tapscript(sig_version, sig, pubkey, script, opts)
|
727
|
+
subscript = script.subscript(opts[:begincodehash]..-1)
|
728
|
+
if sig_version == :base
|
729
|
+
tmp = subscript.find_and_delete(Script.new << sig)
|
730
|
+
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
731
|
+
subscript = tmp
|
732
|
+
end
|
733
|
+
return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
|
734
|
+
success = checker.check_sig(sig, pubkey, subscript, sig_version, allow_hybrid: !flag?(SCRIPT_VERIFY_STRICTENC))
|
735
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL
|
736
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL) if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
|
737
|
+
success
|
738
|
+
end
|
739
|
+
|
740
|
+
def eval_checksig_tapscript(sig, pubkey, opts)
|
741
|
+
success = !sig.empty?
|
742
|
+
if success
|
743
|
+
# Implement the sigops/witnesssize ratio test.
|
744
|
+
opts[:weight_left] -= VALIDATION_WEIGHT_PER_SIGOP_PASSED
|
745
|
+
return set_error(SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT) if opts[:weight_left] < 0
|
746
|
+
end
|
747
|
+
|
748
|
+
pubkey_size = pubkey.htb.bytesize
|
749
|
+
if pubkey_size == 0
|
750
|
+
return set_error(SCRIPT_ERR_PUBKEYTYPE)
|
751
|
+
elsif pubkey_size == 32
|
752
|
+
if success
|
753
|
+
result = checker.check_schnorr_sig(sig, pubkey, :tapscript, opts)
|
754
|
+
return checker.has_error? ? set_error(checker.error_code) : set_error(SCRIPT_ERR_SCHNORR_SIG) unless result
|
755
|
+
end
|
756
|
+
else
|
757
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE)
|
758
|
+
end
|
759
|
+
success
|
760
|
+
# return true
|
662
761
|
end
|
663
762
|
|
664
763
|
end
|