From 9052ff1a86ee5a408b490bedf1eb4ccfe111c94d Mon Sep 17 00:00:00 2001 From: Brahima Dibassi Date: Mon, 21 Jul 2025 15:34:15 +0200 Subject: [PATCH 1/2] Tezlink/Kernel/MIR : Implement From trait for FieldAnnotation to convert from Entrypoint --- contrib/mir/src/ast/annotations.rs | 5 +++++ contrib/mir/src/ast/michelson_address/entrypoint.rs | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/contrib/mir/src/ast/annotations.rs b/contrib/mir/src/ast/annotations.rs index dd79d5fdf9dc..08b0b443853d 100644 --- a/contrib/mir/src/ast/annotations.rs +++ b/contrib/mir/src/ast/annotations.rs @@ -100,6 +100,11 @@ impl<'a> FieldAnnotation<'a> { pub fn from_str_unchecked(s: &'a str) -> Self { FieldAnnotation(Cow::Borrowed(s)) } + + /// Get the field annotation from a string. This will allocate a new + pub fn from_string(s: String) -> Self { + FieldAnnotation(Cow::Owned(s)) + } } impl<'a> Annotations<'a> { diff --git a/contrib/mir/src/ast/michelson_address/entrypoint.rs b/contrib/mir/src/ast/michelson_address/entrypoint.rs index 87c94095d59f..951cc36e3487 100644 --- a/contrib/mir/src/ast/michelson_address/entrypoint.rs +++ b/contrib/mir/src/ast/michelson_address/entrypoint.rs @@ -10,8 +10,8 @@ use std::collections::HashMap; -use crate::ast::annotations::FieldAnnotation; use crate::ast::Type; +use crate::ast::annotations::FieldAnnotation; use nom::branch::alt; use nom::bytes::complete::tag; use nom::combinator::{map, map_res, verify}; @@ -241,6 +241,12 @@ impl TryFrom> for Entrypoint { } } +impl From for FieldAnnotation<'_> { + fn from(entrypoint: Entrypoint) -> Self { + FieldAnnotation::from_string(entrypoint.0) + } +} + pub(crate) fn check_ep_name_len(ep: &[u8]) -> Result<(), ByteReprError> { if ep.len() > MAX_EP_LEN { return Err(ByteReprError::WrongFormat(format!( -- GitLab From f7b3dee841fc44548aec5c16b8ce01f75aad96cd Mon Sep 17 00:00:00 2001 From: Brahima Dibassi Date: Mon, 21 Jul 2025 15:35:22 +0200 Subject: [PATCH 2/2] Tezlink/Kernel/MIR : Use Entrypoint instead of FieldAnnotation for interpreter entry --- contrib/mir/examples/call_entrypoints.rs | 16 +- contrib/mir/src/interpreter.rs | 378 ++++++++++-------- .../kernel_latest/tezos_execution/src/lib.rs | 14 +- 3 files changed, 222 insertions(+), 186 deletions(-) diff --git a/contrib/mir/examples/call_entrypoints.rs b/contrib/mir/examples/call_entrypoints.rs index e49565b04f29..d983ed7b4e3d 100644 --- a/contrib/mir/examples/call_entrypoints.rs +++ b/contrib/mir/examples/call_entrypoints.rs @@ -40,13 +40,15 @@ static SCRIPT: &str = r#" parameter (or (or (or (or (int %decrement) (int %incre fn run_contract<'a>( parameter: Micheline<'a>, storage: Micheline<'a>, - annotation: FieldAnnotation<'a>, + entrypoint: &str, contract_typechecked: &ContractScript<'a>, ctx: &mut Ctx<'a>, parser: &'a Parser<'a>, ) { + let entrypoint = Entrypoint::try_from(entrypoint) + .expect("Entrypoint should be valid, check the string"); let (_, new_storage) = contract_typechecked - .interpret(ctx, &parser.arena, parameter, Some(annotation), storage) + .interpret(ctx, &parser.arena, parameter, Some(entrypoint), storage) .unwrap(); let TypedValue::Int(storage_int) = &new_storage else { unreachable!() @@ -62,7 +64,7 @@ fn main() { run_contract( 30.into(), 20.into(), - FieldAnnotation::from_str_unchecked("increment"), + "increment", &contract_typechecked, &mut ctx, &parser, @@ -70,7 +72,7 @@ fn main() { run_contract( 100.into(), 80.into(), - FieldAnnotation::from_str_unchecked("decrement"), + "decrement", &contract_typechecked, &mut ctx, &parser, @@ -78,7 +80,7 @@ fn main() { run_contract( 7.into(), 123.into(), - FieldAnnotation::from_str_unchecked("set"), + "set", &contract_typechecked, &mut ctx, &parser, @@ -86,7 +88,7 @@ fn main() { run_contract( ().into(), 9.into(), - FieldAnnotation::from_str_unchecked("double"), + "double", &contract_typechecked, &mut ctx, &parser, @@ -94,7 +96,7 @@ fn main() { run_contract( ().into(), 27.into(), - FieldAnnotation::from_str_unchecked("reset"), + "reset", &contract_typechecked, &mut ctx, &parser, diff --git a/contrib/mir/src/interpreter.rs b/contrib/mir/src/interpreter.rs index 3d87a0c25c55..21eb626408d7 100644 --- a/contrib/mir/src/interpreter.rs +++ b/contrib/mir/src/interpreter.rs @@ -9,8 +9,7 @@ //! [Instruction] and [ContractScript], see there for more. use checked::Checked; -use cryptoxide::hashing::{blake2b_256, keccak256, sha256, sha3_256, sha512}; -use entrypoint::DEFAULT_EP_NAME; +use cryptoxide::hashing::{blake2b_256, keccak256, sha3_256, sha256, sha512}; use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; use num_traits::{Signed, ToPrimitive, Zero}; @@ -26,11 +25,11 @@ use crate::ast::*; #[cfg(feature = "bls")] use crate::bls; use crate::context::Ctx; -use crate::gas::{interpret_cost, OutOfGas}; +use crate::gas::{OutOfGas, interpret_cost}; use crate::irrefutable_match::irrefutable_match; use crate::lexer::Prim; use crate::stack::*; -use crate::typechecker::{typecheck_contract_address, typecheck_value, TcError}; +use crate::typechecker::{TcError, typecheck_contract_address, typecheck_value}; /// Errors possible during interpretation. #[derive(Debug, PartialEq, Eq, Clone, thiserror::Error)] @@ -82,11 +81,11 @@ impl<'a> ContractScript<'a> { ctx: &mut Ctx<'a>, arena: &'a Arena>, parameter: Micheline<'a>, - annotation: Option>, + entrypoint: Option, storage: Micheline<'a>, ) -> Result<(impl Iterator>, TypedValue<'a>), ContractInterpretError<'a>> { - let wrapped_parameter = self.wrap_parameter(arena, parameter, annotation, ctx)?; + let wrapped_parameter = self.wrap_parameter(arena, parameter, entrypoint, ctx)?; let storage = typecheck_value(&storage, ctx, &self.storage)?; let tc_val = TypedValue::new_pair(wrapped_parameter, storage); let mut stack = stk![tc_val]; @@ -110,16 +109,13 @@ impl<'a> ContractScript<'a> { &self, arena: &'a Arena>, parameter: Micheline<'a>, - annotation: Option>, + entrypoint: Option, ctx: &mut Ctx<'a>, ) -> Result, TcError> { - let parsed_annotation = match annotation { - None => FieldAnnotation::from_str_unchecked(DEFAULT_EP_NAME), - Some(ann) => ann, - }; + let entrypoint = entrypoint.unwrap_or_else(Entrypoint::default); + let parsed_annotation = FieldAnnotation::from_string(entrypoint.to_string()); if let Some((annotation_path, annotation_type)) = self.annotations.get(&parsed_annotation) { typecheck_value(¶meter, ctx, annotation_type)?; - Entrypoint::try_from(parsed_annotation)?; let mut result = parameter; for direction in annotation_path.into_iter().rev() { match direction { @@ -133,9 +129,7 @@ impl<'a> ContractScript<'a> { } Ok(typecheck_value(&result, ctx, &self.parameter)?) } else { - Err(TcError::NoSuchEntrypoint(Entrypoint::try_from( - parsed_annotation, - )?)) + Err(TcError::NoSuchEntrypoint(entrypoint)) } } } @@ -1819,12 +1813,12 @@ mod interpreter_tests { #[cfg(feature = "bls")] use crate::bls; use crate::gas::Gas; - use chrono::DateTime; - use entrypoint::DEFAULT_EP_NAME; - use num_bigint::BigUint; use Instruction::*; use Option::None; use TypedValue as V; + use chrono::DateTime; + use entrypoint::DEFAULT_EP_NAME; + use num_bigint::BigUint; #[track_caller] fn mk_0x(hex: &str) -> TypedValue { @@ -2095,12 +2089,14 @@ mod interpreter_tests { let mut stack = stk![V::Timestamp(int_o2), V::Timestamp(int_o1),]; let expected_stack = stk![V::Int("29489840".parse().unwrap())]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &Sub(overloads::Sub::TimestampTimestamp), - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret_one( + &Sub(overloads::Sub::TimestampTimestamp), + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2342,12 +2338,14 @@ mod interpreter_tests { let mut stack = stk![V::nat(20), V::nat(5), V::nat(10)]; let expected_stack = stk![V::nat(25), V::nat(10)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &Dip(None, vec![Add(overloads::Add::NatNat)]), - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret_one( + &Dip(None, vec![Add(overloads::Add::NatNat)]), + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2457,12 +2455,14 @@ mod interpreter_tests { let mut stack = stk![V::int(20), V::int(5), V::Bool(true)]; let expected_stack = stk![V::int(20)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &If(vec![Drop(None)], vec![Add(overloads::Add::IntInt)]), - &mut ctx, - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &If(vec![Drop(None)], vec![Add(overloads::Add::IntInt)]), + &mut ctx, + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2471,12 +2471,14 @@ mod interpreter_tests { let mut stack = stk![V::int(20), V::int(5), V::Bool(false)]; let expected_stack = stk![V::int(25)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &If(vec![Drop(None)], vec![Add(overloads::Add::IntInt)]), - &mut ctx, - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &If(vec![Drop(None)], vec![Add(overloads::Add::IntInt)]), + &mut ctx, + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2640,16 +2642,18 @@ mod interpreter_tests { let mut stack = stk![V::nat(20), V::nat(10), V::Bool(false)]; let expected_stack = stk![V::nat(20), V::nat(10)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &Loop(vec![ - Push(V::nat(1)), - Add(overloads::Add::NatNat), - Push(V::Bool(false)) - ]), - &mut ctx, - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Loop(vec![ + Push(V::nat(1)), + Add(overloads::Add::NatNat), + Push(V::Bool(false)) + ]), + &mut ctx, + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2658,16 +2662,18 @@ mod interpreter_tests { let mut stack = stk![V::nat(20), V::nat(10), V::Bool(true)]; let expected_stack = stk![V::nat(20), V::nat(11)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &Loop(vec![ - Push(V::nat(1)), - Add(overloads::Add::NatNat), - Push(V::Bool(false)) - ]), - &mut ctx, - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Loop(vec![ + Push(V::nat(1)), + Add(overloads::Add::NatNat), + Push(V::Bool(false)) + ]), + &mut ctx, + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2676,17 +2682,19 @@ mod interpreter_tests { let mut stack = stk![V::nat(20), V::int(10), V::Bool(true)]; let expected_stack = stk![V::nat(20), V::int(0)]; let mut ctx = Ctx::default(); - assert!(interpret_one( - &Loop(vec![ - Push(V::int(-1)), - Add(overloads::Add::IntInt), - Dup(None), - Gt - ]), - &mut ctx, - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Loop(vec![ + Push(V::int(-1)), + Add(overloads::Add::IntInt), + Dup(None), + Gt + ]), + &mut ctx, + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -2726,12 +2734,14 @@ mod interpreter_tests { V::List(vec![].into()), V::List((1..5).map(V::int).collect()) ]; - assert!(interpret_one( - &Iter(overloads::Iter::List, vec![Cons]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::List, vec![Cons]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); // NB: walking a list start-to-end and CONSing each element effectively // reverses the list. assert_eq!(stack, stk![V::List((1..5).rev().map(V::int).collect())]); @@ -2740,12 +2750,14 @@ mod interpreter_tests { #[test] fn test_iter_list_zero() { let mut stack = stk![V::Unit, V::List(vec![].into())]; - assert!(interpret_one( - &Iter(overloads::Iter::List, vec![Drop(None)]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::List, vec![Drop(None)]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, stk![V::Unit]); } @@ -2759,12 +2771,14 @@ mod interpreter_tests { .collect() ) ]; - assert!(interpret_one( - &Iter(overloads::Iter::Set, vec![Cons]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::Set, vec![Cons]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!( stack, stk![V::List( @@ -2779,12 +2793,14 @@ mod interpreter_tests { #[test] fn test_iter_set_zero() { let mut stack = stk![V::int(0), V::Set(BTreeSet::new())]; - assert!(interpret_one( - &Iter(overloads::Iter::Set, vec![Add(overloads::Add::IntInt)]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::Set, vec![Add(overloads::Add::IntInt)]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, stk![V::int(0)]); } @@ -2802,12 +2818,14 @@ mod interpreter_tests { .collect() ) ]; - assert!(interpret_one( - &Iter(overloads::Iter::Map, vec![Cons]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::Map, vec![Cons]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!( stack, stk![V::List( @@ -2827,12 +2845,14 @@ mod interpreter_tests { #[test] fn test_iter_map_zero() { let mut stack = stk![V::int(0), V::Map(BTreeMap::new())]; - assert!(interpret_one( - &Iter(overloads::Iter::Map, vec![Car, Add(overloads::Add::IntInt)]), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Iter(overloads::Iter::Map, vec![Car, Add(overloads::Add::IntInt)]), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, stk![V::int(0)]); } @@ -2933,12 +2953,14 @@ mod interpreter_tests { mut input_stack: Stack>, expected_stack: Stack>, ) { - assert!(interpret_one( - &Map(overload, vec![Cons, Unit]), - &mut Ctx::default(), - &mut input_stack, - ) - .is_ok()); + assert!( + interpret_one( + &Map(overload, vec![Cons, Unit]), + &mut Ctx::default(), + &mut input_stack, + ) + .is_ok() + ); assert_eq!(input_stack, expected_stack); } @@ -2990,15 +3012,17 @@ mod interpreter_tests { fn test_map_can_modify_underlying_stack() { let mut stack = stk![V::Bool(true), V::List(vec![V::Unit].into())]; let expected_stack = stk![V::Bool(false), V::List(vec![V::Unit].into())]; - assert!(interpret_one( - &Map( - overloads::Map::List, - vec![Dip(None, vec![Not(overloads::Not::Bool)])] - ), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Map( + overloads::Map::List, + vec![Dip(None, vec![Not(overloads::Not::Bool)])] + ), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -3010,15 +3034,17 @@ mod interpreter_tests { // it runs on the stack from the previous iteration. let mut stack = stk![V::int(0), V::List(vec![V::int(1), V::int(2)].into())]; let expected_stack = stk![V::int(3), V::List(vec![V::Unit, V::Unit].into())]; - assert!(interpret_one( - &Map( - overloads::Map::List, - vec![Add(overloads::Add::IntInt), Unit] - ), - &mut Ctx::default(), - &mut stack, - ) - .is_ok()); + assert!( + interpret_one( + &Map( + overloads::Map::List, + vec![Add(overloads::Add::IntInt), Unit] + ), + &mut Ctx::default(), + &mut stack, + ) + .is_ok() + ); assert_eq!(stack, expected_stack); } @@ -3083,15 +3109,17 @@ mod interpreter_tests { fn push_pair() { let mut stack = stk![]; let mut ctx = Ctx::default(); - assert!(interpret( - &[Push(V::new_pair( - V::int(-5), - V::new_pair(V::nat(3), V::Bool(false)) - ))], - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret( + &[Push(V::new_pair( + V::int(-5), + V::new_pair(V::nat(3), V::Bool(false)) + ))], + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!( stack, stk![V::new_pair( @@ -3109,12 +3137,14 @@ mod interpreter_tests { fn push_option() { let mut stack = stk![]; let mut ctx = Ctx::default(); - assert!(interpret( - &[Push(V::new_option(Some(V::int(-5))))], - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret( + &[Push(V::new_option(Some(V::int(-5))))], + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!(stack, stk![V::new_option(Some(V::int(-5)))]); assert_eq!( ctx.gas.milligas(), @@ -3126,18 +3156,20 @@ mod interpreter_tests { fn car() { let mut stack = stk![]; let mut ctx = Ctx::default(); - assert!(interpret( - &[ - Push(V::new_pair( - V::int(-5), - V::new_pair(V::nat(3), V::Bool(false)) - )), - Car - ], - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret( + &[ + Push(V::new_pair( + V::int(-5), + V::new_pair(V::nat(3), V::Bool(false)) + )), + Car + ], + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!(stack, stk![V::int(-5)]); assert_eq!( ctx.gas.milligas(), @@ -3152,18 +3184,20 @@ mod interpreter_tests { fn cdr() { let mut stack = stk![]; let mut ctx = Ctx::default(); - assert!(interpret( - &[ - Push(V::new_pair( - V::new_pair(V::nat(3), V::Bool(false)), - V::int(-5), - )), - Cdr - ], - &mut ctx, - &mut stack - ) - .is_ok()); + assert!( + interpret( + &[ + Push(V::new_pair( + V::new_pair(V::nat(3), V::Bool(false)), + V::int(-5), + )), + Cdr + ], + &mut ctx, + &mut stack + ) + .is_ok() + ); assert_eq!(stack, stk![V::int(-5)]); assert_eq!( ctx.gas.milligas(), diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs index 732afe92c9b6..c44d31909d65 100644 --- a/etherlink/kernel_latest/tezos_execution/src/lib.rs +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -5,7 +5,7 @@ use account_storage::TezlinkAccount; use account_storage::{Manager, TezlinkImplicitAccount, TezlinkOriginatedAccount}; use mir::{ - ast::{annotations::FieldAnnotation, entrypoint, IntoMicheline, Micheline}, + ast::{IntoMicheline, Micheline}, context::Ctx, parser::Parser, }; @@ -134,7 +134,7 @@ pub fn transfer( src: &PublicKeyHash, amount: &Narith, dest: &Contract, - parameter: &Option, + parameter: Option, ) -> ExecutionResult { log!( host, @@ -300,7 +300,7 @@ fn apply_balance_changes( fn execute_smart_contract( host: &mut Host, destination: &mut TezlinkOriginatedAccount, - parameter: &Option, + parameter: Option, ) -> Result, ApplyKernelError> { let parser = Parser::new(); let code = destination.code(host)?; @@ -309,10 +309,10 @@ fn execute_smart_contract( let (entrypoint, value) = match parameter { Some(param) => ( - param.entrypoint.clone(), + Some(param.entrypoint), Micheline::decode_raw(&parser.arena, ¶m.value)?, ), - None => (entrypoint::Entrypoint::default(), Micheline::from(())), + None => (None, Micheline::from(())), }; let mut ctx = Ctx::default(); @@ -324,7 +324,7 @@ fn execute_smart_contract( &mut ctx, &parser.arena, value, - Some(FieldAnnotation::from_str_unchecked(entrypoint.as_str())), + entrypoint, storage, )?; @@ -389,7 +389,7 @@ pub fn apply_operation( parameters, }) => { let transfer_result = - transfer(host, context, source, &amount, &destination, ¶meters)?; + transfer(host, context, source, &amount, &destination, parameters)?; let manager_result = produce_operation_result(vec![src_delta, block_fees], transfer_result); OperationResultSum::Transfer(manager_result) -- GitLab