cairo_lang_sierra/extensions/modules/
coupon.rs

1use super::function_call::SignatureAndFunctionConcreteLibfunc;
2use crate::define_libfunc_hierarchy;
3use crate::extensions::lib_func::{
4    LibfuncSignature, OutputVarInfo, SierraApChange, SignatureSpecializationContext,
5};
6use crate::extensions::type_specialization_context::TypeSpecializationContext;
7use crate::extensions::types::TypeInfo;
8use crate::extensions::{
9    ConcreteType, NamedLibfunc, NamedType, OutputVarReferenceInfo, SpecializationError,
10    args_as_single_type, args_as_single_user_func,
11};
12use crate::ids::{ConcreteTypeId, FunctionId, GenericTypeId};
13use crate::program::GenericArg;
14
15/// Coupon type `Coupon<function>` (`function::Coupon`) which represents that the cost of a
16/// function was paid, without calling the function yet.
17///
18/// Using the coupon the function can be called without paying the cost.
19#[derive(Default)]
20pub struct CouponType {}
21
22impl NamedType for CouponType {
23    type Concrete = CouponConcreteType;
24    const ID: GenericTypeId = GenericTypeId::new_inline("Coupon");
25
26    fn specialize(
27        &self,
28        _context: &dyn TypeSpecializationContext,
29        args: &[GenericArg],
30    ) -> Result<Self::Concrete, SpecializationError> {
31        let function_id = args_as_single_user_func(args)?;
32        let long_id = Self::concrete_type_long_id(args);
33        Ok(Self::Concrete {
34            info: TypeInfo {
35                long_id,
36                duplicatable: false,
37                droppable: true,
38                storable: true,
39                zero_sized: true,
40            },
41            function_id,
42        })
43    }
44}
45
46/// Returns the type `Coupon<func>`.
47pub fn coupon_ty(
48    context: &dyn SignatureSpecializationContext,
49    function_id: FunctionId,
50) -> Result<ConcreteTypeId, SpecializationError> {
51    context.get_concrete_type(CouponType::id(), &[GenericArg::UserFunc(function_id)])
52}
53
54/// Concrete type information for `Coupon<function>`.
55pub struct CouponConcreteType {
56    pub info: TypeInfo,
57    pub function_id: FunctionId,
58}
59impl ConcreteType for CouponConcreteType {
60    fn info(&self) -> &TypeInfo {
61        &self.info
62    }
63}
64
65define_libfunc_hierarchy! {
66    pub enum CouponLibfunc {
67        Buy(CouponBuyLibfunc),
68        Refund(CouponRefundLibfunc),
69    }, CouponConcreteLibfunc
70}
71
72/// Libfunc for buying a coupon for a function.
73///
74/// The cost of the coupon is the cost of running the function (not including the `call` and `ret`
75/// instructions). The coupon can be used to pay in advance for running the function, and run it
76/// later for free (paying only for the `call` and `ret` instructions) using `coupon_call`.
77#[derive(Default)]
78pub struct CouponBuyLibfunc {}
79impl NamedLibfunc for CouponBuyLibfunc {
80    type Concrete = SignatureAndFunctionConcreteLibfunc;
81    const STR_ID: &'static str = "coupon_buy";
82
83    fn specialize_signature(
84        &self,
85        context: &dyn SignatureSpecializationContext,
86        args: &[GenericArg],
87    ) -> Result<LibfuncSignature, SpecializationError> {
88        let coupon_ty = args_as_single_type(args)?;
89        if context.get_type_info(coupon_ty.clone())?.long_id.generic_id != CouponType::id() {
90            return Err(SpecializationError::UnsupportedGenericArg);
91        }
92
93        Ok(LibfuncSignature::new_non_branch(
94            vec![],
95            vec![OutputVarInfo { ty: coupon_ty, ref_info: OutputVarReferenceInfo::ZeroSized }],
96            SierraApChange::Known { new_vars_only: true },
97        ))
98    }
99
100    fn specialize(
101        &self,
102        context: &dyn crate::extensions::lib_func::SpecializationContext,
103        args: &[GenericArg],
104    ) -> Result<Self::Concrete, SpecializationError> {
105        let coupon_ty = args_as_single_type(args)?;
106        let long_id = context.get_type_info(coupon_ty.clone())?.long_id;
107        if long_id.generic_id != CouponType::id() {
108            return Err(SpecializationError::UnsupportedGenericArg);
109        }
110
111        let function_id = args_as_single_user_func(&long_id.generic_args)?;
112
113        Ok(SignatureAndFunctionConcreteLibfunc {
114            function: context.get_function(&function_id)?,
115            signature: self.specialize_signature(context.upcast(), args)?,
116        })
117    }
118}
119
120/// Libfunc for getting a refund for an unused coupon. The refund is the cost of the function
121/// and it is added back to the gas wallet.
122#[derive(Default)]
123pub struct CouponRefundLibfunc {}
124impl NamedLibfunc for CouponRefundLibfunc {
125    type Concrete = SignatureAndFunctionConcreteLibfunc;
126    const STR_ID: &'static str = "coupon_refund";
127
128    fn specialize_signature(
129        &self,
130        context: &dyn SignatureSpecializationContext,
131        args: &[GenericArg],
132    ) -> Result<LibfuncSignature, SpecializationError> {
133        let coupon_ty = args_as_single_type(args)?;
134        if context.get_type_info(coupon_ty.clone())?.long_id.generic_id != CouponType::id() {
135            return Err(SpecializationError::UnsupportedGenericArg);
136        }
137
138        Ok(LibfuncSignature::new_non_branch(vec![coupon_ty], vec![], SierraApChange::Known {
139            new_vars_only: true,
140        }))
141    }
142
143    fn specialize(
144        &self,
145        context: &dyn crate::extensions::lib_func::SpecializationContext,
146        args: &[GenericArg],
147    ) -> Result<Self::Concrete, SpecializationError> {
148        let coupon_ty = args_as_single_type(args)?;
149        let long_id = context.get_type_info(coupon_ty.clone())?.long_id;
150        if long_id.generic_id != CouponType::id() {
151            return Err(SpecializationError::UnsupportedGenericArg);
152        }
153
154        let function_id = args_as_single_user_func(&long_id.generic_args)?;
155
156        Ok(SignatureAndFunctionConcreteLibfunc {
157            function: context.get_function(&function_id)?,
158            signature: self.specialize_signature(context.upcast(), args)?,
159        })
160    }
161}