diff --git a/src/engine.cairo b/src/engine.cairo index 6a65f378..afefbb2f 100644 --- a/src/engine.cairo +++ b/src/engine.cairo @@ -50,10 +50,14 @@ pub trait EngineTrait { fn has_flag(ref self: Engine, flag: ScriptFlags) -> bool; // Return the script since last OP_CODESEPARATOR fn sub_script(ref self: Engine) -> ByteArray; + // Ensure the stack size is within limits + fn check_stack_size(ref self: Engine) -> Result<(), felt252>; // Print engine data as a JSON object fn json(ref self: Engine); } +pub const MAX_STACK_SIZE: u32 = 1000; + pub impl EngineImpl of EngineTrait { fn new( script_pubkey: @ByteArray, transaction: Transaction, tx_idx: u32, flags: u32, amount: i64 @@ -135,8 +139,8 @@ pub impl EngineImpl of EngineTrait { if res.is_err() { return Result::Err(res.unwrap_err()); } + self.check_stack_size()?; self.opcode_idx += 1; - if self.opcode_idx >= script.len() { if self.cond_stack.len() > 0 { return Result::Err(Error::SCRIPT_UNBALANCED_CONDITIONAL_STACK); @@ -183,6 +187,11 @@ pub impl EngineImpl of EngineTrait { err = res.unwrap_err(); break; } + let res = self.check_stack_size(); + if res.is_err() { + err = res.unwrap_err(); + break; + } self.opcode_idx += 1; }; if err != '' { @@ -245,6 +254,13 @@ pub impl EngineImpl of EngineTrait { return sub_script; } + fn check_stack_size(ref self: Engine) -> Result<(), felt252> { + if self.dstack.len() + self.astack.len() > MAX_STACK_SIZE { + return Result::Err(Error::SCRIPT_STACK_SIZE_EXCEEDED); + } + return Result::Ok(()); + } + fn json(ref self: Engine) { self.dstack.json(); } diff --git a/src/errors.cairo b/src/errors.cairo index d2b21ad9..3722f58d 100644 --- a/src/errors.cairo +++ b/src/errors.cairo @@ -14,6 +14,7 @@ pub mod Error { pub const FINALIZED_TX_CLTV: felt252 = 'Finalized tx in OP_CLTV'; pub const INVALID_TX_VERSION: felt252 = 'Invalid transaction version'; pub const SCRIPT_INVALID: felt252 = 'Invalid script data'; + pub const SCRIPT_STACK_SIZE_EXCEEDED: felt252 = 'Script stack size exceeded'; } pub fn byte_array_err(err: felt252) -> ByteArray {