btcruby 1.0.9 → 1.1
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/RELEASE_NOTES.md +7 -0
- data/lib/btcruby.rb +12 -3
- data/lib/btcruby/open_assets/issuance_id.rb +1 -1
- data/lib/btcruby/{opcode.rb → script/opcode.rb} +0 -0
- data/lib/btcruby/{script.rb → script/script.rb} +0 -0
- data/lib/btcruby/script/script_error.rb +139 -0
- data/lib/btcruby/script/script_flags.rb +55 -0
- data/lib/btcruby/script/script_interpreter.rb +886 -0
- data/lib/btcruby/script/script_number.rb +254 -0
- data/lib/btcruby/script/signature_checker.rb +14 -0
- data/lib/btcruby/{signature_hashtype.rb → script/signature_hashtype.rb} +0 -0
- data/lib/btcruby/script/test_signature_checker.rb +53 -0
- data/lib/btcruby/script/transaction_signature_checker.rb +66 -0
- data/lib/btcruby/transaction_input.rb +1 -1
- data/lib/btcruby/version.rb +1 -1
- data/lib/btcruby/wire_format.rb +40 -8
- data/spec/open_assets/issuance_id_spec.rb +5 -5
- data/spec/wire_format_spec.rb +22 -4
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64ee5200a38763d90afb6404bd5b5a5f527f144e
|
4
|
+
data.tar.gz: ebcc849873a50338a4d226bdd111bd6988928689
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04c8322fc7e1ef83c2f8b4a03d54ec0741776955e95fe92210abcdc80c81fbf999899713d15389fd0875ec5983f770c4e30dcd5372ecbbeafebd5668b2ad8f50
|
7
|
+
data.tar.gz: 730d0a4c98735df953eaa01835055df65a1b24296bb17d06848ecbfd3aad2d3baa7b54f4379fcc47d7ec669fe6a896525eec1794e7d0e3d9610603c9613c2fb4
|
data/RELEASE_NOTES.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
BTCRuby Release Notes
|
3
3
|
=====================
|
4
4
|
|
5
|
+
1.1 (July 29, 2015)
|
6
|
+
--------------------
|
7
|
+
|
8
|
+
* Added full script interpreter as in Bitcoin Core 0.11.
|
9
|
+
* Added array encoding/decoding support to `BTC::WireFormat`.
|
10
|
+
|
11
|
+
|
5
12
|
1.0.9 (July 20, 2015)
|
6
13
|
--------------------
|
7
14
|
|
data/lib/btcruby.rb
CHANGED
@@ -27,9 +27,18 @@ require_relative 'btcruby/transaction.rb'
|
|
27
27
|
require_relative 'btcruby/transaction_input.rb'
|
28
28
|
require_relative 'btcruby/transaction_output.rb'
|
29
29
|
require_relative 'btcruby/transaction_outpoint.rb'
|
30
|
-
|
31
|
-
require_relative 'btcruby/
|
32
|
-
require_relative 'btcruby/
|
30
|
+
|
31
|
+
require_relative 'btcruby/script/script_error.rb'
|
32
|
+
require_relative 'btcruby/script/script_flags.rb'
|
33
|
+
require_relative 'btcruby/script/script_number.rb'
|
34
|
+
require_relative 'btcruby/script/script.rb'
|
35
|
+
require_relative 'btcruby/script/script_interpreter.rb'
|
36
|
+
require_relative 'btcruby/script/opcode.rb'
|
37
|
+
require_relative 'btcruby/script/signature_hashtype.rb'
|
38
|
+
require_relative 'btcruby/script/signature_checker.rb'
|
39
|
+
require_relative 'btcruby/script/test_signature_checker.rb'
|
40
|
+
require_relative 'btcruby/script/transaction_signature_checker.rb'
|
41
|
+
|
33
42
|
require_relative 'btcruby/transaction_builder.rb'
|
34
43
|
require_relative 'btcruby/proof_of_work.rb'
|
35
44
|
require_relative 'btcruby/block_header.rb'
|
@@ -17,7 +17,7 @@ module BTC
|
|
17
17
|
if outpoint || amount
|
18
18
|
raise ArgumentError, "Outpoint is missing" if !outpoint
|
19
19
|
raise ArgumentError, "Amount is missing" if !amount || amount < 0
|
20
|
-
data = outpoint.transaction_hash + WireFormat.
|
20
|
+
data = outpoint.transaction_hash + WireFormat.encode_uint32be(outpoint.index) + WireFormat.encode_uint64le(amount)
|
21
21
|
super(hash: BTC.hash160(data), network: network)
|
22
22
|
else
|
23
23
|
super(string: string, hash: hash, network: network, _raw_data: _raw_data)
|
File without changes
|
File without changes
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module BTC
|
2
|
+
SCRIPT_ERR_OK = 0
|
3
|
+
SCRIPT_ERR_UNKNOWN_ERROR = 1
|
4
|
+
SCRIPT_ERR_EVAL_FALSE = 2
|
5
|
+
SCRIPT_ERR_OP_RETURN = 3
|
6
|
+
|
7
|
+
# Max sizes
|
8
|
+
SCRIPT_ERR_SCRIPT_SIZE = 4
|
9
|
+
SCRIPT_ERR_PUSH_SIZE = 5
|
10
|
+
SCRIPT_ERR_OP_COUNT = 6
|
11
|
+
SCRIPT_ERR_STACK_SIZE = 7
|
12
|
+
SCRIPT_ERR_SIG_COUNT = 8
|
13
|
+
SCRIPT_ERR_PUBKEY_COUNT = 9
|
14
|
+
|
15
|
+
# Failed verify operations
|
16
|
+
SCRIPT_ERR_VERIFY = 10
|
17
|
+
SCRIPT_ERR_EQUALVERIFY = 11
|
18
|
+
SCRIPT_ERR_CHECKMULTISIGVERIFY = 12
|
19
|
+
SCRIPT_ERR_CHECKSIGVERIFY = 13
|
20
|
+
SCRIPT_ERR_NUMEQUALVERIFY = 14
|
21
|
+
|
22
|
+
# Logical/Format/Canonical errors
|
23
|
+
SCRIPT_ERR_BAD_OPCODE = 15
|
24
|
+
SCRIPT_ERR_DISABLED_OPCODE = 16
|
25
|
+
SCRIPT_ERR_INVALID_STACK_OPERATION = 17
|
26
|
+
SCRIPT_ERR_INVALID_ALTSTACK_OPERATION = 18
|
27
|
+
SCRIPT_ERR_UNBALANCED_CONDITIONAL = 19
|
28
|
+
|
29
|
+
# OP_CHECKLOCKTIMEVERIFY
|
30
|
+
SCRIPT_ERR_NEGATIVE_LOCKTIME = 20
|
31
|
+
SCRIPT_ERR_UNSATISFIED_LOCKTIME = 21
|
32
|
+
|
33
|
+
# BIP62
|
34
|
+
SCRIPT_ERR_SIG_HASHTYPE = 22
|
35
|
+
SCRIPT_ERR_SIG_DER = 23
|
36
|
+
SCRIPT_ERR_MINIMALDATA = 24
|
37
|
+
SCRIPT_ERR_SIG_PUSHONLY = 25
|
38
|
+
SCRIPT_ERR_SIG_HIGH_S = 26
|
39
|
+
SCRIPT_ERR_SIG_NULLDUMMY = 27
|
40
|
+
SCRIPT_ERR_PUBKEYTYPE = 28
|
41
|
+
SCRIPT_ERR_CLEANSTACK = 29
|
42
|
+
|
43
|
+
# softfork safeness
|
44
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 30
|
45
|
+
|
46
|
+
class ScriptError
|
47
|
+
attr_reader :code
|
48
|
+
attr_reader :message
|
49
|
+
attr_reader :description
|
50
|
+
|
51
|
+
def initialize(code, message = nil)
|
52
|
+
@code = code
|
53
|
+
@message = message
|
54
|
+
end
|
55
|
+
|
56
|
+
def code_name
|
57
|
+
self.class.constants.find{|n| self.class.const_get(n) == @code }
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
"#<#{self.class}:#{code} #{description} (#{code_name})>"
|
62
|
+
end
|
63
|
+
|
64
|
+
def description
|
65
|
+
builtin_description + (@message ? ". #{@message}." : "")
|
66
|
+
end
|
67
|
+
|
68
|
+
def builtin_description
|
69
|
+
case @code
|
70
|
+
when SCRIPT_ERR_OK
|
71
|
+
return "No error"
|
72
|
+
when SCRIPT_ERR_EVAL_FALSE
|
73
|
+
return "Script evaluated without error but finished with a false/empty top stack element"
|
74
|
+
when SCRIPT_ERR_VERIFY
|
75
|
+
return "Script failed an OP_VERIFY operation"
|
76
|
+
when SCRIPT_ERR_EQUALVERIFY
|
77
|
+
return "Script failed an OP_EQUALVERIFY operation"
|
78
|
+
when SCRIPT_ERR_CHECKMULTISIGVERIFY
|
79
|
+
return "Script failed an OP_CHECKMULTISIGVERIFY operation"
|
80
|
+
when SCRIPT_ERR_CHECKSIGVERIFY
|
81
|
+
return "Script failed an OP_CHECKSIGVERIFY operation"
|
82
|
+
when SCRIPT_ERR_NUMEQUALVERIFY
|
83
|
+
return "Script failed an OP_NUMEQUALVERIFY operation"
|
84
|
+
when SCRIPT_ERR_SCRIPT_SIZE
|
85
|
+
return "Script is too big"
|
86
|
+
when SCRIPT_ERR_PUSH_SIZE
|
87
|
+
return "Push value size limit exceeded"
|
88
|
+
when SCRIPT_ERR_OP_COUNT
|
89
|
+
return "Operation limit exceeded"
|
90
|
+
when SCRIPT_ERR_STACK_SIZE
|
91
|
+
return "Stack size limit exceeded"
|
92
|
+
when SCRIPT_ERR_SIG_COUNT
|
93
|
+
return "Signature count negative or greater than pubkey count"
|
94
|
+
when SCRIPT_ERR_PUBKEY_COUNT
|
95
|
+
return "Pubkey count negative or limit exceeded"
|
96
|
+
when SCRIPT_ERR_BAD_OPCODE
|
97
|
+
return "Opcode missing or not understood"
|
98
|
+
when SCRIPT_ERR_DISABLED_OPCODE
|
99
|
+
return "Attempted to use a disabled opcode"
|
100
|
+
when SCRIPT_ERR_INVALID_STACK_OPERATION
|
101
|
+
return "Operation not valid with the current stack size"
|
102
|
+
when SCRIPT_ERR_INVALID_ALTSTACK_OPERATION
|
103
|
+
return "Operation not valid with the current altstack size"
|
104
|
+
when SCRIPT_ERR_OP_RETURN
|
105
|
+
return "OP_RETURN was encountered"
|
106
|
+
when SCRIPT_ERR_UNBALANCED_CONDITIONAL
|
107
|
+
return "Invalid OP_IF construction"
|
108
|
+
when SCRIPT_ERR_NEGATIVE_LOCKTIME
|
109
|
+
return "Negative locktime"
|
110
|
+
when SCRIPT_ERR_UNSATISFIED_LOCKTIME
|
111
|
+
return "Locktime requirement not satisfied"
|
112
|
+
when SCRIPT_ERR_SIG_HASHTYPE
|
113
|
+
return "Signature hash type missing or not understood"
|
114
|
+
when SCRIPT_ERR_SIG_DER
|
115
|
+
return "Non-canonical DER signature"
|
116
|
+
when SCRIPT_ERR_MINIMALDATA
|
117
|
+
return "Data push larger than necessary"
|
118
|
+
when SCRIPT_ERR_SIG_PUSHONLY
|
119
|
+
return "Only non-push operators allowed in signatures"
|
120
|
+
when SCRIPT_ERR_SIG_HIGH_S
|
121
|
+
return "Non-canonical signature: S value is unnecessarily high"
|
122
|
+
when SCRIPT_ERR_SIG_NULLDUMMY
|
123
|
+
return "Dummy CHECKMULTISIG argument must be zero"
|
124
|
+
when SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS
|
125
|
+
return "NOPx reserved for soft-fork upgrades"
|
126
|
+
when SCRIPT_ERR_PUBKEYTYPE
|
127
|
+
return "Public key is neither compressed or uncompressed"
|
128
|
+
when SCRIPT_ERR_UNKNOWN_ERROR
|
129
|
+
when SCRIPT_ERR_ERROR_COUNT
|
130
|
+
end
|
131
|
+
"unknown error"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if $0 == __FILE__
|
137
|
+
puts BTC::ScriptError.new(BTC::ScriptError::SCRIPT_ERR_SIG_HIGH_S).inspect
|
138
|
+
end
|
139
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module BTC
|
2
|
+
module ScriptFlags
|
3
|
+
SCRIPT_VERIFY_NONE = 0
|
4
|
+
|
5
|
+
# Evaluate P2SH subscripts (softfork safe, BIP16).
|
6
|
+
SCRIPT_VERIFY_P2SH = (1 << 0)
|
7
|
+
|
8
|
+
# Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
9
|
+
# Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
10
|
+
# (softfork safe, but not used or intended as a consensus rule).
|
11
|
+
SCRIPT_VERIFY_STRICTENC = (1 << 1)
|
12
|
+
|
13
|
+
# Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
|
14
|
+
SCRIPT_VERIFY_DERSIG = (1 << 2)
|
15
|
+
|
16
|
+
# Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
17
|
+
# (softfork safe, BIP62 rule 5).
|
18
|
+
SCRIPT_VERIFY_LOW_S = (1 << 3)
|
19
|
+
|
20
|
+
# verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7).
|
21
|
+
SCRIPT_VERIFY_NULLDUMMY = (1 << 4)
|
22
|
+
|
23
|
+
# Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
|
24
|
+
SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5)
|
25
|
+
|
26
|
+
# Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
27
|
+
# pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
28
|
+
# any other push causes the script to fail (BIP62 rule 3).
|
29
|
+
# In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
30
|
+
# (softfork safe)
|
31
|
+
SCRIPT_VERIFY_MINIMALDATA = (1 << 6)
|
32
|
+
|
33
|
+
# Discourage use of NOPs reserved for upgrades (NOP1-10)
|
34
|
+
#
|
35
|
+
# Provided so that nodes can avoid accepting or mining transactions
|
36
|
+
# containing executed NOP's whose meaning may change after a soft-fork,
|
37
|
+
# thus rendering the script invalid; with this flag set executing
|
38
|
+
# discouraged NOPs fails the script. This verification flag will never be
|
39
|
+
# a mandatory flag applied to scripts in a block. NOPs that are not
|
40
|
+
# executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
41
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7)
|
42
|
+
|
43
|
+
# Require that only a single stack element remains after evaluation. This changes the success criterion from
|
44
|
+
# "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
45
|
+
# "Exactly one stack element must remain, and when interpreted as a boolean, it must be true".
|
46
|
+
# (softfork safe, BIP62 rule 6)
|
47
|
+
# Note: CLEANSTACK should never be used without P2SH.
|
48
|
+
SCRIPT_VERIFY_CLEANSTACK = (1 << 8)
|
49
|
+
|
50
|
+
# Verify CHECKLOCKTIMEVERIFY
|
51
|
+
#
|
52
|
+
# See BIP65 for details.
|
53
|
+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,886 @@
|
|
1
|
+
if $0 == __FILE__
|
2
|
+
require 'btcruby'
|
3
|
+
require_relative 'script_flags.rb'
|
4
|
+
require_relative 'script_number.rb'
|
5
|
+
end
|
6
|
+
|
7
|
+
module BTC
|
8
|
+
|
9
|
+
MAX_STACK_SIZE = 1000
|
10
|
+
|
11
|
+
# Script is a stack machine (like Forth) that evaluates a predicate
|
12
|
+
# returning a bool indicating valid or not. There are no loops.
|
13
|
+
class ScriptInterpreter
|
14
|
+
include ScriptFlags
|
15
|
+
|
16
|
+
attr_accessor :flags
|
17
|
+
attr_accessor :signature_checker
|
18
|
+
attr_accessor :stack
|
19
|
+
attr_accessor :altstack
|
20
|
+
attr_accessor :error # ScriptError instance
|
21
|
+
|
22
|
+
# Instantiates interpreter with validation flags and an optional checker
|
23
|
+
# (required if the scripts use signature-checking opcodes).
|
24
|
+
# Checker can be transaction checker or block checker
|
25
|
+
def initialize(flags: SCRIPT_VERIFY_NONE,
|
26
|
+
signature_checker: nil,
|
27
|
+
raise_on_failure: false,
|
28
|
+
max_pushdata_size: MAX_SCRIPT_ELEMENT_SIZE,
|
29
|
+
max_op_count: MAX_OPS_PER_SCRIPT,
|
30
|
+
max_stack_size: MAX_STACK_SIZE,
|
31
|
+
integer_max_size: 4,
|
32
|
+
locktime_max_size: 5)
|
33
|
+
@flags = flags
|
34
|
+
@signature_checker = signature_checker
|
35
|
+
@raise_on_failure = raise_on_failure
|
36
|
+
@max_pushdata_size = max_pushdata_size
|
37
|
+
@max_op_count = max_op_count
|
38
|
+
@max_stack_size = max_stack_size
|
39
|
+
@integer_max_size = integer_max_size
|
40
|
+
@locktime_max_size = locktime_max_size
|
41
|
+
|
42
|
+
@stack = []
|
43
|
+
@altstack = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if succeeded or false in case of failure.
|
47
|
+
# If fails, sets the error attribute.
|
48
|
+
def verify_script(signature_script: nil, output_script: nil)
|
49
|
+
|
50
|
+
if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !signature_script.data_only?
|
51
|
+
return set_error(SCRIPT_ERR_SIG_PUSHONLY)
|
52
|
+
end
|
53
|
+
|
54
|
+
if !run_script(signature_script)
|
55
|
+
# error is set in run_script
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
stack_copy = if flag?(SCRIPT_VERIFY_P2SH)
|
60
|
+
@stack.dup
|
61
|
+
end
|
62
|
+
|
63
|
+
if !run_script(output_script)
|
64
|
+
# error is set in run_script
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
|
68
|
+
if @stack.empty?
|
69
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE)
|
70
|
+
end
|
71
|
+
|
72
|
+
if cast_to_bool(@stack.last) == false
|
73
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Additional validation for pay-to-script-hash (P2SH) transactions:
|
77
|
+
if flag?(SCRIPT_VERIFY_P2SH) && output_script.p2sh?
|
78
|
+
|
79
|
+
# scriptSig must be literals-only or validation fails
|
80
|
+
if !signature_script.data_only?
|
81
|
+
return set_error(SCRIPT_ERR_SIG_PUSHONLY)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Restore stack.
|
85
|
+
@stack = stack_copy
|
86
|
+
|
87
|
+
# stack cannot be empty here, because if it was the
|
88
|
+
# P2SH HASH <> EQUAL scriptPubKey would be evaluated with
|
89
|
+
# an empty stack and the EvalScript above would return false.
|
90
|
+
raise "Stack cannot be empty" if @stack.empty?
|
91
|
+
|
92
|
+
serialized_redeem_script = stack_pop
|
93
|
+
begin
|
94
|
+
redeem_script = BTC::Script.new(data: serialized_redeem_script)
|
95
|
+
rescue => e
|
96
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
|
97
|
+
end
|
98
|
+
|
99
|
+
if !run_script(redeem_script)
|
100
|
+
# error is set in run_script
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
|
104
|
+
if @stack.empty?
|
105
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE)
|
106
|
+
end
|
107
|
+
|
108
|
+
if cast_to_bool(@stack.last) == false
|
109
|
+
return set_error(SCRIPT_ERR_EVAL_FALSE)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# The CLEANSTACK check is only performed after potential P2SH evaluation,
|
114
|
+
# as the non-P2SH evaluation of a P2SH script will obviously not result in
|
115
|
+
# a clean stack (the P2SH inputs remain).
|
116
|
+
if flag?(SCRIPT_VERIFY_CLEANSTACK)
|
117
|
+
# Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK
|
118
|
+
# would be possible, which is not a softfork (and P2SH should be one).
|
119
|
+
if !flag?(SCRIPT_VERIFY_P2SH)
|
120
|
+
raise ArgumentError, "CLEANSTACK without P2SH is disallowed"
|
121
|
+
end
|
122
|
+
if @stack.size != 1
|
123
|
+
return set_error(SCRIPT_ERR_CLEANSTACK, "Stack must be clean (should contain one item 'true')")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
return true
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
# Returns true if succeeded or false in case of failure.
|
139
|
+
# If fails, sets the error attribute.
|
140
|
+
# Used internally in `verify_script` and also in unit tests.
|
141
|
+
def run_script(script)
|
142
|
+
|
143
|
+
number_zero = ScriptNumber.new(integer: 0)
|
144
|
+
number_one = ScriptNumber.new(integer: 1)
|
145
|
+
zero_value = "".b
|
146
|
+
false_value = "".b
|
147
|
+
true_value = "\x01".b
|
148
|
+
|
149
|
+
opcount = 0
|
150
|
+
require_minimal = flag?(SCRIPT_VERIFY_MINIMALDATA)
|
151
|
+
condition_flags = []
|
152
|
+
index_after_codeseparator = 0
|
153
|
+
script.chunks.each_with_index do |chunk, chunk_index|
|
154
|
+
|
155
|
+
opcode = chunk.opcode
|
156
|
+
should_execute = !condition_flags.include?(false)
|
157
|
+
|
158
|
+
if chunk.pushdata? && chunk.pushdata.bytesize > @max_pushdata_size
|
159
|
+
return set_error(SCRIPT_ERR_PUSH_SIZE)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Note how OP_RESERVED does not count towards the opcode limit.
|
163
|
+
if opcode > OP_16
|
164
|
+
if (opcount += 1) > @max_op_count
|
165
|
+
return set_error(SCRIPT_ERR_OP_COUNT)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if opcode == OP_CAT ||
|
170
|
+
opcode == OP_SUBSTR ||
|
171
|
+
opcode == OP_LEFT ||
|
172
|
+
opcode == OP_RIGHT ||
|
173
|
+
opcode == OP_INVERT ||
|
174
|
+
opcode == OP_AND ||
|
175
|
+
opcode == OP_OR ||
|
176
|
+
opcode == OP_XOR ||
|
177
|
+
opcode == OP_2MUL ||
|
178
|
+
opcode == OP_2DIV ||
|
179
|
+
opcode == OP_MUL ||
|
180
|
+
opcode == OP_DIV ||
|
181
|
+
opcode == OP_MOD ||
|
182
|
+
opcode == OP_LSHIFT ||
|
183
|
+
opcode == OP_RSHIFT
|
184
|
+
|
185
|
+
return set_error(SCRIPT_ERR_DISABLED_OPCODE)
|
186
|
+
end
|
187
|
+
|
188
|
+
if should_execute && 0 <= opcode && opcode <= OP_PUSHDATA4
|
189
|
+
# Pushdata (including OP_0).
|
190
|
+
if require_minimal && !chunk.canonical?
|
191
|
+
return set_error(SCRIPT_ERR_MINIMALDATA)
|
192
|
+
end
|
193
|
+
stack_push(chunk.pushdata)
|
194
|
+
elsif should_execute || (OP_IF <= opcode && opcode <= OP_ENDIF)
|
195
|
+
|
196
|
+
case opcode
|
197
|
+
when OP_1NEGATE, OP_1..OP_16
|
198
|
+
# ( -- value)
|
199
|
+
num = ScriptNumber.new(integer: opcode - (OP_1 - 1))
|
200
|
+
stack_push(num.data)
|
201
|
+
# The result of these opcodes should always be the minimal way to push the data
|
202
|
+
# they push, so no need for a CheckMinimalPush here.
|
203
|
+
|
204
|
+
|
205
|
+
# Control Operators
|
206
|
+
# -----------------
|
207
|
+
|
208
|
+
when OP_NOP
|
209
|
+
# nothing
|
210
|
+
|
211
|
+
when OP_CHECKLOCKTIMEVERIFY
|
212
|
+
if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
|
213
|
+
# not enabled; treat as a NOP2
|
214
|
+
if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
215
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
|
216
|
+
end
|
217
|
+
break
|
218
|
+
end
|
219
|
+
|
220
|
+
if @stack.size < 1
|
221
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Note that elsewhere numeric opcodes are limited to
|
225
|
+
# operands in the range -2**31+1 to 2**31-1, however it is
|
226
|
+
# legal for opcodes to produce results exceeding that
|
227
|
+
# range. This limitation is implemented by CScriptNum's
|
228
|
+
# default 4-byte limit.
|
229
|
+
#
|
230
|
+
# If we kept to that limit we'd have a year 2038 problem,
|
231
|
+
# even though the nLockTime field in transactions
|
232
|
+
# themselves is uint32 which only becomes meaningless
|
233
|
+
# after the year 2106.
|
234
|
+
#
|
235
|
+
# Thus as a special case we tell CScriptNum to accept up
|
236
|
+
# to 5-byte bignums, which are good until 2**39-1, well
|
237
|
+
# beyond the 2**32-1 limit of the nLockTime field itself.
|
238
|
+
locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
|
239
|
+
|
240
|
+
# In the rare event that the argument may be < 0 due to
|
241
|
+
# some arithmetic being done first, you can always use
|
242
|
+
# 0 MAX CHECKLOCKTIMEVERIFY.
|
243
|
+
if locktime < 0
|
244
|
+
return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Actually compare the specified lock time with the transaction.
|
248
|
+
if !signature_checker.check_lock_time(locktime)
|
249
|
+
return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
|
250
|
+
end
|
251
|
+
|
252
|
+
when OP_NOP1..OP_NOP10
|
253
|
+
if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
254
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
|
255
|
+
end
|
256
|
+
|
257
|
+
when OP_IF, OP_NOTIF
|
258
|
+
# <expression> if [statements] [else [statements]] endif
|
259
|
+
flag = false
|
260
|
+
if should_execute
|
261
|
+
if @stack.size < 1
|
262
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
|
263
|
+
end
|
264
|
+
flag = cast_to_bool(@stack.last)
|
265
|
+
if opcode == OP_NOTIF
|
266
|
+
flag = !flag
|
267
|
+
end
|
268
|
+
stack_pop
|
269
|
+
end
|
270
|
+
condition_flags.push(flag)
|
271
|
+
|
272
|
+
when OP_ELSE
|
273
|
+
if condition_flags.empty?
|
274
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
|
275
|
+
end
|
276
|
+
condition_flags[-1] = !condition_flags.last
|
277
|
+
|
278
|
+
when OP_ENDIF
|
279
|
+
if condition_flags.empty?
|
280
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
|
281
|
+
end
|
282
|
+
condition_flags.pop
|
283
|
+
|
284
|
+
when OP_VERIFY
|
285
|
+
# (true -- ) or
|
286
|
+
# (false -- false) and return
|
287
|
+
if @stack.size < 1
|
288
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
289
|
+
end
|
290
|
+
flag = cast_to_bool(@stack.last)
|
291
|
+
if flag
|
292
|
+
stack_pop
|
293
|
+
else
|
294
|
+
return set_error(SCRIPT_ERR_VERIFY)
|
295
|
+
end
|
296
|
+
|
297
|
+
when OP_RETURN
|
298
|
+
return set_error(SCRIPT_ERR_OP_RETURN)
|
299
|
+
|
300
|
+
|
301
|
+
# Stack Operations
|
302
|
+
# ----------------
|
303
|
+
|
304
|
+
when OP_TOALTSTACK
|
305
|
+
if @stack.size < 1
|
306
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
307
|
+
end
|
308
|
+
@altstack.push(stack_pop)
|
309
|
+
|
310
|
+
when OP_FROMALTSTACK
|
311
|
+
if @altstack.size < 1
|
312
|
+
return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION)
|
313
|
+
end
|
314
|
+
stack_push(@altstack.pop)
|
315
|
+
|
316
|
+
when OP_2DROP
|
317
|
+
# (x1 x2 -- )
|
318
|
+
if @stack.size < 2
|
319
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
320
|
+
end
|
321
|
+
stack_pop
|
322
|
+
stack_pop
|
323
|
+
|
324
|
+
when OP_2DUP
|
325
|
+
# (x1 x2 -- x1 x2 x1 x2)
|
326
|
+
if @stack.size < 2
|
327
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
328
|
+
end
|
329
|
+
a = @stack[-2]
|
330
|
+
b = @stack[-1]
|
331
|
+
stack_push(a)
|
332
|
+
stack_push(b)
|
333
|
+
|
334
|
+
when OP_3DUP
|
335
|
+
# (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
|
336
|
+
if @stack.size < 3
|
337
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
338
|
+
end
|
339
|
+
a = @stack[-3]
|
340
|
+
b = @stack[-2]
|
341
|
+
c = @stack[-1]
|
342
|
+
stack_push(a)
|
343
|
+
stack_push(b)
|
344
|
+
stack_push(c)
|
345
|
+
|
346
|
+
when OP_2OVER
|
347
|
+
# (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
|
348
|
+
if @stack.size < 4
|
349
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
350
|
+
end
|
351
|
+
a = @stack[-4]
|
352
|
+
b = @stack[-3]
|
353
|
+
stack_push(a)
|
354
|
+
stack_push(b)
|
355
|
+
|
356
|
+
when OP_2ROT
|
357
|
+
# (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
|
358
|
+
if @stack.size < 6
|
359
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
360
|
+
end
|
361
|
+
a = @stack[-6]
|
362
|
+
b = @stack[-5]
|
363
|
+
@stack[-6...-4] = []
|
364
|
+
stack_push(a)
|
365
|
+
stack_push(b)
|
366
|
+
|
367
|
+
when OP_2SWAP
|
368
|
+
# (x1 x2 x3 x4 -- x3 x4 x1 x2)
|
369
|
+
if @stack.size < 4
|
370
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
371
|
+
end
|
372
|
+
x1x2 = @stack[-4...-2]
|
373
|
+
@stack[-4...-2] = []
|
374
|
+
@stack += x1x2
|
375
|
+
|
376
|
+
when OP_IFDUP
|
377
|
+
# (x - 0 | x x)
|
378
|
+
if @stack.size < 1
|
379
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
380
|
+
end
|
381
|
+
item = @stack.last
|
382
|
+
if cast_to_bool(item)
|
383
|
+
stack_push(item)
|
384
|
+
end
|
385
|
+
|
386
|
+
when OP_DEPTH
|
387
|
+
# -- stacksize
|
388
|
+
sn = ScriptNumber.new(integer: @stack.size)
|
389
|
+
stack_push(sn.data)
|
390
|
+
|
391
|
+
when OP_DROP
|
392
|
+
# (x -- )
|
393
|
+
if @stack.size < 1
|
394
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
395
|
+
end
|
396
|
+
stack_pop
|
397
|
+
|
398
|
+
when OP_DUP
|
399
|
+
# (x -- x x)
|
400
|
+
if @stack.size < 1
|
401
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
402
|
+
end
|
403
|
+
stack_push(@stack.last)
|
404
|
+
|
405
|
+
when OP_NIP
|
406
|
+
# (x1 x2 -- x2)
|
407
|
+
if @stack.size < 2
|
408
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
409
|
+
end
|
410
|
+
@stack.delete_at(-2)
|
411
|
+
|
412
|
+
when OP_OVER
|
413
|
+
# (x1 x2 -- x1 x2 x1)
|
414
|
+
if @stack.size < 2
|
415
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
416
|
+
end
|
417
|
+
stack_push(@stack[-2])
|
418
|
+
|
419
|
+
when OP_PICK, OP_ROLL
|
420
|
+
# (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
|
421
|
+
# (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
|
422
|
+
if @stack.size < 2
|
423
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
424
|
+
end
|
425
|
+
n = cast_to_number(@stack.last).to_i
|
426
|
+
stack_pop
|
427
|
+
if n < 0 || n >= @stack.size
|
428
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
429
|
+
end
|
430
|
+
item = @stack[-n-1]
|
431
|
+
if opcode == OP_ROLL
|
432
|
+
@stack.delete_at(-n-1)
|
433
|
+
end
|
434
|
+
stack_push(item)
|
435
|
+
|
436
|
+
when OP_ROT
|
437
|
+
# (x1 x2 x3 -- x2 x3 x1)
|
438
|
+
# x2 x1 x3 after first swap
|
439
|
+
# x2 x3 x1 after second swap
|
440
|
+
if @stack.size < 3
|
441
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
442
|
+
end
|
443
|
+
x1 = @stack[-3]
|
444
|
+
@stack.delete_at(-3)
|
445
|
+
stack_push(x1)
|
446
|
+
|
447
|
+
when OP_SWAP
|
448
|
+
# (x1 x2 -- x2 x1)
|
449
|
+
if @stack.size < 2
|
450
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
451
|
+
end
|
452
|
+
x1 = @stack[-2]
|
453
|
+
@stack.delete_at(-2)
|
454
|
+
stack_push(x1)
|
455
|
+
|
456
|
+
when OP_TUCK
|
457
|
+
# (x1 x2 -- x2 x1 x2)
|
458
|
+
if @stack.size < 2
|
459
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
460
|
+
end
|
461
|
+
item = @stack[-1]
|
462
|
+
@stack.insert(-3, item) # -1 inserts in the end, -2 before the last item.
|
463
|
+
|
464
|
+
when OP_SIZE
|
465
|
+
# (in -- in size)
|
466
|
+
if @stack.size < 1
|
467
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
468
|
+
end
|
469
|
+
sn = ScriptNumber.new(integer: @stack.last.size)
|
470
|
+
stack_push(sn.data)
|
471
|
+
|
472
|
+
|
473
|
+
# Bitwise Logic
|
474
|
+
# -------------
|
475
|
+
|
476
|
+
when OP_EQUAL, OP_EQUALVERIFY
|
477
|
+
# there is no OP_NOTEQUAL, use OP_NUMNOTEQUAL instead
|
478
|
+
# (x1 x2 - bool)
|
479
|
+
if @stack.size < 2
|
480
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
481
|
+
end
|
482
|
+
a = @stack[-2]
|
483
|
+
b = @stack[-1]
|
484
|
+
equal = (a == b)
|
485
|
+
# OP_NOTEQUAL is disabled because it would be too easy to say
|
486
|
+
# something like n != 1 and have some wiseguy pass in 1 with extra
|
487
|
+
# zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
|
488
|
+
# if opcode == OP_NOTEQUAL
|
489
|
+
# equal = !equal
|
490
|
+
# end
|
491
|
+
stack_pop
|
492
|
+
stack_pop
|
493
|
+
stack_push(equal ? true_value : false_value)
|
494
|
+
if opcode == OP_EQUALVERIFY
|
495
|
+
if equal
|
496
|
+
stack_pop
|
497
|
+
else
|
498
|
+
return set_error(SCRIPT_ERR_EQUALVERIFY)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
|
503
|
+
# Numeric
|
504
|
+
# -------
|
505
|
+
|
506
|
+
when OP_1ADD,
|
507
|
+
OP_1SUB,
|
508
|
+
OP_NEGATE,
|
509
|
+
OP_ABS,
|
510
|
+
OP_NOT,
|
511
|
+
OP_0NOTEQUAL
|
512
|
+
# (in -- out)
|
513
|
+
if @stack.size < 1
|
514
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
515
|
+
end
|
516
|
+
|
517
|
+
bn = cast_to_number(@stack.last)
|
518
|
+
case opcode
|
519
|
+
when OP_1ADD
|
520
|
+
bn += 1
|
521
|
+
when OP_1SUB
|
522
|
+
bn -= 1
|
523
|
+
when OP_NEGATE
|
524
|
+
bn = -bn
|
525
|
+
when OP_ABS
|
526
|
+
bn = -bn if bn < 0
|
527
|
+
when OP_NOT
|
528
|
+
bn = ScriptNumber.new(boolean: (bn == 0))
|
529
|
+
when OP_0NOTEQUAL
|
530
|
+
bn = ScriptNumber.new(boolean: (bn != 0))
|
531
|
+
else
|
532
|
+
raise "invalid opcode"
|
533
|
+
end
|
534
|
+
stack_pop
|
535
|
+
stack_push(bn.data);
|
536
|
+
|
537
|
+
|
538
|
+
when OP_ADD, OP_SUB, OP_BOOLAND..OP_MAX
|
539
|
+
# (x1 x2 -- out)
|
540
|
+
if @stack.size < 2
|
541
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
542
|
+
end
|
543
|
+
|
544
|
+
bn1 = cast_to_number(@stack[-2])
|
545
|
+
bn2 = cast_to_number(@stack[-1])
|
546
|
+
bn = ScriptNumber.new(integer: 0)
|
547
|
+
|
548
|
+
case opcode
|
549
|
+
when OP_ADD
|
550
|
+
bn = bn1 + bn2
|
551
|
+
when OP_SUB
|
552
|
+
bn = bn1 - bn2
|
553
|
+
when OP_BOOLAND
|
554
|
+
bn = ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
|
555
|
+
when OP_BOOLOR
|
556
|
+
bn = ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
|
557
|
+
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
558
|
+
bn = ScriptNumber.new(boolean: (bn1 == bn2))
|
559
|
+
when OP_NUMNOTEQUAL
|
560
|
+
bn = ScriptNumber.new(boolean: (bn1 != bn2))
|
561
|
+
when OP_LESSTHAN
|
562
|
+
bn = ScriptNumber.new(boolean: (bn1 < bn2))
|
563
|
+
when OP_GREATERTHAN
|
564
|
+
bn = ScriptNumber.new(boolean: (bn1 > bn2))
|
565
|
+
when OP_LESSTHANOREQUAL
|
566
|
+
bn = ScriptNumber.new(boolean: (bn1 <= bn2))
|
567
|
+
when OP_GREATERTHANOREQUAL
|
568
|
+
bn = ScriptNumber.new(boolean: (bn1 >= bn2))
|
569
|
+
when OP_MIN
|
570
|
+
bn = ScriptNumber.new(boolean: (bn1 < bn2 ? bn1 : bn2))
|
571
|
+
when OP_MAX
|
572
|
+
bn = ScriptNumber.new(boolean: (bn1 > bn2 ? bn1 : bn2))
|
573
|
+
else
|
574
|
+
raise "Invalid opcode"
|
575
|
+
end
|
576
|
+
stack_pop
|
577
|
+
stack_pop
|
578
|
+
stack_push(bn.data)
|
579
|
+
|
580
|
+
if opcode == OP_NUMEQUALVERIFY
|
581
|
+
if cast_to_bool(@stack[-1])
|
582
|
+
stack_pop
|
583
|
+
else
|
584
|
+
return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
when OP_WITHIN
|
589
|
+
# (x min max -- out)
|
590
|
+
if @stack.size < 3
|
591
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
592
|
+
end
|
593
|
+
bn1 = cast_to_number(@stack[-3])
|
594
|
+
bn2 = cast_to_number(@stack[-2])
|
595
|
+
bn3 = cast_to_number(@stack[-1])
|
596
|
+
flag = ((bn2 <= bn1) && (bn1 < bn3))
|
597
|
+
stack_pop
|
598
|
+
stack_pop
|
599
|
+
stack_pop
|
600
|
+
stack_push(flag ? true_value : false_value)
|
601
|
+
|
602
|
+
# Crypto
|
603
|
+
# ------
|
604
|
+
|
605
|
+
when OP_RIPEMD160..OP_HASH256
|
606
|
+
# (in -- hash)
|
607
|
+
if @stack.size < 1
|
608
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
609
|
+
end
|
610
|
+
item = @stack[-1]
|
611
|
+
hash = case opcode
|
612
|
+
when OP_RIPEMD160
|
613
|
+
BTC.ripemd160(item)
|
614
|
+
when OP_SHA1
|
615
|
+
BTC.sha1(item)
|
616
|
+
when OP_SHA256
|
617
|
+
BTC.sha256(item)
|
618
|
+
when OP_HASH160
|
619
|
+
BTC.hash160(item)
|
620
|
+
when OP_HASH256
|
621
|
+
BTC.hash256(item)
|
622
|
+
end
|
623
|
+
stack_pop
|
624
|
+
stack_push(hash)
|
625
|
+
|
626
|
+
when OP_CODESEPARATOR
|
627
|
+
# Hash starts after the code separator
|
628
|
+
index_after_codeseparator = chunk_index + 1
|
629
|
+
|
630
|
+
when OP_CHECKSIG, OP_CHECKSIGVERIFY
|
631
|
+
# (sig pubkey -- bool)
|
632
|
+
if @stack.size < 2
|
633
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
634
|
+
end
|
635
|
+
|
636
|
+
sig = @stack[-2]
|
637
|
+
pubkey = @stack[-1]
|
638
|
+
|
639
|
+
# Subset of script starting at the most recent codeseparator
|
640
|
+
subscript = script.subscript(index_after_codeseparator..-1)
|
641
|
+
|
642
|
+
# Drop the signature, since there's no way for a signature to sign itself
|
643
|
+
# Consensus-critical code: must replace sig with minimal encoding.
|
644
|
+
subscript = subscript.find_and_delete(BTC::Script.new << sig)
|
645
|
+
|
646
|
+
if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
|
647
|
+
# error is set already
|
648
|
+
return false
|
649
|
+
end
|
650
|
+
|
651
|
+
success = signature_checker.check_signature(
|
652
|
+
script_signature: sig,
|
653
|
+
public_key: pubkey,
|
654
|
+
script: subscript
|
655
|
+
)
|
656
|
+
|
657
|
+
stack_pop
|
658
|
+
stack_pop
|
659
|
+
stack_push(success ? true_value : false_value)
|
660
|
+
|
661
|
+
if opcode == OP_CHECKSIGVERIFY
|
662
|
+
if success
|
663
|
+
stack_pop
|
664
|
+
else
|
665
|
+
return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
|
670
|
+
when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
671
|
+
# ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)
|
672
|
+
|
673
|
+
i = 1
|
674
|
+
if @stack.size < i
|
675
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
676
|
+
end
|
677
|
+
|
678
|
+
keys_count = cast_to_number(@stack[-i]).to_i
|
679
|
+
if keys_count < 0 || keys_count > 20
|
680
|
+
return set_error(SCRIPT_ERR_PUBKEY_COUNT)
|
681
|
+
end
|
682
|
+
opcount += keys_count
|
683
|
+
if opcount > @max_op_count
|
684
|
+
return set_error(SCRIPT_ERR_OP_COUNT)
|
685
|
+
end
|
686
|
+
ikey = (i+=1)
|
687
|
+
i += keys_count
|
688
|
+
if @stack.size < i
|
689
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
690
|
+
end
|
691
|
+
|
692
|
+
sigs_count = cast_to_number(@stack[-i]).to_i
|
693
|
+
if sigs_count < 0 || sigs_count > keys_count
|
694
|
+
return set_error(SCRIPT_ERR_SIG_COUNT)
|
695
|
+
end
|
696
|
+
|
697
|
+
isig = (i+=1)
|
698
|
+
i += sigs_count
|
699
|
+
|
700
|
+
if @stack.size < i
|
701
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
702
|
+
end
|
703
|
+
|
704
|
+
# Subset of script starting at the most recent codeseparator
|
705
|
+
|
706
|
+
# Subset of script starting at the most recent codeseparator
|
707
|
+
subscript = script.subscript(index_after_codeseparator..-1)
|
708
|
+
|
709
|
+
# Drop the signatures, since there's no way for a signature to sign itself
|
710
|
+
# Consensus-critical code: must replace sig with minimal encoding.
|
711
|
+
sigs_count.times do |k|
|
712
|
+
sig = @stack[-isig-k]
|
713
|
+
subscript = subscript.find_and_delete(BTC::Script.new << sig)
|
714
|
+
end
|
715
|
+
|
716
|
+
success = true
|
717
|
+
while success && sigs_count > 0
|
718
|
+
sig = @stack[-isig]
|
719
|
+
pubkey = @stack[-ikey]
|
720
|
+
# Note how this makes the exact order of pubkey/signature evaluation
|
721
|
+
# distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
|
722
|
+
# See the script_(in)valid tests for details.
|
723
|
+
if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
|
724
|
+
# error is set already
|
725
|
+
return false
|
726
|
+
end
|
727
|
+
|
728
|
+
# Check signature
|
729
|
+
ok = signature_checker.check_signature(
|
730
|
+
script_signature: sig,
|
731
|
+
public_key: pubkey,
|
732
|
+
script: subscript
|
733
|
+
)
|
734
|
+
if ok
|
735
|
+
isig += 1
|
736
|
+
sigs_count -= 1
|
737
|
+
end
|
738
|
+
ikey += 1
|
739
|
+
keys_count -= 1
|
740
|
+
|
741
|
+
# If there are more signatures left than keys left,
|
742
|
+
# then too many signatures have failed. Exit early,
|
743
|
+
# without checking any further signatures.
|
744
|
+
if sigs_count > keys_count
|
745
|
+
success = false
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
# Clean up stack of actual arguments
|
750
|
+
while (i-=1) > 0 # Bitcoin Core: while (i-- > 1)
|
751
|
+
stack_pop
|
752
|
+
end
|
753
|
+
|
754
|
+
# A bug causes CHECKMULTISIG to consume one extra argument
|
755
|
+
# whose contents were not checked in any way.
|
756
|
+
#
|
757
|
+
# Unfortunately this is a potential source of mutability,
|
758
|
+
# so optionally verify it is exactly equal to zero prior
|
759
|
+
# to removing it from the stack.
|
760
|
+
if @stack.size < 1
|
761
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
|
762
|
+
end
|
763
|
+
|
764
|
+
if flag?(SCRIPT_VERIFY_NULLDUMMY) && @stack[-1].size > 0
|
765
|
+
return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
|
766
|
+
end
|
767
|
+
stack_pop
|
768
|
+
stack_push(success ? true_value : false_value)
|
769
|
+
if opcode == OP_CHECKMULTISIGVERIFY
|
770
|
+
if success
|
771
|
+
stack_pop
|
772
|
+
else
|
773
|
+
return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
else # unknown opcode
|
778
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE)
|
779
|
+
|
780
|
+
end # case opcode
|
781
|
+
end # within IF scope
|
782
|
+
|
783
|
+
# Size limits
|
784
|
+
if @stack.size + @altstack.size > @max_stack_size
|
785
|
+
return set_error(SCRIPT_ERR_STACK_SIZE)
|
786
|
+
end
|
787
|
+
|
788
|
+
end # each chunk
|
789
|
+
|
790
|
+
if !condition_flags.empty?
|
791
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
|
792
|
+
end
|
793
|
+
|
794
|
+
return true
|
795
|
+
end # run_script
|
796
|
+
|
797
|
+
|
798
|
+
def stack_pop
|
799
|
+
r = @stack.pop
|
800
|
+
#puts "POPPED FROM STACK: #{@stack.map{|s|s.to_hex}.join(' ')}"
|
801
|
+
r
|
802
|
+
end
|
803
|
+
|
804
|
+
def stack_push(x)
|
805
|
+
@stack.push(x)
|
806
|
+
#puts "PUSHED TO STACK: #{@stack.map{|s|s.to_hex}.join(' ')}"
|
807
|
+
end
|
808
|
+
|
809
|
+
def check_signature_encoding(sig)
|
810
|
+
# Empty signature. Not strictly DER encoded, but allowed to provide a
|
811
|
+
# compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
812
|
+
if sig.size == 0
|
813
|
+
return true
|
814
|
+
end
|
815
|
+
if flag?(SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC) && !valid_signature_encoding(sig)
|
816
|
+
return set_error(SCRIPT_ERR_SIG_DER)
|
817
|
+
elsif flag?(SCRIPT_VERIFY_LOW_S) && !low_der_signature(sig)
|
818
|
+
return set_error(SCRIPT_ERR_SIG_HIGH_S)
|
819
|
+
elsif flag?(SCRIPT_VERIFY_STRICTENC) && !defined_hashtype_signature(sig)
|
820
|
+
return set_error(SCRIPT_ERR_SIG_HASHTYPE)
|
821
|
+
end
|
822
|
+
return true
|
823
|
+
end
|
824
|
+
|
825
|
+
def check_pubkey_encoding(pubkey)
|
826
|
+
if flag?(SCRIPT_VERIFY_STRICTENC) && !compressed_or_uncompressed_pubkey(pubkey)
|
827
|
+
return set_error(SCRIPT_ERR_PUBKEYTYPE)
|
828
|
+
end
|
829
|
+
return true
|
830
|
+
end
|
831
|
+
|
832
|
+
def valid_signature_encoding(sig)
|
833
|
+
BTC::Key.validate_script_signature(sig, verify_lower_s: false, verify_hashtype: false)
|
834
|
+
end
|
835
|
+
|
836
|
+
def low_der_signature(sig)
|
837
|
+
BTC::Key.validate_script_signature(sig, verify_lower_s: true, verify_hashtype: false)
|
838
|
+
end
|
839
|
+
|
840
|
+
def defined_hashtype_signature(sig)
|
841
|
+
BTC::Key.validate_script_signature(sig, verify_lower_s: false, verify_hashtype: true)
|
842
|
+
end
|
843
|
+
|
844
|
+
def compressed_or_uncompressed_pubkey(pubkey)
|
845
|
+
BTC::Key.validate_public_key(pubkey)
|
846
|
+
end
|
847
|
+
|
848
|
+
# If multiple flags are provided, returns true if any of them are present
|
849
|
+
def flag?(flags)
|
850
|
+
(@flags & flags) != 0
|
851
|
+
end
|
852
|
+
|
853
|
+
def cast_to_number(data,
|
854
|
+
require_minimal: flag?(SCRIPT_VERIFY_MINIMALDATA),
|
855
|
+
max_size: @integer_max_size)
|
856
|
+
ScriptNumber.new(data: data, require_minimal: require_minimal, max_size: max_size)
|
857
|
+
end
|
858
|
+
|
859
|
+
def cast_to_bool(data)
|
860
|
+
data.bytes.each_with_index do |byte, i|
|
861
|
+
if byte != 0
|
862
|
+
# Can be negative zero
|
863
|
+
if byte == 0x80 && i == (data.bytesize - 1)
|
864
|
+
return false
|
865
|
+
end
|
866
|
+
return true
|
867
|
+
end
|
868
|
+
end
|
869
|
+
return false
|
870
|
+
end
|
871
|
+
|
872
|
+
def set_error(code, message = nil)
|
873
|
+
error = ScriptError.new(code, message)
|
874
|
+
raise "#{error.description} (#{code.inspect})" if @raise_on_failure
|
875
|
+
@error = error
|
876
|
+
false
|
877
|
+
end
|
878
|
+
|
879
|
+
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
if $0 == __FILE__
|
884
|
+
require 'btcruby'
|
885
|
+
|
886
|
+
end
|