1
1
use crate :: builder:: { Block , StructuredScript } ;
2
+ use crate :: chunker:: Chunk ;
2
3
use bitcoin:: blockdata:: opcodes:: Opcode ;
3
4
use bitcoin:: blockdata:: script:: { read_scriptint, Instruction } ;
4
5
use bitcoin:: opcodes:: all:: * ;
5
6
use bitcoin:: script:: PushBytes ;
7
+ use bitcoin:: ScriptBuf ;
8
+ use script_macro:: script;
6
9
use std:: borrow:: BorrowMut ;
7
- use std:: cmp:: min;
10
+ use std:: cmp:: { max , min} ;
8
11
use std:: panic;
9
12
10
13
#[ derive( Debug , Clone , Default , PartialEq ) ]
@@ -15,6 +18,25 @@ pub struct StackStatus {
15
18
pub altstack_changed : i32 ,
16
19
}
17
20
21
+ impl StackStatus {
22
+ pub fn total_stack ( & self ) -> i32 {
23
+ self . stack_changed + self . altstack_changed
24
+ }
25
+
26
+ pub fn is_valid_final_state_without_inputs ( & self ) -> bool {
27
+ self . stack_changed == 1
28
+ && self . altstack_changed == 0
29
+ && self . deepest_altstack_accessed == 0
30
+ && self . deepest_stack_accessed == 0
31
+ }
32
+
33
+ pub fn is_valid_final_state_with_inputs ( & self ) -> bool {
34
+ self . stack_changed == 1
35
+ && self . altstack_changed == 0
36
+ && self . deepest_altstack_accessed == 0
37
+ }
38
+ }
39
+
18
40
#[ derive( Debug , Clone ) ]
19
41
enum IfStackEle {
20
42
IfFlow ( StackStatus ) ,
@@ -24,11 +46,13 @@ enum IfStackEle {
24
46
25
47
#[ derive( Debug , Clone ) ]
26
48
pub struct StackAnalyzer {
49
+ debug_script : StructuredScript ,
50
+ debug_position : usize ,
27
51
stack_status : StackStatus ,
28
52
// if_stack should be empty after analyzing
29
53
if_stack : Vec < IfStackEle > ,
30
54
// last constant? for handling op_roll and op_pick
31
- last_constant : Option < i64 > ,
55
+ pub last_constant : Option < i64 > ,
32
56
}
33
57
34
58
impl Default for StackAnalyzer {
@@ -40,30 +64,102 @@ impl Default for StackAnalyzer {
40
64
impl StackAnalyzer {
41
65
pub fn new ( ) -> Self {
42
66
StackAnalyzer {
67
+ debug_script : StructuredScript :: new ( "" ) ,
68
+ debug_position : 0 ,
43
69
stack_status : StackStatus :: default ( ) ,
44
70
if_stack : vec ! [ ] ,
45
71
last_constant : None ,
46
72
}
47
73
}
48
74
49
- pub fn analyze_blocks ( & mut self , blocks : & mut Vec < Box < StructuredScript > > ) -> StackStatus {
50
- // println!("===============================");
51
- for block in blocks {
52
- // Maybe remove this clone?
53
- self . handle_sub_script ( block. get_stack ( ) ) ;
75
+ pub fn with ( start_stack : usize , start_altstack : usize , last_constant : Option < i64 > ) -> Self {
76
+ StackAnalyzer {
77
+ debug_script : StructuredScript :: new ( "" ) ,
78
+ debug_position : 0 ,
79
+ stack_status : StackStatus {
80
+ deepest_stack_accessed : 0 ,
81
+ stack_changed : start_stack as i32 ,
82
+ deepest_altstack_accessed : 0 ,
83
+ altstack_changed : start_altstack as i32 ,
84
+ } ,
85
+ if_stack : vec ! [ ] ,
86
+ last_constant,
87
+ }
88
+ }
89
+
90
+ pub fn total_stack_change ( & self ) -> i32 {
91
+ self . stack_status . altstack_changed + self . stack_status . stack_changed
92
+ }
93
+
94
+ pub fn reset ( & mut self ) {
95
+ self . debug_script = StructuredScript :: new ( "" ) ;
96
+ self . debug_position = 0 ;
97
+ self . if_stack = vec ! [ ] ;
98
+ self . stack_status = StackStatus :: default ( ) ;
99
+ }
100
+
101
+ pub fn analyze_blocks ( & mut self , scripts : & Vec < Box < StructuredScript > > ) {
102
+ for script in scripts {
103
+ self . debug_script = * script. clone ( ) ;
104
+ self . debug_position = 0 ;
105
+ match script. stack_hint ( ) {
106
+ Some ( stack_hint) => {
107
+ self . debug_position += script. len ( ) ;
108
+ self . stack_change ( stack_hint)
109
+ }
110
+ None => self . merge_script ( script) ,
111
+ } ;
112
+ }
113
+ }
114
+ pub fn analyze_blocks_status ( & mut self , scripts : & Vec < Box < StructuredScript > > ) -> StackStatus {
115
+ for script in scripts {
116
+ self . debug_script = * script. clone ( ) ;
117
+ self . debug_position = 0 ;
118
+ match script. stack_hint ( ) {
119
+ Some ( stack_hint) => {
120
+ self . debug_position += script. len ( ) ;
121
+ self . stack_change ( stack_hint)
122
+ }
123
+ None => self . merge_script ( script) ,
124
+ } ;
54
125
}
55
126
self . get_status ( )
56
127
}
57
128
58
- pub fn analyze ( & mut self , builder : & mut StructuredScript ) -> StackStatus {
59
- for block in builder. blocks . iter_mut ( ) {
129
+ pub fn analyze_status ( & mut self , script : & StructuredScript ) -> StackStatus {
130
+ self . debug_script = script. clone ( ) ;
131
+ self . debug_position = 0 ;
132
+ match script. stack_hint ( ) {
133
+ Some ( stack_hint) => self . stack_change ( stack_hint) ,
134
+ None => self . merge_script ( script) ,
135
+ } ;
136
+ self . get_status ( )
137
+ }
138
+
139
+ pub fn analyze ( & mut self , script : & StructuredScript ) {
140
+ self . debug_script = script. clone ( ) ;
141
+ self . debug_position = 0 ;
142
+ match script. stack_hint ( ) {
143
+ Some ( stack_hint) => self . stack_change ( stack_hint) ,
144
+ None => self . merge_script ( script) ,
145
+ } ;
146
+ }
147
+
148
+ pub fn merge_script ( & mut self , builder : & StructuredScript ) {
149
+ for block in builder. blocks . iter ( ) {
60
150
match block {
61
151
Block :: Call ( id) => {
62
152
let called_script = builder
63
153
. script_map
64
- . get_mut ( id)
154
+ . get ( id)
65
155
. expect ( "Missing entry for a called script" ) ;
66
- self . handle_sub_script ( called_script. get_stack ( ) ) ;
156
+ match called_script. stack_hint ( ) {
157
+ Some ( stack_hint) => {
158
+ self . debug_position += called_script. len ( ) ;
159
+ self . stack_change ( stack_hint)
160
+ }
161
+ None => self . merge_script ( called_script) ,
162
+ } ;
67
163
}
68
164
Block :: Script ( block_script) => {
69
165
for instruct in block_script. instructions ( ) {
@@ -84,10 +180,10 @@ impl StackAnalyzer {
84
180
}
85
181
}
86
182
}
87
- self . stack_status . clone ( )
88
183
}
89
184
90
185
pub fn handle_push_slice ( & mut self , bytes : & PushBytes ) {
186
+ self . debug_position += bytes. len ( ) + 1 ;
91
187
if let Ok ( x) = read_scriptint ( bytes. as_bytes ( ) ) {
92
188
// if i64(data) < 1000, last_constant is true
93
189
if ( 0 ..=1000 ) . contains ( & x) {
@@ -120,13 +216,29 @@ impl StackAnalyzer {
120
216
}
121
217
}
122
218
219
+ pub fn plain_altstack_status ( x : i32 , y : i32 ) -> StackStatus {
220
+ StackStatus {
221
+ deepest_stack_accessed : 0 ,
222
+ stack_changed : 0 ,
223
+ deepest_altstack_accessed : x,
224
+ altstack_changed : y,
225
+ }
226
+ }
227
+
123
228
pub fn handle_opcode ( & mut self , opcode : Opcode ) {
124
229
// handle if/else flow
125
230
match opcode {
126
231
OP_IF | OP_NOTIF => {
127
232
self . stack_change ( Self :: opcode_stack_table ( & opcode) ) ;
128
233
self . if_stack . push ( IfStackEle :: IfFlow ( Default :: default ( ) ) ) ;
129
234
}
235
+ OP_RESERVED => {
236
+ panic ! (
237
+ "found DEBUG in {:?}\n entire builder: {:?}" ,
238
+ self . debug_script. debug_info( self . debug_position) ,
239
+ self . debug_script
240
+ )
241
+ }
130
242
OP_ELSE => match self . if_stack . pop ( ) . unwrap ( ) {
131
243
IfStackEle :: IfFlow ( i) => {
132
244
self . if_stack
@@ -136,38 +248,47 @@ impl StackAnalyzer {
136
248
panic ! ( "shouldn't happend" )
137
249
}
138
250
} ,
139
- OP_ENDIF => match self . if_stack . pop ( ) . unwrap ( ) {
140
- IfStackEle :: IfFlow ( stack_status) => {
141
- assert_eq ! (
251
+ OP_ENDIF => {
252
+ match self . if_stack . pop ( ) . unwrap ( ) {
253
+ IfStackEle :: IfFlow ( stack_status) => {
254
+ assert_eq ! (
142
255
stack_status. stack_changed, 0 ,
143
- "only_if_flow shouldn't change stack status {:?}" ,
144
- stack_status
256
+ "only_if_flow shouldn't change stack status {:?}\n \t at pos {:?} \n \t in {:?} " ,
257
+ stack_status, self . debug_position , self . debug_script . debug_info ( self . debug_position + 1 )
145
258
) ;
146
- assert_eq ! (
259
+ assert_eq ! (
147
260
stack_status. altstack_changed, 0 ,
148
- "only_if_flow shouldn't change alt stack status {:?}," ,
149
- stack_status
150
- ) ;
151
- self . stack_change ( stack_status) ;
152
- }
153
- IfStackEle :: ElseFlow ( ( stack_status1, stack_status2) ) => {
154
- assert_eq ! (
155
- stack_status1. stack_changed, stack_status2. stack_changed,
156
- "if_flow and else_flow should change stack in the same way"
261
+ "only_if_flow shouldn't change altstack status {:?}\n \t at pos {:?}\n \t in {:?}" ,
262
+ stack_status, self . debug_position, self . debug_script. debug_info( self . debug_position + 1 )
157
263
) ;
158
- assert_eq ! (
159
- stack_status1. altstack_changed, stack_status2. altstack_changed,
160
- "if_flow and else_flow should change alt stack in the same way"
161
- ) ;
162
- self . stack_change ( Self :: min_status ( stack_status1, stack_status2) ) ;
264
+ self . stack_change ( stack_status) ;
265
+ }
266
+ IfStackEle :: ElseFlow ( ( stack_status1, stack_status2) ) => {
267
+ assert_eq ! (
268
+ stack_status1. stack_changed,
269
+ stack_status2. stack_changed,
270
+ "if_flow and else_flow should change stack in the same way in {:?}" ,
271
+ self . debug_script. debug_info( self . debug_position + 1 )
272
+ ) ;
273
+ assert_eq ! (
274
+ stack_status1. altstack_changed,
275
+ stack_status2. altstack_changed,
276
+ "if_flow and else_flow should change altstack in the same way in {:?}" ,
277
+ self . debug_script. debug_info( self . debug_position + 1 )
278
+ ) ;
279
+ self . stack_change ( Self :: min_status ( stack_status1, stack_status2) ) ;
280
+ }
163
281
}
164
- } ,
282
+ }
165
283
OP_PICK => match self . last_constant {
166
284
Some ( x) => {
167
285
self . stack_change ( Self :: plain_stack_status ( -( ( x + 1 + 1 ) as i32 ) , 0 ) ) ;
168
286
}
169
287
None => {
170
- panic ! ( "need to be handled manually for op_pick" )
288
+ panic ! (
289
+ "need to be handled manually for op_pick in {:?}" ,
290
+ self . debug_script. debug_info( self . debug_position)
291
+ )
171
292
}
172
293
} ,
173
294
OP_ROLL => match self . last_constant {
@@ -176,29 +297,30 @@ impl StackAnalyzer {
176
297
// for [x2, x1, x0, 2, OP_PICK]
177
298
}
178
299
None => {
179
- panic ! ( "need to be handled manually for op_roll" )
300
+ panic ! (
301
+ "need to be handled manually for op_roll in {:?}" ,
302
+ self . debug_script. debug_info( self . debug_position)
303
+ )
180
304
}
181
305
} ,
182
306
_ => {
183
307
self . stack_change ( Self :: opcode_stack_table ( & opcode) ) ;
184
308
}
185
309
}
310
+ self . debug_position += 1 ;
186
311
187
312
// handle last constant, used by op_roll and op_pick
188
313
match opcode {
189
314
OP_PUSHNUM_1 | OP_PUSHNUM_2 | OP_PUSHNUM_3 | OP_PUSHNUM_4 | OP_PUSHNUM_5
190
315
| OP_PUSHNUM_6 | OP_PUSHNUM_7 | OP_PUSHNUM_8 | OP_PUSHNUM_9 | OP_PUSHNUM_10
191
316
| OP_PUSHNUM_11 | OP_PUSHNUM_12 | OP_PUSHNUM_13 | OP_PUSHNUM_14 | OP_PUSHNUM_15
192
317
| OP_PUSHNUM_16 => self . last_constant = Some ( ( opcode. to_u8 ( ) - 0x50 ) as i64 ) ,
318
+ OP_DUP => ( ) ,
319
+ OP_PUSHBYTES_0 => self . last_constant = Some ( 0 ) ,
193
320
_ => self . last_constant = None ,
194
321
}
195
322
}
196
323
197
- pub fn handle_sub_script ( & mut self , stack_status : StackStatus ) {
198
- self . last_constant = None ;
199
- self . stack_change ( stack_status) ;
200
- }
201
-
202
324
pub fn get_status ( & self ) -> StackStatus {
203
325
assert ! ( self . if_stack. is_empty( ) , "if stack is not empty" ) ;
204
326
self . stack_status . clone ( )
@@ -222,10 +344,23 @@ impl StackAnalyzer {
222
344
let x = status. deepest_altstack_accessed . borrow_mut ( ) ;
223
345
let y = status. altstack_changed . borrow_mut ( ) ;
224
346
225
- * i = min ( * i, ( * j) + stack_status. deepest_stack_accessed ) ;
347
+ // The second script's deepest stack access is reduced if there are still elements left on
348
+ // the stack from script 1.
349
+ let elements_on_intermediate_stack = ( * j) - ( * i) ;
350
+ let elements_on_intermediate_altstack = ( * y) - ( * x) ;
351
+ assert ! ( elements_on_intermediate_stack >= 0 , "Script1 changes the stack by more items than it accesses. This means there is a bug in the stack_change() logic." ) ;
352
+ assert ! ( elements_on_intermediate_stack >= 0 , "Script1 changes the altstack by more items than it accesses. This means there is a bug in the stack_change() logic." ) ;
353
+
354
+ * i += min (
355
+ 0 ,
356
+ stack_status. deepest_stack_accessed + elements_on_intermediate_stack,
357
+ ) ;
226
358
* j += stack_status. stack_changed ;
227
359
228
- * x = min ( * x, ( * y) + stack_status. deepest_altstack_accessed ) ;
360
+ * x += min (
361
+ 0 ,
362
+ stack_status. deepest_altstack_accessed + elements_on_intermediate_altstack,
363
+ ) ;
229
364
* y += stack_status. altstack_changed ;
230
365
}
231
366
0 commit comments