@@ -62,6 +62,10 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0
62
62
const TWO_POW_224 : U256 = U256 ( [ 0 , 0 , 0 , 0x100000000 ] ) ; //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
63
63
const TWO_POW_248 : U256 = U256 ( [ 0 , 0 , 0 , 0x100000000000000 ] ) ; //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
64
64
65
+ /// Maximal subroutine stack size as specified in
66
+ /// https://eips.ethereum.org/EIPS/eip-2315.
67
+ pub const MAX_SUB_STACK_SIZE : usize = 1024 ;
68
+
65
69
/// Abstraction over raw vector of Bytes. Easier state management of PC.
66
70
struct CodeReader {
67
71
position : ProgramCounter ,
@@ -94,6 +98,8 @@ enum InstructionResult<Gas> {
94
98
Ok ,
95
99
UnusedGas ( Gas ) ,
96
100
JumpToPosition ( U256 ) ,
101
+ JumpToSubroutine ( U256 ) ,
102
+ ReturnFromSubroutine ( usize ) ,
97
103
StopExecutionNeedsReturn {
98
104
/// Gas left.
99
105
gas : Gas ,
@@ -179,8 +185,10 @@ pub struct Interpreter<Cost: CostType> {
179
185
do_trace : bool ,
180
186
done : bool ,
181
187
valid_jump_destinations : Option < Arc < BitSet > > ,
188
+ valid_subroutine_destinations : Option < Arc < BitSet > > ,
182
189
gasometer : Option < Gasometer < Cost > > ,
183
190
stack : VecStack < U256 > ,
191
+ return_stack : Vec < usize > ,
184
192
resume_output_range : Option < ( U256 , U256 ) > ,
185
193
resume_result : Option < InstructionResult < Cost > > ,
186
194
last_stack_ret_len : usize ,
@@ -271,12 +279,14 @@ impl<Cost: CostType> Interpreter<Cost> {
271
279
let params = InterpreterParams :: from ( params) ;
272
280
let informant = informant:: EvmInformant :: new ( depth) ;
273
281
let valid_jump_destinations = None ;
282
+ let valid_subroutine_destinations = None ;
274
283
let gasometer = Cost :: from_u256 ( params. gas ) . ok ( ) . map ( |gas| Gasometer :: < Cost > :: new ( gas) ) ;
275
284
let stack = VecStack :: with_capacity ( schedule. stack_limit , U256 :: zero ( ) ) ;
276
-
285
+ let return_stack = Vec :: with_capacity ( MAX_SUB_STACK_SIZE ) ;
277
286
Interpreter {
278
287
cache, params, reader, informant,
279
- valid_jump_destinations, gasometer, stack,
288
+ valid_jump_destinations, valid_subroutine_destinations,
289
+ gasometer, stack, return_stack,
280
290
done : false ,
281
291
// Overridden in `step_inner` based on
282
292
// the result of `ext.trace_next_instruction`.
@@ -403,7 +413,7 @@ impl<Cost: CostType> Interpreter<Cost> {
403
413
match result {
404
414
InstructionResult :: JumpToPosition ( position) => {
405
415
if self . valid_jump_destinations . is_none ( ) {
406
- self . valid_jump_destinations = Some ( self . cache . jump_destinations ( & self . params . code_hash , & self . reader . code ) ) ;
416
+ self . valid_jump_destinations = Some ( self . cache . jump_and_sub_destinations ( & self . params . code_hash , & self . reader . code ) . 0 ) ;
407
417
}
408
418
let jump_destinations = self . valid_jump_destinations . as_ref ( ) . expect ( "jump_destinations are initialized on first jump; qed" ) ;
409
419
let pos = match self . verify_jump ( position, jump_destinations) {
@@ -412,6 +422,21 @@ impl<Cost: CostType> Interpreter<Cost> {
412
422
} ;
413
423
self . reader . position = pos;
414
424
} ,
425
+ InstructionResult :: JumpToSubroutine ( position) => {
426
+ if self . valid_subroutine_destinations . is_none ( ) {
427
+ self . valid_subroutine_destinations = Some ( self . cache . jump_and_sub_destinations ( & self . params . code_hash , & self . reader . code ) . 1 ) ;
428
+ }
429
+ let subroutine_destinations = self . valid_subroutine_destinations . as_ref ( ) . expect ( "subroutine_destinations are initialized on first jump; qed" ) ;
430
+ let pos = match self . verify_jump ( position, subroutine_destinations) {
431
+ Ok ( x) => x,
432
+ Err ( e) => return InterpreterResult :: Done ( Err ( e) )
433
+ } ;
434
+ self . return_stack . push ( self . reader . position ) ;
435
+ self . reader . position = pos;
436
+ } ,
437
+ InstructionResult :: ReturnFromSubroutine ( pos) => {
438
+ self . reader . position = pos;
439
+ } ,
415
440
InstructionResult :: StopExecutionNeedsReturn { gas, init_off, init_size, apply} => {
416
441
let mem = mem:: replace ( & mut self . mem , Vec :: new ( ) ) ;
417
442
return InterpreterResult :: Done ( Ok ( GasLeft :: NeedsReturn {
@@ -436,15 +461,17 @@ impl<Cost: CostType> Interpreter<Cost> {
436
461
fn verify_instruction ( & self , ext : & dyn vm:: Ext , instruction : Instruction , info : & InstructionInfo ) -> vm:: Result < ( ) > {
437
462
let schedule = ext. schedule ( ) ;
438
463
439
- if ( instruction == instructions:: DELEGATECALL && !schedule. have_delegate_call ) ||
440
- ( instruction == instructions:: CREATE2 && !schedule. have_create2 ) ||
441
- ( instruction == instructions:: STATICCALL && !schedule. have_static_call ) ||
442
- ( ( instruction == instructions:: RETURNDATACOPY || instruction == instructions:: RETURNDATASIZE ) && !schedule. have_return_data ) ||
443
- ( instruction == instructions:: REVERT && !schedule. have_revert ) ||
444
- ( ( instruction == instructions:: SHL || instruction == instructions:: SHR || instruction == instructions:: SAR ) && !schedule. have_bitwise_shifting ) ||
445
- ( instruction == instructions:: EXTCODEHASH && !schedule. have_extcodehash ) ||
446
- ( instruction == instructions:: CHAINID && !schedule. have_chain_id ) ||
447
- ( instruction == instructions:: SELFBALANCE && !schedule. have_selfbalance )
464
+ use instructions:: * ;
465
+ if ( instruction == DELEGATECALL && !schedule. have_delegate_call ) ||
466
+ ( instruction == CREATE2 && !schedule. have_create2 ) ||
467
+ ( instruction == STATICCALL && !schedule. have_static_call ) ||
468
+ ( ( instruction == RETURNDATACOPY || instruction == RETURNDATASIZE ) && !schedule. have_return_data ) ||
469
+ ( instruction == REVERT && !schedule. have_revert ) ||
470
+ ( ( instruction == SHL || instruction == SHR || instruction == SAR ) && !schedule. have_bitwise_shifting ) ||
471
+ ( instruction == EXTCODEHASH && !schedule. have_extcodehash ) ||
472
+ ( instruction == CHAINID && !schedule. have_chain_id ) ||
473
+ ( instruction == SELFBALANCE && !schedule. have_selfbalance ) ||
474
+ ( ( instruction == BEGINSUB || instruction == JUMPSUB || instruction == RETURNSUB ) && !schedule. have_subs )
448
475
{
449
476
return Err ( vm:: Error :: BadInstruction {
450
477
instruction : instruction as u8
@@ -525,6 +552,32 @@ impl<Cost: CostType> Interpreter<Cost> {
525
552
instructions:: JUMPDEST => {
526
553
// ignore
527
554
} ,
555
+ instructions:: BEGINSUB => {
556
+ // ignore
557
+ } ,
558
+ instructions:: JUMPSUB => {
559
+ if self . return_stack . len ( ) >= MAX_SUB_STACK_SIZE {
560
+ return Err ( vm:: Error :: OutOfSubStack {
561
+ wanted : 1 ,
562
+ limit : MAX_SUB_STACK_SIZE ,
563
+ } ) ;
564
+ }
565
+ let sub_destination = self . stack . pop_back ( ) ;
566
+ return Ok ( InstructionResult :: JumpToSubroutine (
567
+ sub_destination
568
+ ) )
569
+ } ,
570
+ instructions:: RETURNSUB => {
571
+ if let Some ( pos) = self . return_stack . pop ( ) {
572
+ return Ok ( InstructionResult :: ReturnFromSubroutine ( pos) )
573
+ } else {
574
+ return Err ( vm:: Error :: SubStackUnderflow {
575
+ wanted : 1 ,
576
+ on_stack : 0 ,
577
+ } ) ;
578
+ }
579
+
580
+ } ,
528
581
instructions:: CREATE | instructions:: CREATE2 => {
529
582
let endowment = self . stack . pop_back ( ) ;
530
583
let init_off = self . stack . pop_back ( ) ;
@@ -1177,6 +1230,7 @@ impl<Cost: CostType> Interpreter<Cost> {
1177
1230
if valid_jump_destinations. contains ( jump) && U256 :: from ( jump) == jump_u {
1178
1231
Ok ( jump)
1179
1232
} else {
1233
+ // Note: if jump > usize, BadJumpDestination value is trimmed
1180
1234
Err ( vm:: Error :: BadJumpDestination {
1181
1235
destination : jump
1182
1236
} )
0 commit comments