11use crate :: builder:: { Block , StructuredScript } ;
2+ use crate :: chunker:: Chunk ;
23use bitcoin:: blockdata:: opcodes:: Opcode ;
34use bitcoin:: blockdata:: script:: { read_scriptint, Instruction } ;
45use bitcoin:: opcodes:: all:: * ;
56use bitcoin:: script:: PushBytes ;
7+ use bitcoin:: ScriptBuf ;
8+ use script_macro:: script;
69use std:: borrow:: BorrowMut ;
7- use std:: cmp:: min;
10+ use std:: cmp:: { max , min} ;
811use std:: panic;
912
1013#[ derive( Debug , Clone , Default , PartialEq ) ]
@@ -15,6 +18,25 @@ pub struct StackStatus {
1518 pub altstack_changed : i32 ,
1619}
1720
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+
1840#[ derive( Debug , Clone ) ]
1941enum IfStackEle {
2042 IfFlow ( StackStatus ) ,
@@ -24,11 +46,13 @@ enum IfStackEle {
2446
2547#[ derive( Debug , Clone ) ]
2648pub struct StackAnalyzer {
49+ debug_script : StructuredScript ,
50+ debug_position : usize ,
2751 stack_status : StackStatus ,
2852 // if_stack should be empty after analyzing
2953 if_stack : Vec < IfStackEle > ,
3054 // last constant? for handling op_roll and op_pick
31- last_constant : Option < i64 > ,
55+ pub last_constant : Option < i64 > ,
3256}
3357
3458impl Default for StackAnalyzer {
@@ -40,30 +64,102 @@ impl Default for StackAnalyzer {
4064impl StackAnalyzer {
4165 pub fn new ( ) -> Self {
4266 StackAnalyzer {
67+ debug_script : StructuredScript :: new ( "" ) ,
68+ debug_position : 0 ,
4369 stack_status : StackStatus :: default ( ) ,
4470 if_stack : vec ! [ ] ,
4571 last_constant : None ,
4672 }
4773 }
4874
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+ } ;
54125 }
55126 self . get_status ( )
56127 }
57128
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 ( ) {
60150 match block {
61151 Block :: Call ( id) => {
62152 let called_script = builder
63153 . script_map
64- . get_mut ( id)
154+ . get ( id)
65155 . 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+ } ;
67163 }
68164 Block :: Script ( block_script) => {
69165 for instruct in block_script. instructions ( ) {
@@ -84,10 +180,10 @@ impl StackAnalyzer {
84180 }
85181 }
86182 }
87- self . stack_status . clone ( )
88183 }
89184
90185 pub fn handle_push_slice ( & mut self , bytes : & PushBytes ) {
186+ self . debug_position += bytes. len ( ) + 1 ;
91187 if let Ok ( x) = read_scriptint ( bytes. as_bytes ( ) ) {
92188 // if i64(data) < 1000, last_constant is true
93189 if ( 0 ..=1000 ) . contains ( & x) {
@@ -120,13 +216,29 @@ impl StackAnalyzer {
120216 }
121217 }
122218
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+
123228 pub fn handle_opcode ( & mut self , opcode : Opcode ) {
124229 // handle if/else flow
125230 match opcode {
126231 OP_IF | OP_NOTIF => {
127232 self . stack_change ( Self :: opcode_stack_table ( & opcode) ) ;
128233 self . if_stack . push ( IfStackEle :: IfFlow ( Default :: default ( ) ) ) ;
129234 }
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+ }
130242 OP_ELSE => match self . if_stack . pop ( ) . unwrap ( ) {
131243 IfStackEle :: IfFlow ( i) => {
132244 self . if_stack
@@ -136,38 +248,47 @@ impl StackAnalyzer {
136248 panic ! ( "shouldn't happend" )
137249 }
138250 } ,
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 ! (
142255 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 )
145258 ) ;
146- assert_eq ! (
259+ assert_eq ! (
147260 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 )
157263 ) ;
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+ }
163281 }
164- } ,
282+ }
165283 OP_PICK => match self . last_constant {
166284 Some ( x) => {
167285 self . stack_change ( Self :: plain_stack_status ( -( ( x + 1 + 1 ) as i32 ) , 0 ) ) ;
168286 }
169287 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+ )
171292 }
172293 } ,
173294 OP_ROLL => match self . last_constant {
@@ -176,29 +297,30 @@ impl StackAnalyzer {
176297 // for [x2, x1, x0, 2, OP_PICK]
177298 }
178299 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+ )
180304 }
181305 } ,
182306 _ => {
183307 self . stack_change ( Self :: opcode_stack_table ( & opcode) ) ;
184308 }
185309 }
310+ self . debug_position += 1 ;
186311
187312 // handle last constant, used by op_roll and op_pick
188313 match opcode {
189314 OP_PUSHNUM_1 | OP_PUSHNUM_2 | OP_PUSHNUM_3 | OP_PUSHNUM_4 | OP_PUSHNUM_5
190315 | OP_PUSHNUM_6 | OP_PUSHNUM_7 | OP_PUSHNUM_8 | OP_PUSHNUM_9 | OP_PUSHNUM_10
191316 | OP_PUSHNUM_11 | OP_PUSHNUM_12 | OP_PUSHNUM_13 | OP_PUSHNUM_14 | OP_PUSHNUM_15
192317 | 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 ) ,
193320 _ => self . last_constant = None ,
194321 }
195322 }
196323
197- pub fn handle_sub_script ( & mut self , stack_status : StackStatus ) {
198- self . last_constant = None ;
199- self . stack_change ( stack_status) ;
200- }
201-
202324 pub fn get_status ( & self ) -> StackStatus {
203325 assert ! ( self . if_stack. is_empty( ) , "if stack is not empty" ) ;
204326 self . stack_status . clone ( )
@@ -222,10 +344,23 @@ impl StackAnalyzer {
222344 let x = status. deepest_altstack_accessed . borrow_mut ( ) ;
223345 let y = status. altstack_changed . borrow_mut ( ) ;
224346
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+ ) ;
226358 * j += stack_status. stack_changed ;
227359
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+ ) ;
229364 * y += stack_status. altstack_changed ;
230365 }
231366
0 commit comments