diff --git a/debug_utils/sierra-emu/src/starknet.rs b/debug_utils/sierra-emu/src/starknet.rs index 0fefb5ceeb..288dd7bfac 100644 --- a/debug_utils/sierra-emu/src/starknet.rs +++ b/debug_utils/sierra-emu/src/starknet.rs @@ -5,8 +5,9 @@ use std::{ pub use self::{ block_info::BlockInfo, execution_info::ExecutionInfo, execution_info_v2::ExecutionInfoV2, - resource_bounds::ResourceBounds, secp256k1_point::Secp256k1Point, - secp256r1_point::Secp256r1Point, tx_info::TxInfo, tx_v2_info::TxV2Info, u256::U256, + execution_info_v3::ExecutionInfoV3, resource_bounds::ResourceBounds, + secp256k1_point::Secp256k1Point, secp256r1_point::Secp256r1Point, tx_info::TxInfo, + tx_v2_info::TxV2Info, tx_v3_info::TxV3Info, u256::U256, }; use k256::elliptic_curve::{ generic_array::GenericArray, @@ -19,11 +20,13 @@ use starknet_types_core::felt::Felt; mod block_info; mod execution_info; mod execution_info_v2; +mod execution_info_v3; mod resource_bounds; mod secp256k1_point; mod secp256r1_point; mod tx_info; mod tx_v2_info; +mod tx_v3_info; mod u256; pub type SyscallResult = Result>; @@ -36,11 +39,18 @@ pub trait StarknetSyscallHandler { fn get_execution_info_v2(&mut self, remaining_gas: &mut u64) -> SyscallResult; + fn get_execution_info_v3( + &mut self, + _remaining_gas: &mut u64, + ) -> SyscallResult { + unimplemented!() + } + fn deploy( &mut self, class_hash: Felt, contract_address_salt: Felt, - calldata: Vec, + calldata: &[Felt], deploy_from_zero: bool, remaining_gas: &mut u64, ) -> SyscallResult<(Felt, Vec)>; @@ -51,7 +61,7 @@ pub trait StarknetSyscallHandler { &mut self, class_hash: Felt, function_selector: Felt, - calldata: Vec, + calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult>; @@ -59,7 +69,7 @@ pub trait StarknetSyscallHandler { &mut self, address: Felt, entry_point_selector: Felt, - calldata: Vec, + calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult>; @@ -80,19 +90,19 @@ pub trait StarknetSyscallHandler { fn emit_event( &mut self, - keys: Vec, - data: Vec, + keys: &[Felt], + data: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()>; fn send_message_to_l1( &mut self, to_address: Felt, - payload: Vec, + payload: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()>; - fn keccak(&mut self, input: Vec, remaining_gas: &mut u64) -> SyscallResult; + fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult; fn secp256k1_new( &mut self, @@ -164,10 +174,10 @@ pub trait StarknetSyscallHandler { fn sha256_process_block( &mut self, - prev_state: [u32; 8], - current_block: [u32; 16], + prev_state: &mut [u32; 8], + current_block: &[u32; 16], remaining_gas: &mut u64, - ) -> SyscallResult<[u32; 8]>; + ) -> SyscallResult<()>; fn sha512_process_block( &mut self, @@ -180,8 +190,8 @@ pub trait StarknetSyscallHandler { &mut self, _address: Felt, _entry_point_selector: Felt, - _calldata: Vec, - _signature: Vec, + _calldata: &[Felt], + _signature: &[Felt], _remaining_gas: &mut u64, ) -> SyscallResult> { unimplemented!(); @@ -195,7 +205,7 @@ pub trait StarknetSyscallHandler { unimplemented!() } - fn cheatcode(&mut self, _selector: Felt, _input: Vec) -> Vec { + fn cheatcode(&mut self, _selector: Felt, _input: &[Felt]) -> Vec { unimplemented!() } } @@ -299,7 +309,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { &mut self, _class_hash: Felt, _contract_address_salt: Felt, - _calldata: Vec, + _calldata: &[Felt], _deploy_from_zero: bool, _remaining_gas: &mut u64, ) -> SyscallResult<(Felt, Vec)> { @@ -314,7 +324,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { &mut self, _class_hash: Felt, _function_selector: Felt, - _calldata: Vec, + _calldata: &[Felt], _remaining_gas: &mut u64, ) -> SyscallResult> { unimplemented!() @@ -324,7 +334,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { &mut self, _address: Felt, _entry_point_selector: Felt, - _calldata: Vec, + _calldata: &[Felt], _remaining_gas: &mut u64, ) -> SyscallResult> { unimplemented!() @@ -356,8 +366,8 @@ impl StarknetSyscallHandler for StubSyscallHandler { fn emit_event( &mut self, - keys: Vec, - data: Vec, + keys: &[Felt], + data: &[Felt], _remaining_gas: &mut u64, ) -> SyscallResult<()> { self.events.push(StubEvent { @@ -370,13 +380,13 @@ impl StarknetSyscallHandler for StubSyscallHandler { fn send_message_to_l1( &mut self, _to_address: Felt, - _payload: Vec, + _payload: &[Felt], _remaining_gas: &mut u64, ) -> SyscallResult<()> { unimplemented!() } - fn keccak(&mut self, input: Vec, gas: &mut u64) -> SyscallResult { + fn keccak(&mut self, input: &[u64], gas: &mut u64) -> SyscallResult { let length = input.len(); if length % 17 != 0 { @@ -435,7 +445,11 @@ impl StarknetSyscallHandler for StubSyscallHandler { ); if bool::from(point.is_some()) { - Ok(Some(Secp256k1Point { x, y })) + Ok(Some(Secp256k1Point { + x, + y, + is_infinity: false, + })) } else { Ok(None) } @@ -496,9 +510,17 @@ impl StarknetSyscallHandler for StubSyscallHandler { let p = p.to_encoded_point(false); let (x, y) = match p.coordinates() { Coordinates::Uncompressed { x, y } => (x, y), + Coordinates::Identity => { + // P + (-P) yields the identity. Return the canonical (0, 0) + + // is_infinity encoding (see Secp256k1Point::into_value). + return Ok(Secp256k1Point { + x: U256 { lo: 0, hi: 0 }, + y: U256 { lo: 0, hi: 0 }, + is_infinity: true, + }); + } _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. + // We explicitly asked for the uncompressed encoding. unreachable!() } }; @@ -516,6 +538,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, }) } @@ -560,9 +583,16 @@ impl StarknetSyscallHandler for StubSyscallHandler { let p = p.to_encoded_point(false); let (x, y) = match p.coordinates() { Coordinates::Uncompressed { x, y } => (x, y), + Coordinates::Identity => { + // m * P can be the identity (e.g. m = ord(P)). + return Ok(Secp256k1Point { + x: U256 { lo: 0, hi: 0 }, + y: U256 { lo: 0, hi: 0 }, + is_infinity: true, + }); + } _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. + // We explicitly asked for the uncompressed encoding. unreachable!() } }; @@ -580,6 +610,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, }) } @@ -628,6 +659,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, })) } else { Ok(None) @@ -665,7 +697,11 @@ impl StarknetSyscallHandler for StubSyscallHandler { ); if bool::from(point.is_some()) { - Ok(Some(Secp256r1Point { x, y })) + Ok(Some(Secp256r1Point { + x, + y, + is_infinity: false, + })) } else { Ok(None) } @@ -726,9 +762,17 @@ impl StarknetSyscallHandler for StubSyscallHandler { let p = p.to_encoded_point(false); let (x, y) = match p.coordinates() { Coordinates::Uncompressed { x, y } => (x, y), + Coordinates::Identity => { + // P + (-P) yields the identity. Return the canonical (0, 0) + + // is_infinity encoding (see Secp256r1Point::into_value). + return Ok(Secp256r1Point { + x: U256 { lo: 0, hi: 0 }, + y: U256 { lo: 0, hi: 0 }, + is_infinity: true, + }); + } _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. + // We explicitly asked for the uncompressed encoding. unreachable!() } }; @@ -746,6 +790,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, }) } @@ -789,9 +834,16 @@ impl StarknetSyscallHandler for StubSyscallHandler { let p = p.to_encoded_point(false); let (x, y) = match p.coordinates() { Coordinates::Uncompressed { x, y } => (x, y), + Coordinates::Identity => { + // m * P can be the identity (e.g. m = ord(P)). + return Ok(Secp256r1Point { + x: U256 { lo: 0, hi: 0 }, + y: U256 { lo: 0, hi: 0 }, + is_infinity: true, + }); + } _ => { - // This should be unreachable because we explicitly asked for the uncompressed - // encoding. + // We explicitly asked for the uncompressed encoding. unreachable!() } }; @@ -809,6 +861,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, }) } @@ -846,6 +899,7 @@ impl StarknetSyscallHandler for StubSyscallHandler { hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), }, + is_infinity: false, })) } else { Ok(None) @@ -862,17 +916,16 @@ impl StarknetSyscallHandler for StubSyscallHandler { fn sha256_process_block( &mut self, - prev_state: [u32; 8], - current_block: [u32; 16], + prev_state: &mut [u32; 8], + current_block: &[u32; 16], _remaining_gas: &mut u64, - ) -> SyscallResult<[u32; 8]> { - let mut state = prev_state; + ) -> SyscallResult<()> { let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter( current_block.iter().flat_map(|x| x.to_be_bytes()), ) .unwrap(); - sha2::compress256(&mut state, &[data_as_bytes]); - Ok(state) + sha2::compress256(prev_state, &[data_as_bytes]); + Ok(()) } fn sha512_process_block( diff --git a/debug_utils/sierra-emu/src/starknet/execution_info_v3.rs b/debug_utils/sierra-emu/src/starknet/execution_info_v3.rs new file mode 100644 index 0000000000..01a2f798bc --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/execution_info_v3.rs @@ -0,0 +1,12 @@ +use super::{BlockInfo, TxV3Info}; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct ExecutionInfoV3 { + pub block_info: BlockInfo, + pub tx_info: TxV3Info, + pub caller_address: Felt, + pub contract_address: Felt, + pub entry_point_selector: Felt, +} diff --git a/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs b/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs index 0c4844baa1..cee80a608a 100644 --- a/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs +++ b/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs @@ -1,18 +1,27 @@ use super::U256; use crate::Value; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Secp256k1Point { pub x: U256, pub y: U256, + pub is_infinity: bool, } impl Secp256k1Point { #[allow(unused)] pub fn into_value(self) -> Value { + // Sierra has no slot for `is_infinity`; encode the identity element as the + // canonical (0, 0) sentinel so `from_value` can recover the flag losslessly. + // (0, 0) is not on the curve, so this aliasing is unambiguous. + let (x, y) = if self.is_infinity { + (U256 { lo: 0, hi: 0 }, U256 { lo: 0, hi: 0 }) + } else { + (self.x, self.y) + }; Value::Struct(vec![ - Value::Struct(vec![Value::U128(self.x.lo), Value::U128(self.x.hi)]), - Value::Struct(vec![Value::U128(self.y.lo), Value::U128(self.y.hi)]), + Value::Struct(vec![Value::U128(x.lo), Value::U128(x.hi)]), + Value::Struct(vec![Value::U128(y.lo), Value::U128(y.hi)]), ]) } @@ -22,6 +31,9 @@ impl Secp256k1Point { let y = U256::from_value(v.remove(1)); let x = U256::from_value(v.remove(0)); - Self { x, y } + // Recover the flag from the (0, 0) sentinel — see `into_value`. + let is_infinity = x.lo == 0 && x.hi == 0 && y.lo == 0 && y.hi == 0; + + Self { x, y, is_infinity } } } diff --git a/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs b/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs index 5036f78ee1..770a8763d0 100644 --- a/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs +++ b/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs @@ -1,18 +1,27 @@ use super::U256; use crate::Value; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct Secp256r1Point { pub x: U256, pub y: U256, + pub is_infinity: bool, } impl Secp256r1Point { #[allow(unused)] pub fn into_value(self) -> Value { + // Sierra has no slot for `is_infinity`; encode the identity element as the + // canonical (0, 0) sentinel so `from_value` can recover the flag losslessly. + // (0, 0) is not on the curve, so this aliasing is unambiguous. + let (x, y) = if self.is_infinity { + (U256 { lo: 0, hi: 0 }, U256 { lo: 0, hi: 0 }) + } else { + (self.x, self.y) + }; Value::Struct(vec![ - Value::Struct(vec![Value::U128(self.x.lo), Value::U128(self.x.hi)]), - Value::Struct(vec![Value::U128(self.y.lo), Value::U128(self.y.hi)]), + Value::Struct(vec![Value::U128(x.lo), Value::U128(x.hi)]), + Value::Struct(vec![Value::U128(y.lo), Value::U128(y.hi)]), ]) } @@ -22,6 +31,9 @@ impl Secp256r1Point { let y = U256::from_value(v.remove(1)); let x = U256::from_value(v.remove(0)); - Self { x, y } + // Recover the flag from the (0, 0) sentinel — see `into_value`. + let is_infinity = x.lo == 0 && x.hi == 0 && y.lo == 0 && y.hi == 0; + + Self { x, y, is_infinity } } } diff --git a/debug_utils/sierra-emu/src/starknet/tx_v3_info.rs b/debug_utils/sierra-emu/src/starknet/tx_v3_info.rs new file mode 100644 index 0000000000..25431ef518 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/tx_v3_info.rs @@ -0,0 +1,21 @@ +use super::ResourceBounds; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct TxV3Info { + pub version: Felt, + pub account_contract_address: Felt, + pub max_fee: u128, + pub signature: Vec, + pub transaction_hash: Felt, + pub chain_id: Felt, + pub nonce: Felt, + pub resource_bounds: Vec, + pub tip: u128, + pub paymaster_data: Vec, + pub nonce_data_availability_mode: u32, + pub fee_data_availability_mode: u32, + pub account_deployment_data: Vec, + pub proof_facts: Vec, +} diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs index c1d23fcf6d..058f487106 100644 --- a/debug_utils/sierra-emu/src/vm/starknet.rs +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -138,10 +138,48 @@ pub fn eval( }, StarknetConcreteLibfunc::GetClassHashAt(info) => eval_get_class_hash_at(registry, info, args, syscall_handler), StarknetConcreteLibfunc::MetaTxV0(info) => eval_meta_tx_v0(registry, info, args, syscall_handler), - StarknetConcreteLibfunc::GetExecutionInfoV3(_) => todo!(), + StarknetConcreteLibfunc::GetExecutionInfoV3(info) => { + eval_get_execution_info_v3(registry, info, args) + } } } +/// Soft-fail for `get_execution_info_v3`. +/// +/// The trait surface includes `get_execution_info_v3` (mirroring cairo-native), but the +/// Sierra `Value` lowering for `ExecutionInfoV3` / `TxV3Info` is not yet wired in the +/// VM. Rather than panic the host on every v3 invocation, return the syscall's error +/// branch with a descriptive felt so the contract sees a normal syscall failure. +fn eval_get_execution_info_v3( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U64(gas), system]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + let felt_ty = match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: vec![Value::Felt(Felt::from_bytes_be_slice( + b"get_execution_info_v3 not yet wired in sierra-emu" + ))], + } + ], + ) +} + fn eval_secp256_new( registry: &ProgramRegistry, info: &SignatureOnlyConcreteLibfunc, @@ -613,7 +651,7 @@ fn eval_call_contract( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); // get felt type from the error branch array let felt_ty = { @@ -626,7 +664,7 @@ fn eval_call_contract( } }; - let result = syscall_handler.call_contract(address, entry_point_selector, calldata, &mut gas); + let result = syscall_handler.call_contract(address, entry_point_selector, &calldata, &mut gas); match result { Ok(return_values) => EvalAction::NormalBranch( @@ -772,16 +810,16 @@ fn eval_emit_event( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); let data = data .into_iter() .map(|x| match x { Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); - let result = syscall_handler.emit_event(keys, data, &mut gas); + let result = syscall_handler.emit_event(&keys, &data, &mut gas); match result { Ok(_) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), @@ -994,7 +1032,7 @@ fn eval_deploy( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); // get felt type from the error branch array let felt_ty = { @@ -1010,7 +1048,7 @@ fn eval_deploy( let result = syscall_handler.deploy( class_hash, contract_address_salt, - calldata, + &calldata, deploy_from_zero, &mut gas, ); @@ -1066,9 +1104,9 @@ fn eval_keccak( Value::U64(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); - let result = syscall_handler.keccak(input, &mut gas); + let result = syscall_handler.keccak(&input, &mut gas); // get felt type from the error branch array let felt_ty = { @@ -1125,7 +1163,7 @@ fn eval_library_call( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); // get felt type from the error branch array let felt_ty = { @@ -1138,7 +1176,7 @@ fn eval_library_call( } }; - let result = syscall_handler.library_call(class_hash, function_selector, calldata, &mut gas); + let result = syscall_handler.library_call(class_hash, function_selector, &calldata, &mut gas); match result { Ok(return_values) => EvalAction::NormalBranch( @@ -1235,7 +1273,7 @@ fn eval_send_message_to_l1( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); // get felt type from the error branch array let felt_ty = { @@ -1248,7 +1286,7 @@ fn eval_send_message_to_l1( } }; - let result = syscall_handler.send_message_to_l1(address, payload, &mut gas); + let result = syscall_handler.send_message_to_l1(address, &payload, &mut gas); match result { Ok(()) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), @@ -1298,7 +1336,7 @@ fn eval_sha256_process_block( panic!() }; - let prev_state: [u32; 8] = prev_state + let mut prev_state: [u32; 8] = prev_state .into_iter() .map(|v| { let Value::U32(v) = v else { panic!() }; @@ -1328,9 +1366,9 @@ fn eval_sha256_process_block( } }; - match syscall_handler.sha256_process_block(prev_state, current_block, &mut gas) { - Ok(payload) => { - let payload = payload.into_iter().map(Value::U32).collect::>(); + match syscall_handler.sha256_process_block(&mut prev_state, ¤t_block, &mut gas) { + Ok(()) => { + let payload = prev_state.into_iter().map(Value::U32).collect::>(); EvalAction::NormalBranch( 0, smallvec![Value::U64(gas), system, Value::Struct(payload)], @@ -1501,7 +1539,7 @@ fn eval_meta_tx_v0( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); let signature = signature .into_iter() @@ -1509,7 +1547,7 @@ fn eval_meta_tx_v0( Value::Felt(x) => x, _ => unreachable!(), }) - .collect(); + .collect::>(); let felt_ty = { match registry @@ -1521,7 +1559,13 @@ fn eval_meta_tx_v0( } }; - match syscall_handler.meta_tx_v0(address, entry_point_selector, calldata, signature, &mut gas) { + match syscall_handler.meta_tx_v0( + address, + entry_point_selector, + &calldata, + &signature, + &mut gas, + ) { Ok(res) => EvalAction::NormalBranch( 0, smallvec![ diff --git a/src/metadata/trace_dump.rs b/src/metadata/trace_dump.rs index 66f21516bb..b7b3104ba8 100644 --- a/src/metadata/trace_dump.rs +++ b/src/metadata/trace_dump.rs @@ -773,6 +773,7 @@ pub mod trace_dump_runtime { lo: point.y.lo, hi: point.y.hi, }, + is_infinity: point.is_infinity, }; emu_point.into_value() } @@ -787,6 +788,7 @@ pub mod trace_dump_runtime { lo: point.y.lo, hi: point.y.hi, }, + is_infinity: point.is_infinity, }; emu_point.into_value() } diff --git a/src/starknet.rs b/src/starknet.rs index f02db67d1c..5a394f67d2 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -421,7 +421,6 @@ pub trait StarknetSyscallHandler { remaining_gas: &mut u64, ) -> SyscallResult>; - #[cfg(feature = "with-cheatcode")] fn cheatcode(&mut self, _selector: Felt, _input: &[Felt]) -> Vec { unimplemented!(); }