cairo_lang_sierra/extensions/modules/
coupon.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use super::function_call::SignatureAndFunctionConcreteLibfunc;
use crate::define_libfunc_hierarchy;
use crate::extensions::lib_func::{
    LibfuncSignature, OutputVarInfo, SierraApChange, SignatureSpecializationContext,
};
use crate::extensions::type_specialization_context::TypeSpecializationContext;
use crate::extensions::types::TypeInfo;
use crate::extensions::{
    ConcreteType, NamedLibfunc, NamedType, OutputVarReferenceInfo, SpecializationError,
    args_as_single_type, args_as_single_user_func,
};
use crate::ids::{ConcreteTypeId, FunctionId, GenericTypeId};
use crate::program::GenericArg;

/// Coupon type `Coupon<function>` (`function::Coupon`) which represents that the cost of a
/// function was paid, without calling the function yet.
///
/// Using the coupon the function can be called without paying the cost.
#[derive(Default)]
pub struct CouponType {}

impl NamedType for CouponType {
    type Concrete = CouponConcreteType;
    const ID: GenericTypeId = GenericTypeId::new_inline("Coupon");

    fn specialize(
        &self,
        _context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        let function_id = args_as_single_user_func(args)?;
        let long_id = Self::concrete_type_long_id(args);
        Ok(Self::Concrete {
            info: TypeInfo {
                long_id,
                duplicatable: false,
                droppable: true,
                storable: true,
                zero_sized: true,
            },
            function_id,
        })
    }
}

/// Returns the type `Coupon<func>`.
pub fn coupon_ty(
    context: &dyn SignatureSpecializationContext,
    function_id: FunctionId,
) -> Result<ConcreteTypeId, SpecializationError> {
    context.get_concrete_type(CouponType::id(), &[GenericArg::UserFunc(function_id)])
}

/// Concrete type information for `Coupon<function>`.
pub struct CouponConcreteType {
    pub info: TypeInfo,
    pub function_id: FunctionId,
}
impl ConcreteType for CouponConcreteType {
    fn info(&self) -> &TypeInfo {
        &self.info
    }
}

define_libfunc_hierarchy! {
    pub enum CouponLibfunc {
        Buy(CouponBuyLibfunc),
        Refund(CouponRefundLibfunc),
    }, CouponConcreteLibfunc
}

/// Libfunc for buying a coupon for a function.
///
/// The cost of the coupon is the cost of running the function (not including the `call` and `ret`
/// instructions). The coupon can be used to pay in advance for running the function, and run it
/// later for free (paying only for the `call` and `ret` instructions) using `coupon_call`.
#[derive(Default)]
pub struct CouponBuyLibfunc {}
impl NamedLibfunc for CouponBuyLibfunc {
    type Concrete = SignatureAndFunctionConcreteLibfunc;
    const STR_ID: &'static str = "coupon_buy";

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        let coupon_ty = args_as_single_type(args)?;
        if context.get_type_info(coupon_ty.clone())?.long_id.generic_id != CouponType::id() {
            return Err(SpecializationError::UnsupportedGenericArg);
        }

        Ok(LibfuncSignature::new_non_branch(
            vec![],
            vec![OutputVarInfo { ty: coupon_ty, ref_info: OutputVarReferenceInfo::ZeroSized }],
            SierraApChange::Known { new_vars_only: true },
        ))
    }

    fn specialize(
        &self,
        context: &dyn crate::extensions::lib_func::SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        let coupon_ty = args_as_single_type(args)?;
        let long_id = context.get_type_info(coupon_ty.clone())?.long_id;
        if long_id.generic_id != CouponType::id() {
            return Err(SpecializationError::UnsupportedGenericArg);
        }

        let function_id = args_as_single_user_func(&long_id.generic_args)?;

        Ok(SignatureAndFunctionConcreteLibfunc {
            function: context.get_function(&function_id)?,
            signature: self.specialize_signature(context.upcast(), args)?,
        })
    }
}

/// Libfunc for getting a refund for an unused coupon. The refund is the cost of the function
/// and it is added back to the gas wallet.
#[derive(Default)]
pub struct CouponRefundLibfunc {}
impl NamedLibfunc for CouponRefundLibfunc {
    type Concrete = SignatureAndFunctionConcreteLibfunc;
    const STR_ID: &'static str = "coupon_refund";

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        let coupon_ty = args_as_single_type(args)?;
        if context.get_type_info(coupon_ty.clone())?.long_id.generic_id != CouponType::id() {
            return Err(SpecializationError::UnsupportedGenericArg);
        }

        Ok(LibfuncSignature::new_non_branch(vec![coupon_ty], vec![], SierraApChange::Known {
            new_vars_only: true,
        }))
    }

    fn specialize(
        &self,
        context: &dyn crate::extensions::lib_func::SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        let coupon_ty = args_as_single_type(args)?;
        let long_id = context.get_type_info(coupon_ty.clone())?.long_id;
        if long_id.generic_id != CouponType::id() {
            return Err(SpecializationError::UnsupportedGenericArg);
        }

        let function_id = args_as_single_user_func(&long_id.generic_args)?;

        Ok(SignatureAndFunctionConcreteLibfunc {
            function: context.get_function(&function_id)?,
            signature: self.specialize_signature(context.upcast(), args)?,
        })
    }
}